mirror of
https://github.com/bringout/oca-technical.git
synced 2026-04-18 13:52:05 +02:00
Initial commit: OCA Technical packages (595 packages)
This commit is contained in:
commit
2cc02aac6e
24950 changed files with 2318079 additions and 0 deletions
|
|
@ -0,0 +1,2 @@
|
|||
from . import iot_device
|
||||
from . import iot_device_input
|
||||
|
|
@ -0,0 +1,77 @@
|
|||
import logging
|
||||
|
||||
from odoo import _, api, fields, models
|
||||
|
||||
_logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
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)
|
||||
|
||||
def action_show_input(self):
|
||||
self.ensure_one()
|
||||
action = self.env.ref("iot_input_oca.iot_device_input_action")
|
||||
result = action.read()[0]
|
||||
|
||||
result["context"] = {
|
||||
"default_device_id": self.id,
|
||||
}
|
||||
result["domain"] = [("device_id", "=", self.id)]
|
||||
if len(self.input_ids) == 1:
|
||||
result["views"] = [(False, "form")]
|
||||
result["res_id"] = self.input_ids.id
|
||||
return result
|
||||
|
||||
def parse_single_input(self, uuid=False, address=False, **kwargs):
|
||||
"""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
|
||||
"""
|
||||
msg = {}
|
||||
if uuid:
|
||||
msg["uuid"] = uuid
|
||||
if not address:
|
||||
_logger.warning("Address for Input is required")
|
||||
msg.update(
|
||||
{"status": "error", "message": _("Address for Input is required")}
|
||||
)
|
||||
return msg
|
||||
device_input = self.input_ids.filtered(lambda i: i.address == str(address))
|
||||
if len(device_input) == 1:
|
||||
if not device_input.active:
|
||||
_logger.warning(
|
||||
"Input with address %s is inactive, no data will be logged",
|
||||
device_input.address,
|
||||
)
|
||||
msg.update(
|
||||
{
|
||||
"status": "error",
|
||||
"message": _("Server Error. Check server logs"),
|
||||
}
|
||||
)
|
||||
return msg
|
||||
res = device_input.call_device(**kwargs)
|
||||
if uuid:
|
||||
res["uuid"] = uuid
|
||||
return res
|
||||
else:
|
||||
_logger.warning("Input with address %s not found", address)
|
||||
msg.update(
|
||||
{"status": "error", "message": _("Server Error. Check server logs")}
|
||||
)
|
||||
return msg
|
||||
|
|
@ -0,0 +1,146 @@
|
|||
import logging
|
||||
import traceback
|
||||
from io import StringIO
|
||||
|
||||
from odoo import _, api, fields, models
|
||||
from odoo.exceptions import UserError, ValidationError
|
||||
|
||||
_logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class IotDeviceInput(models.Model):
|
||||
_name = "iot.device.input"
|
||||
_description = "Device input"
|
||||
_order = "name"
|
||||
|
||||
name = fields.Char(required=True)
|
||||
device_id = fields.Many2one(
|
||||
"iot.device", required=True, readonly=True, auto_join=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,
|
||||
)
|
||||
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, *args, **kwargs):
|
||||
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)(*args, **kwargs)
|
||||
|
||||
def parse_args(self, serial, passphrase):
|
||||
if not serial or not passphrase:
|
||||
raise ValidationError(_("Serial and passphrase are required"))
|
||||
return [
|
||||
("serial", "=", serial),
|
||||
("passphrase", "=", passphrase),
|
||||
("device_id.active", "=", True),
|
||||
]
|
||||
|
||||
@api.model
|
||||
def get_device(self, serial, passphrase):
|
||||
return self.search(self.parse_args(serial, passphrase), limit=1)
|
||||
|
||||
def call_device(self, **kwargs):
|
||||
if not self:
|
||||
return {"status": "error", "message": _("Device cannot be found")}
|
||||
new_kwargs = kwargs.copy()
|
||||
args = []
|
||||
if "value" in new_kwargs and len(new_kwargs) == 1:
|
||||
args.append(new_kwargs.pop("value"))
|
||||
try:
|
||||
# We want to control that if an error happens,
|
||||
# everything will return to normal but we can process it properly
|
||||
with self.env.cr.savepoint():
|
||||
res = self._call_device(*args, **new_kwargs)
|
||||
if not res.get("status"):
|
||||
res["status"] = "ok"
|
||||
error = False
|
||||
except self._swallable_exceptions():
|
||||
buff = StringIO()
|
||||
traceback.print_exc(file=buff)
|
||||
error = buff.getvalue()
|
||||
_logger.error(error)
|
||||
res = {"status": "ko"}
|
||||
self.device_id.last_contact_date = fields.Datetime.now()
|
||||
self.env["iot.device.input.action"].create(
|
||||
self._add_action_vals(res, error, args, new_kwargs)
|
||||
)
|
||||
return res
|
||||
|
||||
def _swallable_exceptions(self):
|
||||
# TODO: improve this list
|
||||
return (UserError, ValidationError, AttributeError, TypeError)
|
||||
|
||||
def _add_action_vals(self, res, error, args, kwargs):
|
||||
new_res = res.copy()
|
||||
if error:
|
||||
new_res["error"] = error
|
||||
return {
|
||||
"input_id": self.id,
|
||||
"args": str(args or kwargs),
|
||||
"res": str(res),
|
||||
}
|
||||
|
||||
def test_input_device(self, value):
|
||||
return {"value": value}
|
||||
|
||||
def test_model_function(self, value):
|
||||
return {"status": "ok", "message": value}
|
||||
|
||||
def parse_multi_input(self, values):
|
||||
"""Handle multiple inputs for device
|
||||
: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.device_id
|
||||
if not values:
|
||||
_logger.warning(
|
||||
"Empty values array for input with identification %s",
|
||||
self.serial,
|
||||
)
|
||||
return {"status": "ko"}
|
||||
res = []
|
||||
for d in values:
|
||||
res.append(device.parse_single_input(**d))
|
||||
return {"result": res}
|
||||
|
||||
|
||||
class IoTDeviceAction(models.Model):
|
||||
_name = "iot.device.input.action"
|
||||
_description = "Action of device inputs"
|
||||
|
||||
input_id = fields.Many2one("iot.device.input")
|
||||
args = fields.Char()
|
||||
kwargs = fields.Char()
|
||||
res = fields.Char()
|
||||
Loading…
Add table
Add a link
Reference in a new issue