From d9c178f2529d21ea137aa8c4397e343f46f4339e Mon Sep 17 00:00:00 2001
From: Enric Tobella
Date: Fri, 6 Mar 2020 12:06:07 +0100
Subject: [PATCH 01/31] [12.0][ADD] iot_input
---
iot_input_oca/README.rst | 110 +++++
iot_input_oca/__init__.py | 2 +
iot_input_oca/__manifest__.py | 22 +
iot_input_oca/controller/__init__.py | 1 +
.../controller/iot_input_controller.py | 30 ++
iot_input_oca/i18n/iot_input.pot | 171 +++++++
iot_input_oca/models/__init__.py | 3 +
iot_input_oca/models/iot_device.py | 32 ++
iot_input_oca/models/iot_device_input.py | 83 ++++
iot_input_oca/models/mail_message.py | 16 +
iot_input_oca/readme/CONTRIBUTORS.rst | 1 +
iot_input_oca/readme/DESCRIPTION.rst | 14 +
iot_input_oca/readme/USAGE.rst | 12 +
iot_input_oca/security/ir.model.access.csv | 5 +
iot_input_oca/static/description/icon.png | Bin 0 -> 4151 bytes
iot_input_oca/static/description/index.html | 449 ++++++++++++++++++
iot_input_oca/tests/__init__.py | 1 +
iot_input_oca/tests/test_iot_in.py | 32 ++
.../views/iot_device_input_views.xml | 53 +++
iot_input_oca/views/iot_device_views.xml | 21 +
20 files changed, 1058 insertions(+)
create mode 100644 iot_input_oca/README.rst
create mode 100644 iot_input_oca/__init__.py
create mode 100644 iot_input_oca/__manifest__.py
create mode 100644 iot_input_oca/controller/__init__.py
create mode 100644 iot_input_oca/controller/iot_input_controller.py
create mode 100644 iot_input_oca/i18n/iot_input.pot
create mode 100644 iot_input_oca/models/__init__.py
create mode 100644 iot_input_oca/models/iot_device.py
create mode 100644 iot_input_oca/models/iot_device_input.py
create mode 100644 iot_input_oca/models/mail_message.py
create mode 100644 iot_input_oca/readme/CONTRIBUTORS.rst
create mode 100644 iot_input_oca/readme/DESCRIPTION.rst
create mode 100644 iot_input_oca/readme/USAGE.rst
create mode 100644 iot_input_oca/security/ir.model.access.csv
create mode 100644 iot_input_oca/static/description/icon.png
create mode 100644 iot_input_oca/static/description/index.html
create mode 100644 iot_input_oca/tests/__init__.py
create mode 100644 iot_input_oca/tests/test_iot_in.py
create mode 100644 iot_input_oca/views/iot_device_input_views.xml
create mode 100644 iot_input_oca/views/iot_device_views.xml
diff --git a/iot_input_oca/README.rst b/iot_input_oca/README.rst
new file mode 100644
index 00000000..bf89d639
--- /dev/null
+++ b/iot_input_oca/README.rst
@@ -0,0 +1,110 @@
+=========
+IoT Input
+=========
+
+.. !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+ !! 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_input
+ :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_input
+ :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|
+
+This addon allows to use a device in order to input data to odoo automatically.
+
+It opens a URL that a device can use to connect (with a password) that can only
+execute an specific action.
+
+Inputs are useful when a device wants to communicate to odoo for a single
+and simple action.
+This way, the device does not need to be configured with a odoo user and
+password, it is handled by odoo devices.
+
+Examples:
+
+* Sending the temperature every three minutes.
+* Sending the RFID that the device has received in order to perform some action
+
+**Table of contents**
+
+.. contents::
+ :local:
+
+Usage
+=====
+
+1. Create a Device on `IoT > Config Devices`
+2. Access the Inputs section of the device
+3. Create an input. You must define a serial, passphrase, function and model
+
+The function that the system will call must be of the following kind::
+
+ @api.model
+ def call_function(self, key):
+ return {}
+
+Where `key` is the input string send by the device and the result must be a dictionary
+that will be responded to the device as a JSON.
+
+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.
+
+.. |maintainer-etobella| image:: https://github.com/etobella.png?size=40px
+ :target: https://github.com/etobella
+ :alt: etobella
+
+Current `maintainer `__:
+
+|maintainer-etobella|
+
+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_input_oca/__init__.py b/iot_input_oca/__init__.py
new file mode 100644
index 00000000..5607426d
--- /dev/null
+++ b/iot_input_oca/__init__.py
@@ -0,0 +1,2 @@
+from . import models
+from . import controller
diff --git a/iot_input_oca/__manifest__.py b/iot_input_oca/__manifest__.py
new file mode 100644
index 00000000..54ff9d9b
--- /dev/null
+++ b/iot_input_oca/__manifest__.py
@@ -0,0 +1,22 @@
+# Copyright (C) 2018 Creu Blanca
+# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
+{
+ 'name': 'IoT Input',
+ 'version': '12.0.1.0.0',
+ 'category': 'IoT',
+ 'author': "Creu Blanca, "
+ "Odoo Community Association (OCA)",
+ 'license': 'AGPL-3',
+ 'installable': True,
+ 'summary': 'IoT Input module',
+ 'depends': [
+ 'iot_output',
+ 'mail',
+ ],
+ 'maintainers': ['etobella'],
+ 'data': [
+ 'security/ir.model.access.csv',
+ 'views/iot_device_views.xml',
+ 'views/iot_device_input_views.xml',
+ ],
+}
diff --git a/iot_input_oca/controller/__init__.py b/iot_input_oca/controller/__init__.py
new file mode 100644
index 00000000..c204f049
--- /dev/null
+++ b/iot_input_oca/controller/__init__.py
@@ -0,0 +1 @@
+from . import iot_input_controller
diff --git a/iot_input_oca/controller/iot_input_controller.py b/iot_input_oca/controller/iot_input_controller.py
new file mode 100644
index 00000000..7e085e76
--- /dev/null
+++ b/iot_input_oca/controller/iot_input_controller.py
@@ -0,0 +1,30 @@
+# Copyright 2018 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//action',
+ ], type='http', auth="none", methods=['POST'], csrf=False)
+ def call_unauthorized_iot(self, serial, *args, **kwargs):
+ request = http.request
+ if not request.env:
+ return json.dumps(False)
+ return json.dumps(request.env['iot.device.input'].sudo().get_device(
+ serial, kwargs['passphrase']).call_device(kwargs['value']))
+
+ @http.route([
+ '/iot//check',
+ ], type='http', auth="none", methods=['POST'], csrf=False)
+ def check_unauthorized_iot(self, serial, *args, **kwargs):
+ request = http.request
+ if not request.env:
+ return json.dumps(False)
+ device = request.env['iot.device.input'].sudo().get_device(
+ serial, kwargs['passphrase'])
+ if device:
+ return json.dumps({"state": True})
+ return json.dumps({"state": False})
diff --git a/iot_input_oca/i18n/iot_input.pot b/iot_input_oca/i18n/iot_input.pot
new file mode 100644
index 00000000..ea768a32
--- /dev/null
+++ b/iot_input_oca/i18n/iot_input.pot
@@ -0,0 +1,171 @@
+# Translation of Odoo Server.
+# This file contains the translation of the following modules:
+# * iot_input
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: Odoo Server 12.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_input
+#: model:ir.model.fields,field_description:iot_input.field_iot_device_input__action_ids
+msgid "Action"
+msgstr ""
+
+#. module: iot_input
+#: model:ir.model.fields,field_description:iot_input.field_iot_device_input__action_count
+msgid "Action Count"
+msgstr ""
+
+#. module: iot_input
+#: model:ir.model,name:iot_input.model_iot_device_input_action
+msgid "Action of device inputs"
+msgstr ""
+
+#. module: iot_input
+#: model:ir.model.fields,field_description:iot_input.field_iot_device_input_action__args
+msgid "Args"
+msgstr ""
+
+#. module: iot_input
+#: model:ir.model.fields,field_description:iot_input.field_iot_device_input__call_function
+msgid "Call Function"
+msgstr ""
+
+#. module: iot_input
+#: model:ir.model.fields,field_description:iot_input.field_iot_device_input__call_model_id
+msgid "Call Model"
+msgstr ""
+
+#. module: iot_input
+#: model:ir.model.fields,field_description:iot_input.field_iot_device_input__create_uid
+#: model:ir.model.fields,field_description:iot_input.field_iot_device_input_action__create_uid
+msgid "Created by"
+msgstr ""
+
+#. module: iot_input
+#: model:ir.model.fields,field_description:iot_input.field_iot_device_input__create_date
+#: model:ir.model.fields,field_description:iot_input.field_iot_device_input_action__create_date
+msgid "Created on"
+msgstr ""
+
+#. module: iot_input
+#: code:addons/iot_input/models/mail_message.py:15
+#, python-format
+msgid "Detected automatically by %s"
+msgstr ""
+
+#. module: iot_input
+#: model:ir.model.fields,field_description:iot_input.field_iot_device_input__device_id
+msgid "Device"
+msgstr ""
+
+#. module: iot_input
+#: code:addons/iot_input/models/iot_device_input.py:58
+#, python-format
+msgid "Device cannot be found"
+msgstr ""
+
+#. module: iot_input
+#: model:ir.model,name:iot_input.model_iot_device_input
+msgid "Device input"
+msgstr ""
+
+#. module: iot_input
+#: model:ir.model.fields,field_description:iot_input.field_iot_device_input__display_name
+#: model:ir.model.fields,field_description:iot_input.field_iot_device_input_action__display_name
+msgid "Display Name"
+msgstr ""
+
+#. module: iot_input
+#: model:ir.model.fields,field_description:iot_input.field_iot_device_input__id
+#: model:ir.model.fields,field_description:iot_input.field_iot_device_input_action__id
+msgid "ID"
+msgstr ""
+
+#. module: iot_input
+#: model:ir.model.fields,field_description:iot_input.field_iot_device__input_ids
+#: model:ir.model.fields,field_description:iot_input.field_iot_device_input_action__input_id
+msgid "Input"
+msgstr ""
+
+#. module: iot_input
+#: model:ir.model.fields,field_description:iot_input.field_iot_device__input_count
+msgid "Input Count"
+msgstr ""
+
+#. module: iot_input
+#: model_terms:ir.ui.view,arch_db:iot_input.iot_device_form
+msgid "Inputs"
+msgstr ""
+
+#. module: iot_input
+#: model:ir.model,name:iot_input.model_iot_device
+msgid "IoT Device"
+msgstr ""
+
+#. module: iot_input
+#: model:ir.actions.act_window,name:iot_input.iot_device_input_action
+msgid "IoT Inputs"
+msgstr ""
+
+#. module: iot_input
+#: model:ir.model.fields,field_description:iot_input.field_iot_device_input__lang
+msgid "Language"
+msgstr ""
+
+#. module: iot_input
+#: model:ir.model.fields,field_description:iot_input.field_iot_device_input____last_update
+#: model:ir.model.fields,field_description:iot_input.field_iot_device_input_action____last_update
+msgid "Last Modified on"
+msgstr ""
+
+#. module: iot_input
+#: model:ir.model.fields,field_description:iot_input.field_iot_device_input__write_uid
+#: model:ir.model.fields,field_description:iot_input.field_iot_device_input_action__write_uid
+msgid "Last Updated by"
+msgstr ""
+
+#. module: iot_input
+#: model:ir.model.fields,field_description:iot_input.field_iot_device_input__write_date
+#: model:ir.model.fields,field_description:iot_input.field_iot_device_input_action__write_date
+msgid "Last Updated on"
+msgstr ""
+
+#. module: iot_input
+#: model:ir.model,name:iot_input.model_mail_message
+msgid "Message"
+msgstr ""
+
+#. module: iot_input
+#: model:ir.model.fields,field_description:iot_input.field_iot_device_input__name
+msgid "Name"
+msgstr ""
+
+#. module: iot_input
+#: model:ir.model.fields,field_description:iot_input.field_iot_device_input__passphrase
+msgid "Passphrase"
+msgstr ""
+
+#. module: iot_input
+#: model:ir.model.fields,field_description:iot_input.field_iot_device_input_action__res
+msgid "Res"
+msgstr ""
+
+#. module: iot_input
+#: model:ir.model.fields,field_description:iot_input.field_iot_device_input__serial
+msgid "Serial"
+msgstr ""
+
+#. module: iot_input
+#: code:addons/iot_input/models/iot_device_input.py:44
+#, python-format
+msgid "Serial and passphrase are required"
+msgstr ""
+
diff --git a/iot_input_oca/models/__init__.py b/iot_input_oca/models/__init__.py
new file mode 100644
index 00000000..58626c86
--- /dev/null
+++ b/iot_input_oca/models/__init__.py
@@ -0,0 +1,3 @@
+from . import mail_message
+from . import iot_device
+from . import iot_device_input
diff --git a/iot_input_oca/models/iot_device.py b/iot_input_oca/models/iot_device.py
new file mode 100644
index 00000000..527bce41
--- /dev/null
+++ b/iot_input_oca/models/iot_device.py
@@ -0,0 +1,32 @@
+from odoo import api, fields, models
+
+
+class IotDevice(models.Model):
+ _inherit = 'iot.device'
+
+ input_ids = fields.One2many(
+ 'iot.device.input',
+ inverse_name='device_id'
+ )
+ input_count = fields.Integer(compute='_compute_input_count')
+
+ @api.depends('input_ids')
+ def _compute_input_count(self):
+ for r in self:
+ r.input_count = len(r.input_ids)
+
+ @api.multi
+ def action_show_input(self):
+ self.ensure_one()
+ action = self.env.ref('iot_input.iot_device_input_action')
+ result = action.read()[0]
+
+ result['context'] = {
+ 'default_device_id': self.id,
+ }
+ result['domain'] = "[('device_id', '=', " + \
+ str(self.id) + ")]"
+ if len(self.input_ids) == 1:
+ result['views'] = [(False, 'form')]
+ result['res_id'] = self.input_ids.id
+ return result
diff --git a/iot_input_oca/models/iot_device_input.py b/iot_input_oca/models/iot_device_input.py
new file mode 100644
index 00000000..2e9dbd6f
--- /dev/null
+++ b/iot_input_oca/models/iot_device_input.py
@@ -0,0 +1,83 @@
+from odoo import api, fields, models, _
+from odoo.exceptions import ValidationError
+
+
+class IotDeviceInput(models.Model):
+ _name = 'iot.device.input'
+ _description = "Device input"
+
+ name = fields.Char(required=True)
+ device_id = fields.Many2one('iot.device', required=True, readonly=True)
+ call_model_id = fields.Many2one('ir.model')
+ call_function = fields.Char(required=True)
+ serial = fields.Char()
+ passphrase = fields.Char()
+ action_ids = fields.One2many(
+ 'iot.device.input.action', inverse_name='input_id', readonly=True,
+ )
+ action_count = fields.Integer(compute='_compute_action_count')
+ lang = fields.Selection(
+ selection=lambda self: self.env['res.lang'].get_installed(),
+ string='Language',
+ )
+
+ @api.depends('action_ids')
+ def _compute_action_count(self):
+ for r in self:
+ r.action_count = len(r.action_ids)
+
+ def _call_device(self, value):
+ self.ensure_one()
+ obj = self
+ if self.call_model_id:
+ obj = self.env[self.call_model_id.model].with_context(
+ iot_device_input_id=self.id,
+ iot_device_name=self.device_id.name,
+ iot_device_id=self.device_id.id,
+ )
+ if self.lang:
+ obj = obj.with_context(lang=self.lang)
+ return getattr(obj, self.call_function)(value)
+
+ def parse_args(self, serial, passphrase):
+ if not serial or not passphrase:
+ raise ValidationError(_('Serial and passphrase are required'))
+ return [('serial', '=', serial), ('passphrase', '=', passphrase)]
+
+ @api.model
+ def get_device(self, serial, passphrase):
+ return self.search(self.parse_args(serial, passphrase), limit=1)
+
+ @api.model
+ def get_auth_device(self, serial):
+ return self.search([('serial', '=', serial)], limit=1)
+
+ @api.multi
+ def call_device(self, value):
+ if not self:
+ return {'status': 'error', 'message': _('Device cannot be found')}
+ else:
+ res = self._call_device(value)
+ res['status'] = 'ok'
+ self.env['iot.device.input.action'].create(
+ self._add_action_vals(value, res))
+ return res
+
+ def _add_action_vals(self, value, res):
+ return {
+ 'input_id': self.id,
+ 'args': str(value),
+ 'res': str(res),
+ }
+
+ def test_input_device(self, value):
+ return {'value': value}
+
+
+class IoTDeviceAction(models.Model):
+ _name = 'iot.device.input.action'
+ _description = 'Action of device inputs'
+
+ input_id = fields.Many2one('iot.device.input')
+ args = fields.Char()
+ res = fields.Char()
diff --git a/iot_input_oca/models/mail_message.py b/iot_input_oca/models/mail_message.py
new file mode 100644
index 00000000..e0d47b4b
--- /dev/null
+++ b/iot_input_oca/models/mail_message.py
@@ -0,0 +1,16 @@
+from odoo import api, models, _
+
+
+class MailMessage(models.Model):
+ _inherit = 'mail.message'
+
+ @api.model
+ def create(self, vals):
+ device = self.env.context.get('iot_device_name', False)
+ if device:
+ body = vals.get('body', '')
+ if len(body) > 0:
+ body += ' '
+ vals['body'] = '%s%s' % (
+ body, _('Detected automatically by %s') % device)
+ return super().create(vals)
diff --git a/iot_input_oca/readme/CONTRIBUTORS.rst b/iot_input_oca/readme/CONTRIBUTORS.rst
new file mode 100644
index 00000000..93ec993e
--- /dev/null
+++ b/iot_input_oca/readme/CONTRIBUTORS.rst
@@ -0,0 +1 @@
+* Enric Tobella
diff --git a/iot_input_oca/readme/DESCRIPTION.rst b/iot_input_oca/readme/DESCRIPTION.rst
new file mode 100644
index 00000000..0ca60637
--- /dev/null
+++ b/iot_input_oca/readme/DESCRIPTION.rst
@@ -0,0 +1,14 @@
+This addon allows to use a device in order to input data to odoo automatically.
+
+It opens a URL that a device can use to connect (with a password) that can only
+execute an specific action.
+
+Inputs are useful when a device wants to communicate to odoo for a single
+and simple action.
+This way, the device does not need to be configured with a odoo user and
+password, it is handled by odoo devices.
+
+Examples:
+
+* Sending the temperature every three minutes.
+* Sending the RFID that the device has received in order to perform some action
diff --git a/iot_input_oca/readme/USAGE.rst b/iot_input_oca/readme/USAGE.rst
new file mode 100644
index 00000000..3dbd5bc9
--- /dev/null
+++ b/iot_input_oca/readme/USAGE.rst
@@ -0,0 +1,12 @@
+1. Create a Device on `IoT > Config Devices`
+2. Access the Inputs section of the device
+3. Create an input. You must define a serial, passphrase, function and model
+
+The function that the system will call must be of the following kind::
+
+ @api.model
+ def call_function(self, key):
+ return {}
+
+Where `key` is the input string send by the device and the result must be a dictionary
+that will be responded to the device as a JSON.
diff --git a/iot_input_oca/security/ir.model.access.csv b/iot_input_oca/security/ir.model.access.csv
new file mode 100644
index 00000000..ee3e9c5d
--- /dev/null
+++ b/iot_input_oca/security/ir.model.access.csv
@@ -0,0 +1,5 @@
+id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
+access_iot_device_input,access_iot_device_input,model_iot_device_input,iot.group_iot_user,1,1,1,0
+manage_iot_device_input,access_iot_device_input,model_iot_device_input,iot.group_iot_manager,1,1,1,1
+access_iot_device_input_action,access_iot_device_input_action,model_iot_device_input_action,iot.group_iot_user,1,1,1,0
+manage_iot_device_input_action,access_iot_device_input_action,model_iot_device_input_action,iot.group_iot_manager,1,1,1,1
diff --git a/iot_input_oca/static/description/icon.png b/iot_input_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_input_oca/static/description/index.html b/iot_input_oca/static/description/index.html
new file mode 100644
index 00000000..bde85965
--- /dev/null
+++ b/iot_input_oca/static/description/index.html
@@ -0,0 +1,449 @@
+
+
+
+
+
+
+IoT Input
+
+
+
+
+
IoT Input
+
+
+
+
This addon allows to use a device in order to input data to odoo automatically.
+
It opens a URL that a device can use to connect (with a password) that can only
+execute an specific action.
+
Inputs are useful when a device wants to communicate to odoo for a single
+and simple action.
+This way, the device does not need to be configured with a odoo user and
+password, it is handled by odoo devices.
+
Examples:
+
+
Sending the temperature every three minutes.
+
Sending the RFID that the device has received in order to perform some action
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.
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.
+
+
diff --git a/iot_input_oca/tests/__init__.py b/iot_input_oca/tests/__init__.py
new file mode 100644
index 00000000..5ec21d93
--- /dev/null
+++ b/iot_input_oca/tests/__init__.py
@@ -0,0 +1 @@
+from . import test_iot_in
diff --git a/iot_input_oca/tests/test_iot_in.py b/iot_input_oca/tests/test_iot_in.py
new file mode 100644
index 00000000..813ad4d5
--- /dev/null
+++ b/iot_input_oca/tests/test_iot_in.py
@@ -0,0 +1,32 @@
+from odoo.tests.common import TransactionCase
+
+
+class TestIotIn(TransactionCase):
+ def test_device(self):
+ serial = 'testingdeviceserial'
+ passphrase = 'password'
+ device = self.env['iot.device'].create({
+ 'name': 'Device',
+ })
+ input = self.env['iot.device.input'].create({
+ 'name': 'Input',
+ 'device_id': device.id,
+ 'serial': serial,
+ 'passphrase': passphrase,
+ 'call_model_id': self.ref('iot_input.model_iot_device_input'),
+ 'call_function': 'test_input_device'
+ })
+ iot = self.env['iot.device.input']
+ self.assertFalse(
+ iot.get_device(serial=serial + serial, passphrase=passphrase))
+ self.assertFalse(
+ iot.get_device(serial=serial, passphrase=passphrase + passphrase))
+ iot = iot.get_device(
+ serial=serial, passphrase=passphrase)
+ self.assertEqual(iot, input)
+ args = 'hello'
+ res = iot.call_device(args)
+ self.assertEqual(res, {'status': 'ok', 'value': args})
+ self.assertTrue(input.action_ids)
+ self.assertEqual(input.action_ids.args, str(args))
+ self.assertEqual(input.action_ids.res, str(res))
diff --git a/iot_input_oca/views/iot_device_input_views.xml b/iot_input_oca/views/iot_device_input_views.xml
new file mode 100644
index 00000000..84fe0c58
--- /dev/null
+++ b/iot_input_oca/views/iot_device_input_views.xml
@@ -0,0 +1,53 @@
+
+
+
+
+ iot.device.input.tree
+ iot.device.input
+
+
+
+
+
+
+
+
+
+ iot.device.input.form
+ iot.device.input
+
+
+
+
+
+
+ IoT Inputs
+ ir.actions.act_window
+ iot.device.input
+ form
+ tree,form
+
+
+
diff --git a/iot_input_oca/views/iot_device_views.xml b/iot_input_oca/views/iot_device_views.xml
new file mode 100644
index 00000000..0985c6ce
--- /dev/null
+++ b/iot_input_oca/views/iot_device_views.xml
@@ -0,0 +1,21 @@
+
+
+
+
+ iot.device.form
+ iot.device
+
+
+
+
+
+
+
+
+
From 3b31ae4bf35016832ed0c2cc8a89f64c920a16fd Mon Sep 17 00:00:00 2001
From: "Dimitrios T. Tanis"
Date: Sat, 25 Jul 2020 00:52:56 +0300
Subject: [PATCH 02/31] [UPD] Adds new fields, multi_input controller, views
Adds active field in device, input
Adds device_identification and passphrase in device
Adds address field in input
Adds new controller that can take multi event and/or multi input data
Updates tests
[FIX] Don't remove existing users from security group
Updating the module removed existing users from Manager group.
This changes behaviour so that users that are already in this group
are not removed.
---
iot_input_oca/README.rst | 38 ++++++
iot_input_oca/__manifest__.py | 2 +-
.../controller/iot_input_controller.py | 54 +++++++-
iot_input_oca/i18n/iot_input.pot | 82 +++++++++++-
iot_input_oca/models/iot_device.py | 100 ++++++++++++++-
iot_input_oca/models/iot_device_input.py | 7 ++
iot_input_oca/readme/CONTRIBUTORS.rst | 1 +
iot_input_oca/readme/USAGE.rst | 37 ++++++
iot_input_oca/static/description/index.html | 30 +++++
iot_input_oca/tests/test_iot_in.py | 118 +++++++++++++++++-
.../views/iot_device_input_views.xml | 22 +++-
11 files changed, 478 insertions(+), 13 deletions(-)
diff --git a/iot_input_oca/README.rst b/iot_input_oca/README.rst
index bf89d639..fe9af15d 100644
--- a/iot_input_oca/README.rst
+++ b/iot_input_oca/README.rst
@@ -48,6 +48,12 @@ Examples:
Usage
=====
+There are two endpoints you can use:
+Endpoint 1: /iot//action
+
+Takes `application/x-www-form-urlencoded` parameters:
+passphase, value (where value is a JSON object)
+
1. Create a Device on `IoT > Config Devices`
2. Access the Inputs section of the device
3. Create an input. You must define a serial, passphrase, function and model
@@ -61,6 +67,37 @@ The function that the system will call must be of the following kind::
Where `key` is the input string send by the device and the result must be a dictionary
that will be responded to the device as a JSON.
+Endpoint 2: /iot//multi_input
+It can be used to send values with multiple data in one POST request such as:
+- Values for inputs of the same device with different address (multi input)
+- Values for inputs of the same device with same address, different values (multi event)
+- Mix of the above (multi input, multi event)
+
+Takes `application/x-www-form-urlencoded` parameters:
+passphase, values (a JSON array of JSON objects)
+
+It is called using device_identification and passing two POST parameters: device passphrase and
+a JSON string containing and array of values for input
+
+It requires the function that the system will call must be of the following kind::
+
+ @api.model
+ def call_function(self, key):
+ 'do something
+ if err:
+ return {'status': 'error', 'message': 'The error message you want to send to the device'}
+ return {'status': 'ok', 'message': 'Optional success message'}
+
+Where `key` is a dict send by the device having at least value for keys: 'address', 'value'
+
+The function must always return a JSON with status and message. If value contains a value
+with 'uuid' as key, it is return along with the object for the IoT device to identify
+success/failure per record.
+
+It has full error reporting and the return value is a JSON array of dicts containing at
+least status and message. Error message respose is at some points generic, though
+extended logging is done in Odoo server logs.
+
Bug Tracker
===========
@@ -83,6 +120,7 @@ Contributors
~~~~~~~~~~~~
* Enric Tobella
+* Dimitrios Tanis
Maintainers
~~~~~~~~~~~
diff --git a/iot_input_oca/__manifest__.py b/iot_input_oca/__manifest__.py
index 54ff9d9b..99d7376f 100644
--- a/iot_input_oca/__manifest__.py
+++ b/iot_input_oca/__manifest__.py
@@ -2,7 +2,7 @@
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
{
'name': 'IoT Input',
- 'version': '12.0.1.0.0',
+ 'version': '12.0.1.1.0',
'category': 'IoT',
'author': "Creu Blanca, "
"Odoo Community Association (OCA)",
diff --git a/iot_input_oca/controller/iot_input_controller.py b/iot_input_oca/controller/iot_input_controller.py
index 7e085e76..bf23778f 100644
--- a/iot_input_oca/controller/iot_input_controller.py
+++ b/iot_input_oca/controller/iot_input_controller.py
@@ -2,13 +2,16 @@
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
import json
-from odoo import http
+import logging
+from odoo import http, _
+
+_logger = logging.getLogger(__name__)
class CallIot(http.Controller):
@http.route([
'/iot//action',
- ], type='http', auth="none", methods=['POST'], csrf=False)
+ ], type='http', auth="none", methods=['POST'], csrf=False)
def call_unauthorized_iot(self, serial, *args, **kwargs):
request = http.request
if not request.env:
@@ -16,9 +19,54 @@ def call_unauthorized_iot(self, serial, *args, **kwargs):
return json.dumps(request.env['iot.device.input'].sudo().get_device(
serial, kwargs['passphrase']).call_device(kwargs['value']))
+ @http.route([
+ '/iot//multi_input',
+ ], type='http', auth="none", methods=['POST'], csrf=False)
+ def call_unauthorized_iot_multi_input(
+ self, device_identification, *args, **kwargs):
+ '''Controller to write multiple input data to device inputs
+
+ :param string passphrase:
+ Device passphrase in POST data.
+
+ :param string values:
+ JSON formatted string containing a JSON an array of JSON objects.
+ '''
+ request = http.request
+ if not request.env:
+ _logger.warning('env not set')
+ return json.dumps({'status': 'error',
+ 'message': _('Server Error')})
+ if 'passphrase' not in kwargs:
+ _logger.warning('Passphrase is required')
+ return json.dumps({'status': 'error',
+ 'message': _('Passphrase is required')})
+ if 'values' not in kwargs:
+ _logger.warning('Values is required')
+ return json.dumps({'status': 'error',
+ 'message': _('Values is required')})
+ # Decode JSON object here and use pure python objects in further calls
+ try:
+ values = json.loads(kwargs['values'])
+ if not isinstance(values, list):
+ raise SyntaxError
+ except json.decoder.JSONDecodeError:
+ _logger.warning('Values is not a valid JSON')
+ return json.dumps({'status': 'error',
+ 'message': _('Values is not a valid JSON')})
+ except SyntaxError:
+ _logger.warning('Values should be a JSON array of JSON objects')
+ return json.dumps({'status': 'error',
+ 'message':
+ _('Values should be a JSON array of JSON objects')})
+ # Encode response to JSON and return
+ return json.dumps(
+ request.env['iot.device'].sudo().parse_multi_input(
+ device_identification, kwargs['passphrase'], values))
+
@http.route([
'/iot//check',
- ], type='http', auth="none", methods=['POST'], csrf=False)
+ ], type='http', auth="none", methods=['POST'], csrf=False)
def check_unauthorized_iot(self, serial, *args, **kwargs):
request = http.request
if not request.env:
diff --git a/iot_input_oca/i18n/iot_input.pot b/iot_input_oca/i18n/iot_input.pot
index ea768a32..08f0b158 100644
--- a/iot_input_oca/i18n/iot_input.pot
+++ b/iot_input_oca/i18n/iot_input.pot
@@ -28,6 +28,23 @@ msgstr ""
msgid "Action of device inputs"
msgstr ""
+#. module: iot_input
+#: model:ir.model.fields,field_description:iot_input.field_iot_device_input__active
+#: model_terms:ir.ui.view,arch_db:iot_input.iot_device_input_search
+msgid "Active"
+msgstr ""
+
+#. module: iot_input
+#: model:ir.model.fields,field_description:iot_input.field_iot_device_input__address
+msgid "Address"
+msgstr ""
+
+#. module: iot_input
+#: code:addons/iot_input/models/iot_device.py:54
+#, python-format
+msgid "Address for Input is required"
+msgstr ""
+
#. module: iot_input
#: model:ir.model.fields,field_description:iot_input.field_iot_device_input_action__args
msgid "Args"
@@ -67,7 +84,7 @@ msgid "Device"
msgstr ""
#. module: iot_input
-#: code:addons/iot_input/models/iot_device_input.py:58
+#: code:addons/iot_input/models/iot_device_input.py:60
#, python-format
msgid "Device cannot be found"
msgstr ""
@@ -83,12 +100,23 @@ msgstr ""
msgid "Display Name"
msgstr ""
+#. module: iot_input
+#: code:addons/iot_input/models/iot_device.py:126
+#, python-format
+msgid "Empty values array"
+msgstr ""
+
#. module: iot_input
#: model:ir.model.fields,field_description:iot_input.field_iot_device_input__id
#: model:ir.model.fields,field_description:iot_input.field_iot_device_input_action__id
msgid "ID"
msgstr ""
+#. module: iot_input
+#: model_terms:ir.ui.view,arch_db:iot_input.iot_device_input_search
+msgid "Inactive"
+msgstr ""
+
#. module: iot_input
#: model:ir.model.fields,field_description:iot_input.field_iot_device__input_ids
#: model:ir.model.fields,field_description:iot_input.field_iot_device_input_action__input_id
@@ -110,6 +138,11 @@ msgstr ""
msgid "IoT Device"
msgstr ""
+#. module: iot_input
+#: model_terms:ir.ui.view,arch_db:iot_input.iot_device_input_search
+msgid "IoT Device Input Search"
+msgstr ""
+
#. module: iot_input
#: model:ir.actions.act_window,name:iot_input.iot_device_input_action
msgid "IoT Inputs"
@@ -153,6 +186,12 @@ msgstr ""
msgid "Passphrase"
msgstr ""
+#. module: iot_input
+#: code:addons/iot_input/controller/iot_input_controller.py:43
+#, python-format
+msgid "Passphrase is required"
+msgstr ""
+
#. module: iot_input
#: model:ir.model.fields,field_description:iot_input.field_iot_device_input_action__res
msgid "Res"
@@ -164,8 +203,47 @@ msgid "Serial"
msgstr ""
#. module: iot_input
-#: code:addons/iot_input/models/iot_device_input.py:44
+#: code:addons/iot_input/models/iot_device_input.py:46
#, python-format
msgid "Serial and passphrase are required"
msgstr ""
+#. module: iot_input
+#: code:addons/iot_input/controller/iot_input_controller.py:39
+#, python-format
+msgid "Server Error"
+msgstr ""
+
+#. module: iot_input
+#: code:addons/iot_input/models/iot_device.py:77
+#: code:addons/iot_input/models/iot_device.py:107
+#: code:addons/iot_input/models/iot_device.py:113
+#: code:addons/iot_input/models/iot_device.py:119
+#, python-format
+msgid "Server Error. Check server logs"
+msgstr ""
+
+#. module: iot_input
+#: code:addons/iot_input/models/iot_device.py:61
+#, python-format
+msgid "Value for Input is required"
+msgstr ""
+
+#. module: iot_input
+#: code:addons/iot_input/controller/iot_input_controller.py:56
+#, python-format
+msgid "Values is not a valid JSON"
+msgstr ""
+
+#. module: iot_input
+#: code:addons/iot_input/controller/iot_input_controller.py:47
+#, python-format
+msgid "Values is required"
+msgstr ""
+
+#. module: iot_input
+#: code:addons/iot_input/controller/iot_input_controller.py:61
+#, python-format
+msgid "Values should be a JSON array of JSON objects"
+msgstr ""
+
diff --git a/iot_input_oca/models/iot_device.py b/iot_input_oca/models/iot_device.py
index 527bce41..3d6a7d06 100644
--- a/iot_input_oca/models/iot_device.py
+++ b/iot_input_oca/models/iot_device.py
@@ -1,4 +1,8 @@
-from odoo import api, fields, models
+import logging
+
+from odoo import api, fields, models, _
+
+_logger = logging.getLogger(__name__)
class IotDevice(models.Model):
@@ -30,3 +34,97 @@ def action_show_input(self):
result['views'] = [(False, 'form')]
result['res_id'] = self.input_ids.id
return result
+
+ @api.multi
+ def parse_single_input(self, value):
+ '''Handle single input for device
+
+ :param dict value:
+ Dict containing at least keys 'address', 'value'
+ :returns: dict with keys 'status', 'message' where:
+ - status='ok' when value is parsed without errors
+ - status='error' and message='error message' when error occurs
+ If value contains a value with key 'uuid', it is passed in the return dict
+ to identify result for each entry at the iot end
+ :rtype: dict
+ '''
+ if 'address' not in value.keys():
+ _logger.warning('Address for Input is required')
+ msg = {'status': 'error',
+ 'message': _('Address for Input is required')}
+ if 'uuid' in value.keys():
+ msg['uuid'] = value['uuid']
+ return msg
+ if 'value' not in value.keys():
+ _logger.warning('Value for Input is required')
+ msg = {'status': 'error',
+ 'message': _('Value for Input is required')}
+ if 'uuid' in value.keys():
+ msg['uuid'] = value['uuid']
+ return msg
+
+ device_input = self.input_ids.filtered(lambda i: i.address == value['address'])
+ if len(device_input) == 1:
+ res = device_input._call_device(value)
+ self.env['iot.device.input.action'].create(
+ device_input._add_action_vals(value, res))
+ if 'uuid' in value:
+ res['uuid'] = value['uuid']
+ return res
+ else:
+ _logger.warning('Input with address %s not found', value['address'])
+ msg = {'status': 'error',
+ 'message': _('Server Error. Check server logs')}
+ if 'uuid' in value:
+ msg['uuid'] = value['uuid']
+ return msg
+
+ @api.model
+ def parse_multi_input(self, device_identification, passphrase, values):
+ '''Handle multiple inputs for device
+
+ :param string device_identification:
+ Device identification.
+ :param string passphrase:
+ Device passphrase.
+ :param list values:
+ Values is a list of dicts with at least values for keys 'address', 'value'
+ Each dict in the list can have:
+ - Different address (multi input)
+ - Same address, different values (multi event)
+ - Mix of the above (multi input, multi event)
+ :returns: JSON encodable list of dicts
+ :rtype: list
+ '''
+ device = self.with_context(
+ active_test=False).search(
+ [('device_identification', '=', device_identification)])
+ if not device:
+ _logger.warning(
+ 'Device with identification %s not found',
+ device_identification)
+ return {'status': 'error',
+ 'message': _('Server Error. Check server logs')}
+ if not device.active:
+ _logger.warning(
+ 'Device with identification %s is inactive, no data will be logged',
+ device.device_identification)
+ return {'status': 'error',
+ 'message': _('Server Error. Check server logs')}
+ if device.passphrase != passphrase:
+ _logger.warning(
+ 'Wrong passphrase for device with identification %s',
+ device.device_identification)
+ return {'status': 'error',
+ 'message': _('Server Error. Check server logs')}
+
+ if not values:
+ _logger.warning(
+ 'Empty values array for device with identification %s',
+ device.device_identification)
+ return {'status': 'error',
+ 'message': _('Empty values array')}
+ res = []
+ for d in values:
+ res.append(device.parse_single_input(d))
+ return res
diff --git a/iot_input_oca/models/iot_device_input.py b/iot_input_oca/models/iot_device_input.py
index 2e9dbd6f..75749d36 100644
--- a/iot_input_oca/models/iot_device_input.py
+++ b/iot_input_oca/models/iot_device_input.py
@@ -10,7 +10,9 @@ class IotDeviceInput(models.Model):
device_id = fields.Many2one('iot.device', required=True, readonly=True)
call_model_id = fields.Many2one('ir.model')
call_function = fields.Char(required=True)
+ active = fields.Boolean(default=True)
serial = fields.Char()
+ address = fields.Char()
passphrase = fields.Char()
action_ids = fields.One2many(
'iot.device.input.action', inverse_name='input_id', readonly=True,
@@ -73,6 +75,11 @@ def _add_action_vals(self, value, res):
def test_input_device(self, value):
return {'value': value}
+ def test_model_function(self, value):
+ return {'status': 'ok',
+ 'message': value
+ }
+
class IoTDeviceAction(models.Model):
_name = 'iot.device.input.action'
diff --git a/iot_input_oca/readme/CONTRIBUTORS.rst b/iot_input_oca/readme/CONTRIBUTORS.rst
index 93ec993e..61fe3232 100644
--- a/iot_input_oca/readme/CONTRIBUTORS.rst
+++ b/iot_input_oca/readme/CONTRIBUTORS.rst
@@ -1 +1,2 @@
* Enric Tobella
+* Dimitrios Tanis
diff --git a/iot_input_oca/readme/USAGE.rst b/iot_input_oca/readme/USAGE.rst
index 3dbd5bc9..5412cef4 100644
--- a/iot_input_oca/readme/USAGE.rst
+++ b/iot_input_oca/readme/USAGE.rst
@@ -1,3 +1,9 @@
+There are two endpoints you can use:
+Endpoint 1: /iot//action
+
+Takes `application/x-www-form-urlencoded` parameters:
+passphase, value (where value is a JSON object)
+
1. Create a Device on `IoT > Config Devices`
2. Access the Inputs section of the device
3. Create an input. You must define a serial, passphrase, function and model
@@ -10,3 +16,34 @@ The function that the system will call must be of the following kind::
Where `key` is the input string send by the device and the result must be a dictionary
that will be responded to the device as a JSON.
+
+Endpoint 2: /iot//multi_input
+It can be used to send values with multiple data in one POST request such as:
+- Values for inputs of the same device with different address (multi input)
+- Values for inputs of the same device with same address, different values (multi event)
+- Mix of the above (multi input, multi event)
+
+Takes `application/x-www-form-urlencoded` parameters:
+passphase, values (a JSON array of JSON objects)
+
+It is called using device_identification and passing two POST parameters: device passphrase and
+a JSON string containing and array of values for input
+
+It requires the function that the system will call must be of the following kind::
+
+ @api.model
+ def call_function(self, key):
+ 'do something
+ if err:
+ return {'status': 'error', 'message': 'The error message you want to send to the device'}
+ return {'status': 'ok', 'message': 'Optional success message'}
+
+Where `key` is a dict send by the device having at least value for keys: 'address', 'value'
+
+The function must always return a JSON with status and message. If value contains a value
+with 'uuid' as key, it is return along with the object for the IoT device to identify
+success/failure per record.
+
+It has full error reporting and the return value is a JSON array of dicts containing at
+least status and message. Error message respose is at some points generic, though
+extended logging is done in Odoo server logs.
diff --git a/iot_input_oca/static/description/index.html b/iot_input_oca/static/description/index.html
index bde85965..bc4ae2f3 100644
--- a/iot_input_oca/static/description/index.html
+++ b/iot_input_oca/static/description/index.html
@@ -395,6 +395,10 @@
Where key is the input string send by the device and the result must be a dictionary
that will be responded to the device as a JSON.
+
Endpoint 2: /iot/<device_identification>/multi_input
+It can be used to send values with multiple data in one POST request such as:
+- Values for inputs of the same device with different address (multi input)
+- Values for inputs of the same device with same address, different values (multi event)
+- Mix of the above (multi input, multi event)
+
Takes application/x-www-form-urlencoded parameters:
+passphase, values (a JSON array of JSON objects)
+
It is called using device_identification and passing two POST parameters: device passphrase and
+a JSON string containing and array of values for input
+
It requires the function that the system will call must be of the following kind:
+
+@api.model
+ def call_function(self, key):
+ 'do something
+ if err:
+ return {'status': 'error', 'message': 'The error message you want to send to the device'}
+ return {'status': 'ok', 'message': 'Optional success message'}
+
+
Where key is a dict send by the device having at least value for keys: ‘address’, ‘value’
+
The function must always return a JSON with status and message. If value contains a value
+with ‘uuid’ as key, it is return along with the object for the IoT device to identify
+success/failure per record.
+
It has full error reporting and the return value is a JSON array of dicts containing at
+least status and message. Error message respose is at some points generic, though
+extended logging is done in Odoo server logs.
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.
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.
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.