from datetime import timedelta import logging import secrets from urllib.parse import urlsplit from odoo import api, fields, models from odoo.http import request _logger = logging.getLogger(__name__) class IotBox(models.Model): _name = 'iot.box' _description = 'IoT Box' name = fields.Char('Name', required=True) identifier = fields.Char(string='Identifier', readonly=True) device_ids = fields.One2many('iot.device', 'iot_id', string="Devices") device_count = fields.Integer(compute='_compute_device_count') ip = fields.Char('Domain Address', readonly=True) drivers_auto_update = fields.Boolean('Automatic drivers update', help='Automatically update drivers when the IoT Box boots', default=True) version = fields.Char('Image Version', readonly=True) version_commit_url = fields.Html(readonly=True, compute='_compute_commit_url') company_id = fields.Many2one('res.company', 'Company') ssl_certificate_end_date = fields.Datetime('SSL Certificate End Date', readonly=True) must_install_fdm_module = fields.Boolean( "A fiscal data module is connected to this IoT Box", readonly=True, compute="_compute_must_install_fdm_module" ) def _default_token(self): """Generate a token used in the iot box "token" field or by the wizards used to connect a new IoT Box. Also clears the token from the configuration parameters if used to assign the "token" field to make the token unique per IoT Box. :return: The token generated by the wizard. """ icp_sudo = self.env['ir.config_parameter'].sudo() iot_token_param = icp_sudo.search([('key', '=', 'iot.iot_token')], limit=1) iot_token_value = iot_token_param.value if iot_token_param else None if ( not iot_token_param or not iot_token_value or iot_token_param.write_date + timedelta(minutes=15) < fields.Datetime.now() ): _logger.info("No valid token found in the configuration parameters, generating a new one.") iot_token_value = secrets.token_hex(16) icp_sudo.set_param('iot.iot_token', iot_token_value) return iot_token_value token = fields.Char(default=lambda self: self._default_token(), readonly=True) def _compute_device_count(self): for box in self: box.device_count = len(box.device_ids) @api.ondelete(at_uninstall=True) def _unlink_iot_box(self): self.env['iot.channel'].send_message({ "iot_identifiers": self.mapped('identifier') }, 'server_clear') def open_homepage(self): self.ensure_one() scheme = urlsplit(request.httprequest.referrer).scheme return { 'type': 'ir.actions.act_url', 'url': f'{scheme}://{self.ip}' if scheme == 'https' else f'{scheme}://{self.ip}:8069', 'target': 'new', } @api.model def connect_iot_box(self, local_iot_boxes): """ This method is called when pressing the "Connect" button in the IoT app. Used to connect a new IoT Box to a database. :return: action to open the wizard view depending on the result of the iot-proxy request sent by the wizard """ wizard = self.env['add.iot.box'].create({}) self.env['iot.discovered.box'].create([ { "pairing_code": box["pairing_code"], "serial_number": box.get("serial_number"), "add_iot_box_wizard_id": wizard.id, } for box in local_iot_boxes ]) return wizard.add_iot_box_wizard_action() @api.depends('device_ids') def _compute_must_install_fdm_module(self): is_module_installed = ( self.env['ir.module.module'].sudo().search([('name', '=', 'pos_blackbox_be')], limit=1).state == 'installed' ) for box in self: box.must_install_fdm_module = ( self.env.company.country_id.code == "BE" and not is_module_installed and any(device.type == 'fiscal_data_module' for device in box.device_ids) ) def install_fdm_module(self): """Install the pos_blackbox_be module if it is not installed and a fiscal data module is connected to the IoT Box.""" if not self.must_install_fdm_module: return module = self.env['ir.module.module'].sudo().search([('name', '=', 'pos_blackbox_be')], limit=1) if module and module.state != 'installed': module.button_immediate_install() _logger.info("pos_blackbox_be module installed successfully.") return { 'type': 'ir.actions.client', 'tag': 'reload', } _logger.warning("pos_blackbox_be module is already installed or not found.") return None @api.depends('version') def _compute_commit_url(self): base_url = "https://www.github.com/odoo/odoo/commit/" for box in self: if box.version and "#" in box.version: image_version, commit_hash = box.version.split("#", 1) box.version_commit_url = ( f'{image_version}#{commit_hash}' ) else: box.version_commit_url = f'{box.version}' if box.version else False