From e29c2a814f05375910b3b18718f85c495bda4786 Mon Sep 17 00:00:00 2001 From: Enric Tobella Date: Mon, 18 Mar 2019 10:45:28 +0100 Subject: [PATCH 01/37] [ADD] iot_template --- iot_template_oca/README.rst | 90 ++++ iot_template_oca/__init__.py | 3 + iot_template_oca/__manifest__.py | 20 + iot_template_oca/controller/__init__.py | 1 + iot_template_oca/controller/iot_controller.py | 18 + iot_template_oca/models/__init__.py | 4 + iot_template_oca/models/iot_device.py | 25 + iot_template_oca/models/iot_device_input.py | 19 + iot_template_oca/models/iot_device_output.py | 13 + iot_template_oca/models/iot_template.py | 124 +++++ iot_template_oca/readme/CONTRIBUTORS.rst | 1 + iot_template_oca/readme/DESCRIPTION.rst | 7 + iot_template_oca/readme/USAGE.rst | 7 + iot_template_oca/security/ir.model.access.csv | 9 + iot_template_oca/static/description/icon.png | Bin 0 -> 4151 bytes .../static/description/index.html | 436 ++++++++++++++++++ iot_template_oca/tests/__init__.py | 1 + iot_template_oca/tests/test_iot_template.py | 75 +++ iot_template_oca/views/iot_template_views.xml | 56 +++ iot_template_oca/wizards/__init__.py | 1 + .../wizards/iot_device_configure.py | 51 ++ .../wizards/iot_device_configure.xml | 45 ++ 22 files changed, 1006 insertions(+) create mode 100644 iot_template_oca/README.rst create mode 100644 iot_template_oca/__init__.py create mode 100644 iot_template_oca/__manifest__.py create mode 100644 iot_template_oca/controller/__init__.py create mode 100644 iot_template_oca/controller/iot_controller.py create mode 100644 iot_template_oca/models/__init__.py create mode 100644 iot_template_oca/models/iot_device.py create mode 100644 iot_template_oca/models/iot_device_input.py create mode 100644 iot_template_oca/models/iot_device_output.py create mode 100644 iot_template_oca/models/iot_template.py create mode 100644 iot_template_oca/readme/CONTRIBUTORS.rst create mode 100644 iot_template_oca/readme/DESCRIPTION.rst create mode 100644 iot_template_oca/readme/USAGE.rst create mode 100644 iot_template_oca/security/ir.model.access.csv create mode 100644 iot_template_oca/static/description/icon.png create mode 100644 iot_template_oca/static/description/index.html create mode 100644 iot_template_oca/tests/__init__.py create mode 100644 iot_template_oca/tests/test_iot_template.py create mode 100644 iot_template_oca/views/iot_template_views.xml create mode 100644 iot_template_oca/wizards/__init__.py create mode 100644 iot_template_oca/wizards/iot_device_configure.py create mode 100644 iot_template_oca/wizards/iot_device_configure.xml diff --git a/iot_template_oca/README.rst b/iot_template_oca/README.rst new file mode 100644 index 00000000..03eef927 --- /dev/null +++ b/iot_template_oca/README.rst @@ -0,0 +1,90 @@ +============= +IoT Templates +============= + +.. !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + !! This file is generated by oca-gen-addon-readme !! + !! changes will be overwritten. !! + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + +.. |badge1| image:: https://img.shields.io/badge/maturity-Beta-yellow.png + :target: https://odoo-community.org/page/development-status + :alt: Beta +.. |badge2| image:: https://img.shields.io/badge/licence-AGPL--3-blue.png + :target: http://www.gnu.org/licenses/agpl-3.0-standalone.html + :alt: License: AGPL-3 +.. |badge3| image:: https://img.shields.io/badge/github-OCA%2Fiot-lightgray.png?logo=github + :target: https://github.com/OCA/iot/tree/12.0/iot_template + :alt: OCA/iot +.. |badge4| image:: https://img.shields.io/badge/weblate-Translate%20me-F47D42.png + :target: https://translation.odoo-community.org/projects/iot-12-0/iot-12-0-iot_template + :alt: Translate me on Weblate +.. |badge5| image:: https://img.shields.io/badge/runbot-Try%20me-875A7B.png + :target: https://runbot.odoo-community.org/runbot/269/12.0 + :alt: Try me on Runbot + +|badge1| |badge2| |badge3| |badge4| |badge5| + +Create a system of templates for IoT devices. + +When we are using a template we can configure a device +with a simple URL configuration. +The device will contact odoo and send the template name. +Odoo will create the device and respond with all the +expected data. + +**Table of contents** + +.. contents:: + :local: + +Usage +===== + +1. Create a template with a module or in `Iot > Templates` +2. Access `Iot > Config Device` +3. Confirm that you want to configurate a new device +4. Copy the URL +5. Access the device and send it the configuration url +6. The device will contact odoo and automatically configure itself. + + +Bug Tracker +=========== + +Bugs are tracked on `GitHub Issues `_. +In case of trouble, please check there if your issue has already been reported. +If you spotted it first, help us smashing it by providing a detailed and welcomed +`feedback `_. + +Do not contact contributors directly about support or help with technical issues. + +Credits +======= + +Authors +~~~~~~~ + +* Creu Blanca + +Contributors +~~~~~~~~~~~~ + +* Enric Tobella + +Maintainers +~~~~~~~~~~~ + +This module is maintained by the OCA. + +.. image:: https://odoo-community.org/logo.png + :alt: Odoo Community Association + :target: https://odoo-community.org + +OCA, or the Odoo Community Association, is a nonprofit organization whose +mission is to support the collaborative development of Odoo features and +promote its widespread use. + +This module is part of the `OCA/iot `_ project on GitHub. + +You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute. diff --git a/iot_template_oca/__init__.py b/iot_template_oca/__init__.py new file mode 100644 index 00000000..fa0ba4bf --- /dev/null +++ b/iot_template_oca/__init__.py @@ -0,0 +1,3 @@ +from . import controller +from . import models +from . import wizards diff --git a/iot_template_oca/__manifest__.py b/iot_template_oca/__manifest__.py new file mode 100644 index 00000000..67c45f9a --- /dev/null +++ b/iot_template_oca/__manifest__.py @@ -0,0 +1,20 @@ +# Copyright (C) 2018 Creu Blanca +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). +{ + 'name': 'IoT Templates', + 'version': '12.0.1.0.0', + 'category': 'IoT', + 'author': "Creu Blanca, " + "Odoo Community Association (OCA)", + 'license': 'AGPL-3', + 'installable': True, + 'summary': 'IoT base module', + 'depends': [ + 'iot_input', + ], + 'data': [ + 'security/ir.model.access.csv', + 'wizards/iot_device_configure.xml', + 'views/iot_template_views.xml', + ], +} diff --git a/iot_template_oca/controller/__init__.py b/iot_template_oca/controller/__init__.py new file mode 100644 index 00000000..96960bb9 --- /dev/null +++ b/iot_template_oca/controller/__init__.py @@ -0,0 +1 @@ +from . import iot_controller diff --git a/iot_template_oca/controller/iot_controller.py b/iot_template_oca/controller/iot_controller.py new file mode 100644 index 00000000..379309a9 --- /dev/null +++ b/iot_template_oca/controller/iot_controller.py @@ -0,0 +1,18 @@ +# Copyright 2020 Creu Blanca +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +import json +from odoo import http + + +class CallIot(http.Controller): + @http.route([ + '/iot//configure', + ], type='http', auth="none", methods=['POST'], csrf=False) + def configure_iot(self, serial, *args, **kwargs): + request = http.request + template = kwargs.get('template', False) + if not request.env: + return json.dumps(False) + return json.dumps(request.env['iot.device.configure'].sudo().configure( + serial, template)) diff --git a/iot_template_oca/models/__init__.py b/iot_template_oca/models/__init__.py new file mode 100644 index 00000000..947d0d6d --- /dev/null +++ b/iot_template_oca/models/__init__.py @@ -0,0 +1,4 @@ +from . import iot_device +from . import iot_device_input +from . import iot_device_output +from . import iot_template diff --git a/iot_template_oca/models/iot_device.py b/iot_template_oca/models/iot_device.py new file mode 100644 index 00000000..6bec32e8 --- /dev/null +++ b/iot_template_oca/models/iot_device.py @@ -0,0 +1,25 @@ +# Copyright 2020 Creu Blanca +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). +from odoo import api, models + + +class IotDevice(models.Model): + _inherit = 'iot.device' + + @api.multi + def get_iot_configuration(self): + self.ensure_one() + return { + 'host': self.env['ir.config_parameter'].sudo().get_param( + 'web.base.url' + ), + 'name': self.name, + 'outputs': { + output.name: output.get_configuration() + for output in self.output_ids + }, + 'inputs': { + iot_input.name: iot_input.get_configuration() + for iot_input in self.input_ids + } + } diff --git a/iot_template_oca/models/iot_device_input.py b/iot_template_oca/models/iot_device_input.py new file mode 100644 index 00000000..72829615 --- /dev/null +++ b/iot_template_oca/models/iot_device_input.py @@ -0,0 +1,19 @@ +# Copyright 2020 Creu Blanca +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). +from odoo import api, fields, models + + +class IotDeviceInput(models.Model): + _inherit = 'iot.device.input' + + template_input_id = fields.Many2one( + 'iot.template.input', + readonly=True, + ) + + @api.multi + def get_configuration(self): + return { + 'serial': self.serial, + 'passphrase': self.passphrase, + } diff --git a/iot_template_oca/models/iot_device_output.py b/iot_template_oca/models/iot_device_output.py new file mode 100644 index 00000000..c7a56a23 --- /dev/null +++ b/iot_template_oca/models/iot_device_output.py @@ -0,0 +1,13 @@ +# Copyright 2020 Creu Blanca +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). +from odoo import api, models + + +class IotDeviceOutput(models.Model): + _inherit = 'iot.device.output' + + @api.multi + def get_configuration(self): + return { + + } diff --git a/iot_template_oca/models/iot_template.py b/iot_template_oca/models/iot_template.py new file mode 100644 index 00000000..e0b539ad --- /dev/null +++ b/iot_template_oca/models/iot_template.py @@ -0,0 +1,124 @@ +# Copyright 2020 Creu Blanca +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). +from odoo import api, exceptions, fields, models, _ +from odoo.tools.safe_eval import safe_eval +from jinja2.sandbox import SandboxedEnvironment +from uuid import uuid4 + +mako_template_env = SandboxedEnvironment( + block_start_string="<%", + block_end_string="%>", + variable_start_string="${", + variable_end_string="}", + comment_start_string="<%doc>", + comment_end_string="", + line_statement_prefix="%", + line_comment_prefix="##", + trim_blocks=True, # do not output newline after blocks + autoescape=True, # XML/HTML automatic escaping +) + + +class IotTemplate(models.Model): + _name = 'iot.template' + _description = 'IoT Template for Device' + _parent_name = 'parent_id' + _parent_store = True + _parent_order = 'name' + + parent_path = fields.Char(index=True) + name = fields.Char(required=True) + input_ids = fields.One2many( + 'iot.template.input', inverse_name='template_id', + ) + output_ids = fields.One2many( + 'iot.template.output', inverse_name='template_id', + ) + key_ids = fields.One2many('iot.template.key', inverse_name='template_id') + parent_id = fields.Many2one('iot.template', ondelete='restrict') + + @api.multi + def _get_keys(self, serial): + if self.parent_id: + keys = self.parent_id._get_keys(serial) + else: + keys = {'serial': serial} + keys.update({ + key.key: key._generate_value() for key in self.key_ids + }) + return keys + + @api.multi + @api.constrains('parent_id') + def _check_recursion_parent_id(self): + if not self._check_recursion(): + raise exceptions.ValidationError( + _('Error! You are attempting to create a recursive template.')) + + @api.multi + def apply_template(self, device, keys): + self.ensure_one() + for element in self.input_ids: + element._apply_template(device, keys) + for element in self.output_ids: + element._apply_template(device, keys) + if self.parent_id: + self.parent_id.apply_template(device, keys) + + +class IotTemplateInput(models.Model): + _name = 'iot.template.input' + _description = 'IoT Input for Template' + + template_id = fields.Many2one('iot.template', required=True) + name = fields.Char(required=True) + params = fields.Text() + call_model_id = fields.Many2one('ir.model') + call_function = fields.Char(required=True) + + def _apply_template(self, device, keys): + real_vals = { + 'device_id': device.id, + 'name': self.name, + 'call_function': self.call_function, + 'call_model_id': self.call_model_id.id, + 'template_input_id': self.id, + } + vals = safe_eval(self.params) + for key in vals: + vals[key] = mako_template_env.from_string(vals[key]).render(keys) + real_vals.update(vals) + return self.env['iot.device.input'].create(real_vals) + + +class IotTemplateOutput(models.Model): + _name = 'iot.template.output' + _description = 'Output templates for IoT' + + template_id = fields.Many2one('iot.template', required=True) + name = fields.Char(required=True) + system_id = fields.Many2one('iot.system', required=True) + params = fields.Text() + + def _apply_template(self, device, keys): + real_vals = { + 'device_id': device.id, + 'name': self.name, + 'system_id': self.system_id.id, + } + vals = safe_eval(self.params) + for key in vals: + vals[key] = mako_template_env.from_string(vals[key]).render(keys) + real_vals.update(vals) + return self.env['iot.device.output'].create(real_vals) + + +class IotTemplateKey(models.Model): + _name = 'iot.template.key' + _description = 'IoT Keys for configuration' + + template_id = fields.Many2one('iot.template', required=True) + key = fields.Char(required=True) + + def _generate_value(self): + return uuid4() diff --git a/iot_template_oca/readme/CONTRIBUTORS.rst b/iot_template_oca/readme/CONTRIBUTORS.rst new file mode 100644 index 00000000..93ec993e --- /dev/null +++ b/iot_template_oca/readme/CONTRIBUTORS.rst @@ -0,0 +1 @@ +* Enric Tobella diff --git a/iot_template_oca/readme/DESCRIPTION.rst b/iot_template_oca/readme/DESCRIPTION.rst new file mode 100644 index 00000000..6f0e14e8 --- /dev/null +++ b/iot_template_oca/readme/DESCRIPTION.rst @@ -0,0 +1,7 @@ +Create a system of templates for IoT devices. + +When we are using a template we can configure a device +with a simple URL configuration. +The device will contact odoo and send the template name. +Odoo will create the device and respond with all the +expected data. diff --git a/iot_template_oca/readme/USAGE.rst b/iot_template_oca/readme/USAGE.rst new file mode 100644 index 00000000..72a81dde --- /dev/null +++ b/iot_template_oca/readme/USAGE.rst @@ -0,0 +1,7 @@ +1. Create a template with a module or in `Iot > Templates` +2. Access `Iot > Config Device` +3. Confirm that you want to configurate a new device +4. Copy the URL +5. Access the device and send it the configuration url +6. The device will contact odoo and automatically configure itself. + diff --git a/iot_template_oca/security/ir.model.access.csv b/iot_template_oca/security/ir.model.access.csv new file mode 100644 index 00000000..9c50b1dc --- /dev/null +++ b/iot_template_oca/security/ir.model.access.csv @@ -0,0 +1,9 @@ +id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink +access_iot_template,access_iot_template,model_iot_template,iot.group_iot_user,1,0,0,0 +manage_iot_template,access_iot_template,model_iot_template,iot.group_iot_manager,1,1,1,1 +access_iot_template_input,access_iot_template_input,model_iot_template_input,iot.group_iot_user,1,0,0,0 +manage_iot_template_input,access_iot_template_input,model_iot_template_input,iot.group_iot_manager,1,1,1,1 +access_iot_template_output,access_iot_template_output,model_iot_template_output,iot.group_iot_user,1,0,0,0 +manage_iot_template_output,access_iot_template_output,model_iot_template_output,iot.group_iot_manager,1,1,1,1 +access_iot_template_key,access_iot_template_key,model_iot_template_key,iot.group_iot_user,1,0,0,0 +manage_iot_template_key,access_iot_template_key,model_iot_template_key,iot.group_iot_manager,1,1,1,1 diff --git a/iot_template_oca/static/description/icon.png b/iot_template_oca/static/description/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..da43f6f07766d54a65259a14a0688ace6b65ccf0 GIT binary patch literal 4151 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 literal 0 HcmV?d00001 diff --git a/iot_template_oca/static/description/index.html b/iot_template_oca/static/description/index.html new file mode 100644 index 00000000..bdf0031a --- /dev/null +++ b/iot_template_oca/static/description/index.html @@ -0,0 +1,436 @@ + + + + + + +IoT Templates + + + +
+

