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')
|
||||||
|
@ -471,7 +472,7 @@ class LightHuePlugin(LightPlugin):
|
||||||
groups = []
|
groups = []
|
||||||
if lights is None:
|
if lights is None:
|
||||||
lights = []
|
lights = []
|
||||||
return self._exec('bri', int(value) % (self.MAX_BRI+1),
|
return self._exec('bri', int(value) % (self.MAX_BRI + 1),
|
||||||
lights=lights, groups=groups, **kwargs)
|
lights=lights, groups=groups, **kwargs)
|
||||||
|
|
||||||
@action
|
@action
|
||||||
|
@ -488,7 +489,7 @@ class LightHuePlugin(LightPlugin):
|
||||||
groups = []
|
groups = []
|
||||||
if lights is None:
|
if lights is None:
|
||||||
lights = []
|
lights = []
|
||||||
return self._exec('sat', int(value) % (self.MAX_SAT+1),
|
return self._exec('sat', int(value) % (self.MAX_SAT + 1),
|
||||||
lights=lights, groups=groups, **kwargs)
|
lights=lights, groups=groups, **kwargs)
|
||||||
|
|
||||||
@action
|
@action
|
||||||
|
@ -505,7 +506,7 @@ class LightHuePlugin(LightPlugin):
|
||||||
groups = []
|
groups = []
|
||||||
if lights is None:
|
if lights is None:
|
||||||
lights = []
|
lights = []
|
||||||
return self._exec('hue', int(value) % (self.MAX_HUE+1),
|
return self._exec('hue', int(value) % (self.MAX_HUE + 1),
|
||||||
lights=lights, groups=groups, **kwargs)
|
lights=lights, groups=groups, **kwargs)
|
||||||
|
|
||||||
@action
|
@action
|
||||||
|
@ -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([
|
||||||
|
@ -571,10 +575,10 @@ class LightHuePlugin(LightPlugin):
|
||||||
if light['name'] in self.lights
|
if light['name'] in self.lights
|
||||||
])
|
])
|
||||||
|
|
||||||
delta *= (self.MAX_BRI/100)
|
delta *= (self.MAX_BRI / 100)
|
||||||
if bri+delta < 0:
|
if bri + delta < 0:
|
||||||
bri = 0
|
bri = 0
|
||||||
elif bri+delta > self.MAX_BRI:
|
elif bri + delta > self.MAX_BRI:
|
||||||
bri = self.MAX_BRI
|
bri = self.MAX_BRI
|
||||||
else:
|
else:
|
||||||
bri += delta
|
bri += delta
|
||||||
|
@ -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([
|
||||||
|
@ -616,10 +619,10 @@ class LightHuePlugin(LightPlugin):
|
||||||
if light['name'] in self.lights
|
if light['name'] in self.lights
|
||||||
])
|
])
|
||||||
|
|
||||||
delta *= (self.MAX_SAT/100)
|
delta *= (self.MAX_SAT / 100)
|
||||||
if sat+delta < 0:
|
if sat + delta < 0:
|
||||||
sat = 0
|
sat = 0
|
||||||
elif sat+delta > self.MAX_SAT:
|
elif sat + delta > self.MAX_SAT:
|
||||||
sat = self.MAX_SAT
|
sat = self.MAX_SAT
|
||||||
else:
|
else:
|
||||||
sat += delta
|
sat += delta
|
||||||
|
@ -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([
|
||||||
|
@ -661,10 +663,10 @@ class LightHuePlugin(LightPlugin):
|
||||||
if light['name'] in self.lights
|
if light['name'] in self.lights
|
||||||
])
|
])
|
||||||
|
|
||||||
delta *= (self.MAX_HUE/100)
|
delta *= (self.MAX_HUE / 100)
|
||||||
if hue+delta < 0:
|
if hue + delta < 0:
|
||||||
hue = 0
|
hue = 0
|
||||||
elif hue+delta > self.MAX_HUE:
|
elif hue + delta > self.MAX_HUE:
|
||||||
hue = self.MAX_HUE
|
hue = self.MAX_HUE
|
||||||
else:
|
else:
|
||||||
hue += delta
|
hue += delta
|
||||||
|
@ -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,32 +776,32 @@ 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:
|
||||||
for (light, attrs) in lights.items():
|
for (light, attrs) in lights.items():
|
||||||
for (attr, value) in attrs.items():
|
for (attr, value) in attrs.items():
|
||||||
attr_range = [0,0]
|
attr_range = [0, 0]
|
||||||
attr_step = 0
|
attr_step = 0
|
||||||
|
|
||||||
if attr == 'hue':
|
if attr == 'hue':
|
||||||
|
@ -815,24 +815,19 @@ class LightHuePlugin(LightPlugin):
|
||||||
attr_step = sat_step
|
attr_step = sat_step
|
||||||
|
|
||||||
lights[light][attr] = ((value - attr_range[0] + attr_step) %
|
lights[light][attr] = ((value - attr_range[0] + attr_step) %
|
||||||
(attr_range[1]-attr_range[0]+1)) + \
|
(attr_range[1] - attr_range[0] + 1)) + \
|
||||||
attr_range[0]
|
attr_range[0]
|
||||||
elif animation == self.Animation.BLINK:
|
elif animation == self.Animation.BLINK:
|
||||||
lights = { light: {
|
lights = {light: {
|
||||||
'on': False if attrs['on'] else True,
|
'on': False if attrs['on'] else True,
|
||||||
'bri': self.MAX_BRI,
|
'bri': self.MAX_BRI,
|
||||||
'transitiontime': 0,
|
'transitiontime': 0,
|
||||||
} for (light, attrs) in lights.items() }
|
} for (light, attrs) in lights.items()}
|
||||||
|
|
||||||
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