diff --git a/homeassistant/components/homekit/accessories.py b/homeassistant/components/homekit/accessories.py index adf5273b639..2c41885e311 100644 --- a/homeassistant/components/homekit/accessories.py +++ b/homeassistant/components/homekit/accessories.py @@ -9,7 +9,8 @@ from pyhap.accessory_driver import AccessoryDriver from pyhap.const import CATEGORY_OTHER from homeassistant.const import ( - __version__, ATTR_BATTERY_CHARGING, ATTR_BATTERY_LEVEL) + __version__, ATTR_BATTERY_CHARGING, ATTR_BATTERY_LEVEL, ATTR_ENTITY_ID, + ATTR_SERVICE) from homeassistant.core import callback as ha_callback from homeassistant.core import split_entity_id from homeassistant.helpers.event import ( @@ -17,9 +18,10 @@ from homeassistant.helpers.event import ( from homeassistant.util import dt as dt_util from .const import ( - BRIDGE_MODEL, BRIDGE_SERIAL_NUMBER, CHAR_BATTERY_LEVEL, - CHAR_CHARGING_STATE, CHAR_STATUS_LOW_BATTERY, DEBOUNCE_TIMEOUT, - MANUFACTURER, SERV_BATTERY_SERVICE) + ATTR_DISPLAY_NAME, ATTR_VALUE, BRIDGE_MODEL, BRIDGE_SERIAL_NUMBER, + CHAR_BATTERY_LEVEL, CHAR_CHARGING_STATE, CHAR_STATUS_LOW_BATTERY, + DEBOUNCE_TIMEOUT, EVENT_HOMEKIT_CHANGED, MANUFACTURER, + SERV_BATTERY_SERVICE) from .util import ( convert_to_float, show_setup_message, dismiss_setup_message) @@ -137,6 +139,27 @@ class HomeAccessory(Accessory): """ raise NotImplementedError() + def call_service(self, domain, service, service_data, value=None): + """Fire event and call service for changes from HomeKit.""" + self.hass.add_job( + self.async_call_service, domain, service, service_data, value) + + async def async_call_service(self, domain, service, service_data, + value=None): + """Fire event and call service for changes from HomeKit. + + This method must be run in the event loop. + """ + event_data = { + ATTR_ENTITY_ID: self.entity_id, + ATTR_DISPLAY_NAME: self.display_name, + ATTR_SERVICE: service, + ATTR_VALUE: value + } + + self.hass.bus.async_fire(EVENT_HOMEKIT_CHANGED, event_data) + await self.hass.services.async_call(domain, service, service_data) + class HomeBridge(Bridge): """Adapter class for Bridge.""" diff --git a/homeassistant/components/homekit/const.py b/homeassistant/components/homekit/const.py index 617dd3f4f22..6c143f7f0da 100644 --- a/homeassistant/components/homekit/const.py +++ b/homeassistant/components/homekit/const.py @@ -5,6 +5,10 @@ DOMAIN = 'homekit' HOMEKIT_FILE = '.homekit.state' HOMEKIT_NOTIFY_ID = 4663548 +# #### Attributes #### +ATTR_DISPLAY_NAME = 'display_name' +ATTR_VALUE = 'value' + # #### Config #### CONF_AUTO_START = 'auto_start' CONF_ENTITY_CONFIG = 'entity_config' @@ -22,6 +26,9 @@ FEATURE_PLAY_PAUSE = 'play_pause' FEATURE_PLAY_STOP = 'play_stop' FEATURE_TOGGLE_MUTE = 'toggle_mute' +# #### HomeKit Component Event #### +EVENT_HOMEKIT_CHANGED = 'homekit_state_change' + # #### HomeKit Component Services #### SERVICE_HOMEKIT_START = 'start' diff --git a/homeassistant/components/homekit/type_covers.py b/homeassistant/components/homekit/type_covers.py index cf0620a4e30..787e0e52b1d 100644 --- a/homeassistant/components/homekit/type_covers.py +++ b/homeassistant/components/homekit/type_covers.py @@ -47,10 +47,10 @@ class GarageDoorOpener(HomeAccessory): params = {ATTR_ENTITY_ID: self.entity_id} if value == 0: self.char_current_state.set_value(3) - self.hass.services.call(DOMAIN, SERVICE_OPEN_COVER, params) + self.call_service(DOMAIN, SERVICE_OPEN_COVER, params) elif value == 1: self.char_current_state.set_value(2) - self.hass.services.call(DOMAIN, SERVICE_CLOSE_COVER, params) + self.call_service(DOMAIN, SERVICE_CLOSE_COVER, params) def update_state(self, new_state): """Update cover state after state changed.""" @@ -88,7 +88,7 @@ class WindowCovering(HomeAccessory): self.homekit_target = value params = {ATTR_ENTITY_ID: self.entity_id, ATTR_POSITION: value} - self.hass.services.call(DOMAIN, SERVICE_SET_COVER_POSITION, params) + self.call_service(DOMAIN, SERVICE_SET_COVER_POSITION, params, value) def update_state(self, new_state): """Update cover position after state changed.""" @@ -143,7 +143,7 @@ class WindowCoveringBasic(HomeAccessory): service, position = (SERVICE_CLOSE_COVER, 0) params = {ATTR_ENTITY_ID: self.entity_id} - self.hass.services.call(DOMAIN, service, params) + self.call_service(DOMAIN, service, params) # Snap the current/target position to the expected final position. self.char_current_position.set_value(position) diff --git a/homeassistant/components/homekit/type_fans.py b/homeassistant/components/homekit/type_fans.py index aa44b11fefb..49eb525aa51 100644 --- a/homeassistant/components/homekit/type_fans.py +++ b/homeassistant/components/homekit/type_fans.py @@ -61,7 +61,7 @@ class Fan(HomeAccessory): self._flag[CHAR_ACTIVE] = True service = SERVICE_TURN_ON if value == 1 else SERVICE_TURN_OFF params = {ATTR_ENTITY_ID: self.entity_id} - self.hass.services.call(DOMAIN, service, params) + self.call_service(DOMAIN, service, params) def set_direction(self, value): """Set state if call came from HomeKit.""" @@ -69,7 +69,7 @@ class Fan(HomeAccessory): self._flag[CHAR_ROTATION_DIRECTION] = True direction = DIRECTION_REVERSE if value == 1 else DIRECTION_FORWARD params = {ATTR_ENTITY_ID: self.entity_id, ATTR_DIRECTION: direction} - self.hass.services.call(DOMAIN, SERVICE_SET_DIRECTION, params) + self.call_service(DOMAIN, SERVICE_SET_DIRECTION, params, direction) def set_oscillating(self, value): """Set state if call came from HomeKit.""" @@ -78,7 +78,7 @@ class Fan(HomeAccessory): oscillating = True if value == 1 else False params = {ATTR_ENTITY_ID: self.entity_id, ATTR_OSCILLATING: oscillating} - self.hass.services.call(DOMAIN, SERVICE_OSCILLATE, params) + self.call_service(DOMAIN, SERVICE_OSCILLATE, params, oscillating) def update_state(self, new_state): """Update fan after state change.""" diff --git a/homeassistant/components/homekit/type_lights.py b/homeassistant/components/homekit/type_lights.py index da012799602..b8125e24eba 100644 --- a/homeassistant/components/homekit/type_lights.py +++ b/homeassistant/components/homekit/type_lights.py @@ -83,7 +83,7 @@ class Light(HomeAccessory): self._flag[CHAR_ON] = True params = {ATTR_ENTITY_ID: self.entity_id} service = SERVICE_TURN_ON if value == 1 else SERVICE_TURN_OFF - self.hass.services.call(DOMAIN, service, params) + self.call_service(DOMAIN, service, params) @debounce def set_brightness(self, value): @@ -94,14 +94,16 @@ class Light(HomeAccessory): self.set_state(0) # Turn off light return params = {ATTR_ENTITY_ID: self.entity_id, ATTR_BRIGHTNESS_PCT: value} - self.hass.services.call(DOMAIN, SERVICE_TURN_ON, params) + self.call_service(DOMAIN, SERVICE_TURN_ON, params, + "brightness at {}%".format(value)) def set_color_temperature(self, value): """Set color temperature if call came from HomeKit.""" _LOGGER.debug('%s: Set color temp to %s', self.entity_id, value) self._flag[CHAR_COLOR_TEMPERATURE] = True params = {ATTR_ENTITY_ID: self.entity_id, ATTR_COLOR_TEMP: value} - self.hass.services.call(DOMAIN, SERVICE_TURN_ON, params) + self.call_service(DOMAIN, SERVICE_TURN_ON, params, + "color temperature at {}".format(value)) def set_saturation(self, value): """Set saturation if call came from HomeKit.""" @@ -126,7 +128,8 @@ class Light(HomeAccessory): self._flag.update({ CHAR_HUE: False, CHAR_SATURATION: False, RGB_COLOR: True}) params = {ATTR_ENTITY_ID: self.entity_id, ATTR_HS_COLOR: color} - self.hass.services.call(DOMAIN, SERVICE_TURN_ON, params) + self.call_service(DOMAIN, SERVICE_TURN_ON, params, + "set color at {}".format(color)) def update_state(self, new_state): """Update light after state change.""" diff --git a/homeassistant/components/homekit/type_locks.py b/homeassistant/components/homekit/type_locks.py index 05ab6c6f822..a3d9c3b6fac 100644 --- a/homeassistant/components/homekit/type_locks.py +++ b/homeassistant/components/homekit/type_locks.py @@ -54,7 +54,7 @@ class Lock(HomeAccessory): params = {ATTR_ENTITY_ID: self.entity_id} if self._code: params[ATTR_CODE] = self._code - self.hass.services.call(DOMAIN, service, params) + self.call_service(DOMAIN, service, params) def update_state(self, new_state): """Update lock after state changed.""" diff --git a/homeassistant/components/homekit/type_media_players.py b/homeassistant/components/homekit/type_media_players.py index ec41b9fd618..09088871fd2 100644 --- a/homeassistant/components/homekit/type_media_players.py +++ b/homeassistant/components/homekit/type_media_players.py @@ -76,7 +76,7 @@ class MediaPlayer(HomeAccessory): self._flag[FEATURE_ON_OFF] = True service = SERVICE_TURN_ON if value else SERVICE_TURN_OFF params = {ATTR_ENTITY_ID: self.entity_id} - self.hass.services.call(DOMAIN, service, params) + self.call_service(DOMAIN, service, params) def set_play_pause(self, value): """Move switch state to value if call came from HomeKit.""" @@ -85,7 +85,7 @@ class MediaPlayer(HomeAccessory): self._flag[FEATURE_PLAY_PAUSE] = True service = SERVICE_MEDIA_PLAY if value else SERVICE_MEDIA_PAUSE params = {ATTR_ENTITY_ID: self.entity_id} - self.hass.services.call(DOMAIN, service, params) + self.call_service(DOMAIN, service, params) def set_play_stop(self, value): """Move switch state to value if call came from HomeKit.""" @@ -94,7 +94,7 @@ class MediaPlayer(HomeAccessory): self._flag[FEATURE_PLAY_STOP] = True service = SERVICE_MEDIA_PLAY if value else SERVICE_MEDIA_STOP params = {ATTR_ENTITY_ID: self.entity_id} - self.hass.services.call(DOMAIN, service, params) + self.call_service(DOMAIN, service, params) def set_toggle_mute(self, value): """Move switch state to value if call came from HomeKit.""" @@ -103,7 +103,7 @@ class MediaPlayer(HomeAccessory): self._flag[FEATURE_TOGGLE_MUTE] = True params = {ATTR_ENTITY_ID: self.entity_id, ATTR_MEDIA_VOLUME_MUTED: value} - self.hass.services.call(DOMAIN, SERVICE_VOLUME_MUTE, params) + self.call_service(DOMAIN, SERVICE_VOLUME_MUTE, params) def update_state(self, new_state): """Update switch state after state changed.""" diff --git a/homeassistant/components/homekit/type_security_systems.py b/homeassistant/components/homekit/type_security_systems.py index a7d36720cab..206da3e2889 100644 --- a/homeassistant/components/homekit/type_security_systems.py +++ b/homeassistant/components/homekit/type_security_systems.py @@ -59,7 +59,7 @@ class SecuritySystem(HomeAccessory): params = {ATTR_ENTITY_ID: self.entity_id} if self._alarm_code: params[ATTR_CODE] = self._alarm_code - self.hass.services.call(DOMAIN, service, params) + self.call_service(DOMAIN, service, params) def update_state(self, new_state): """Update security state after state changed.""" diff --git a/homeassistant/components/homekit/type_switches.py b/homeassistant/components/homekit/type_switches.py index 82a5d68d644..1090205ff56 100644 --- a/homeassistant/components/homekit/type_switches.py +++ b/homeassistant/components/homekit/type_switches.py @@ -51,7 +51,7 @@ class Outlet(HomeAccessory): self.flag_target_state = True params = {ATTR_ENTITY_ID: self.entity_id} service = SERVICE_TURN_ON if value else SERVICE_TURN_OFF - self.hass.services.call(DOMAIN, service, params) + self.call_service(DOMAIN, service, params) def update_state(self, new_state): """Update switch state after state changed.""" @@ -84,7 +84,7 @@ class Switch(HomeAccessory): self.flag_target_state = True params = {ATTR_ENTITY_ID: self.entity_id} service = SERVICE_TURN_ON if value else SERVICE_TURN_OFF - self.hass.services.call(self._domain, service, params) + self.call_service(self._domain, service, params) def update_state(self, new_state): """Update switch state after state changed.""" @@ -123,7 +123,7 @@ class Valve(HomeAccessory): self.char_in_use.set_value(value) params = {ATTR_ENTITY_ID: self.entity_id} service = SERVICE_TURN_ON if value else SERVICE_TURN_OFF - self.hass.services.call(DOMAIN, service, params) + self.call_service(DOMAIN, service, params) def update_state(self, new_state): """Update switch state after state changed.""" diff --git a/homeassistant/components/homekit/type_thermostats.py b/homeassistant/components/homekit/type_thermostats.py index 8517122f6a8..a7344995021 100644 --- a/homeassistant/components/homekit/type_thermostats.py +++ b/homeassistant/components/homekit/type_thermostats.py @@ -122,12 +122,13 @@ class Thermostat(HomeAccessory): if self.support_power_state is True: params = {ATTR_ENTITY_ID: self.entity_id} if hass_value == STATE_OFF: - self.hass.services.call(DOMAIN, SERVICE_TURN_OFF, params) + self.call_service(DOMAIN, SERVICE_TURN_OFF, params) return self.hass.services.call(DOMAIN, SERVICE_TURN_ON, params) params = {ATTR_ENTITY_ID: self.entity_id, ATTR_OPERATION_MODE: hass_value} - self.hass.services.call(DOMAIN, SERVICE_SET_OPERATION_MODE, params) + self.call_service( + DOMAIN, SERVICE_SET_OPERATION_MODE, params, hass_value) @debounce def set_cooling_threshold(self, value): @@ -136,11 +137,14 @@ class Thermostat(HomeAccessory): self.entity_id, value) self.coolingthresh_flag_target_state = True low = self.char_heating_thresh_temp.value + temperature = temperature_to_states(value, self._unit) params = { ATTR_ENTITY_ID: self.entity_id, - ATTR_TARGET_TEMP_HIGH: temperature_to_states(value, self._unit), + ATTR_TARGET_TEMP_HIGH: temperature, ATTR_TARGET_TEMP_LOW: temperature_to_states(low, self._unit)} - self.hass.services.call(DOMAIN, SERVICE_SET_TEMPERATURE, params) + self.call_service(DOMAIN, SERVICE_SET_TEMPERATURE, params, + "cooling threshold {}{}".format(temperature, + self._unit)) @debounce def set_heating_threshold(self, value): @@ -149,11 +153,14 @@ class Thermostat(HomeAccessory): self.entity_id, value) self.heatingthresh_flag_target_state = True high = self.char_cooling_thresh_temp.value + temperature = temperature_to_states(value, self._unit) params = { ATTR_ENTITY_ID: self.entity_id, ATTR_TARGET_TEMP_HIGH: temperature_to_states(high, self._unit), - ATTR_TARGET_TEMP_LOW: temperature_to_states(value, self._unit)} - self.hass.services.call(DOMAIN, SERVICE_SET_TEMPERATURE, params) + ATTR_TARGET_TEMP_LOW: temperature} + self.call_service(DOMAIN, SERVICE_SET_TEMPERATURE, params, + "heating threshold {}{}".format(temperature, + self._unit)) @debounce def set_target_temperature(self, value): @@ -161,10 +168,13 @@ class Thermostat(HomeAccessory): _LOGGER.debug('%s: Set target temperature to %.2f°C', self.entity_id, value) self.temperature_flag_target_state = True + temperature = temperature_to_states(value, self._unit) params = { ATTR_ENTITY_ID: self.entity_id, - ATTR_TEMPERATURE: temperature_to_states(value, self._unit)} - self.hass.services.call(DOMAIN, SERVICE_SET_TEMPERATURE, params) + ATTR_TEMPERATURE: temperature} + self.call_service(DOMAIN, SERVICE_SET_TEMPERATURE, params, + "target {}{}".format(temperature, + self._unit)) def update_state(self, new_state): """Update security state after state changed.""" diff --git a/homeassistant/components/logbook.py b/homeassistant/components/logbook.py index 5cbd2b9432b..5bd7ed0d2f5 100644 --- a/homeassistant/components/logbook.py +++ b/homeassistant/components/logbook.py @@ -14,13 +14,16 @@ from homeassistant.loader import bind_hass from homeassistant.components import sun from homeassistant.components.http import HomeAssistantView from homeassistant.const import ( - ATTR_DOMAIN, ATTR_ENTITY_ID, ATTR_HIDDEN, ATTR_NAME, CONF_EXCLUDE, - CONF_INCLUDE, EVENT_HOMEASSISTANT_START, EVENT_HOMEASSISTANT_STOP, - EVENT_LOGBOOK_ENTRY, EVENT_STATE_CHANGED, HTTP_BAD_REQUEST, STATE_NOT_HOME, - STATE_OFF, STATE_ON) + ATTR_DOMAIN, ATTR_ENTITY_ID, ATTR_HIDDEN, ATTR_NAME, ATTR_SERVICE, + CONF_EXCLUDE, CONF_INCLUDE, EVENT_HOMEASSISTANT_START, + EVENT_HOMEASSISTANT_STOP, EVENT_LOGBOOK_ENTRY, EVENT_STATE_CHANGED, + HTTP_BAD_REQUEST, STATE_NOT_HOME, STATE_OFF, STATE_ON) from homeassistant.core import ( DOMAIN as HA_DOMAIN, State, callback, split_entity_id) from homeassistant.components.alexa.smart_home import EVENT_ALEXA_SMART_HOME +from homeassistant.components.homekit.const import ( + ATTR_DISPLAY_NAME, ATTR_VALUE, DOMAIN as DOMAIN_HOMEKIT, + EVENT_HOMEKIT_CHANGED) import homeassistant.helpers.config_validation as cv import homeassistant.util.dt as dt_util @@ -56,7 +59,7 @@ CONFIG_SCHEMA = vol.Schema({ ALL_EVENT_TYPES = [ EVENT_STATE_CHANGED, EVENT_LOGBOOK_ENTRY, EVENT_HOMEASSISTANT_START, EVENT_HOMEASSISTANT_STOP, - EVENT_ALEXA_SMART_HOME + EVENT_ALEXA_SMART_HOME, EVENT_HOMEKIT_CHANGED ] LOG_MESSAGE_SCHEMA = vol.Schema({ @@ -294,6 +297,25 @@ def humanify(hass, events): 'context_user_id': event.context.user_id } + elif event.event_type == EVENT_HOMEKIT_CHANGED: + data = event.data + entity_id = data.get(ATTR_ENTITY_ID) + value = data.get(ATTR_VALUE) + + value_msg = " to {}".format(value) if value else '' + message = "send command {}{} for {}".format( + data[ATTR_SERVICE], value_msg, data[ATTR_DISPLAY_NAME]) + + yield { + 'when': event.time_fired, + 'name': 'HomeKit', + 'message': message, + 'domain': DOMAIN_HOMEKIT, + 'entity_id': entity_id, + 'context_id': event.context.id, + 'context_user_id': event.context.user_id + } + def _get_events(hass, config, start_day, end_day, entity_id=None): """Get events for a period of time.""" diff --git a/tests/components/homekit/conftest.py b/tests/components/homekit/conftest.py index 55e02de7526..326845cf74f 100644 --- a/tests/components/homekit/conftest.py +++ b/tests/components/homekit/conftest.py @@ -3,6 +3,9 @@ from unittest.mock import patch import pytest +from homeassistant.components.homekit.const import EVENT_HOMEKIT_CHANGED +from homeassistant.core import callback + from pyhap.accessory_driver import AccessoryDriver @@ -14,3 +17,13 @@ def hk_driver(): patch('pyhap.accessory_driver.HAPServer'), \ patch('pyhap.accessory_driver.AccessoryDriver.publish'): return AccessoryDriver(pincode=b'123-45-678', address='127.0.0.1') + + +@pytest.fixture +def events(hass): + """Yield caught homekit_changed events.""" + events = [] + hass.bus.async_listen( + EVENT_HOMEKIT_CHANGED, + callback(lambda e: events.append(e))) + yield events diff --git a/tests/components/homekit/test_accessories.py b/tests/components/homekit/test_accessories.py index edb1c7175f8..15ab6d7413e 100644 --- a/tests/components/homekit/test_accessories.py +++ b/tests/components/homekit/test_accessories.py @@ -10,14 +10,17 @@ import pytest from homeassistant.components.homekit.accessories import ( debounce, HomeAccessory, HomeBridge, HomeDriver) from homeassistant.components.homekit.const import ( + ATTR_DISPLAY_NAME, ATTR_VALUE, BRIDGE_MODEL, BRIDGE_NAME, BRIDGE_SERIAL_NUMBER, CHAR_FIRMWARE_REVISION, CHAR_MANUFACTURER, CHAR_MODEL, CHAR_NAME, CHAR_SERIAL_NUMBER, MANUFACTURER, SERV_ACCESSORY_INFO) from homeassistant.const import ( - __version__, ATTR_BATTERY_CHARGING, ATTR_BATTERY_LEVEL, ATTR_NOW, - EVENT_TIME_CHANGED) + __version__, ATTR_BATTERY_CHARGING, ATTR_BATTERY_LEVEL, ATTR_ENTITY_ID, + ATTR_SERVICE, ATTR_NOW, EVENT_TIME_CHANGED) import homeassistant.util.dt as dt_util +from tests.common import async_mock_service + async def test_debounce(hass): """Test add_timeout decorator function.""" @@ -146,6 +149,37 @@ async def test_battery_service(hass, hk_driver): assert acc._char_charging.value == 0 +async def test_call_service(hass, hk_driver, events): + """Test call_service method.""" + entity_id = 'homekit.accessory' + hass.states.async_set(entity_id, None) + await hass.async_block_till_done() + + acc = HomeAccessory(hass, hk_driver, 'Home Accessory', entity_id, 2, None) + call_service = async_mock_service(hass, 'cover', 'open_cover') + + test_domain = 'cover' + test_service = 'open_cover' + test_value = 'value' + + await acc.async_call_service( + test_domain, test_service, {ATTR_ENTITY_ID: entity_id}, test_value) + await hass.async_block_till_done() + + assert len(events) == 1 + assert events[0].data == { + ATTR_ENTITY_ID: acc.entity_id, + ATTR_DISPLAY_NAME: acc.display_name, + ATTR_SERVICE: test_service, + ATTR_VALUE: test_value + } + + assert len(call_service) == 1 + assert call_service[0].domain == test_domain + assert call_service[0].service == test_service + assert call_service[0].data == {ATTR_ENTITY_ID: entity_id} + + def test_home_bridge(hk_driver): """Test HomeBridge class.""" bridge = HomeBridge('hass', hk_driver, BRIDGE_NAME) diff --git a/tests/components/homekit/test_type_covers.py b/tests/components/homekit/test_type_covers.py index 04ed5df5702..c32abaef0dd 100644 --- a/tests/components/homekit/test_type_covers.py +++ b/tests/components/homekit/test_type_covers.py @@ -5,6 +5,7 @@ import pytest from homeassistant.components.cover import ( ATTR_CURRENT_POSITION, ATTR_POSITION, DOMAIN, SUPPORT_STOP) +from homeassistant.components.homekit.const import ATTR_VALUE from homeassistant.const import ( ATTR_ENTITY_ID, ATTR_SUPPORTED_FEATURES, STATE_CLOSED, STATE_OPEN, STATE_UNAVAILABLE, STATE_UNKNOWN) @@ -28,7 +29,7 @@ def cls(): patcher.stop() -async def test_garage_door_open_close(hass, hk_driver, cls): +async def test_garage_door_open_close(hass, hk_driver, cls, events): """Test if accessory and HA are updated accordingly.""" entity_id = 'cover.garage_door' @@ -73,6 +74,8 @@ async def test_garage_door_open_close(hass, hk_driver, cls): assert call_close_cover[0].data[ATTR_ENTITY_ID] == entity_id assert acc.char_current_state.value == 2 assert acc.char_target_state.value == 1 + assert len(events) == 1 + assert events[-1].data[ATTR_VALUE] is None hass.states.async_set(entity_id, STATE_CLOSED) await hass.async_block_till_done() @@ -83,9 +86,11 @@ async def test_garage_door_open_close(hass, hk_driver, cls): assert call_open_cover[0].data[ATTR_ENTITY_ID] == entity_id assert acc.char_current_state.value == 3 assert acc.char_target_state.value == 0 + assert len(events) == 2 + assert events[-1].data[ATTR_VALUE] is None -async def test_window_set_cover_position(hass, hk_driver, cls): +async def test_window_set_cover_position(hass, hk_driver, cls, events): """Test if accessory and HA are updated accordingly.""" entity_id = 'cover.window' @@ -123,6 +128,8 @@ async def test_window_set_cover_position(hass, hk_driver, cls): assert call_set_cover_position[0].data[ATTR_POSITION] == 25 assert acc.char_current_position.value == 50 assert acc.char_target_position.value == 25 + assert len(events) == 1 + assert events[-1].data[ATTR_VALUE] == 25 await hass.async_add_job(acc.char_target_position.client_update_value, 75) await hass.async_block_till_done() @@ -131,9 +138,11 @@ async def test_window_set_cover_position(hass, hk_driver, cls): assert call_set_cover_position[1].data[ATTR_POSITION] == 75 assert acc.char_current_position.value == 50 assert acc.char_target_position.value == 75 + assert len(events) == 2 + assert events[-1].data[ATTR_VALUE] == 75 -async def test_window_open_close(hass, hk_driver, cls): +async def test_window_open_close(hass, hk_driver, cls, events): """Test if accessory and HA are updated accordingly.""" entity_id = 'cover.window' @@ -178,6 +187,8 @@ async def test_window_open_close(hass, hk_driver, cls): assert acc.char_current_position.value == 0 assert acc.char_target_position.value == 0 assert acc.char_position_state.value == 2 + assert len(events) == 1 + assert events[-1].data[ATTR_VALUE] is None await hass.async_add_job(acc.char_target_position.client_update_value, 90) await hass.async_block_till_done() @@ -186,6 +197,8 @@ async def test_window_open_close(hass, hk_driver, cls): assert acc.char_current_position.value == 100 assert acc.char_target_position.value == 100 assert acc.char_position_state.value == 2 + assert len(events) == 2 + assert events[-1].data[ATTR_VALUE] is None await hass.async_add_job(acc.char_target_position.client_update_value, 55) await hass.async_block_till_done() @@ -194,9 +207,11 @@ async def test_window_open_close(hass, hk_driver, cls): assert acc.char_current_position.value == 100 assert acc.char_target_position.value == 100 assert acc.char_position_state.value == 2 + assert len(events) == 3 + assert events[-1].data[ATTR_VALUE] is None -async def test_window_open_close_stop(hass, hk_driver, cls): +async def test_window_open_close_stop(hass, hk_driver, cls, events): """Test if accessory and HA are updated accordingly.""" entity_id = 'cover.window' @@ -217,6 +232,8 @@ async def test_window_open_close_stop(hass, hk_driver, cls): assert acc.char_current_position.value == 0 assert acc.char_target_position.value == 0 assert acc.char_position_state.value == 2 + assert len(events) == 1 + assert events[-1].data[ATTR_VALUE] is None await hass.async_add_job(acc.char_target_position.client_update_value, 90) await hass.async_block_till_done() @@ -225,6 +242,8 @@ async def test_window_open_close_stop(hass, hk_driver, cls): assert acc.char_current_position.value == 100 assert acc.char_target_position.value == 100 assert acc.char_position_state.value == 2 + assert len(events) == 2 + assert events[-1].data[ATTR_VALUE] is None await hass.async_add_job(acc.char_target_position.client_update_value, 55) await hass.async_block_till_done() @@ -233,3 +252,5 @@ async def test_window_open_close_stop(hass, hk_driver, cls): assert acc.char_current_position.value == 50 assert acc.char_target_position.value == 50 assert acc.char_position_state.value == 2 + assert len(events) == 3 + assert events[-1].data[ATTR_VALUE] is None diff --git a/tests/components/homekit/test_type_fans.py b/tests/components/homekit/test_type_fans.py index 87a481ff06f..27b6cec0790 100644 --- a/tests/components/homekit/test_type_fans.py +++ b/tests/components/homekit/test_type_fans.py @@ -6,6 +6,7 @@ import pytest from homeassistant.components.fan import ( ATTR_DIRECTION, ATTR_OSCILLATING, DIRECTION_FORWARD, DIRECTION_REVERSE, DOMAIN, SUPPORT_DIRECTION, SUPPORT_OSCILLATE) +from homeassistant.components.homekit.const import ATTR_VALUE from homeassistant.const import ( ATTR_ENTITY_ID, ATTR_SUPPORTED_FEATURES, STATE_ON, STATE_OFF, STATE_UNKNOWN) @@ -26,7 +27,7 @@ def cls(): patcher.stop() -async def test_fan_basic(hass, hk_driver, cls): +async def test_fan_basic(hass, hk_driver, cls, events): """Test fan with char state.""" entity_id = 'fan.demo' @@ -62,6 +63,8 @@ async def test_fan_basic(hass, hk_driver, cls): await hass.async_block_till_done() assert call_turn_on assert call_turn_on[0].data[ATTR_ENTITY_ID] == entity_id + assert len(events) == 1 + assert events[-1].data[ATTR_VALUE] is None hass.states.async_set(entity_id, STATE_ON) await hass.async_block_till_done() @@ -70,9 +73,11 @@ async def test_fan_basic(hass, hk_driver, cls): await hass.async_block_till_done() assert call_turn_off assert call_turn_off[0].data[ATTR_ENTITY_ID] == entity_id + assert len(events) == 2 + assert events[-1].data[ATTR_VALUE] is None -async def test_fan_direction(hass, hk_driver, cls): +async def test_fan_direction(hass, hk_driver, cls, events): """Test fan with direction.""" entity_id = 'fan.demo' @@ -101,15 +106,19 @@ async def test_fan_direction(hass, hk_driver, cls): assert call_set_direction[0] assert call_set_direction[0].data[ATTR_ENTITY_ID] == entity_id assert call_set_direction[0].data[ATTR_DIRECTION] == DIRECTION_FORWARD + assert len(events) == 1 + assert events[-1].data[ATTR_VALUE] == DIRECTION_FORWARD await hass.async_add_job(acc.char_direction.client_update_value, 1) await hass.async_block_till_done() assert call_set_direction[1] assert call_set_direction[1].data[ATTR_ENTITY_ID] == entity_id assert call_set_direction[1].data[ATTR_DIRECTION] == DIRECTION_REVERSE + assert len(events) == 2 + assert events[-1].data[ATTR_VALUE] == DIRECTION_REVERSE -async def test_fan_oscillate(hass, hk_driver, cls): +async def test_fan_oscillate(hass, hk_driver, cls, events): """Test fan with oscillate.""" entity_id = 'fan.demo' @@ -136,9 +145,13 @@ async def test_fan_oscillate(hass, hk_driver, cls): assert call_oscillate[0] assert call_oscillate[0].data[ATTR_ENTITY_ID] == entity_id assert call_oscillate[0].data[ATTR_OSCILLATING] is False + assert len(events) == 1 + assert events[-1].data[ATTR_VALUE] is False await hass.async_add_job(acc.char_swing.client_update_value, 1) await hass.async_block_till_done() assert call_oscillate[1] assert call_oscillate[1].data[ATTR_ENTITY_ID] == entity_id assert call_oscillate[1].data[ATTR_OSCILLATING] is True + assert len(events) == 2 + assert events[-1].data[ATTR_VALUE] is True diff --git a/tests/components/homekit/test_type_lights.py b/tests/components/homekit/test_type_lights.py index aab6274f484..540d9a73f48 100644 --- a/tests/components/homekit/test_type_lights.py +++ b/tests/components/homekit/test_type_lights.py @@ -3,6 +3,7 @@ from collections import namedtuple import pytest +from homeassistant.components.homekit.const import ATTR_VALUE from homeassistant.components.light import ( ATTR_BRIGHTNESS, ATTR_BRIGHTNESS_PCT, ATTR_COLOR_TEMP, ATTR_HS_COLOR, DOMAIN, SUPPORT_BRIGHTNESS, SUPPORT_COLOR_TEMP, SUPPORT_COLOR) @@ -26,7 +27,7 @@ def cls(): patcher.stop() -async def test_light_basic(hass, hk_driver, cls): +async def test_light_basic(hass, hk_driver, cls, events): """Test light with char state.""" entity_id = 'light.demo' @@ -62,6 +63,8 @@ async def test_light_basic(hass, hk_driver, cls): await hass.async_block_till_done() assert call_turn_on assert call_turn_on[0].data[ATTR_ENTITY_ID] == entity_id + assert len(events) == 1 + assert events[-1].data[ATTR_VALUE] is None hass.states.async_set(entity_id, STATE_ON) await hass.async_block_till_done() @@ -70,11 +73,14 @@ async def test_light_basic(hass, hk_driver, cls): await hass.async_block_till_done() assert call_turn_off assert call_turn_off[0].data[ATTR_ENTITY_ID] == entity_id + assert len(events) == 2 + assert events[-1].data[ATTR_VALUE] is None -async def test_light_brightness(hass, hk_driver, cls): +async def test_light_brightness(hass, hk_driver, cls, events): """Test light with brightness.""" entity_id = 'light.demo' + event_value = "brightness at " hass.states.async_set(entity_id, STATE_ON, { ATTR_SUPPORTED_FEATURES: SUPPORT_BRIGHTNESS, ATTR_BRIGHTNESS: 255}) @@ -101,6 +107,8 @@ async def test_light_brightness(hass, hk_driver, cls): assert call_turn_on[0] assert call_turn_on[0].data[ATTR_ENTITY_ID] == entity_id assert call_turn_on[0].data[ATTR_BRIGHTNESS_PCT] == 20 + assert len(events) == 1 + assert events[-1].data[ATTR_VALUE] == "{}20%".format(event_value) await hass.async_add_job(acc.char_on.client_update_value, 1) await hass.async_add_job(acc.char_brightness.client_update_value, 40) @@ -108,15 +116,19 @@ async def test_light_brightness(hass, hk_driver, cls): assert call_turn_on[1] assert call_turn_on[1].data[ATTR_ENTITY_ID] == entity_id assert call_turn_on[1].data[ATTR_BRIGHTNESS_PCT] == 40 + assert len(events) == 2 + assert events[-1].data[ATTR_VALUE] == "{}40%".format(event_value) await hass.async_add_job(acc.char_on.client_update_value, 1) await hass.async_add_job(acc.char_brightness.client_update_value, 0) await hass.async_block_till_done() assert call_turn_off assert call_turn_off[0].data[ATTR_ENTITY_ID] == entity_id + assert len(events) == 3 + assert events[-1].data[ATTR_VALUE] is None -async def test_light_color_temperature(hass, hk_driver, cls): +async def test_light_color_temperature(hass, hk_driver, cls, events): """Test light with color temperature.""" entity_id = 'light.demo' @@ -141,9 +153,11 @@ async def test_light_color_temperature(hass, hk_driver, cls): assert call_turn_on assert call_turn_on[0].data[ATTR_ENTITY_ID] == entity_id assert call_turn_on[0].data[ATTR_COLOR_TEMP] == 250 + assert len(events) == 1 + assert events[-1].data[ATTR_VALUE] == "color temperature at 250" -async def test_light_rgb_color(hass, hk_driver, cls): +async def test_light_rgb_color(hass, hk_driver, cls, events): """Test light with rgb_color.""" entity_id = 'light.demo' @@ -170,3 +184,5 @@ async def test_light_rgb_color(hass, hk_driver, cls): assert call_turn_on assert call_turn_on[0].data[ATTR_ENTITY_ID] == entity_id assert call_turn_on[0].data[ATTR_HS_COLOR] == (145, 75) + assert len(events) == 1 + assert events[-1].data[ATTR_VALUE] == "set color at (145, 75)" diff --git a/tests/components/homekit/test_type_locks.py b/tests/components/homekit/test_type_locks.py index 8f18a591019..e7e52c65559 100644 --- a/tests/components/homekit/test_type_locks.py +++ b/tests/components/homekit/test_type_locks.py @@ -1,6 +1,7 @@ """Test different accessory types: Locks.""" import pytest +from homeassistant.components.homekit.const import ATTR_VALUE from homeassistant.components.homekit.type_locks import Lock from homeassistant.components.lock import DOMAIN from homeassistant.const import ( @@ -9,7 +10,7 @@ from homeassistant.const import ( from tests.common import async_mock_service -async def test_lock_unlock(hass, hk_driver): +async def test_lock_unlock(hass, hk_driver, events): """Test if accessory and HA are updated accordingly.""" code = '1234' config = {ATTR_CODE: code} @@ -56,6 +57,8 @@ async def test_lock_unlock(hass, hk_driver): assert call_lock[0].data[ATTR_ENTITY_ID] == entity_id assert call_lock[0].data[ATTR_CODE] == code assert acc.char_target_state.value == 1 + assert len(events) == 1 + assert events[-1].data[ATTR_VALUE] is None await hass.async_add_job(acc.char_target_state.client_update_value, 0) await hass.async_block_till_done() @@ -63,10 +66,12 @@ async def test_lock_unlock(hass, hk_driver): assert call_unlock[0].data[ATTR_ENTITY_ID] == entity_id assert call_unlock[0].data[ATTR_CODE] == code assert acc.char_target_state.value == 0 + assert len(events) == 2 + assert events[-1].data[ATTR_VALUE] is None @pytest.mark.parametrize('config', [{}, {ATTR_CODE: None}]) -async def test_no_code(hass, hk_driver, config): +async def test_no_code(hass, hk_driver, config, events): """Test accessory if lock doesn't require a code.""" entity_id = 'lock.kitchen_door' @@ -83,3 +88,5 @@ async def test_no_code(hass, hk_driver, config): assert call_lock[0].data[ATTR_ENTITY_ID] == entity_id assert ATTR_CODE not in call_lock[0].data assert acc.char_target_state.value == 1 + assert len(events) == 1 + assert events[-1].data[ATTR_VALUE] is None diff --git a/tests/components/homekit/test_type_media_players.py b/tests/components/homekit/test_type_media_players.py index 681cbba7252..299570a6923 100644 --- a/tests/components/homekit/test_type_media_players.py +++ b/tests/components/homekit/test_type_media_players.py @@ -1,8 +1,8 @@ """Test different accessory types: Media Players.""" from homeassistant.components.homekit.const import ( - CONF_FEATURE_LIST, FEATURE_ON_OFF, FEATURE_PLAY_PAUSE, FEATURE_PLAY_STOP, - FEATURE_TOGGLE_MUTE) + ATTR_VALUE, CONF_FEATURE_LIST, FEATURE_ON_OFF, FEATURE_PLAY_PAUSE, + FEATURE_PLAY_STOP, FEATURE_TOGGLE_MUTE) from homeassistant.components.homekit.type_media_players import MediaPlayer from homeassistant.components.media_player import ( ATTR_MEDIA_VOLUME_MUTED, DOMAIN) @@ -13,7 +13,7 @@ from homeassistant.const import ( from tests.common import async_mock_service -async def test_media_player_set_state(hass, hk_driver): +async def test_media_player_set_state(hass, hk_driver, events): """Test if accessory and HA are updated accordingly.""" config = {CONF_FEATURE_LIST: { FEATURE_ON_OFF: None, FEATURE_PLAY_PAUSE: None, @@ -69,36 +69,48 @@ async def test_media_player_set_state(hass, hk_driver): await hass.async_block_till_done() assert call_turn_on assert call_turn_on[0].data[ATTR_ENTITY_ID] == entity_id + assert len(events) == 1 + assert events[-1].data[ATTR_VALUE] is None await hass.async_add_job(acc.chars[FEATURE_ON_OFF] .client_update_value, False) await hass.async_block_till_done() assert call_turn_off assert call_turn_off[0].data[ATTR_ENTITY_ID] == entity_id + assert len(events) == 2 + assert events[-1].data[ATTR_VALUE] is None await hass.async_add_job(acc.chars[FEATURE_PLAY_PAUSE] .client_update_value, True) await hass.async_block_till_done() assert call_media_play assert call_media_play[0].data[ATTR_ENTITY_ID] == entity_id + assert len(events) == 3 + assert events[-1].data[ATTR_VALUE] is None await hass.async_add_job(acc.chars[FEATURE_PLAY_PAUSE] .client_update_value, False) await hass.async_block_till_done() assert call_media_pause assert call_media_pause[0].data[ATTR_ENTITY_ID] == entity_id + assert len(events) == 4 + assert events[-1].data[ATTR_VALUE] is None await hass.async_add_job(acc.chars[FEATURE_PLAY_STOP] .client_update_value, True) await hass.async_block_till_done() assert call_media_play assert call_media_play[1].data[ATTR_ENTITY_ID] == entity_id + assert len(events) == 5 + assert events[-1].data[ATTR_VALUE] is None await hass.async_add_job(acc.chars[FEATURE_PLAY_STOP] .client_update_value, False) await hass.async_block_till_done() assert call_media_stop assert call_media_stop[0].data[ATTR_ENTITY_ID] == entity_id + assert len(events) == 6 + assert events[-1].data[ATTR_VALUE] is None await hass.async_add_job(acc.chars[FEATURE_TOGGLE_MUTE] .client_update_value, True) @@ -106,6 +118,8 @@ async def test_media_player_set_state(hass, hk_driver): assert call_toggle_mute assert call_toggle_mute[0].data[ATTR_ENTITY_ID] == entity_id assert call_toggle_mute[0].data[ATTR_MEDIA_VOLUME_MUTED] is True + assert len(events) == 7 + assert events[-1].data[ATTR_VALUE] is None await hass.async_add_job(acc.chars[FEATURE_TOGGLE_MUTE] .client_update_value, False) @@ -113,3 +127,5 @@ async def test_media_player_set_state(hass, hk_driver): assert call_toggle_mute assert call_toggle_mute[1].data[ATTR_ENTITY_ID] == entity_id assert call_toggle_mute[1].data[ATTR_MEDIA_VOLUME_MUTED] is False + assert len(events) == 8 + assert events[-1].data[ATTR_VALUE] is None diff --git a/tests/components/homekit/test_type_security_systems.py b/tests/components/homekit/test_type_security_systems.py index 3ddce0f36eb..3753a1aa433 100644 --- a/tests/components/homekit/test_type_security_systems.py +++ b/tests/components/homekit/test_type_security_systems.py @@ -2,6 +2,7 @@ import pytest from homeassistant.components.alarm_control_panel import DOMAIN +from homeassistant.components.homekit.const import ATTR_VALUE from homeassistant.components.homekit.type_security_systems import \ SecuritySystem from homeassistant.const import ( @@ -12,7 +13,7 @@ from homeassistant.const import ( from tests.common import async_mock_service -async def test_switch_set_state(hass, hk_driver): +async def test_switch_set_state(hass, hk_driver, events): """Test if accessory and HA are updated accordingly.""" code = '1234' config = {ATTR_CODE: code} @@ -72,6 +73,8 @@ async def test_switch_set_state(hass, hk_driver): assert call_arm_home[0].data[ATTR_ENTITY_ID] == entity_id assert call_arm_home[0].data[ATTR_CODE] == code assert acc.char_target_state.value == 0 + assert len(events) == 1 + assert events[-1].data[ATTR_VALUE] is None await hass.async_add_job(acc.char_target_state.client_update_value, 1) await hass.async_block_till_done() @@ -79,6 +82,8 @@ async def test_switch_set_state(hass, hk_driver): assert call_arm_away[0].data[ATTR_ENTITY_ID] == entity_id assert call_arm_away[0].data[ATTR_CODE] == code assert acc.char_target_state.value == 1 + assert len(events) == 2 + assert events[-1].data[ATTR_VALUE] is None await hass.async_add_job(acc.char_target_state.client_update_value, 2) await hass.async_block_till_done() @@ -86,6 +91,8 @@ async def test_switch_set_state(hass, hk_driver): assert call_arm_night[0].data[ATTR_ENTITY_ID] == entity_id assert call_arm_night[0].data[ATTR_CODE] == code assert acc.char_target_state.value == 2 + assert len(events) == 3 + assert events[-1].data[ATTR_VALUE] is None await hass.async_add_job(acc.char_target_state.client_update_value, 3) await hass.async_block_till_done() @@ -93,10 +100,12 @@ async def test_switch_set_state(hass, hk_driver): assert call_disarm[0].data[ATTR_ENTITY_ID] == entity_id assert call_disarm[0].data[ATTR_CODE] == code assert acc.char_target_state.value == 3 + assert len(events) == 4 + assert events[-1].data[ATTR_VALUE] is None @pytest.mark.parametrize('config', [{}, {ATTR_CODE: None}]) -async def test_no_alarm_code(hass, hk_driver, config): +async def test_no_alarm_code(hass, hk_driver, config, events): """Test accessory if security_system doesn't require an alarm_code.""" entity_id = 'alarm_control_panel.test' @@ -114,3 +123,5 @@ async def test_no_alarm_code(hass, hk_driver, config): assert call_arm_home[0].data[ATTR_ENTITY_ID] == entity_id assert ATTR_CODE not in call_arm_home[0].data assert acc.char_target_state.value == 0 + assert len(events) == 1 + assert events[-1].data[ATTR_VALUE] is None diff --git a/tests/components/homekit/test_type_switches.py b/tests/components/homekit/test_type_switches.py index bc44a93884a..d170647d492 100644 --- a/tests/components/homekit/test_type_switches.py +++ b/tests/components/homekit/test_type_switches.py @@ -2,7 +2,7 @@ import pytest from homeassistant.components.homekit.const import ( - TYPE_FAUCET, TYPE_SHOWER, TYPE_SPRINKLER, TYPE_VALVE) + ATTR_VALUE, TYPE_FAUCET, TYPE_SHOWER, TYPE_SPRINKLER, TYPE_VALVE) from homeassistant.components.homekit.type_switches import ( Outlet, Switch, Valve) from homeassistant.const import ATTR_ENTITY_ID, CONF_TYPE, STATE_OFF, STATE_ON @@ -11,7 +11,7 @@ from homeassistant.core import split_entity_id from tests.common import async_mock_service -async def test_outlet_set_state(hass, hk_driver): +async def test_outlet_set_state(hass, hk_driver, events): """Test if Outlet accessory and HA are updated accordingly.""" entity_id = 'switch.outlet_test' @@ -43,11 +43,15 @@ async def test_outlet_set_state(hass, hk_driver): await hass.async_block_till_done() assert call_turn_on assert call_turn_on[0].data[ATTR_ENTITY_ID] == entity_id + assert len(events) == 1 + assert events[-1].data[ATTR_VALUE] is None await hass.async_add_job(acc.char_on.client_update_value, False) await hass.async_block_till_done() assert call_turn_off assert call_turn_off[0].data[ATTR_ENTITY_ID] == entity_id + assert len(events) == 2 + assert events[-1].data[ATTR_VALUE] is None @pytest.mark.parametrize('entity_id', [ @@ -57,7 +61,7 @@ async def test_outlet_set_state(hass, hk_driver): 'script.test', 'switch.test', ]) -async def test_switch_set_state(hass, hk_driver, entity_id): +async def test_switch_set_state(hass, hk_driver, entity_id, events): """Test if accessory and HA are updated accordingly.""" domain = split_entity_id(entity_id)[0] @@ -88,14 +92,18 @@ async def test_switch_set_state(hass, hk_driver, entity_id): await hass.async_block_till_done() assert call_turn_on assert call_turn_on[0].data[ATTR_ENTITY_ID] == entity_id + assert len(events) == 1 + assert events[-1].data[ATTR_VALUE] is None await hass.async_add_job(acc.char_on.client_update_value, False) await hass.async_block_till_done() assert call_turn_off assert call_turn_off[0].data[ATTR_ENTITY_ID] == entity_id + assert len(events) == 2 + assert events[-1].data[ATTR_VALUE] is None -async def test_valve_set_state(hass, hk_driver): +async def test_valve_set_state(hass, hk_driver, events): """Test if Valve accessory and HA are updated accordingly.""" entity_id = 'switch.valve_test' @@ -154,9 +162,13 @@ async def test_valve_set_state(hass, hk_driver): assert acc.char_in_use.value is True assert call_turn_on assert call_turn_on[0].data[ATTR_ENTITY_ID] == entity_id + assert len(events) == 1 + assert events[-1].data[ATTR_VALUE] is None await hass.async_add_job(acc.char_active.client_update_value, False) await hass.async_block_till_done() assert acc.char_in_use.value is False assert call_turn_off assert call_turn_off[0].data[ATTR_ENTITY_ID] == entity_id + assert len(events) == 2 + assert events[-1].data[ATTR_VALUE] is None diff --git a/tests/components/homekit/test_type_thermostats.py b/tests/components/homekit/test_type_thermostats.py index 687a9e9513c..e3187b6cf02 100644 --- a/tests/components/homekit/test_type_thermostats.py +++ b/tests/components/homekit/test_type_thermostats.py @@ -10,7 +10,7 @@ from homeassistant.components.climate import ( ATTR_OPERATION_LIST, DEFAULT_MAX_TEMP, DEFAULT_MIN_TEMP, DOMAIN, STATE_AUTO, STATE_COOL, STATE_HEAT) from homeassistant.components.homekit.const import ( - PROP_MAX_VALUE, PROP_MIN_VALUE) + ATTR_VALUE, PROP_MAX_VALUE, PROP_MIN_VALUE) from homeassistant.const import ( ATTR_ENTITY_ID, ATTR_SUPPORTED_FEATURES, CONF_TEMPERATURE_UNIT, STATE_OFF, TEMP_FAHRENHEIT) @@ -31,7 +31,7 @@ def cls(): patcher.stop() -async def test_default_thermostat(hass, hk_driver, cls): +async def test_default_thermostat(hass, hk_driver, cls, events): """Test if accessory and HA are updated accordingly.""" entity_id = 'climate.test' @@ -157,6 +157,9 @@ async def test_default_thermostat(hass, hk_driver, cls): assert call_set_temperature[0].data[ATTR_ENTITY_ID] == entity_id assert call_set_temperature[0].data[ATTR_TEMPERATURE] == 19.0 assert acc.char_target_temp.value == 19.0 + assert len(events) == 1 + assert events[-1].data[ATTR_VALUE] == "target {}°C".format( + acc.char_target_temp.value) await hass.async_add_job(acc.char_target_heat_cool.client_update_value, 1) await hass.async_block_till_done() @@ -164,9 +167,11 @@ async def test_default_thermostat(hass, hk_driver, cls): assert call_set_operation_mode[0].data[ATTR_ENTITY_ID] == entity_id assert call_set_operation_mode[0].data[ATTR_OPERATION_MODE] == STATE_HEAT assert acc.char_target_heat_cool.value == 1 + assert len(events) == 2 + assert events[-1].data[ATTR_VALUE] == STATE_HEAT -async def test_auto_thermostat(hass, hk_driver, cls): +async def test_auto_thermostat(hass, hk_driver, cls, events): """Test if accessory and HA are updated accordingly.""" entity_id = 'climate.test' @@ -238,6 +243,9 @@ async def test_auto_thermostat(hass, hk_driver, cls): assert call_set_temperature[0].data[ATTR_ENTITY_ID] == entity_id assert call_set_temperature[0].data[ATTR_TARGET_TEMP_LOW] == 20.0 assert acc.char_heating_thresh_temp.value == 20.0 + assert len(events) == 1 + assert events[-1].data[ATTR_VALUE] == "heating threshold {}°C".format( + acc.char_heating_thresh_temp.value) await hass.async_add_job( acc.char_cooling_thresh_temp.client_update_value, 25.0) @@ -246,9 +254,12 @@ async def test_auto_thermostat(hass, hk_driver, cls): assert call_set_temperature[1].data[ATTR_ENTITY_ID] == entity_id assert call_set_temperature[1].data[ATTR_TARGET_TEMP_HIGH] == 25.0 assert acc.char_cooling_thresh_temp.value == 25.0 + assert len(events) == 2 + assert events[-1].data[ATTR_VALUE] == "cooling threshold {}°C".format( + acc.char_cooling_thresh_temp.value) -async def test_power_state(hass, hk_driver, cls): +async def test_power_state(hass, hk_driver, cls, events): """Test if accessory and HA are updated accordingly.""" entity_id = 'climate.test' @@ -297,15 +308,19 @@ async def test_power_state(hass, hk_driver, cls): assert call_set_operation_mode[0].data[ATTR_ENTITY_ID] == entity_id assert call_set_operation_mode[0].data[ATTR_OPERATION_MODE] == STATE_HEAT assert acc.char_target_heat_cool.value == 1 + assert len(events) == 1 + assert events[-1].data[ATTR_VALUE] == STATE_HEAT await hass.async_add_job(acc.char_target_heat_cool.client_update_value, 0) await hass.async_block_till_done() assert call_turn_off assert call_turn_off[0].data[ATTR_ENTITY_ID] == entity_id assert acc.char_target_heat_cool.value == 0 + assert len(events) == 2 + assert events[-1].data[ATTR_VALUE] is None -async def test_thermostat_fahrenheit(hass, hk_driver, cls): +async def test_thermostat_fahrenheit(hass, hk_driver, cls, events): """Test if accessory and HA are updated accordingly.""" entity_id = 'climate.test' @@ -341,6 +356,8 @@ async def test_thermostat_fahrenheit(hass, hk_driver, cls): assert call_set_temperature[0].data[ATTR_ENTITY_ID] == entity_id assert call_set_temperature[0].data[ATTR_TARGET_TEMP_HIGH] == 73.4 assert call_set_temperature[0].data[ATTR_TARGET_TEMP_LOW] == 68 + assert len(events) == 1 + assert events[-1].data[ATTR_VALUE] == "cooling threshold 73.4°F" await hass.async_add_job( acc.char_heating_thresh_temp.client_update_value, 22) @@ -349,12 +366,16 @@ async def test_thermostat_fahrenheit(hass, hk_driver, cls): assert call_set_temperature[1].data[ATTR_ENTITY_ID] == entity_id assert call_set_temperature[1].data[ATTR_TARGET_TEMP_HIGH] == 73.4 assert call_set_temperature[1].data[ATTR_TARGET_TEMP_LOW] == 71.6 + assert len(events) == 2 + assert events[-1].data[ATTR_VALUE] == "heating threshold 71.6°F" await hass.async_add_job(acc.char_target_temp.client_update_value, 24.0) await hass.async_block_till_done() assert call_set_temperature[2] assert call_set_temperature[2].data[ATTR_ENTITY_ID] == entity_id assert call_set_temperature[2].data[ATTR_TEMPERATURE] == 75.2 + assert len(events) == 3 + assert events[-1].data[ATTR_VALUE] == "target 75.2°F" async def test_get_temperature_range(hass, hk_driver, cls): diff --git a/tests/components/test_logbook.py b/tests/components/test_logbook.py index 3bb3ae57c68..1100a16b381 100644 --- a/tests/components/test_logbook.py +++ b/tests/components/test_logbook.py @@ -7,11 +7,15 @@ import unittest from homeassistant.components import sun import homeassistant.core as ha from homeassistant.const import ( + ATTR_ENTITY_ID, ATTR_SERVICE, EVENT_STATE_CHANGED, EVENT_HOMEASSISTANT_START, EVENT_HOMEASSISTANT_STOP, ATTR_HIDDEN, STATE_NOT_HOME, STATE_ON, STATE_OFF) import homeassistant.util.dt as dt_util from homeassistant.components import logbook, recorder from homeassistant.components.alexa.smart_home import EVENT_ALEXA_SMART_HOME +from homeassistant.components.homekit.const import ( + ATTR_DISPLAY_NAME, ATTR_VALUE, DOMAIN as DOMAIN_HOMEKIT, + EVENT_HOMEKIT_CHANGED) from homeassistant.setup import setup_component, async_setup_component from tests.common import ( @@ -684,3 +688,31 @@ async def test_humanify_alexa_event(hass): assert event3['message'] == \ 'send command Alexa.PowerController/TurnOn for light.non_existing' assert event3['entity_id'] == 'light.non_existing' + + +async def test_humanify_homekit_changed_event(hass): + """Test humanifying HomeKit changed event.""" + event1, event2 = list(logbook.humanify(hass, [ + ha.Event(EVENT_HOMEKIT_CHANGED, { + ATTR_ENTITY_ID: 'lock.front_door', + ATTR_DISPLAY_NAME: 'Front Door', + ATTR_SERVICE: 'lock', + }), + ha.Event(EVENT_HOMEKIT_CHANGED, { + ATTR_ENTITY_ID: 'cover.window', + ATTR_DISPLAY_NAME: 'Window', + ATTR_SERVICE: 'set_cover_position', + ATTR_VALUE: 75, + }), + ])) + + assert event1['name'] == 'HomeKit' + assert event1['domain'] == DOMAIN_HOMEKIT + assert event1['message'] == 'send command lock for Front Door' + assert event1['entity_id'] == 'lock.front_door' + + assert event2['name'] == 'HomeKit' + assert event1['domain'] == DOMAIN_HOMEKIT + assert event2['message'] == \ + 'send command set_cover_position to 75 for Window' + assert event2['entity_id'] == 'cover.window'