Moved more methods out of HomeAssistant object

This commit is contained in:
Paulus Schoutsen 2014-11-30 18:42:52 -08:00
parent c08676aa81
commit 5835d502c7
13 changed files with 149 additions and 111 deletions

View file

@ -25,7 +25,7 @@ def setup(hass, config):
hass.track_time_change(print, second=[0, 30])
# See also (defined in homeassistant/__init__.py):
# hass.track_state_change
# hass.states.track_change
# hass.track_point_in_time
# Tells the bootstrapper that the component was succesfully initialized

View file

@ -27,7 +27,7 @@ EVENT_HOMEASSISTANT_START = "homeassistant_start"
EVENT_HOMEASSISTANT_STOP = "homeassistant_stop"
EVENT_STATE_CHANGED = "state_changed"
EVENT_TIME_CHANGED = "time_changed"
EVENT_CALL_SERVICE = "call_service"
EVENT_CALL_SERVICE = "services.call"
ATTR_NOW = "now"
ATTR_DOMAIN = "domain"
@ -92,42 +92,6 @@ class HomeAssistant(object):
self.stop()
def call_service(self, domain, service, service_data=None):
""" Fires event to call specified service. """
event_data = service_data or {}
event_data[ATTR_DOMAIN] = domain
event_data[ATTR_SERVICE] = service
self.bus.fire(EVENT_CALL_SERVICE, event_data)
def track_state_change(self, entity_ids, action,
from_state=None, to_state=None):
"""
Track specific state changes.
entity_ids, from_state and to_state can be string or list.
Use list to match multiple.
"""
from_state = _process_match_param(from_state)
to_state = _process_match_param(to_state)
# Ensure it is a list with entity ids we want to match on
if isinstance(entity_ids, str):
entity_ids = [entity_ids]
@ft.wraps(action)
def state_listener(event):
""" The listener that listens for specific state changes. """
if event.data['entity_id'] in entity_ids and \
'old_state' in event.data and \
_matcher(event.data['old_state'].state, from_state) and \
_matcher(event.data['new_state'].state, to_state):
action(event.data['entity_id'],
event.data['old_state'],
event.data['new_state'])
self.bus.listen(EVENT_STATE_CHANGED, state_listener)
def track_point_in_time(self, action, point_in_time):
"""
Adds a listener that fires once at or after a spefic point in time.
@ -231,6 +195,33 @@ class HomeAssistant(object):
self.bus.listen_once(event_type, listener)
def track_state_change(self, entity_ids, action,
from_state=None, to_state=None):
"""
Track specific state changes.
entity_ids, from_state and to_state can be string or list.
Use list to match multiple.
THIS METHOD IS DEPRECATED. Use hass.states.track_change
"""
_LOGGER.warning((
"hass.track_state_change is deprecated. "
"Use hass.states.track_change"))
self.states.track_change(entity_ids, action, from_state, to_state)
def call_service(self, domain, service, service_data=None):
"""
Fires event to call specified service.
THIS METHOD IS DEPRECATED. Use hass.services.call
"""
_LOGGER.warning((
"hass.services.call is deprecated. "
"Use hass.services.call"))
self.services.call(domain, service, service_data)
def _process_match_param(parameter):
""" Wraps parameter in a list if it is not one and returns it. """
@ -561,6 +552,33 @@ class StateMachine(object):
self._bus.fire(EVENT_STATE_CHANGED, event_data)
def track_change(self, entity_ids, action, from_state=None, to_state=None):
"""
Track specific state changes.
entity_ids, from_state and to_state can be string or list.
Use list to match multiple.
"""
from_state = _process_match_param(from_state)
to_state = _process_match_param(to_state)
# Ensure it is a list with entity ids we want to match on
if isinstance(entity_ids, str):
entity_ids = [entity_ids]
@ft.wraps(action)
def state_listener(event):
""" The listener that listens for specific state changes. """
if event.data['entity_id'] in entity_ids and \
'old_state' in event.data and \
_matcher(event.data['old_state'].state, from_state) and \
_matcher(event.data['new_state'].state, to_state):
action(event.data['entity_id'],
event.data['old_state'],
event.data['new_state'])
self._bus.listen(EVENT_STATE_CHANGED, state_listener)
# pylint: disable=too-few-public-methods
class ServiceCall(object):
@ -588,6 +606,7 @@ class ServiceRegistry(object):
self._services = {}
self._lock = threading.Lock()
self._pool = pool or create_worker_pool()
self._bus = bus
bus.listen(EVENT_CALL_SERVICE, self._event_to_service_call)
@property
@ -609,6 +628,23 @@ class ServiceRegistry(object):
else:
self._services[domain] = {service: service_func}
def call(self, domain, service, service_data=None):
"""
Fires event to call specified service.
This method will fire an event to call the service.
This event will be picked up by this ServiceRegistry and any
other ServiceRegistry that is listening on the EventBus.
Because the service is sent as an event you are not allowed to use
the keys ATTR_DOMAIN and ATTR_SERVICE in your service_data.
"""
event_data = service_data or {}
event_data[ATTR_DOMAIN] = domain
event_data[ATTR_SERVICE] = service
self._bus.fire(EVENT_CALL_SERVICE, event_data)
def _event_to_service_call(self, event):
""" Calls a service from an event. """
service_data = dict(event.data)

View file

@ -85,7 +85,7 @@ def turn_on(hass, entity_id=None, **service_data):
if entity_id is not None:
service_data[ATTR_ENTITY_ID] = entity_id
hass.call_service(ha.DOMAIN, SERVICE_TURN_ON, service_data)
hass.services.call(ha.DOMAIN, SERVICE_TURN_ON, service_data)
def turn_off(hass, entity_id=None, **service_data):
@ -93,7 +93,7 @@ def turn_off(hass, entity_id=None, **service_data):
if entity_id is not None:
service_data[ATTR_ENTITY_ID] = entity_id
hass.call_service(ha.DOMAIN, SERVICE_TURN_OFF, service_data)
hass.services.call(ha.DOMAIN, SERVICE_TURN_OFF, service_data)
def extract_entity_ids(hass, service):
@ -195,7 +195,7 @@ def setup(hass, config):
# ent_ids is a generator, convert it to a list.
data[ATTR_ENTITY_ID] = list(ent_ids)
hass.call_service(domain, service.service, data)
hass.services.call(domain, service.service, data)
hass.services.register(ha.DOMAIN, SERVICE_TURN_OFF, handle_turn_service)
hass.services.register(ha.DOMAIN, SERVICE_TURN_ON, handle_turn_service)

View file

@ -48,56 +48,56 @@ def turn_off(hass, entity_id=None):
""" Will turn off specified Chromecast or all. """
data = {components.ATTR_ENTITY_ID: entity_id} if entity_id else {}
hass.call_service(DOMAIN, components.SERVICE_TURN_OFF, data)
hass.services.call(DOMAIN, components.SERVICE_TURN_OFF, data)
def volume_up(hass, entity_id=None):
""" Send the chromecast the command for volume up. """
data = {components.ATTR_ENTITY_ID: entity_id} if entity_id else {}
hass.call_service(DOMAIN, components.SERVICE_VOLUME_UP, data)
hass.services.call(DOMAIN, components.SERVICE_VOLUME_UP, data)
def volume_down(hass, entity_id=None):
""" Send the chromecast the command for volume down. """
data = {components.ATTR_ENTITY_ID: entity_id} if entity_id else {}
hass.call_service(DOMAIN, components.SERVICE_VOLUME_DOWN, data)
hass.services.call(DOMAIN, components.SERVICE_VOLUME_DOWN, data)
def media_play_pause(hass, entity_id=None):
""" Send the chromecast the command for play/pause. """
data = {components.ATTR_ENTITY_ID: entity_id} if entity_id else {}
hass.call_service(DOMAIN, components.SERVICE_MEDIA_PLAY_PAUSE, data)
hass.services.call(DOMAIN, components.SERVICE_MEDIA_PLAY_PAUSE, data)
def media_play(hass, entity_id=None):
""" Send the chromecast the command for play/pause. """
data = {components.ATTR_ENTITY_ID: entity_id} if entity_id else {}
hass.call_service(DOMAIN, components.SERVICE_MEDIA_PLAY, data)
hass.services.call(DOMAIN, components.SERVICE_MEDIA_PLAY, data)
def media_pause(hass, entity_id=None):
""" Send the chromecast the command for play/pause. """
data = {components.ATTR_ENTITY_ID: entity_id} if entity_id else {}
hass.call_service(DOMAIN, components.SERVICE_MEDIA_PAUSE, data)
hass.services.call(DOMAIN, components.SERVICE_MEDIA_PAUSE, data)
def media_next_track(hass, entity_id=None):
""" Send the chromecast the command for next track. """
data = {components.ATTR_ENTITY_ID: entity_id} if entity_id else {}
hass.call_service(DOMAIN, components.SERVICE_MEDIA_NEXT_TRACK, data)
hass.services.call(DOMAIN, components.SERVICE_MEDIA_NEXT_TRACK, data)
def media_prev_track(hass, entity_id=None):
""" Send the chromecast the command for prev track. """
data = {components.ATTR_ENTITY_ID: entity_id} if entity_id else {}
hass.call_service(DOMAIN, components.SERVICE_MEDIA_PREV_TRACK, data)
hass.services.call(DOMAIN, components.SERVICE_MEDIA_PREV_TRACK, data)
# pylint: disable=too-many-locals, too-many-branches

View file

@ -92,8 +92,8 @@ def setup(hass, config):
# Track every time sun rises so we can schedule a time-based
# pre-sun set event
hass.track_state_change(sun.ENTITY_ID, schedule_light_on_sun_rise,
sun.STATE_BELOW_HORIZON, sun.STATE_ABOVE_HORIZON)
hass.states.track_change(sun.ENTITY_ID, schedule_light_on_sun_rise,
sun.STATE_BELOW_HORIZON, sun.STATE_ABOVE_HORIZON)
# If the sun is already above horizon
# schedule the time-based pre-sun set event
@ -152,12 +152,14 @@ def setup(hass, config):
light.turn_off(hass)
# Track home coming of each device
hass.track_state_change(device_entity_ids, check_light_on_dev_state_change,
components.STATE_NOT_HOME, components.STATE_HOME)
hass.states.track_change(
device_entity_ids, check_light_on_dev_state_change,
components.STATE_NOT_HOME, components.STATE_HOME)
# Track when all devices are gone to shut down lights
hass.track_state_change(device_tracker.ENTITY_ID_ALL_DEVICES,
check_light_on_dev_state_change,
components.STATE_HOME, components.STATE_NOT_HOME)
hass.states.track_change(
device_tracker.ENTITY_ID_ALL_DEVICES,
check_light_on_dev_state_change,
components.STATE_HOME, components.STATE_NOT_HOME)
return True

View file

@ -186,7 +186,7 @@ def setup_group(hass, name, entity_ids, user_defined=True):
if entity_id != ent_id]):
hass.states.set(group_entity_id, group_off, state_attr)
hass.track_state_change(entity_ids, update_group_state)
hass.states.track_change(entity_ids, update_group_state)
hass.states.set(group_entity_id, group_state, state_attr)

View file

@ -481,7 +481,7 @@ class RequestHandler(SimpleHTTPRequestHandler):
domain = path_match.group('domain')
service = path_match.group('service')
self.server.hass.call_service(domain, service, data)
self.server.hass.services.call(domain, service, data)
self._json_message("Service {}/{} called.".format(domain, service))

View file

@ -14,32 +14,32 @@ DEPENDENCIES = []
def volume_up(hass):
""" Press the keyboard button for volume up. """
hass.call_service(DOMAIN, components.SERVICE_VOLUME_UP)
hass.services.call(DOMAIN, components.SERVICE_VOLUME_UP)
def volume_down(hass):
""" Press the keyboard button for volume down. """
hass.call_service(DOMAIN, components.SERVICE_VOLUME_DOWN)
hass.services.call(DOMAIN, components.SERVICE_VOLUME_DOWN)
def volume_mute(hass):
""" Press the keyboard button for muting volume. """
hass.call_service(DOMAIN, components.SERVICE_VOLUME_MUTE)
hass.services.call(DOMAIN, components.SERVICE_VOLUME_MUTE)
def media_play_pause(hass):
""" Press the keyboard button for play/pause. """
hass.call_service(DOMAIN, components.SERVICE_MEDIA_PLAY_PAUSE)
hass.services.call(DOMAIN, components.SERVICE_MEDIA_PLAY_PAUSE)
def media_next_track(hass):
""" Press the keyboard button for next track. """
hass.call_service(DOMAIN, components.SERVICE_MEDIA_NEXT_TRACK)
hass.services.call(DOMAIN, components.SERVICE_MEDIA_NEXT_TRACK)
def media_prev_track(hass):
""" Press the keyboard button for prev track. """
hass.call_service(DOMAIN, components.SERVICE_MEDIA_PREV_TRACK)
hass.services.call(DOMAIN, components.SERVICE_MEDIA_PREV_TRACK)
# pylint: disable=unused-argument

View file

@ -118,7 +118,7 @@ def turn_on(hass, entity_id=None, transition=None, brightness=None,
if xy_color:
data[ATTR_XY_COLOR] = xy_color
hass.call_service(DOMAIN, SERVICE_TURN_ON, data)
hass.services.call(DOMAIN, SERVICE_TURN_ON, data)
def turn_off(hass, entity_id=None, transition=None):
@ -131,7 +131,7 @@ def turn_off(hass, entity_id=None, transition=None):
if transition is not None:
data[ATTR_TRANSITION] = transition
hass.call_service(DOMAIN, SERVICE_TURN_OFF, data)
hass.services.call(DOMAIN, SERVICE_TURN_OFF, data)
# pylint: disable=too-many-branches, too-many-locals

View file

@ -43,14 +43,14 @@ def turn_on(hass, entity_id=None):
""" Turns all or specified switch on. """
data = {ATTR_ENTITY_ID: entity_id} if entity_id else None
hass.call_service(DOMAIN, SERVICE_TURN_ON, data)
hass.services.call(DOMAIN, SERVICE_TURN_ON, data)
def turn_off(hass, entity_id=None):
""" Turns all or specified switch off. """
data = {ATTR_ENTITY_ID: entity_id} if entity_id else None
hass.call_service(DOMAIN, SERVICE_TURN_OFF, data)
hass.services.call(DOMAIN, SERVICE_TURN_OFF, data)
# pylint: disable=too-many-branches

View file

@ -32,14 +32,14 @@ class TestDemo(unittest.TestCase):
# Focus on 1 entity
entity_id = self.hass.states.entity_ids(domain)[0]
self.hass.call_service(
self.hass.services.call(
domain, SERVICE_TURN_ON, {ATTR_ENTITY_ID: entity_id})
self.hass._pool.block_till_done()
self.assertEqual(STATE_ON, self.hass.states.get(entity_id).state)
self.hass.call_service(
self.hass.services.call(
domain, SERVICE_TURN_OFF, {ATTR_ENTITY_ID: entity_id})
self.hass._pool.block_till_done()
@ -47,7 +47,7 @@ class TestDemo(unittest.TestCase):
self.assertEqual(STATE_OFF, self.hass.states.get(entity_id).state)
# Act on all
self.hass.call_service(domain, SERVICE_TURN_ON)
self.hass.services.call(domain, SERVICE_TURN_ON)
self.hass._pool.block_till_done()
@ -55,7 +55,7 @@ class TestDemo(unittest.TestCase):
self.assertEqual(
STATE_ON, self.hass.states.get(entity_id).state)
self.hass.call_service(domain, SERVICE_TURN_OFF)
self.hass.services.call(domain, SERVICE_TURN_OFF)
self.hass._pool.block_till_done()

View file

@ -52,7 +52,7 @@ class TestHomeAssistant(unittest.TestCase):
self.assertTrue(blocking_thread.is_alive())
self.hass.call_service(ha.DOMAIN, ha.SERVICE_HOMEASSISTANT_STOP)
self.hass.services.call(ha.DOMAIN, ha.SERVICE_HOMEASSISTANT_STOP)
self.hass._pool.block_till_done()
# hass.block_till_stopped checks every second if it should quit
@ -64,43 +64,6 @@ class TestHomeAssistant(unittest.TestCase):
self.assertFalse(blocking_thread.is_alive())
def test_track_state_change(self):
""" Test track_state_change. """
# 2 lists to track how often our callbacks got called
specific_runs = []
wildcard_runs = []
self.hass.track_state_change(
'light.Bowl', lambda a, b, c: specific_runs.append(1), 'on', 'off')
self.hass.track_state_change(
'light.Bowl', lambda a, b, c: wildcard_runs.append(1),
ha.MATCH_ALL, ha.MATCH_ALL)
# Set same state should not trigger a state change/listener
self.hass.states.set('light.Bowl', 'on')
self.hass._pool.block_till_done()
self.assertEqual(0, len(specific_runs))
self.assertEqual(0, len(wildcard_runs))
# State change off -> on
self.hass.states.set('light.Bowl', 'off')
self.hass._pool.block_till_done()
self.assertEqual(1, len(specific_runs))
self.assertEqual(1, len(wildcard_runs))
# State change off -> off
self.hass.states.set('light.Bowl', 'off', {"some_attr": 1})
self.hass._pool.block_till_done()
self.assertEqual(1, len(specific_runs))
self.assertEqual(2, len(wildcard_runs))
# State change off -> on
self.hass.states.set('light.Bowl', 'on')
self.hass._pool.block_till_done()
self.assertEqual(1, len(specific_runs))
self.assertEqual(3, len(wildcard_runs))
def test_track_point_in_time(self):
""" Test track point in time. """
before_birthday = datetime(1985, 7, 9, 12, 0, 0)
@ -285,6 +248,43 @@ class TestStateMachine(unittest.TestCase):
# If it does not exist, we should get False
self.assertFalse(self.states.remove('light.Bowl'))
def test_track_change(self):
""" Test states.track_change. """
# 2 lists to track how often our callbacks got called
specific_runs = []
wildcard_runs = []
self.states.track_change(
'light.Bowl', lambda a, b, c: specific_runs.append(1), 'on', 'off')
self.states.track_change(
'light.Bowl', lambda a, b, c: wildcard_runs.append(1),
ha.MATCH_ALL, ha.MATCH_ALL)
# Set same state should not trigger a state change/listener
self.states.set('light.Bowl', 'on')
self.bus._pool.block_till_done()
self.assertEqual(0, len(specific_runs))
self.assertEqual(0, len(wildcard_runs))
# State change off -> on
self.states.set('light.Bowl', 'off')
self.bus._pool.block_till_done()
self.assertEqual(1, len(specific_runs))
self.assertEqual(1, len(wildcard_runs))
# State change off -> off
self.states.set('light.Bowl', 'off', {"some_attr": 1})
self.bus._pool.block_till_done()
self.assertEqual(1, len(specific_runs))
self.assertEqual(2, len(wildcard_runs))
# State change off -> on
self.states.set('light.Bowl', 'on')
self.bus._pool.block_till_done()
self.assertEqual(1, len(specific_runs))
self.assertEqual(3, len(wildcard_runs))
class TestServiceCall(unittest.TestCase):
""" Test ServiceCall class. """

View file

@ -154,7 +154,7 @@ class TestRemoteMethods(unittest.TestCase):
self.assertEqual({}, remote.get_services(broken_api))
def test_call_service(self):
""" Test Python API call_service. """
""" Test Python API services.call. """
test_value = []
def listener(service_call): # pylint: disable=unused-argument