IoT Templates

+ + +

Beta License: AGPL-3 OCA/iot Translate me on Weblate Try me on Runbot

+

Create a system of templates for IoT devices.

+

When we are using a template we can configure a device +with a simple URL configuration. +The device will contact odoo and send the template name. +Odoo will create the device and respond with all the +expected data.

+

Table of contents

+ +
+

Usage

+
    +
  1. Create a template with a module or in Iot > Templates
  2. +
  3. Access Iot > Config Device
  4. +
  5. Confirm that you want to configurate a new device
  6. +
  7. Copy the URL
  8. +
  9. Access the device and send it the configuration url
  10. +
  11. The device will contact odoo and automatically configure itself.
  12. +
+
+
+

Bug Tracker

+

Bugs are tracked on GitHub Issues. +In case of trouble, please check there if your issue has already been reported. +If you spotted it first, help us smashing it by providing a detailed and welcomed +feedback.

+

Do not contact contributors directly about support or help with technical issues.

+
+
+

Credits

+
+

Authors

+
    +
  • Creu Blanca
  • +
+
+
+

Contributors

+ +
+
+

Maintainers

+

This module is maintained by the OCA.

+Odoo Community Association +

OCA, or the Odoo Community Association, is a nonprofit organization whose +mission is to support the collaborative development of Odoo features and +promote its widespread use.

