diff --git a/docs/source/backends.rst b/docs/source/backends.rst index c32a4516..ffda2c4b 100644 --- a/docs/source/backends.rst +++ b/docs/source/backends.rst @@ -15,6 +15,7 @@ Backends platypush/backend/http.rst platypush/backend/inotify.rst platypush/backend/kafka.rst + platypush/backend/light.hue.rst platypush/backend/midi.rst platypush/backend/mqtt.rst platypush/backend/music.mpd.rst diff --git a/docs/source/events.rst b/docs/source/events.rst index 3aba2819..3f2a81e8 100644 --- a/docs/source/events.rst +++ b/docs/source/events.rst @@ -13,6 +13,7 @@ Events platypush/events/http.rst platypush/events/joystick.rst platypush/events/kafka.rst + platypush/events/light.rst platypush/events/midi.rst platypush/events/music.rst platypush/events/music.snapcast.rst diff --git a/platypush/backend/light/hue.py b/platypush/backend/light/hue.py new file mode 100644 index 00000000..8a0f0455 --- /dev/null +++ b/platypush/backend/light/hue.py @@ -0,0 +1,93 @@ +import json +import time + +from threading import Thread + +from platypush.backend import Backend +from platypush.context import get_plugin +from platypush.message.event.light import LightStatusChangeEvent + + +class LightHueBackend(Backend): + """ + This backend will periodically check for the status of your configured + Philips Hue light devices and trigger events when the status of a device + (power, saturation, brightness or hue) changes. + + Triggers: + + * :class:`platypush.message.event.light.LightStatusChangeEvent` when the + status of a lightbulb changes + + Requires: + + * The :class:`platypush.plugins.light.hue.LightHuePlugin` plugin to be + active and configured. + """ + + _DEFAULT_POLL_SECONDS = 10 + + def __init__(self, poll_seconds=_DEFAULT_POLL_SECONDS, *args, **kwargs): + """ + :param poll_seconds: How often the backend will poll the Hue plugin for + status updates. Default: 10 seconds + :type poll_seconds: float + """ + + super().__init__(*args, **kwargs) + self.poll_seconds = poll_seconds + + + def _get_lights(self): + return get_plugin('light.hue').get_lights().output + + def _listener(self): + def _thread(): + lights = self._get_lights() + + while not self.should_stop(): + try: + lights_new = self._get_lights() + + for light_id, light in lights_new.items(): + event_args = {} + state = light.get('state') + prev_state = lights.get(light_id, {}).get('state', {}) + + if 'on' in state and state.get('on') != prev_state.get('on'): + event_args['on'] = state.get('on') + if 'bri' in state and state.get('bri') != prev_state.get('bri'): + event_args['bri'] = state.get('bri') + if 'sat' in state and state.get('sat') != prev_state.get('sat'): + event_args['sat'] = state.get('sat') + if 'hue' in state and state.get('hue') != prev_state.get('hue'): + event_args['hue'] = state.get('hue') + if 'ct' in state and state.get('ct') != prev_state.get('ct'): + event_args['ct'] = state.get('ct') + + if event_args: + event_args['light_id'] = light_id + event_args['light_name'] = light.get('name') + self.bus.post(LightStatusChangeEvent(**event_args)) + + lights = lights_new + except Exception as e: + self.logger.exception(e) + finally: + time.sleep(self.poll_seconds) + + return _thread + + def run(self): + super().run() + + while not self.should_stop(): + try: + poll_thread = Thread(target=self._listener()) + poll_thread.start() + poll_thread.join() + except Exception as e: + self.logger.exception(e) + time.sleep(self.poll_seconds) + +# vim:sw=4:ts=4:et: diff --git a/platypush/message/event/light.py b/platypush/message/event/light.py new file mode 100644 index 00000000..aa88779f --- /dev/null +++ b/platypush/message/event/light.py @@ -0,0 +1,55 @@ +from platypush.message.event import Event + + +class LightStatusChangeEvent(Event): + """ + Event triggered when the state of a lightbulb changes + """ + + def __init__(self, light_id=None, light_name=None, on=None, bri=None, + sat=None, hue=None, ct=None, *args, **kwargs): + """ + :param light_id: Light ID that triggered the event + :type light_id: int + + :param light_name: Light name that triggered the event + :type light_name: str + + :param on: Set if the power state of the bulb changed + :type on: bool + + :param bri: Set if the brightness state of the bulb changed + :type bri: int + + :param sat: Set if the saturation state of the bulb changed + :type sat: int + + :param hue: Set if the hue state of the bulb changed + :type hue: int + + :param ct: Set if the color temperature state of the bulb changed + :type ct: int + """ + + attrs = {} + + if light_id is not None: + attrs['light_id'] = light_id + if light_name is not None: + attrs['light_name'] = light_name + if on is not None: + attrs['on'] = on + if bri is not None: + attrs['bri'] = bri + if sat is not None: + attrs['sat'] = sat + if hue is not None: + attrs['hue'] = hue + if ct is not None: + attrs['ct'] = ct + + super().__init__(*args, **attrs, **kwargs) + + +# vim:sw=4:ts=4:et: +