forked from platypush/platypush
Use a threading Event to synchronize with the Hue animation thread instead of relying on the Redis backend
This commit is contained in:
parent
6d7f1502ce
commit
77530b4a06
1 changed files with 58 additions and 71 deletions
|
@ -3,11 +3,8 @@ import statistics
|
||||||
import time
|
import time
|
||||||
|
|
||||||
from enum import Enum
|
from enum import Enum
|
||||||
from threading import Thread
|
from threading import Thread, Event
|
||||||
from redis import Redis
|
|
||||||
from redis.exceptions import TimeoutError as QueueTimeoutError
|
|
||||||
|
|
||||||
from platypush.context import get_backend
|
|
||||||
from platypush.plugins import action
|
from platypush.plugins import action
|
||||||
from platypush.plugins.light import LightPlugin
|
from platypush.plugins.light import LightPlugin
|
||||||
from platypush.utils import set_thread_name
|
from platypush.utils import set_thread_name
|
||||||
|
@ -58,7 +55,8 @@ class LightHuePlugin(LightPlugin):
|
||||||
self.logger.info('Initializing Hue lights plugin - bridge: "{}"'.format(self.bridge_address))
|
self.logger.info('Initializing Hue lights plugin - bridge: "{}"'.format(self.bridge_address))
|
||||||
|
|
||||||
self.connect()
|
self.connect()
|
||||||
self.lights = []; self.groups = []
|
self.lights = []
|
||||||
|
self.groups = []
|
||||||
|
|
||||||
if lights:
|
if lights:
|
||||||
self.lights = lights
|
self.lights = lights
|
||||||
|
@ -66,19 +64,20 @@ class LightHuePlugin(LightPlugin):
|
||||||
self.groups = groups
|
self.groups = groups
|
||||||
self._expand_groups()
|
self._expand_groups()
|
||||||
else:
|
else:
|
||||||
self.lights = [l.name for l in self.bridge.lights]
|
# noinspection PyUnresolvedReferences
|
||||||
|
self.lights = [light.name for light in self.bridge.lights]
|
||||||
|
|
||||||
self.redis = None
|
|
||||||
self.animation_thread = None
|
self.animation_thread = None
|
||||||
self.animations = {}
|
self.animations = {}
|
||||||
|
self._animation_stop = Event()
|
||||||
self._init_animations()
|
self._init_animations()
|
||||||
self.logger.info('Configured lights: "{}"'.format(self.lights))
|
self.logger.info('Configured lights: "{}"'.format(self.lights))
|
||||||
|
|
||||||
def _expand_groups(self):
|
def _expand_groups(self):
|
||||||
groups = [g for g in self.bridge.groups if g.name in self.groups]
|
groups = [g for g in self.bridge.groups if g.name in self.groups]
|
||||||
for g in groups:
|
for group in groups:
|
||||||
for l in g.lights:
|
for light in group.lights:
|
||||||
self.lights += [l.name]
|
self.lights += [light.name]
|
||||||
|
|
||||||
def _init_animations(self):
|
def _init_animations(self):
|
||||||
self.animations = {
|
self.animations = {
|
||||||
|
@ -86,10 +85,10 @@ class LightHuePlugin(LightPlugin):
|
||||||
'lights': {},
|
'lights': {},
|
||||||
}
|
}
|
||||||
|
|
||||||
for g in self.bridge.groups:
|
for group in self.bridge.groups:
|
||||||
self.animations['groups'][g.group_id] = None
|
self.animations['groups'][group.group_id] = None
|
||||||
for l in self.bridge.lights:
|
for light in self.bridge.lights:
|
||||||
self.animations['lights'][l.light_id] = None
|
self.animations['lights'][light.light_id] = None
|
||||||
|
|
||||||
@action
|
@action
|
||||||
def connect(self):
|
def connect(self):
|
||||||
|
@ -295,7 +294,9 @@ class LightHuePlugin(LightPlugin):
|
||||||
self.bridge = None
|
self.bridge = None
|
||||||
raise e
|
raise e
|
||||||
|
|
||||||
lights = []; groups = []
|
lights = []
|
||||||
|
groups = []
|
||||||
|
|
||||||
if 'lights' in kwargs:
|
if 'lights' in kwargs:
|
||||||
lights = kwargs.pop('lights').split(',').strip() \
|
lights = kwargs.pop('lights').split(',').strip() \
|
||||||
if isinstance(lights, str) else kwargs.pop('lights')
|
if isinstance(lights, str) else kwargs.pop('lights')
|
||||||
|
@ -515,6 +516,8 @@ class LightHuePlugin(LightPlugin):
|
||||||
|
|
||||||
:param value: xY value
|
:param value: xY value
|
||||||
:type value: list[float] containing the two values
|
:type value: list[float] containing the two values
|
||||||
|
:param lights: List of lights.
|
||||||
|
:param groups: List of groups.
|
||||||
"""
|
"""
|
||||||
if groups is None:
|
if groups is None:
|
||||||
groups = []
|
groups = []
|
||||||
|
@ -529,6 +532,8 @@ class LightHuePlugin(LightPlugin):
|
||||||
|
|
||||||
:param value: Temperature value (range: 0-255)
|
:param value: Temperature value (range: 0-255)
|
||||||
:type value: int
|
:type value: int
|
||||||
|
:param lights: List of lights.
|
||||||
|
:param groups: List of groups.
|
||||||
"""
|
"""
|
||||||
if groups is None:
|
if groups is None:
|
||||||
groups = []
|
groups = []
|
||||||
|
@ -550,7 +555,6 @@ class LightHuePlugin(LightPlugin):
|
||||||
groups = []
|
groups = []
|
||||||
if lights is None:
|
if lights is None:
|
||||||
lights = []
|
lights = []
|
||||||
bri = 0
|
|
||||||
|
|
||||||
if lights:
|
if lights:
|
||||||
bri = statistics.mean([
|
bri = statistics.mean([
|
||||||
|
@ -595,7 +599,6 @@ class LightHuePlugin(LightPlugin):
|
||||||
groups = []
|
groups = []
|
||||||
if lights is None:
|
if lights is None:
|
||||||
lights = []
|
lights = []
|
||||||
sat = 0
|
|
||||||
|
|
||||||
if lights:
|
if lights:
|
||||||
sat = statistics.mean([
|
sat = statistics.mean([
|
||||||
|
@ -640,7 +643,6 @@ class LightHuePlugin(LightPlugin):
|
||||||
groups = []
|
groups = []
|
||||||
if lights is None:
|
if lights is None:
|
||||||
lights = []
|
lights = []
|
||||||
hue = 0
|
|
||||||
|
|
||||||
if lights:
|
if lights:
|
||||||
hue = statistics.mean([
|
hue = statistics.mean([
|
||||||
|
@ -693,17 +695,15 @@ class LightHuePlugin(LightPlugin):
|
||||||
:returns: True if there is an animation running, false otherwise.
|
:returns: True if there is an animation running, false otherwise.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
return self.animation_thread is not None
|
return self.animation_thread is not None and self.animation_thread.is_alive()
|
||||||
|
|
||||||
@action
|
@action
|
||||||
def stop_animation(self):
|
def stop_animation(self):
|
||||||
"""
|
"""
|
||||||
Stop a running animation if any
|
Stop a running animation.
|
||||||
"""
|
"""
|
||||||
|
if self.is_animation_running():
|
||||||
if self.animation_thread and self.animation_thread.is_alive():
|
self._animation_stop.set()
|
||||||
redis = self._get_redis()
|
|
||||||
redis.rpush(self.ANIMATION_CTRL_QUEUE_NAME, 'STOP')
|
|
||||||
self._init_animations()
|
self._init_animations()
|
||||||
|
|
||||||
@action
|
@action
|
||||||
|
@ -756,10 +756,10 @@ class LightHuePlugin(LightPlugin):
|
||||||
if groups:
|
if groups:
|
||||||
groups = [g for g in self.bridge.groups if g.name in groups or g.group_id in groups]
|
groups = [g for g in self.bridge.groups if g.name in groups or g.group_id in groups]
|
||||||
lights = lights or []
|
lights = lights or []
|
||||||
for g in groups:
|
for group in groups:
|
||||||
lights.extend([l.name for l in g.lights])
|
lights.extend([light.name for light in group.lights])
|
||||||
elif lights:
|
elif lights:
|
||||||
lights = [l.name for l in self.bridge.lights if l.name in lights or l.light_id in lights]
|
lights = [light.name for light in self.bridge.lights if light.name in lights or light.light_id in lights]
|
||||||
else:
|
else:
|
||||||
lights = self.lights
|
lights = self.lights
|
||||||
|
|
||||||
|
@ -776,26 +776,26 @@ class LightHuePlugin(LightPlugin):
|
||||||
}
|
}
|
||||||
|
|
||||||
if groups:
|
if groups:
|
||||||
for g in groups:
|
for group in groups:
|
||||||
self.animations['groups'][g.group_id] = info
|
self.animations['groups'][group.group_id] = info
|
||||||
|
|
||||||
for l in self.bridge.lights:
|
for light in self.bridge.lights:
|
||||||
if l.name in lights:
|
if light.name in lights:
|
||||||
self.animations['lights'][l.light_id] = info
|
self.animations['lights'][light.light_id] = info
|
||||||
|
|
||||||
def _initialize_light_attrs(lights):
|
def _initialize_light_attrs(lights):
|
||||||
if animation == self.Animation.COLOR_TRANSITION:
|
if animation == self.Animation.COLOR_TRANSITION:
|
||||||
return { l: {
|
return {light: {
|
||||||
'hue': random.randint(hue_range[0], hue_range[1]),
|
'hue': random.randint(hue_range[0], hue_range[1]),
|
||||||
'sat': random.randint(sat_range[0], sat_range[1]),
|
'sat': random.randint(sat_range[0], sat_range[1]),
|
||||||
'bri': random.randint(bri_range[0], bri_range[1]),
|
'bri': random.randint(bri_range[0], bri_range[1]),
|
||||||
} for l in lights }
|
} for light in lights}
|
||||||
elif animation == self.Animation.BLINK:
|
elif animation == self.Animation.BLINK:
|
||||||
return { l: {
|
return {light: {
|
||||||
'on': True,
|
'on': True,
|
||||||
'bri': self.MAX_BRI,
|
'bri': self.MAX_BRI,
|
||||||
'transitiontime': 0,
|
'transitiontime': 0,
|
||||||
} for l in lights }
|
} for light in lights}
|
||||||
|
|
||||||
def _next_light_attrs(lights):
|
def _next_light_attrs(lights):
|
||||||
if animation == self.Animation.COLOR_TRANSITION:
|
if animation == self.Animation.COLOR_TRANSITION:
|
||||||
|
@ -827,12 +827,7 @@ class LightHuePlugin(LightPlugin):
|
||||||
return lights
|
return lights
|
||||||
|
|
||||||
def _should_stop():
|
def _should_stop():
|
||||||
try:
|
return self._animation_stop.is_set()
|
||||||
redis = self._get_redis(transition_seconds)
|
|
||||||
redis.blpop(self.ANIMATION_CTRL_QUEUE_NAME)
|
|
||||||
return True
|
|
||||||
except QueueTimeoutError:
|
|
||||||
return False
|
|
||||||
|
|
||||||
def _animate_thread(lights):
|
def _animate_thread(lights):
|
||||||
set_thread_name('HueAnimate')
|
set_thread_name('HueAnimate')
|
||||||
|
@ -874,24 +869,16 @@ class LightHuePlugin(LightPlugin):
|
||||||
|
|
||||||
self.logger.info('Stopping animation')
|
self.logger.info('Stopping animation')
|
||||||
self.animation_thread = None
|
self.animation_thread = None
|
||||||
self.redis = None
|
|
||||||
|
|
||||||
self.stop_animation()
|
self.stop_animation()
|
||||||
|
self._animation_stop.clear()
|
||||||
self.animation_thread = Thread(target=_animate_thread,
|
self.animation_thread = Thread(target=_animate_thread,
|
||||||
name='HueAnimate',
|
name='HueAnimate',
|
||||||
args=(lights,))
|
args=(lights,))
|
||||||
self.animation_thread.start()
|
self.animation_thread.start()
|
||||||
|
|
||||||
def _get_redis(self, socket_timeout=1.0):
|
|
||||||
if not self.redis:
|
|
||||||
redis_args = get_backend('redis').redis_args
|
|
||||||
redis_args['socket_timeout'] = socket_timeout
|
|
||||||
self.redis = Redis(**redis_args)
|
|
||||||
return self.redis
|
|
||||||
|
|
||||||
def status(self):
|
def status(self):
|
||||||
# TODO
|
# TODO
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
# vim:sw=4:ts=4:et:
|
# vim:sw=4:ts=4:et:
|
||||||
|
|
Loading…
Reference in a new issue