+

This module is part of the OCA/iot project on GitHub.

+

You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute.

+
+
+
+ + diff --git a/iot_template_oca/tests/__init__.py b/iot_template_oca/tests/__init__.py new file mode 100644 index 00000000..c6ac672f --- /dev/null +++ b/iot_template_oca/tests/__init__.py @@ -0,0 +1 @@ +from . import test_iot_template diff --git a/iot_template_oca/tests/test_iot_template.py b/iot_template_oca/tests/test_iot_template.py new file mode 100644 index 00000000..b048100d --- /dev/null +++ b/iot_template_oca/tests/test_iot_template.py @@ -0,0 +1,75 @@ +# Copyright 2020 Creu Blanca +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from odoo.tests.common import TransactionCase + + +class TestIotTemplate(TransactionCase): + + def setUp(self): + super(TestIotTemplate, self).setUp() + self.parent_template = self.env['iot.template'].create({ + 'name': 'Parent template', + 'key_ids': [ + (0, 0, { + 'key': 'passphrase', + }) + ], + 'input_ids': [ + (0, 0, { + 'name': 'INPUT 1', + 'call_model_id': False, + 'call_function': 'iot_ras_default_action', + 'params': "{'serial': '${serial}', " + "'passphrase': '${passphrase}'}" + }) + ], + + }) + self.template = self.env['iot.template'].create({ + 'name': 'template', + 'parent_id': self.parent_template.id, + 'key_ids': [ + (0, 0, { + 'key': 'serial2', + }), + ], + 'input_ids': [ + (0, 0, { + 'name': 'INPUT 2', + 'call_model_id': False, + 'call_function': 'iot_ras_default_action', + 'params': "{'serial': '${serial2}', " + "'passphrase': '${passphrase}'}" + }) + ], + }) + + def test_generation(self): + wizard = self.env['iot.device.configure'].create({}) + self.assertFalse(wizard.serial) + wizard.run() + self.assertTrue(wizard.serial) + device_config = self.env['iot.device.configure'].configure( + wizard.serial, self.template.name + ) + device = self.env['iot.device'].search([ + ('name', '=', device_config['name'])]) + self.assertTrue(device) + self.assertEqual(1, len(device)) + self.assertEqual(2, len(device.input_ids)) + input1 = device.input_ids.filtered( + lambda r: r.template_input_id == self.parent_template.input_ids + ) + self.assertTrue(input1) + input2 = device.input_ids.filtered( + lambda r: r.template_input_id == self.template.input_ids + ) + self.assertTrue(input2) + self.assertNotEqual(input1, input2) + self.assertGreater(len(input1.passphrase), 0) + self.assertGreater(len(input2.passphrase), 0) + self.assertEqual(input1.passphrase, input2.passphrase) + self.assertGreater(len(input1.serial), 0) + self.assertGreater(len(input2.serial), 0) + self.assertNotEqual(input1.serial, input2.serial) diff --git a/iot_template_oca/views/iot_template_views.xml b/iot_template_oca/views/iot_template_views.xml new file mode 100644 index 00000000..ef634538 --- /dev/null +++ b/iot_template_oca/views/iot_template_views.xml @@ -0,0 +1,56 @@ + + + + + iot.template.form + iot.template + +
+
+ +
+
+

+ +

