appd - Completed redesign of entity manager

This commit is contained in:
Thomas Lovén 2018-12-28 21:47:23 +01:00
parent 301ea0304d
commit a070dc679f
7 changed files with 103 additions and 177 deletions

View File

@ -1,168 +0,0 @@
import appdaemon.plugins.hass.hassapi as hass
class Base(hass.Hass):
def initialize(self):
if(getattr(super(), 'initialize', False)):
super().initialize()
class Timers(Base):
def initialize(self):
if(getattr(super(), 'initialize', False)):
super().initialize()
self._timers = {}
functions = [
'run_in',
'run_once',
'run_at',
'run_daily',
'run_hourly',
'run_minutely',
'run_every',
'run_at_sunrise',
'run_at_sunset',
]
for f in functions:
self._override(f)
setattr(self, '_cancel_timer', super().cancel_timer)
def cancel_timer(self, name, *args, **kwargs):
if type(name) is str:
if name in self._timers:
return super().cancel_timer(self._timers[name])
else:
return super().cancel_timer(*args, **kwargs)
def _override(self, f):
setattr(self, f'_{f}', getattr(self, f))
def fn(name, *args, **kwargs):
if type(name) is str:
if name in self._timers:
super().cancel_timer(self._timers[name])
self._timers[name] = getattr(self, f'_{f}')(*args, **kwargs)
return self._timers[name]
else:
return getattr(self, f'_{f}')(name, *args, **kwargs)
setattr(self, f, fn)
class Entities(Base):
def initialize(self):
if(getattr(super(), 'initialize', False)):
super().initialize()
self.e = {}
def register_entity(self, name, entity, managed=False, default=None, attributes=None):
domain, _ = entity.split('.')
controller = {
'light': Entities.LightEntity,
'input_datetime': Entities.InputDateTimeEntity,
'input_number': Entities.InputNumberEntity,
}.get(domain, Entities.Entity)
self.e[name] = controller(self, entity, managed, default, attributes)
class Entity:
def __init__(self, hass, entity, managed = False, default = None, attributes = None):
self._entity = entity
self._hass = hass
self._hass.listen_state(self._listener, entity=entity, attributes='all')
self._listeners = []
if managed:
if default:
self.state = default
self.update(attributes)
def listen(self, callback, kwarg=None):
""" Listen to changes to entity state """
self._listeners.append({
'callback': callback,
'kwarg': kwarg,
})
return self._listeners[-1]
def unlisten(self, handle):
""" Remove state change listener """
if handle in self._listeners:
self._listeners.remove(handle)
def _listener(self, entity, attribute, old, new, kwargs):
for l in self._listeners:
l['callback'](l['kwarg'])
def __getattr__(self, key):
if key == 'state':
if self.get_state:
return self.get_state()
return self._hass.get_state(self._entity)
return self._hass.get_state(self._entity,
attribute=key)
def __setattr__(self, key, value):
if key.startswith('_'):
self.__dict__[key] = value
return
if key == 'state':
if self.set_state:
self.set_state(value)
return self._hass.set_state(self._entity, state=value)
attr = self._hass.get_state(self._entity,
attribute='all')
attr = attr.get('attributes', {}) if attr else {}
attr[key] = value
self._hass.set_state(self._entity, attributes=attr)
def __delattr__(self, key):
if key.startswith('_'):
del self.__dict__[key]
return
attr = self._hass.get_state(self._entity,
attribute='all').get('attributes', {})
attr[key] = ''
self._hass.set_state(self._entity, attributes=attr)
def update(self, new):
attr = self._hass.get_state(self._entity, attribute='all').get('attributes', {})
attr.update(new)
self._hass.set_state(self._entity, attributes=attr)
class LightEntity(Entities.Entity):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
def set_state(self, state):
if state == 'on':
self._hass.call_service('light/turn_on', entity_id =
self._entity)
elif state == 'off':
self._hass.call_service('light/turn_off', entity_id =
self._entity)
else:
return
class InputNumberEntity(Entities.Entity):
def __init__(self, hass, entity, managed = False, default = None, attributes = None):
super().__init__(hass, entity, managed, default, attributes)
if managed:
hass.listen_event(self.service_callback, event = 'call_service')
def service_callback(self, event, data, kwargs):
if data['service_data'].get('entity_id', '') == self._entity and data['service'] == 'set_value':
self._hass.log("Value changed!")
class InputDateTimeEntity(Entities.Entity):
def __init__(self, hass, entity, managed = False, default = None, attributes = None):
super().__init__(hass, entity, managed, default, attributes)
if managed:
hass.listen_event(self.service_callback, event = 'call_service')
def service_callback(self, event, data, kwargs):
if data['service_data'].get('entity_id', '') == self._entity and data['service'] == 'set_datetime':
self._hass.log("Datetime changed!")
def set_state(self, state):
time = state.split(':')
time += ['00'] * (3 - len(time))
self._hass.set_state(self._entity, state=':'.join(time))
self.update({
'hour': int(time[0], base=10),
'minute': int(time[1], base=10),
'second': int(time[2], base=10),
})

