Add LogBook support to HomeKit (#17180)
This commit is contained in:
parent
71ab8a9b1a
commit
fee87cd6ed
22 changed files with 327 additions and 66 deletions
|
@ -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."""
|
||||
|
|
|
@ -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'
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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."""
|
||||
|
|
|
@ -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."""
|
||||
|
|
|
@ -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."""
|
||||
|
|
|
@ -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."""
|
||||
|
|
|
@ -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."""
|
||||
|
|
|
@ -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."""
|
||||
|
|
|
@ -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."""
|
||||
|
|
|
@ -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."""
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)"
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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):
|
||||
|
|
|
@ -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'
|
||||
|
|
Loading…
Add table
Reference in a new issue