diff --git a/platypush/plugins/sensor/distance/vl53l1x/__init__.py b/platypush/plugins/sensor/distance/vl53l1x/__init__.py index 238fd929..0e366cc7 100644 --- a/platypush/plugins/sensor/distance/vl53l1x/__init__.py +++ b/platypush/plugins/sensor/distance/vl53l1x/__init__.py @@ -1,49 +1,84 @@ -import time +from contextlib import contextmanager +from threading import RLock +from typing import List, Mapping +from typing_extensions import override -from platypush.plugins import action -from platypush.plugins.gpio.sensor import GpioSensorPlugin +from platypush.context import get_bus +from platypush.entities.managers.sensors import SensorEntityManager +from platypush.entities.devices import Device +from platypush.entities.distance import DistanceSensor +from platypush.message.event.sensor import SensorDataChangeEvent +from platypush.plugins import RunnablePlugin, action +from platypush.utils import get_plugin_name_by_class -class GpioSensorDistanceVl53L1XPlugin(GpioSensorPlugin): +class SensorDistanceVl53l1xPlugin(RunnablePlugin, SensorEntityManager): """ - Plugin to interact with an `VL53L1x `_ + Plugin to interact with an `VL53L1x + `_ laser ranger/distance sensor Requires: * ``smbus2`` (``pip install smbus2``) * ``vl53l1x`` (``pip install vl53l1x``) + + Triggers: + + * :class:`platypush.message.event.sensor.SensorDataChangeEvent` + """ - def __init__(self, i2c_bus=1, i2c_address=0x29, **kwargs): + def __init__(self, i2c_bus=1, i2c_address=0x29, poll_interval=3, **kwargs): """ :param i2c_bus: I2C bus number (default: 1) :param i2c_address: I2C address (default: 0x29) + :param poll_interval: How often the integration should poll for new + measurements (default: 3 seconds). """ - super().__init__(**kwargs) + super().__init__(poll_interval=poll_interval, **kwargs) self.i2c_bus = i2c_bus self.i2c_address = i2c_address self._device = None + self._device_lock = RLock() + self._last_data = {} - # noinspection PyUnresolvedReferences + @contextmanager def _get_device(self, ranging=1): - if self._device: - return self._device + from VL53L1X import VL53L1X # type: ignore - from VL53L1X import VL53L1X - self._device = VL53L1X(i2c_bus=self.i2c_bus, i2c_address=self.i2c_address) - self._device.open() - self._device.start_ranging(ranging) - return self._device + with self._device_lock: + if self._device: + yield self._device + + self._device = VL53L1X(i2c_bus=self.i2c_bus, i2c_address=self.i2c_address) + + try: + self._device.open() + self._device.start_ranging(ranging) + yield self._device + finally: + try: + self._device.stop_ranging() + except Exception: + pass + + self._device.close() + self._device = None @action - def get_measurement(self, short=True, medium=False, long=False): + @override + def status(self, *_, **__): + self.publish_entities(self._last_data) + return self._last_data + + def _status(self, *_, short=True, medium=True, long=True, **__): """ :param short: Enable short range measurement (default: True) - :param medium: Enable medium range measurement (default: False) - :param long: Enable long range measurement (default: False) + :param medium: Enable medium range measurement (default: True) + :param long: Enable long range measurement (default: True) :returns: dict. Example: @@ -51,40 +86,90 @@ class GpioSensorDistanceVl53L1XPlugin(GpioSensorPlugin): output = { "short": 83, # Short range measurement in mm - "medium": 103, # Medium range measurement - "long": 43, # Long range measurement + "medium": 103, # Medium range measurement in mm + "long": 200, # Long range measurement } """ + ret = {} range_idx = 0 range_name = None + range_conf = { + 'short': short, + 'medium': medium, + 'long': long, + } - for i, r in enumerate(['short', 'medium', 'long']): - if eval(r): - range_idx = i+1 - range_name = r - break + for i, (key, enabled) in enumerate(range_conf.items()): + if not enabled: + continue - assert range_name is not None - device = self._get_device(ranging=range_idx) - ret = {} + range_idx = i + 1 + range_name = key - try: - ret[range_name] = device.get_distance() - except Exception as e: - self.logger.exception(e) - - try: - self._device.stop_ranging() - self._device.close() - except Exception as e: - self.logger.warning('Error while closing device: {}'.format(str(e))) - - self._device = None - time.sleep(1) + with self._get_device(ranging=range_idx) as device: + try: + ret[range_name] = device.get_distance() + except Exception as e: + self.logger.exception(e) + self.wait_stop(1) return ret + @action + def get_data(self, *args, **kwargs): + """ + (Deprecated) alias for :meth:`.status`. + """ + return self.status(*args, **kwargs) + + @action + def get_measurement(self, *args, **kwargs): + """ + (Deprecated) alias for :meth:`.status`. + """ + return self.status(*args, **kwargs) + + @override + def transform_entities(self, entities: Mapping[str, int]) -> List[Device]: + return super().transform_entities( # type: ignore + [ + Device( + id='vl53l1x', + name='VL53L1X Distance Sensor', + children=[ + DistanceSensor( + id=f'vl53l1x:{key}', + name=f'{key} distance', + value=value, + unit='mm', + ) + for key, value in entities.items() + ], + ) + ] + ) + + @override + def publish_entities(self, entities: Mapping[str, int], *_, **__) -> List[Device]: + get_bus().post( + SensorDataChangeEvent( + data=entities, + source=get_plugin_name_by_class(self.__class__), + ) + ) + return super().publish_entities(entities) # type: ignore + + @override + def main(self): + while not self.should_stop(): + status = self._status() + if status != self._last_data: + self.publish_entities(status) + + self._last_data = status + self.wait_stop(self.poll_interval) + # vim:sw=4:ts=4:et: diff --git a/platypush/plugins/sensor/distance/vl53l1x/manifest.yaml b/platypush/plugins/sensor/distance/vl53l1x/manifest.yaml index ed530f87..f041778e 100644 --- a/platypush/plugins/sensor/distance/vl53l1x/manifest.yaml +++ b/platypush/plugins/sensor/distance/vl53l1x/manifest.yaml @@ -1,8 +1,10 @@ manifest: - events: [] + events: + platypush.message.event.sensor import SensorDataChangeEvent: + install: pip: - smbus2 - vl53l1x - package: platypush.plugins.gpio.sensor.distance.vl53l1x + package: platypush.plugins.sensor.distance.vl53l1x type: plugin