Add LogBook support to HomeKit (#17180)

This commit is contained in:
ehendrix23 2018-10-16 05:32:53 -06:00 committed by cdce8p
parent 71ab8a9b1a
commit fee87cd6ed
22 changed files with 327 additions and 66 deletions

View file

@ -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."""

View file

@ -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'

View file

@ -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)

View file

@ -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."""

View file

@ -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."""

View file

@ -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."""

View file

@ -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."""

View file

@ -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."""

View file

@ -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."""

View file

@ -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."""

View file

@ -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."""

View file

@ -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

View file

@ -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)

View file

@ -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

View file

@ -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

View file

@ -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)"

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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):

View file

@ -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'