71 lines
3.0 KiB
Python
71 lines
3.0 KiB
Python
|
# -*- coding: utf-8 -*-
|
||
|
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||
|
|
||
|
import logging
|
||
|
from threading import Thread
|
||
|
import time
|
||
|
|
||
|
from odoo.addons.hw_drivers.main import drivers, interfaces, iot_devices
|
||
|
|
||
|
_logger = logging.getLogger(__name__)
|
||
|
|
||
|
|
||
|
class InterfaceMetaClass(type):
|
||
|
def __new__(cls, clsname, bases, attrs):
|
||
|
new_interface = super(InterfaceMetaClass, cls).__new__(cls, clsname, bases, attrs)
|
||
|
interfaces[clsname] = new_interface
|
||
|
return new_interface
|
||
|
|
||
|
|
||
|
class Interface(Thread, metaclass=InterfaceMetaClass):
|
||
|
_loop_delay = 3 # Delay (in seconds) between calls to get_devices or 0 if it should be called only once
|
||
|
_detected_devices = {}
|
||
|
connection_type = ''
|
||
|
|
||
|
def __init__(self):
|
||
|
super(Interface, self).__init__()
|
||
|
self.drivers = sorted([d for d in drivers if d.connection_type == self.connection_type], key=lambda d: d.priority, reverse=True)
|
||
|
|
||
|
def run(self):
|
||
|
while self.connection_type and self.drivers:
|
||
|
self.update_iot_devices(self.get_devices())
|
||
|
if not self._loop_delay:
|
||
|
break
|
||
|
time.sleep(self._loop_delay)
|
||
|
|
||
|
def update_iot_devices(self, devices={}):
|
||
|
added = devices.keys() - self._detected_devices
|
||
|
removed = self._detected_devices - devices.keys()
|
||
|
# keys() returns a dict_keys, and the values of that stay in sync with the
|
||
|
# original dictionary if it changes. This means that get_devices needs to return
|
||
|
# a newly created dictionary every time. If it doesn't do that and reuses the
|
||
|
# same dictionary, this logic won't detect any changes that are made. Could be
|
||
|
# avoided by converting the dict_keys into a regular dict. The current logic
|
||
|
# also can't detect if a device is replaced by a different one with the same
|
||
|
# key. Also, _detected_devices starts out as a class variable but gets turned
|
||
|
# into an instance variable here. It would be better if it was an instance
|
||
|
# variable from the start to avoid confusion.
|
||
|
self._detected_devices = devices.keys()
|
||
|
|
||
|
for identifier in removed:
|
||
|
if identifier in iot_devices:
|
||
|
iot_devices[identifier].disconnect()
|
||
|
_logger.info('Device %s is now disconnected', identifier)
|
||
|
|
||
|
for identifier in added:
|
||
|
for driver in self.drivers:
|
||
|
if driver.supported(devices[identifier]):
|
||
|
_logger.info('Device %s is now connected', identifier)
|
||
|
d = driver(identifier, devices[identifier])
|
||
|
d.daemon = True
|
||
|
iot_devices[identifier] = d
|
||
|
# Start the thread after creating the iot_devices entry so the
|
||
|
# thread can assume the iot_devices entry will exist while it's
|
||
|
# running, at least until the `disconnect` above gets triggered
|
||
|
# when `removed` is not empty.
|
||
|
d.start()
|
||
|
break
|
||
|
|
||
|
def get_devices(self):
|
||
|
raise NotImplementedError()
|