View File

@ -1,7 +1,3 @@
global_modules:
- base
- entities
hello_world: hello_world:
global_dependencies: global_dependencies:
- base - base

View File

@ -0,0 +1,6 @@
import appdaemon.plugins.hass.hassapi as hass
class Base(hass.Hass):
def initialize(self):
if(getattr(super(), 'initialize', False)):
super().initialize()

View File

@ -1,10 +1,10 @@
import base import base
class Entities(base.Base): class Entities(base.Base):
def initialize(self): def initialize(self):
if(getattr(super(), 'initialize', False)): if(getattr(super(), 'initialize', False)):
super().initialize() super().initialize()
self.e = {} self.e = {}
def register_entity(self, name, entity, managed=False, default=None, attributes=None): def register_entity(self, name, entity, managed=False, default=None, attributes=None):
@ -12,10 +12,15 @@ class Entities(base.Base):
controller = { controller = {
'light': LightEntity, 'light': LightEntity,
'switch': SwitchEntity, 'switch': SwitchEntity,
'input_number': InputNumberEntity,
'input_datetime': InputDateTimeEntity,
'input_select': InputSelectEntity,
}.get(domain, Entity) }.get(domain, Entity)
self.e[name] = controller(self, entity, managed, default, attributes) self.e[name] = controller(self, entity, managed, default, attributes)
class Entity: class Entity:
def __init__(self, hass, entity, managed=False, state=None, attributes=None): def __init__(self, hass, entity, managed=False, state=None, attributes=None):
self._hass = hass self._hass = hass
self._entity = entity self._entity = entity
@ -33,15 +38,18 @@ class Entity:
else: else:
self.pull() self.pull()
# State change listeners
def listen(self, callback, kwarg=None): def listen(self, callback, kwarg=None):
self._listeners.append({ self._listeners.append({
'callback': callback, 'callback': callback,
'kwarg': kwarg, 'kwarg': kwarg,
}) })
return self._listeners[-1] return self._listeners[-1]
def unlisten(self, handle): def unlisten(self, handle):
if handle in self._listeners: if handle in self._listeners:
self._listeners.remove(handle) self._listeners.remove(handle)
def _listener(self, entity, attribute, old, new, kwargs): def _listener(self, entity, attribute, old, new, kwargs):
self._state = new['state'] self._state = new['state']
self._attributes = new['attributes'] self._attributes = new['attributes']
@ -49,13 +57,13 @@ class Entity:
if old != new: if old != new:
self._laststate = new self._laststate = new
self._callback(old, new) self._callback(old, new)
def _callback(self, old, new): def _callback(self, old, new):
for l in self._listeners: for l in self._listeners:
l['callback'](old, new, l['kwarg']) l['callback'](old, new, l['kwarg'])
pass pass
# Updating state
def pull(self): def pull(self):
d = self._hass.get_state(self._entity, attribute='all') d = self._hass.get_state(self._entity, attribute='all')
self._state = d['state'] self._state = d['state']
@ -74,12 +82,15 @@ class Entity:
def set_state(self, old, new): def set_state(self, old, new):
pass pass
# If the entity is controller by appd, changes made in the GUI will be communicated via service calls
def _service_listener(self, event, data, kwarg): def _service_listener(self, event, data, kwarg):
if data['service_data'].get('entity_id', '') == self._entity: if data['service_data'].get('entity_id', '') == self._entity:
self.service_callback(data) self.service_callback(data)
def service_callback(self, data): def service_callback(self, data):
pass pass
#
@property @property
def state(self): def state(self):
return self._state return self._state
@ -91,17 +102,51 @@ class Entity:
def attr(self): def attr(self):
return self._attributes return self._attributes
class LightEntity(Entity): class LightEntity(Entity):
def set_state(self, old, new): def set_state(self, old, new):
if new == "on": if new == "on":
self._hass.call_service("light/turn_on", entity_id = self._entity) self._hass.call_service("light/turn_on", entity_id = self._entity)
elif new == "off": elif new == "off":
self._hass.call_service("light/turn_off", entity_id = self._entity) self._hass.call_service("light/turn_off", entity_id = self._entity)
class SwitchEntity(Entity): class SwitchEntity(Entity):
def service_callback(self, data): def service_callback(self, data):
if data['service'] == 'turn_on': if data['service'] == 'turn_on':
self._state = "on" self._state = "on"
if data['service'] == 'turn_off': if data['service'] == 'turn_off':
self._state = "off" self._state = "off"
self.push() self.push()
class InputNumberEntity(Entity):
def service_callback(self, data):
if data['domain'] == 'input_number' and data['service'] == 'set_value':
self._state = data['service_data']['value']
self.push()
class InputDateTimeEntity(Entity):
def set_state(self, old, new):
time = new.split(':')
time += ["00"] * (3 - len(time))
self._state = ':'.join(time)
self.attr['hour'] = int(time[0], base=10)
self.attr['minute'] = int(time[1], base=10)
self.attr['seconf'] = int(time[2], base=10)
def service_callback(self, data):
if data['service'] == 'set_datetime':
self._state = data['service_data'].get('time', "00:00:00")
self.push()
class InputSelectEntity(Entity):
def service_callback(self, data):
if data['service'] == 'select_option':
self._state = data['service_data'].get('option', None)
self.push()

