interface.py 3.0 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071
  1. # -*- coding: utf-8 -*-
  2. # Part of Odoo. See LICENSE file for full copyright and licensing details.
  3. import logging
  4. from threading import Thread
  5. import time
  6. from odoo.addons.hw_drivers.main import drivers, interfaces, iot_devices
  7. _logger = logging.getLogger(__name__)
  8. class InterfaceMetaClass(type):
  9. def __new__(cls, clsname, bases, attrs):
  10. new_interface = super(InterfaceMetaClass, cls).__new__(cls, clsname, bases, attrs)
  11. interfaces[clsname] = new_interface
  12. return new_interface
  13. class Interface(Thread, metaclass=InterfaceMetaClass):
  14. _loop_delay = 3 # Delay (in seconds) between calls to get_devices or 0 if it should be called only once
  15. _detected_devices = {}
  16. connection_type = ''
  17. def __init__(self):
  18. super(Interface, self).__init__()
  19. self.drivers = sorted([d for d in drivers if d.connection_type == self.connection_type], key=lambda d: d.priority, reverse=True)
  20. def run(self):
  21. while self.connection_type and self.drivers:
  22. self.update_iot_devices(self.get_devices())
  23. if not self._loop_delay:
  24. break
  25. time.sleep(self._loop_delay)
  26. def update_iot_devices(self, devices={}):
  27. added = devices.keys() - self._detected_devices
  28. removed = self._detected_devices - devices.keys()
  29. # keys() returns a dict_keys, and the values of that stay in sync with the
  30. # original dictionary if it changes. This means that get_devices needs to return
  31. # a newly created dictionary every time. If it doesn't do that and reuses the
  32. # same dictionary, this logic won't detect any changes that are made. Could be
  33. # avoided by converting the dict_keys into a regular dict. The current logic
  34. # also can't detect if a device is replaced by a different one with the same
  35. # key. Also, _detected_devices starts out as a class variable but gets turned
  36. # into an instance variable here. It would be better if it was an instance
  37. # variable from the start to avoid confusion.
  38. self._detected_devices = devices.keys()
  39. for identifier in removed:
  40. if identifier in iot_devices:
  41. iot_devices[identifier].disconnect()
  42. _logger.info('Device %s is now disconnected', identifier)
  43. for identifier in added:
  44. for driver in self.drivers:
  45. if driver.supported(devices[identifier]):
  46. _logger.info('Device %s is now connected', identifier)
  47. d = driver(identifier, devices[identifier])
  48. d.daemon = True
  49. iot_devices[identifier] = d
  50. # Start the thread after creating the iot_devices entry so the
  51. # thread can assume the iot_devices entry will exist while it's
  52. # running, at least until the `disconnect` above gets triggered
  53. # when `removed` is not empty.
  54. d.start()
  55. break
  56. def get_devices(self):
  57. raise NotImplementedError()