+
+ + + + + + + + + + + + + + + + + iot.template.form + iot.template + + + + + + + + IoT Templates + ir.actions.act_window + iot.template + form + tree,form + + + + diff --git a/iot_template_oca/wizards/__init__.py b/iot_template_oca/wizards/__init__.py new file mode 100644 index 00000000..e77c356e --- /dev/null +++ b/iot_template_oca/wizards/__init__.py @@ -0,0 +1 @@ +from . import iot_device_configure diff --git a/iot_template_oca/wizards/iot_device_configure.py b/iot_template_oca/wizards/iot_device_configure.py new file mode 100644 index 00000000..b9ae57b9 --- /dev/null +++ b/iot_template_oca/wizards/iot_device_configure.py @@ -0,0 +1,51 @@ +# Copyright 2020 Creu Blanca +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). +from odoo import api, fields, models, _ +from uuid import uuid4 + + +class IotDeviceConfigure(models.TransientModel): + _name = 'iot.device.configure' + _description = "Configure a IoT device" + + generated = fields.Boolean(default=False) + serial = fields.Char(readonly=True) + url = fields.Char(compute='_compute_url') + + @api.depends('serial') + def _compute_url(self): + for record in self: + if record.generated: + record.url = self.env['ir.config_parameter'].sudo().get_param( + 'web.base.url' + ) + '/iot/' + record.serial + '/configure' + + @api.multi + def run(self): + if not self.generated: + self.write({'generated': True, 'serial': uuid4()}) + return { + 'name': _('Configure device'), + 'type': 'ir.actions.act_window', + 'res_model': 'iot.device.configure', + 'view_mode': 'form', + 'target': 'new', + 'res_id': self.id, + 'context': self.env.context, + } + + @api.model + def configure(self, serial, template_id): + config = self.search([('serial', '=', serial)]) + if not config: + return {} + config.unlink() + device = self.env['iot.device'].create({ + 'name': serial + }) + template = self.env['iot.template'].search([ + ('name', '=', template_id) + ]) + if template: + template.apply_template(device, template._get_keys(serial)) + return device.get_iot_configuration() diff --git a/iot_template_oca/wizards/iot_device_configure.xml b/iot_template_oca/wizards/iot_device_configure.xml new file mode 100644 index 00000000..4caa1584 --- /dev/null +++ b/iot_template_oca/wizards/iot_device_configure.xml @@ -0,0 +1,45 @@ + + + + + + + + iot.device.configure + iot.device.configure + form + +
+ + + + +
+
+
+
+
+ + + + + +
From 6bfdfe7578d4828fed03b592ad6b16be35d67140 Mon Sep 17 00:00:00 2001 From: Enric Tobella Date: Wed, 30 Dec 2020 15:13:18 +0100 Subject: [PATCH 02/37] [IMP] iot_template: Add a demo example --- iot_template_oca/__manifest__.py | 4 ++++ iot_template_oca/demo/iot_template.xml | 18 ++++++++++++++++++ iot_template_oca/models/iot_template.py | 7 +++++++ 3 files changed, 29 insertions(+) create mode 100644 iot_template_oca/demo/iot_template.xml diff --git a/iot_template_oca/__manifest__.py b/iot_template_oca/__manifest__.py index 67c45f9a..167be6b4 100644 --- a/iot_template_oca/__manifest__.py +++ b/iot_template_oca/__manifest__.py @@ -17,4 +17,8 @@ 'wizards/iot_device_configure.xml', 'views/iot_template_views.xml', ], + 'demo': [ + 'demo/iot_template.xml', + ] + } diff --git a/iot_template_oca/demo/iot_template.xml b/iot_template_oca/demo/iot_template.xml new file mode 100644 index 00000000..2548746d --- /dev/null +++ b/iot_template_oca/demo/iot_template.xml @@ -0,0 +1,18 @@ + + + + demo_template + + + + serial + + + + demo_input + + test_input_device + {} + + + diff --git a/iot_template_oca/models/iot_template.py b/iot_template_oca/models/iot_template.py index e0b539ad..70101f91 100644 --- a/iot_template_oca/models/iot_template.py +++ b/iot_template_oca/models/iot_template.py @@ -2,6 +2,7 @@ # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). from odoo import api, exceptions, fields, models, _ from odoo.tools.safe_eval import safe_eval +from uuid import uuid4 from jinja2.sandbox import SandboxedEnvironment from uuid import uuid4 @@ -83,11 +84,15 @@ def _apply_template(self, device, keys): 'call_function': self.call_function, 'call_model_id': self.call_model_id.id, 'template_input_id': self.id, + 'serial': uuid4(), + 'passphrase': uuid4(), } vals = safe_eval(self.params) for key in vals: vals[key] = mako_template_env.from_string(vals[key]).render(keys) real_vals.update(vals) + print(real_vals) + print(vals) return self.env['iot.device.input'].create(real_vals) @@ -105,6 +110,8 @@ def _apply_template(self, device, keys): 'device_id': device.id, 'name': self.name, 'system_id': self.system_id.id, + 'key_serial': uuid4(), + 'passphrase': uuid4(), } vals = safe_eval(self.params) for key in vals: From e6b6da2d632e947ffbce34b276d9844d83871263 Mon Sep 17 00:00:00 2001 From: Olga Marco Date: Thu, 10 Jun 2021 09:44:45 +0200 Subject: [PATCH 03/37] [IMP] iot_template_oca: black, isort, prettier --- iot_template_oca/__manifest__.py | 32 +++---- iot_template_oca/controller/iot_controller.py | 18 ++-- iot_template_oca/demo/iot_template.xml | 7 +- iot_template_oca/models/iot_device.py | 17 ++-- iot_template_oca/models/iot_device_input.py | 11 +-- iot_template_oca/models/iot_device_output.py | 6 +- iot_template_oca/models/iot_template.py | 92 +++++++++---------- iot_template_oca/readme/USAGE.rst | 1 - iot_template_oca/tests/test_iot_template.py | 83 ++++++++--------- .../wizards/iot_device_configure.py | 44 ++++----- 10 files changed, 149 insertions(+), 162 deletions(-) diff --git a/iot_template_oca/__manifest__.py b/iot_template_oca/__manifest__.py index 167be6b4..f6d00eb5 100644 --- a/iot_template_oca/__manifest__.py +++ b/iot_template_oca/__manifest__.py @@ -1,24 +1,18 @@ # Copyright (C) 2018 Creu Blanca # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). { - 'name': 'IoT Templates', - 'version': '12.0.1.0.0', - 'category': 'IoT', - 'author': "Creu Blanca, " - "Odoo Community Association (OCA)", - 'license': 'AGPL-3', - 'installable': True, - 'summary': 'IoT base module', - 'depends': [ - 'iot_input', + "name": "IoT Templates", + "version": "12.0.1.0.0", + "category": "IoT", + "author": "Creu Blanca, " "Odoo Community Association (OCA)", + "license": "AGPL-3", + "installable": True, + "summary": "IoT base module", + "depends": ["iot_input",], + "data": [ + "security/ir.model.access.csv", + "wizards/iot_device_configure.xml", + "views/iot_template_views.xml", ], - 'data': [ - 'security/ir.model.access.csv', - 'wizards/iot_device_configure.xml', - 'views/iot_template_views.xml', - ], - 'demo': [ - 'demo/iot_template.xml', - ] - + "demo": ["demo/iot_template.xml",], } diff --git a/iot_template_oca/controller/iot_controller.py b/iot_template_oca/controller/iot_controller.py index 379309a9..b7b21154 100644 --- a/iot_template_oca/controller/iot_controller.py +++ b/iot_template_oca/controller/iot_controller.py @@ -2,17 +2,23 @@ # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). import json + from odoo import http class CallIot(http.Controller): - @http.route([ - '/iot//configure', - ], type='http', auth="none", methods=['POST'], csrf=False) + @http.route( + ["/iot//configure",], + type="http", + auth="none", + methods=["POST"], + csrf=False, + ) def configure_iot(self, serial, *args, **kwargs): request = http.request - template = kwargs.get('template', False) + template = kwargs.get("template", False) if not request.env: return json.dumps(False) - return json.dumps(request.env['iot.device.configure'].sudo().configure( - serial, template)) + return json.dumps( + request.env["iot.device.configure"].sudo().configure(serial, template) + ) diff --git a/iot_template_oca/demo/iot_template.xml b/iot_template_oca/demo/iot_template.xml index 2548746d..b92ca8c5 100644 --- a/iot_template_oca/demo/iot_template.xml +++ b/iot_template_oca/demo/iot_template.xml @@ -4,15 +4,14 @@ demo_template - + serial - + demo_input - + test_input_device {} - diff --git a/iot_template_oca/models/iot_device.py b/iot_template_oca/models/iot_device.py index 6bec32e8..09d7fab4 100644 --- a/iot_template_oca/models/iot_device.py +++ b/iot_template_oca/models/iot_device.py @@ -4,22 +4,19 @@ class IotDevice(models.Model): - _inherit = 'iot.device' + _inherit = "iot.device" @api.multi def get_iot_configuration(self): self.ensure_one() return { - 'host': self.env['ir.config_parameter'].sudo().get_param( - 'web.base.url' - ), - 'name': self.name, - 'outputs': { - output.name: output.get_configuration() - for output in self.output_ids + "host": self.env["ir.config_parameter"].sudo().get_param("web.base.url"), + "name": self.name, + "outputs": { + output.name: output.get_configuration() for output in self.output_ids }, - 'inputs': { + "inputs": { iot_input.name: iot_input.get_configuration() for iot_input in self.input_ids - } + }, } diff --git a/iot_template_oca/models/iot_device_input.py b/iot_template_oca/models/iot_device_input.py index 72829615..e7211667 100644 --- a/iot_template_oca/models/iot_device_input.py +++ b/iot_template_oca/models/iot_device_input.py @@ -4,16 +4,13 @@ class IotDeviceInput(models.Model): - _inherit = 'iot.device.input' + _inherit = "iot.device.input" - template_input_id = fields.Many2one( - 'iot.template.input', - readonly=True, - ) + template_input_id = fields.Many2one("iot.template.input", readonly=True,) @api.multi def get_configuration(self): return { - 'serial': self.serial, - 'passphrase': self.passphrase, + "serial": self.serial, + "passphrase": self.passphrase, } diff --git a/iot_template_oca/models/iot_device_output.py b/iot_template_oca/models/iot_device_output.py index c7a56a23..e3b0e9e1 100644 --- a/iot_template_oca/models/iot_device_output.py +++ b/iot_template_oca/models/iot_device_output.py @@ -4,10 +4,8 @@ class IotDeviceOutput(models.Model): - _inherit = 'iot.device.output' + _inherit = "iot.device.output" @api.multi def get_configuration(self): - return { - - } + return {} diff --git a/iot_template_oca/models/iot_template.py b/iot_template_oca/models/iot_template.py index 70101f91..4a410c82 100644 --- a/iot_template_oca/models/iot_template.py +++ b/iot_template_oca/models/iot_template.py @@ -1,10 +1,11 @@ # Copyright 2020 Creu Blanca # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). -from odoo import api, exceptions, fields, models, _ -from odoo.tools.safe_eval import safe_eval from uuid import uuid4 + from jinja2.sandbox import SandboxedEnvironment -from uuid import uuid4 + +from odoo import _, api, exceptions, fields, models +from odoo.tools.safe_eval import safe_eval mako_template_env = SandboxedEnvironment( block_start_string="<%", @@ -15,46 +16,41 @@ comment_end_string="", line_statement_prefix="%", line_comment_prefix="##", - trim_blocks=True, # do not output newline after blocks - autoescape=True, # XML/HTML automatic escaping + trim_blocks=True, # do not output newline after blocks + autoescape=True, # XML/HTML automatic escaping ) class IotTemplate(models.Model): - _name = 'iot.template' - _description = 'IoT Template for Device' - _parent_name = 'parent_id' + _name = "iot.template" + _description = "IoT Template for Device" + _parent_name = "parent_id" _parent_store = True - _parent_order = 'name' + _parent_order = "name" parent_path = fields.Char(index=True) name = fields.Char(required=True) - input_ids = fields.One2many( - 'iot.template.input', inverse_name='template_id', - ) - output_ids = fields.One2many( - 'iot.template.output', inverse_name='template_id', - ) - key_ids = fields.One2many('iot.template.key', inverse_name='template_id') - parent_id = fields.Many2one('iot.template', ondelete='restrict') + input_ids = fields.One2many("iot.template.input", inverse_name="template_id",) + output_ids = fields.One2many("iot.template.output", inverse_name="template_id",) + key_ids = fields.One2many("iot.template.key", inverse_name="template_id") + parent_id = fields.Many2one("iot.template", ondelete="restrict") @api.multi def _get_keys(self, serial): if self.parent_id: keys = self.parent_id._get_keys(serial) else: - keys = {'serial': serial} - keys.update({ - key.key: key._generate_value() for key in self.key_ids - }) + keys = {"serial": serial} + keys.update({key.key: key._generate_value() for key in self.key_ids}) return keys @api.multi - @api.constrains('parent_id') + @api.constrains("parent_id") def _check_recursion_parent_id(self): if not self._check_recursion(): raise exceptions.ValidationError( - _('Error! You are attempting to create a recursive template.')) + _("Error! You are attempting to create a recursive template.") + ) @api.multi def apply_template(self, device, keys): @@ -68,24 +64,24 @@ def apply_template(self, device, keys): class IotTemplateInput(models.Model): - _name = 'iot.template.input' - _description = 'IoT Input for Template' + _name = "iot.template.input" + _description = "IoT Input for Template" - template_id = fields.Many2one('iot.template', required=True) + template_id = fields.Many2one("iot.template", required=True) name = fields.Char(required=True) params = fields.Text() - call_model_id = fields.Many2one('ir.model') + call_model_id = fields.Many2one("ir.model") call_function = fields.Char(required=True) def _apply_template(self, device, keys): real_vals = { - 'device_id': device.id, - 'name': self.name, - 'call_function': self.call_function, - 'call_model_id': self.call_model_id.id, - 'template_input_id': self.id, - 'serial': uuid4(), - 'passphrase': uuid4(), + "device_id": device.id, + "name": self.name, + "call_function": self.call_function, + "call_model_id": self.call_model_id.id, + "template_input_id": self.id, + "serial": uuid4(), + "passphrase": uuid4(), } vals = safe_eval(self.params) for key in vals: @@ -93,38 +89,38 @@ def _apply_template(self, device, keys): real_vals.update(vals) print(real_vals) print(vals) - return self.env['iot.device.input'].create(real_vals) + return self.env["iot.device.input"].create(real_vals) class IotTemplateOutput(models.Model): - _name = 'iot.template.output' - _description = 'Output templates for IoT' + _name = "iot.template.output" + _description = "Output templates for IoT" - template_id = fields.Many2one('iot.template', required=True) + template_id = fields.Many2one("iot.template", required=True) name = fields.Char(required=True) - system_id = fields.Many2one('iot.system', required=True) + system_id = fields.Many2one("iot.system", required=True) params = fields.Text() def _apply_template(self, device, keys): real_vals = { - 'device_id': device.id, - 'name': self.name, - 'system_id': self.system_id.id, - 'key_serial': uuid4(), - 'passphrase': uuid4(), + "device_id": device.id, + "name": self.name, + "system_id": self.system_id.id, + "key_serial": uuid4(), + "passphrase": uuid4(), } vals = safe_eval(self.params) for key in vals: vals[key] = mako_template_env.from_string(vals[key]).render(keys) real_vals.update(vals) - return self.env['iot.device.output'].create(real_vals) + return self.env["iot.device.output"].create(real_vals) class IotTemplateKey(models.Model): - _name = 'iot.template.key' - _description = 'IoT Keys for configuration' + _name = "iot.template.key" + _description = "IoT Keys for configuration" - template_id = fields.Many2one('iot.template', required=True) + template_id = fields.Many2one("iot.template", required=True) key = fields.Char(required=True) def _generate_value(self): diff --git a/iot_template_oca/readme/USAGE.rst b/iot_template_oca/readme/USAGE.rst index 72a81dde..26a3e425 100644 --- a/iot_template_oca/readme/USAGE.rst +++ b/iot_template_oca/readme/USAGE.rst @@ -4,4 +4,3 @@ 4. Copy the URL 5. Access the device and send it the configuration url 6. The device will contact odoo and automatically configure itself. - diff --git a/iot_template_oca/tests/test_iot_template.py b/iot_template_oca/tests/test_iot_template.py index b048100d..7f2413f6 100644 --- a/iot_template_oca/tests/test_iot_template.py +++ b/iot_template_oca/tests/test_iot_template.py @@ -5,56 +5,57 @@ class TestIotTemplate(TransactionCase): - def setUp(self): super(TestIotTemplate, self).setUp() - self.parent_template = self.env['iot.template'].create({ - 'name': 'Parent template', - 'key_ids': [ - (0, 0, { - 'key': 'passphrase', - }) - ], - 'input_ids': [ - (0, 0, { - 'name': 'INPUT 1', - 'call_model_id': False, - 'call_function': 'iot_ras_default_action', - 'params': "{'serial': '${serial}', " - "'passphrase': '${passphrase}'}" - }) - ], - - }) - self.template = self.env['iot.template'].create({ - 'name': 'template', - 'parent_id': self.parent_template.id, - 'key_ids': [ - (0, 0, { - 'key': 'serial2', - }), - ], - 'input_ids': [ - (0, 0, { - 'name': 'INPUT 2', - 'call_model_id': False, - 'call_function': 'iot_ras_default_action', - 'params': "{'serial': '${serial2}', " - "'passphrase': '${passphrase}'}" - }) - ], - }) + self.parent_template = self.env["iot.template"].create( + { + "name": "Parent template", + "key_ids": [(0, 0, {"key": "passphrase",})], + "input_ids": [ + ( + 0, + 0, + { + "name": "INPUT 1", + "call_model_id": False, + "call_function": "iot_ras_default_action", + "params": "{'serial': '${serial}', " + "'passphrase': '${passphrase}'}", + }, + ) + ], + } + ) + self.template = self.env["iot.template"].create( + { + "name": "template", + "parent_id": self.parent_template.id, + "key_ids": [(0, 0, {"key": "serial2",}),], + "input_ids": [ + ( + 0, + 0, + { + "name": "INPUT 2", + "call_model_id": False, + "call_function": "iot_ras_default_action", + "params": "{'serial': '${serial2}', " + "'passphrase': '${passphrase}'}", + }, + ) + ], + } + ) def test_generation(self): - wizard = self.env['iot.device.configure'].create({}) + wizard = self.env["iot.device.configure"].create({}) self.assertFalse(wizard.serial) wizard.run() self.assertTrue(wizard.serial) - device_config = self.env['iot.device.configure'].configure( + device_config = self.env["iot.device.configure"].configure( wizard.serial, self.template.name ) - device = self.env['iot.device'].search([ - ('name', '=', device_config['name'])]) + device = self.env["iot.device"].search([("name", "=", device_config["name"])]) self.assertTrue(device) self.assertEqual(1, len(device)) self.assertEqual(2, len(device.input_ids)) diff --git a/iot_template_oca/wizards/iot_device_configure.py b/iot_template_oca/wizards/iot_device_configure.py index b9ae57b9..e88ec4be 100644 --- a/iot_template_oca/wizards/iot_device_configure.py +++ b/iot_template_oca/wizards/iot_device_configure.py @@ -1,51 +1,51 @@ # Copyright 2020 Creu Blanca # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). -from odoo import api, fields, models, _ from uuid import uuid4 +from odoo import _, api, fields, models + class IotDeviceConfigure(models.TransientModel): - _name = 'iot.device.configure' + _name = "iot.device.configure" _description = "Configure a IoT device" generated = fields.Boolean(default=False) serial = fields.Char(readonly=True) - url = fields.Char(compute='_compute_url') + url = fields.Char(compute="_compute_url") - @api.depends('serial') + @api.depends("serial") def _compute_url(self): for record in self: if record.generated: - record.url = self.env['ir.config_parameter'].sudo().get_param( - 'web.base.url' - ) + '/iot/' + record.serial + '/configure' + record.url = ( + self.env["ir.config_parameter"].sudo().get_param("web.base.url") + + "/iot/" + + record.serial + + "/configure" + ) @api.multi def run(self): if not self.generated: - self.write({'generated': True, 'serial': uuid4()}) + self.write({"generated": True, "serial": uuid4()}) return { - 'name': _('Configure device'), - 'type': 'ir.actions.act_window', - 'res_model': 'iot.device.configure', - 'view_mode': 'form', - 'target': 'new', - 'res_id': self.id, - 'context': self.env.context, + "name": _("Configure device"), + "type": "ir.actions.act_window", + "res_model": "iot.device.configure", + "view_mode": "form", + "target": "new", + "res_id": self.id, + "context": self.env.context, } @api.model def configure(self, serial, template_id): - config = self.search([('serial', '=', serial)]) + config = self.search([("serial", "=", serial)]) if not config: return {} config.unlink() - device = self.env['iot.device'].create({ - 'name': serial - }) - template = self.env['iot.template'].search([ - ('name', '=', template_id) - ]) + device = self.env["iot.device"].create({"name": serial}) + template = self.env["iot.template"].search([("name", "=", template_id)]) if template: template.apply_template(device, template._get_keys(serial)) return device.get_iot_configuration() From 527d378a2a45c02d855e7c8b9df7715e6b02a1c3 Mon Sep 17 00:00:00 2001 From: Olga Marco Date: Thu, 10 Jun 2021 12:35:51 +0200 Subject: [PATCH 04/37] [MIG] iot_template_oca: Migration to 13.0 --- iot_template_oca/README.rst | 11 +- iot_template_oca/__manifest__.py | 8 +- iot_template_oca/controller/iot_controller.py | 2 +- iot_template_oca/i18n/iot_template_oca.pot | 251 ++++++++++++++++++ iot_template_oca/models/iot_device.py | 3 +- iot_template_oca/models/iot_device_input.py | 3 +- iot_template_oca/models/iot_device_output.py | 5 +- iot_template_oca/models/iot_template.py | 19 +- iot_template_oca/security/ir.model.access.csv | 16 +- .../static/description/index.html | 8 +- iot_template_oca/tests/test_iot_template.py | 80 +++++- iot_template_oca/views/iot_template_views.xml | 32 ++- .../wizards/iot_device_configure.py | 5 +- .../wizards/iot_device_configure.xml | 47 ++-- 14 files changed, 397 insertions(+), 93 deletions(-) create mode 100644 iot_template_oca/i18n/iot_template_oca.pot diff --git a/iot_template_oca/README.rst b/iot_template_oca/README.rst index 03eef927..fd60f38d 100644 --- a/iot_template_oca/README.rst +++ b/iot_template_oca/README.rst @@ -14,13 +14,13 @@ IoT Templates :target: http://www.gnu.org/licenses/agpl-3.0-standalone.html :alt: License: AGPL-3 .. |badge3| image:: https://img.shields.io/badge/github-OCA%2Fiot-lightgray.png?logo=github - :target: https://github.com/OCA/iot/tree/12.0/iot_template + :target: https://github.com/OCA/iot/tree/13.0/iot_template_oca :alt: OCA/iot .. |badge4| image:: https://img.shields.io/badge/weblate-Translate%20me-F47D42.png - :target: https://translation.odoo-community.org/projects/iot-12-0/iot-12-0-iot_template + :target: https://translation.odoo-community.org/projects/iot-13-0/iot-13-0-iot_template_oca :alt: Translate me on Weblate .. |badge5| image:: https://img.shields.io/badge/runbot-Try%20me-875A7B.png - :target: https://runbot.odoo-community.org/runbot/269/12.0 + :target: https://runbot.odoo-community.org/runbot/269/13.0 :alt: Try me on Runbot |badge1| |badge2| |badge3| |badge4| |badge5| @@ -48,14 +48,13 @@ Usage 5. Access the device and send it the configuration url 6. The device will contact odoo and automatically configure itself. - Bug Tracker =========== Bugs are tracked on `GitHub Issues `_. In case of trouble, please check there if your issue has already been reported. If you spotted it first, help us smashing it by providing a detailed and welcomed -`feedback `_. +`feedback `_. Do not contact contributors directly about support or help with technical issues. @@ -85,6 +84,6 @@ OCA, or the Odoo Community Association, is a nonprofit organization whose mission is to support the collaborative development of Odoo features and promote its widespread use. -This module is part of the `OCA/iot `_ project on GitHub. +This module is part of the `OCA/iot `_ project on GitHub. You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute. diff --git a/iot_template_oca/__manifest__.py b/iot_template_oca/__manifest__.py index f6d00eb5..ba3f17d5 100644 --- a/iot_template_oca/__manifest__.py +++ b/iot_template_oca/__manifest__.py @@ -2,17 +2,17 @@ # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). { "name": "IoT Templates", - "version": "12.0.1.0.0", + "version": "13.0.1.0.0", "category": "IoT", - "author": "Creu Blanca, " "Odoo Community Association (OCA)", + "author": "Creu Blanca, Odoo Community Association (OCA)", "license": "AGPL-3", "installable": True, "summary": "IoT base module", - "depends": ["iot_input",], + "depends": ["iot_input_oca"], "data": [ "security/ir.model.access.csv", "wizards/iot_device_configure.xml", "views/iot_template_views.xml", ], - "demo": ["demo/iot_template.xml",], + "demo": ["demo/iot_template.xml"], } diff --git a/iot_template_oca/controller/iot_controller.py b/iot_template_oca/controller/iot_controller.py index b7b21154..a9cf267d 100644 --- a/iot_template_oca/controller/iot_controller.py +++ b/iot_template_oca/controller/iot_controller.py @@ -8,7 +8,7 @@ class CallIot(http.Controller): @http.route( - ["/iot//configure",], + ["/iot//configure"], type="http", auth="none", methods=["POST"], diff --git a/iot_template_oca/i18n/iot_template_oca.pot b/iot_template_oca/i18n/iot_template_oca.pot new file mode 100644 index 00000000..8af6bb90 --- /dev/null +++ b/iot_template_oca/i18n/iot_template_oca.pot @@ -0,0 +1,251 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * iot_template_oca +# +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 13.0\n" +"Report-Msgid-Bugs-To: \n" +"Last-Translator: \n" +"Language-Team: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: \n" +"Plural-Forms: \n" + +#. module: iot_template_oca +#: model_terms:ir.ui.view,arch_db:iot_template_oca.configure_form +msgid "Apply" +msgstr "" + +#. module: iot_template_oca +#: model:ir.actions.act_window,name:iot_template_oca.configure_action +#: model_terms:ir.ui.view,arch_db:iot_template_oca.configure_form +msgid "Apply template" +msgstr "" + +#. module: iot_template_oca +#: model:ir.model.fields,field_description:iot_template_oca.field_iot_template_input__call_function +msgid "Call Function" +msgstr "" + +#. module: iot_template_oca +#: model:ir.model.fields,field_description:iot_template_oca.field_iot_template_input__call_model_id +msgid "Call Model" +msgstr "" + +#. module: iot_template_oca +#: model_terms:ir.ui.view,arch_db:iot_template_oca.configure_form +msgid "Cancel" +msgstr "" + +#. module: iot_template_oca +#: model:ir.ui.menu,name:iot_template_oca.iot_device_configuration_menu +msgid "Config Device" +msgstr "" + +#. module: iot_template_oca +#: model:ir.model,name:iot_template_oca.model_iot_device_configure +msgid "Configure a IoT device" +msgstr "" + +#. module: iot_template_oca +#: code:addons/iot_template_oca/wizards/iot_device_configure.py:0 +#, python-format +msgid "Configure device" +msgstr "" + +#. module: iot_template_oca +#: model:ir.model.fields,field_description:iot_template_oca.field_iot_device_configure__create_uid +#: model:ir.model.fields,field_description:iot_template_oca.field_iot_template__create_uid +#: model:ir.model.fields,field_description:iot_template_oca.field_iot_template_input__create_uid +#: model:ir.model.fields,field_description:iot_template_oca.field_iot_template_key__create_uid +#: model:ir.model.fields,field_description:iot_template_oca.field_iot_template_output__create_uid +msgid "Created by" +msgstr "" + +#. module: iot_template_oca +#: model:ir.model.fields,field_description:iot_template_oca.field_iot_device_configure__create_date +#: model:ir.model.fields,field_description:iot_template_oca.field_iot_template__create_date +#: model:ir.model.fields,field_description:iot_template_oca.field_iot_template_input__create_date +#: model:ir.model.fields,field_description:iot_template_oca.field_iot_template_key__create_date +#: model:ir.model.fields,field_description:iot_template_oca.field_iot_template_output__create_date +msgid "Created on" +msgstr "" + +#. module: iot_template_oca +#: model:ir.model,name:iot_template_oca.model_iot_device_input +msgid "Device input" +msgstr "" + +#. module: iot_template_oca +#: model:ir.model.fields,field_description:iot_template_oca.field_iot_device_configure__display_name +#: model:ir.model.fields,field_description:iot_template_oca.field_iot_template__display_name +#: model:ir.model.fields,field_description:iot_template_oca.field_iot_template_input__display_name +#: model:ir.model.fields,field_description:iot_template_oca.field_iot_template_key__display_name +#: model:ir.model.fields,field_description:iot_template_oca.field_iot_template_output__display_name +msgid "Display Name" +msgstr "" + +#. module: iot_template_oca +#: model:ir.model.fields,field_description:iot_template_oca.field_iot_device_configure__generated +msgid "Generated" +msgstr "" + +#. module: iot_template_oca +#: model:ir.model.fields,field_description:iot_template_oca.field_iot_device_configure__id +#: model:ir.model.fields,field_description:iot_template_oca.field_iot_template__id +#: model:ir.model.fields,field_description:iot_template_oca.field_iot_template_input__id +#: model:ir.model.fields,field_description:iot_template_oca.field_iot_template_key__id +#: model:ir.model.fields,field_description:iot_template_oca.field_iot_template_output__id +msgid "ID" +msgstr "" + +#. module: iot_template_oca +#: model:ir.model.fields,field_description:iot_template_oca.field_iot_template__input_ids +msgid "Input" +msgstr "" + +#. module: iot_template_oca +#: model_terms:ir.ui.view,arch_db:iot_template_oca.iot_template_form +msgid "Inputs" +msgstr "" + +#. module: iot_template_oca +#: model:ir.model,name:iot_template_oca.model_iot_device +#: model:ir.model,name:iot_template_oca.model_iot_device_output +msgid "IoT Device" +msgstr "" + +#. module: iot_template_oca +#: model:ir.model,name:iot_template_oca.model_iot_template_input +msgid "IoT Input for Template" +msgstr "" + +#. module: iot_template_oca +#: model:ir.model,name:iot_template_oca.model_iot_template_key +msgid "IoT Keys for configuration" +msgstr "" + +#. module: iot_template_oca +#: model:ir.model,name:iot_template_oca.model_iot_template +msgid "IoT Template for Device" +msgstr "" + +#. module: iot_template_oca +#: model:ir.actions.act_window,name:iot_template_oca.iot_template_action +msgid "IoT Templates" +msgstr "" + +#. module: iot_template_oca +#: model:ir.model.fields,field_description:iot_template_oca.field_iot_template__key_ids +#: model:ir.model.fields,field_description:iot_template_oca.field_iot_template_key__key +msgid "Key" +msgstr "" + +#. module: iot_template_oca +#: model_terms:ir.ui.view,arch_db:iot_template_oca.iot_template_form +msgid "Keys" +msgstr "" + +#. module: iot_template_oca +#: model:ir.model.fields,field_description:iot_template_oca.field_iot_device_configure____last_update +#: model:ir.model.fields,field_description:iot_template_oca.field_iot_template____last_update +#: model:ir.model.fields,field_description:iot_template_oca.field_iot_template_input____last_update +#: model:ir.model.fields,field_description:iot_template_oca.field_iot_template_key____last_update +#: model:ir.model.fields,field_description:iot_template_oca.field_iot_template_output____last_update +msgid "Last Modified on" +msgstr "" + +#. module: iot_template_oca +#: model:ir.model.fields,field_description:iot_template_oca.field_iot_device_configure__write_uid +#: model:ir.model.fields,field_description:iot_template_oca.field_iot_template__write_uid +#: model:ir.model.fields,field_description:iot_template_oca.field_iot_template_input__write_uid +#: model:ir.model.fields,field_description:iot_template_oca.field_iot_template_key__write_uid +#: model:ir.model.fields,field_description:iot_template_oca.field_iot_template_output__write_uid +msgid "Last Updated by" +msgstr "" + +#. module: iot_template_oca +#: model:ir.model.fields,field_description:iot_template_oca.field_iot_device_configure__write_date +#: model:ir.model.fields,field_description:iot_template_oca.field_iot_template__write_date +#: model:ir.model.fields,field_description:iot_template_oca.field_iot_template_input__write_date +#: model:ir.model.fields,field_description:iot_template_oca.field_iot_template_key__write_date +#: model:ir.model.fields,field_description:iot_template_oca.field_iot_template_output__write_date +msgid "Last Updated on" +msgstr "" + +#. module: iot_template_oca +#: model:ir.model.fields,field_description:iot_template_oca.field_iot_template__name +#: model:ir.model.fields,field_description:iot_template_oca.field_iot_template_input__name +#: model:ir.model.fields,field_description:iot_template_oca.field_iot_template_output__name +msgid "Name" +msgstr "" + +#. module: iot_template_oca +#: model:ir.model.fields,field_description:iot_template_oca.field_iot_template__output_ids +msgid "Output" +msgstr "" + +#. module: iot_template_oca +#: model:ir.model,name:iot_template_oca.model_iot_template_output +msgid "Output templates for IoT" +msgstr "" + +#. module: iot_template_oca +#: model_terms:ir.ui.view,arch_db:iot_template_oca.iot_template_form +msgid "Outputs" +msgstr "" + +#. module: iot_template_oca +#: model:ir.model.fields,field_description:iot_template_oca.field_iot_template_input__params +#: model:ir.model.fields,field_description:iot_template_oca.field_iot_template_output__params +msgid "Params" +msgstr "" + +#. module: iot_template_oca +#: model:ir.model.fields,field_description:iot_template_oca.field_iot_template__parent_id +msgid "Parent" +msgstr "" + +#. module: iot_template_oca +#: model:ir.model.fields,field_description:iot_template_oca.field_iot_template__parent_path +msgid "Parent Path" +msgstr "" + +#. module: iot_template_oca +#: model:ir.model.fields,field_description:iot_template_oca.field_iot_device_configure__serial +msgid "Serial" +msgstr "" + +#. module: iot_template_oca +#: model:ir.model.fields,field_description:iot_template_oca.field_iot_template_output__system_id +msgid "System" +msgstr "" + +#. module: iot_template_oca +#: model:ir.model.fields,field_description:iot_template_oca.field_iot_template_input__template_id +#: model:ir.model.fields,field_description:iot_template_oca.field_iot_template_key__template_id +#: model:ir.model.fields,field_description:iot_template_oca.field_iot_template_output__template_id +msgid "Template" +msgstr "" + +#. module: iot_template_oca +#: model:ir.model.fields,field_description:iot_template_oca.field_iot_device_input__template_input_id +msgid "Template Input" +msgstr "" + +#. module: iot_template_oca +#: model:ir.model.fields,field_description:iot_template_oca.field_iot_device_output__template_output_id +msgid "Template Output" +msgstr "" + +#. module: iot_template_oca +#: model:ir.ui.menu,name:iot_template_oca.iot_template_menu +msgid "Templates" +msgstr "" + +#. module: iot_template_oca +#: model:ir.model.fields,field_description:iot_template_oca.field_iot_device_configure__url +msgid "Url" +msgstr "" diff --git a/iot_template_oca/models/iot_device.py b/iot_template_oca/models/iot_device.py index 09d7fab4..98b7241b 100644 --- a/iot_template_oca/models/iot_device.py +++ b/iot_template_oca/models/iot_device.py @@ -1,12 +1,11 @@ # Copyright 2020 Creu Blanca # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). -from odoo import api, models +from odoo import models class IotDevice(models.Model): _inherit = "iot.device" - @api.multi def get_iot_configuration(self): self.ensure_one() return { diff --git a/iot_template_oca/models/iot_device_input.py b/iot_template_oca/models/iot_device_input.py index e7211667..9b2f52de 100644 --- a/iot_template_oca/models/iot_device_input.py +++ b/iot_template_oca/models/iot_device_input.py @@ -1,6 +1,6 @@ # Copyright 2020 Creu Blanca # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). -from odoo import api, fields, models +from odoo import fields, models class IotDeviceInput(models.Model): @@ -8,7 +8,6 @@ class IotDeviceInput(models.Model): template_input_id = fields.Many2one("iot.template.input", readonly=True,) - @api.multi def get_configuration(self): return { "serial": self.serial, diff --git a/iot_template_oca/models/iot_device_output.py b/iot_template_oca/models/iot_device_output.py index e3b0e9e1..e8169773 100644 --- a/iot_template_oca/models/iot_device_output.py +++ b/iot_template_oca/models/iot_device_output.py @@ -1,11 +1,12 @@ # Copyright 2020 Creu Blanca # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). -from odoo import api, models +from odoo import fields, models class IotDeviceOutput(models.Model): _inherit = "iot.device.output" - @api.multi + template_output_id = fields.Many2one("iot.template.output", readonly=True,) + def get_configuration(self): return {} diff --git a/iot_template_oca/models/iot_template.py b/iot_template_oca/models/iot_template.py index 4a410c82..e372029a 100644 --- a/iot_template_oca/models/iot_template.py +++ b/iot_template_oca/models/iot_template.py @@ -4,7 +4,7 @@ from jinja2.sandbox import SandboxedEnvironment -from odoo import _, api, exceptions, fields, models +from odoo import fields, models from odoo.tools.safe_eval import safe_eval mako_template_env = SandboxedEnvironment( @@ -35,7 +35,6 @@ class IotTemplate(models.Model): key_ids = fields.One2many("iot.template.key", inverse_name="template_id") parent_id = fields.Many2one("iot.template", ondelete="restrict") - @api.multi def _get_keys(self, serial): if self.parent_id: keys = self.parent_id._get_keys(serial) @@ -44,15 +43,6 @@ def _get_keys(self, serial): keys.update({key.key: key._generate_value() for key in self.key_ids}) return keys - @api.multi - @api.constrains("parent_id") - def _check_recursion_parent_id(self): - if not self._check_recursion(): - raise exceptions.ValidationError( - _("Error! You are attempting to create a recursive template.") - ) - - @api.multi def apply_template(self, device, keys): self.ensure_one() for element in self.input_ids: @@ -87,8 +77,6 @@ def _apply_template(self, device, keys): for key in vals: vals[key] = mako_template_env.from_string(vals[key]).render(keys) real_vals.update(vals) - print(real_vals) - print(vals) return self.env["iot.device.input"].create(real_vals) @@ -106,10 +94,9 @@ def _apply_template(self, device, keys): "device_id": device.id, "name": self.name, "system_id": self.system_id.id, - "key_serial": uuid4(), - "passphrase": uuid4(), + "template_output_id": self.id, } - vals = safe_eval(self.params) + vals = safe_eval(self.params or "{}") for key in vals: vals[key] = mako_template_env.from_string(vals[key]).render(keys) real_vals.update(vals) diff --git a/iot_template_oca/security/ir.model.access.csv b/iot_template_oca/security/ir.model.access.csv index 9c50b1dc..ac396f0c 100644 --- a/iot_template_oca/security/ir.model.access.csv +++ b/iot_template_oca/security/ir.model.access.csv @@ -1,9 +1,9 @@ id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink -access_iot_template,access_iot_template,model_iot_template,iot.group_iot_user,1,0,0,0 -manage_iot_template,access_iot_template,model_iot_template,iot.group_iot_manager,1,1,1,1 -access_iot_template_input,access_iot_template_input,model_iot_template_input,iot.group_iot_user,1,0,0,0 -manage_iot_template_input,access_iot_template_input,model_iot_template_input,iot.group_iot_manager,1,1,1,1 -access_iot_template_output,access_iot_template_output,model_iot_template_output,iot.group_iot_user,1,0,0,0 -manage_iot_template_output,access_iot_template_output,model_iot_template_output,iot.group_iot_manager,1,1,1,1 -access_iot_template_key,access_iot_template_key,model_iot_template_key,iot.group_iot_user,1,0,0,0 -manage_iot_template_key,access_iot_template_key,model_iot_template_key,iot.group_iot_manager,1,1,1,1 +access_iot_template,access_iot_template,model_iot_template,iot_oca.group_iot_user,1,0,0,0 +manage_iot_template,access_iot_template,model_iot_template,iot_oca.group_iot_manager,1,1,1,1 +access_iot_template_input,access_iot_template_input,model_iot_template_input,iot_oca.group_iot_user,1,0,0,0 +manage_iot_template_input,access_iot_template_input,model_iot_template_input,iot_oca.group_iot_manager,1,1,1,1 +access_iot_template_output,access_iot_template_output,model_iot_template_output,iot_oca.group_iot_user,1,0,0,0 +manage_iot_template_output,access_iot_template_output,model_iot_template_output,iot_oca.group_iot_manager,1,1,1,1 +access_iot_template_key,access_iot_template_key,model_iot_template_key,iot_oca.group_iot_user,1,0,0,0 +manage_iot_template_key,access_iot_template_key,model_iot_template_key,iot_oca.group_iot_manager,1,1,1,1 diff --git a/iot_template_oca/static/description/index.html b/iot_template_oca/static/description/index.html index bdf0031a..9529e717 100644 --- a/iot_template_oca/static/description/index.html +++ b/iot_template_oca/static/description/index.html @@ -3,7 +3,7 @@ - + IoT Templates