View File

@ -0,0 +1,4 @@
global_modules:
- base
- entities
- timers

View File

@ -0,0 +1,42 @@
import base
class Timers(base.Base):
def initialize(self):
if(getattr(super(), 'initialize', False)):
super().initialize()
self._timers = {}
functions = [
'run_in',
'run_once',
'run_at',
'run_daily',
'run_hourly',
'run_minutely',
'run_every',
'run_at_sunrise',
'run_at_sunset',
]
for f in functions:
self._override(f)
setattr(self, '_cancel_timer', super().cancel_timer)
def cancel_timer(self, name, *args, **kwargs):
if type(name) is str:
if name in self._timers:
return super().cancel_timer(self._timers[name])
else:
return super().cancel_timer(*args, **kwargs)
def _override(self, f):
setattr(self, f'_{f}', getattr(self, f))
def fn(name, *args, **kwargs):
if type(name) is str:
if name in self._timers:
super().cancel_timer(self._timers[name])
self._timers[name] = getattr(self, f'_{f}')(*args, **kwargs)
return self._timers[name]
else:
return getattr(self, f'_{f}')(name, *args, **kwargs)
setattr(self, f, fn)

View File

@ -1,5 +1,6 @@
import base import base
class TimeOfDay(base.Entities): import entities
class TimeOfDay(entities.Entities):
def initialize(self): def initialize(self):
super().initialize() super().initialize()
@ -18,5 +19,5 @@ class TimeOfDay(base.Entities):
self.e['sunrise'].listen(self.input_listener, {'changed': "sunrise"}) self.e['sunrise'].listen(self.input_listener, {'changed': "sunrise"})
def input_listener(self, kwargs): def input_listener(self, old, new, kwargs):
self.log(kwargs) self.log(kwargs)