mirror of
https://github.com/home-assistant/core.git
synced 2025-07-23 21:27:38 +00:00
Homekit refactor (#13707)
This commit is contained in:
parent
8d48164f25
commit
2a5751c09d
@ -8,11 +8,9 @@ from zlib import adler32
|
||||
|
||||
import voluptuous as vol
|
||||
|
||||
from homeassistant.components.climate import (
|
||||
SUPPORT_TARGET_TEMPERATURE_HIGH, SUPPORT_TARGET_TEMPERATURE_LOW)
|
||||
from homeassistant.components.cover import SUPPORT_SET_POSITION
|
||||
from homeassistant.const import (
|
||||
ATTR_CODE, ATTR_SUPPORTED_FEATURES, ATTR_UNIT_OF_MEASUREMENT,
|
||||
ATTR_SUPPORTED_FEATURES, ATTR_UNIT_OF_MEASUREMENT,
|
||||
CONF_PORT, TEMP_CELSIUS, TEMP_FAHRENHEIT,
|
||||
EVENT_HOMEASSISTANT_START, EVENT_HOMEASSISTANT_STOP)
|
||||
import homeassistant.helpers.config_validation as cv
|
||||
@ -79,63 +77,46 @@ def get_accessory(hass, state, aid, config):
|
||||
state.entity_id)
|
||||
return None
|
||||
|
||||
if state.domain == 'sensor':
|
||||
unit = state.attributes.get(ATTR_UNIT_OF_MEASUREMENT)
|
||||
if unit == TEMP_CELSIUS or unit == TEMP_FAHRENHEIT:
|
||||
_LOGGER.debug('Add "%s" as "%s"',
|
||||
state.entity_id, 'TemperatureSensor')
|
||||
return TYPES['TemperatureSensor'](hass, state.entity_id,
|
||||
state.name, aid=aid)
|
||||
elif unit == '%':
|
||||
_LOGGER.debug('Add "%s" as %s"',
|
||||
state.entity_id, 'HumiditySensor')
|
||||
return TYPES['HumiditySensor'](hass, state.entity_id, state.name,
|
||||
aid=aid)
|
||||
a_type = None
|
||||
config = config or {}
|
||||
|
||||
if state.domain == 'alarm_control_panel':
|
||||
a_type = 'SecuritySystem'
|
||||
|
||||
elif state.domain == 'binary_sensor' or state.domain == 'device_tracker':
|
||||
_LOGGER.debug('Add "%s" as "%s"', state.entity_id, 'BinarySensor')
|
||||
return TYPES['BinarySensor'](hass, state.entity_id,
|
||||
state.name, aid=aid)
|
||||
a_type = 'BinarySensor'
|
||||
|
||||
elif state.domain == 'climate':
|
||||
a_type = 'Thermostat'
|
||||
|
||||
elif state.domain == 'cover':
|
||||
# Only add covers that support set_cover_position
|
||||
features = state.attributes.get(ATTR_SUPPORTED_FEATURES, 0)
|
||||
if features & SUPPORT_SET_POSITION:
|
||||
_LOGGER.debug('Add "%s" as "%s"',
|
||||
state.entity_id, 'WindowCovering')
|
||||
return TYPES['WindowCovering'](hass, state.entity_id, state.name,
|
||||
aid=aid)
|
||||
|
||||
elif state.domain == 'alarm_control_panel':
|
||||
_LOGGER.debug('Add "%s" as "%s"', state.entity_id, 'SecuritySystem')
|
||||
return TYPES['SecuritySystem'](hass, state.entity_id, state.name,
|
||||
alarm_code=config.get(ATTR_CODE),
|
||||
aid=aid)
|
||||
|
||||
elif state.domain == 'climate':
|
||||
features = state.attributes.get(ATTR_SUPPORTED_FEATURES, 0)
|
||||
support_temp_range = SUPPORT_TARGET_TEMPERATURE_LOW | \
|
||||
SUPPORT_TARGET_TEMPERATURE_HIGH
|
||||
# Check if climate device supports auto mode
|
||||
support_auto = bool(features & support_temp_range)
|
||||
|
||||
_LOGGER.debug('Add "%s" as "%s"', state.entity_id, 'Thermostat')
|
||||
return TYPES['Thermostat'](hass, state.entity_id,
|
||||
state.name, support_auto, aid=aid)
|
||||
a_type = 'WindowCovering'
|
||||
|
||||
elif state.domain == 'light':
|
||||
_LOGGER.debug('Add "%s" as "%s"', state.entity_id, 'Light')
|
||||
return TYPES['Light'](hass, state.entity_id, state.name, aid=aid)
|
||||
a_type = 'Light'
|
||||
|
||||
elif state.domain == 'lock':
|
||||
return TYPES['Lock'](hass, state.entity_id, state.name, aid=aid)
|
||||
a_type = 'Lock'
|
||||
|
||||
elif state.domain == 'sensor':
|
||||
unit = state.attributes.get(ATTR_UNIT_OF_MEASUREMENT)
|
||||
if unit == TEMP_CELSIUS or unit == TEMP_FAHRENHEIT:
|
||||
a_type = 'TemperatureSensor'
|
||||
elif unit == '%':
|
||||
a_type = 'HumiditySensor'
|
||||
|
||||
elif state.domain == 'switch' or state.domain == 'remote' \
|
||||
or state.domain == 'input_boolean' or state.domain == 'script':
|
||||
_LOGGER.debug('Add "%s" as "%s"', state.entity_id, 'Switch')
|
||||
return TYPES['Switch'](hass, state.entity_id, state.name, aid=aid)
|
||||
a_type = 'Switch'
|
||||
|
||||
return None
|
||||
if a_type is None:
|
||||
return None
|
||||
|
||||
_LOGGER.debug('Add "%s" as "%s"', state.entity_id, a_type)
|
||||
return TYPES[a_type](hass, state.name, state.entity_id, aid, config=config)
|
||||
|
||||
|
||||
def generate_aid(entity_id):
|
||||
@ -151,7 +132,7 @@ class HomeKit():
|
||||
|
||||
def __init__(self, hass, port, entity_filter, entity_config):
|
||||
"""Initialize a HomeKit object."""
|
||||
self._hass = hass
|
||||
self.hass = hass
|
||||
self._port = port
|
||||
self._filter = entity_filter
|
||||
self._config = entity_config
|
||||
@ -164,11 +145,11 @@ class HomeKit():
|
||||
"""Setup bridge and accessory driver."""
|
||||
from .accessories import HomeBridge, HomeDriver
|
||||
|
||||
self._hass.bus.async_listen_once(
|
||||
self.hass.bus.async_listen_once(
|
||||
EVENT_HOMEASSISTANT_STOP, self.stop)
|
||||
|
||||
path = self._hass.config.path(HOMEKIT_FILE)
|
||||
self.bridge = HomeBridge(self._hass)
|
||||
path = self.hass.config.path(HOMEKIT_FILE)
|
||||
self.bridge = HomeBridge(self.hass)
|
||||
self.driver = HomeDriver(self.bridge, self._port, get_local_ip(), path)
|
||||
|
||||
def add_bridge_accessory(self, state):
|
||||
@ -177,7 +158,7 @@ class HomeKit():
|
||||
return
|
||||
aid = generate_aid(state.entity_id)
|
||||
conf = self._config.pop(state.entity_id, {})
|
||||
acc = get_accessory(self._hass, state, aid, conf)
|
||||
acc = get_accessory(self.hass, state, aid, conf)
|
||||
if acc is not None:
|
||||
self.bridge.add_accessory(acc)
|
||||
|
||||
@ -192,12 +173,12 @@ class HomeKit():
|
||||
type_covers, type_lights, type_locks, type_security_systems,
|
||||
type_sensors, type_switches, type_thermostats)
|
||||
|
||||
for state in self._hass.states.all():
|
||||
for state in self.hass.states.all():
|
||||
self.add_bridge_accessory(state)
|
||||
self.bridge.set_broker(self.driver)
|
||||
|
||||
if not self.bridge.paired:
|
||||
show_setup_message(self.bridge, self._hass)
|
||||
show_setup_message(self.hass, self.bridge)
|
||||
|
||||
_LOGGER.debug('Driver start')
|
||||
self.driver.start()
|
||||
|
@ -7,14 +7,14 @@ import logging
|
||||
from pyhap.accessory import Accessory, Bridge, Category
|
||||
from pyhap.accessory_driver import AccessoryDriver
|
||||
|
||||
from homeassistant.core import callback
|
||||
from homeassistant.core import callback as ha_callback
|
||||
from homeassistant.helpers.event import (
|
||||
async_track_state_change, track_point_in_utc_time)
|
||||
from homeassistant.util import dt as dt_util
|
||||
|
||||
from .const import (
|
||||
DEBOUNCE_TIMEOUT, ACCESSORY_MODEL, ACCESSORY_NAME, BRIDGE_MODEL,
|
||||
BRIDGE_NAME, MANUFACTURER, SERV_ACCESSORY_INFO, CHAR_MANUFACTURER,
|
||||
DEBOUNCE_TIMEOUT, BRIDGE_MODEL, BRIDGE_NAME, MANUFACTURER,
|
||||
SERV_ACCESSORY_INFO, CHAR_MANUFACTURER,
|
||||
CHAR_MODEL, CHAR_NAME, CHAR_SERIAL_NUMBER)
|
||||
from .util import (
|
||||
show_setup_message, dismiss_setup_message)
|
||||
@ -24,7 +24,7 @@ _LOGGER = logging.getLogger(__name__)
|
||||
|
||||
def debounce(func):
|
||||
"""Decorator function. Debounce callbacks form HomeKit."""
|
||||
@callback
|
||||
@ha_callback
|
||||
def call_later_listener(*args):
|
||||
"""Callback listener called from call_later."""
|
||||
# pylint: disable=unsubscriptable-object
|
||||
@ -72,6 +72,18 @@ def add_preload_service(acc, service, chars=None):
|
||||
return service
|
||||
|
||||
|
||||
def setup_char(char_name, service, value=None, properties=None, callback=None):
|
||||
"""Helper function to return fully configured characteristic."""
|
||||
char = service.get_characteristic(char_name)
|
||||
if value:
|
||||
char.value = value
|
||||
if properties:
|
||||
char.override_properties(properties)
|
||||
if callback:
|
||||
char.setter_callback = callback
|
||||
return char
|
||||
|
||||
|
||||
def set_accessory_info(acc, name, model, manufacturer=MANUFACTURER,
|
||||
serial_number='0000'):
|
||||
"""Set the default accessory information."""
|
||||
@ -85,14 +97,13 @@ def set_accessory_info(acc, name, model, manufacturer=MANUFACTURER,
|
||||
class HomeAccessory(Accessory):
|
||||
"""Adapter class for Accessory."""
|
||||
|
||||
# pylint: disable=no-member
|
||||
|
||||
def __init__(self, name=ACCESSORY_NAME, model=ACCESSORY_MODEL,
|
||||
category='OTHER', **kwargs):
|
||||
def __init__(self, hass, name, entity_id, aid, category):
|
||||
"""Initialize a Accessory object."""
|
||||
super().__init__(name, **kwargs)
|
||||
set_accessory_info(self, name, model)
|
||||
super().__init__(name, aid=aid)
|
||||
set_accessory_info(self, name, model=entity_id)
|
||||
self.category = getattr(Category, category, Category.OTHER)
|
||||
self.entity_id = entity_id
|
||||
self.hass = hass
|
||||
|
||||
def _set_services(self):
|
||||
add_preload_service(self, SERV_ACCESSORY_INFO)
|
||||
@ -100,19 +111,33 @@ class HomeAccessory(Accessory):
|
||||
def run(self):
|
||||
"""Method called by accessory after driver is started."""
|
||||
state = self.hass.states.get(self.entity_id)
|
||||
self.update_state(new_state=state)
|
||||
self.update_state_callback(new_state=state)
|
||||
async_track_state_change(
|
||||
self.hass, self.entity_id, self.update_state)
|
||||
self.hass, self.entity_id, self.update_state_callback)
|
||||
|
||||
def update_state_callback(self, entity_id=None, old_state=None,
|
||||
new_state=None):
|
||||
"""Callback from state change listener."""
|
||||
_LOGGER.debug('New_state: %s', new_state)
|
||||
if new_state is None:
|
||||
return
|
||||
self.update_state(new_state)
|
||||
|
||||
def update_state(self, new_state):
|
||||
"""Method called on state change to update HomeKit value.
|
||||
|
||||
Overridden by accessory types.
|
||||
"""
|
||||
pass
|
||||
|
||||
|
||||
class HomeBridge(Bridge):
|
||||
"""Adapter class for Bridge."""
|
||||
|
||||
def __init__(self, hass, name=BRIDGE_NAME,
|
||||
model=BRIDGE_MODEL, **kwargs):
|
||||
def __init__(self, hass, name=BRIDGE_NAME):
|
||||
"""Initialize a Bridge object."""
|
||||
super().__init__(name, **kwargs)
|
||||
set_accessory_info(self, name, model)
|
||||
super().__init__(name)
|
||||
set_accessory_info(self, name, model=BRIDGE_MODEL)
|
||||
self.hass = hass
|
||||
|
||||
def _set_services(self):
|
||||
@ -130,7 +155,7 @@ class HomeBridge(Bridge):
|
||||
def remove_paired_client(self, client_uuid):
|
||||
"""Override super function to show setup message if unpaired."""
|
||||
super().remove_paired_client(client_uuid)
|
||||
show_setup_message(self, self.hass)
|
||||
show_setup_message(self.hass, self)
|
||||
|
||||
|
||||
class HomeDriver(AccessoryDriver):
|
||||
|
@ -18,8 +18,6 @@ DEFAULT_PORT = 51827
|
||||
SERVICE_HOMEKIT_START = 'start'
|
||||
|
||||
# #### STRING CONSTANTS ####
|
||||
ACCESSORY_MODEL = 'homekit.accessory'
|
||||
ACCESSORY_NAME = 'Home Accessory'
|
||||
BRIDGE_MODEL = 'homekit.bridge'
|
||||
BRIDGE_NAME = 'Home Assistant'
|
||||
MANUFACTURER = 'HomeAssistant'
|
||||
|
@ -4,12 +4,11 @@ import logging
|
||||
from homeassistant.components.cover import ATTR_CURRENT_POSITION
|
||||
|
||||
from . import TYPES
|
||||
from .accessories import HomeAccessory, add_preload_service
|
||||
from .accessories import HomeAccessory, add_preload_service, setup_char
|
||||
from .const import (
|
||||
CATEGORY_WINDOW_COVERING, SERV_WINDOW_COVERING,
|
||||
CHAR_CURRENT_POSITION, CHAR_TARGET_POSITION, CHAR_POSITION_STATE)
|
||||
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
|
||||
@ -20,29 +19,20 @@ class WindowCovering(HomeAccessory):
|
||||
The cover entity must support: set_cover_position.
|
||||
"""
|
||||
|
||||
def __init__(self, hass, entity_id, display_name, **kwargs):
|
||||
def __init__(self, *args, config):
|
||||
"""Initialize a WindowCovering accessory object."""
|
||||
super().__init__(display_name, entity_id,
|
||||
CATEGORY_WINDOW_COVERING, **kwargs)
|
||||
|
||||
self.hass = hass
|
||||
self.entity_id = entity_id
|
||||
|
||||
super().__init__(*args, category=CATEGORY_WINDOW_COVERING)
|
||||
self.current_position = None
|
||||
self.homekit_target = None
|
||||
|
||||
serv_cover = add_preload_service(self, SERV_WINDOW_COVERING)
|
||||
self.char_current_position = serv_cover. \
|
||||
get_characteristic(CHAR_CURRENT_POSITION)
|
||||
self.char_target_position = serv_cover. \
|
||||
get_characteristic(CHAR_TARGET_POSITION)
|
||||
self.char_position_state = serv_cover. \
|
||||
get_characteristic(CHAR_POSITION_STATE)
|
||||
self.char_current_position.value = 0
|
||||
self.char_target_position.value = 0
|
||||
self.char_position_state.value = 0
|
||||
|
||||
self.char_target_position.setter_callback = self.move_cover
|
||||
self.char_current_position = setup_char(
|
||||
CHAR_CURRENT_POSITION, serv_cover, value=0)
|
||||
self.char_target_position = setup_char(
|
||||
CHAR_TARGET_POSITION, serv_cover, value=0,
|
||||
callback=self.move_cover)
|
||||
self.char_position_state = setup_char(
|
||||
CHAR_POSITION_STATE, serv_cover, value=0)
|
||||
|
||||
def move_cover(self, value):
|
||||
"""Move cover to value if call came from HomeKit."""
|
||||
@ -56,11 +46,8 @@ class WindowCovering(HomeAccessory):
|
||||
self.hass.components.cover.set_cover_position(
|
||||
value, self.entity_id)
|
||||
|
||||
def update_state(self, entity_id=None, old_state=None, new_state=None):
|
||||
def update_state(self, new_state):
|
||||
"""Update cover position after state changed."""
|
||||
if new_state is None:
|
||||
return
|
||||
|
||||
current_position = new_state.attributes.get(ATTR_CURRENT_POSITION)
|
||||
if isinstance(current_position, int):
|
||||
self.current_position = current_position
|
||||
|
@ -7,7 +7,8 @@ from homeassistant.components.light import (
|
||||
from homeassistant.const import ATTR_SUPPORTED_FEATURES, STATE_ON, STATE_OFF
|
||||
|
||||
from . import TYPES
|
||||
from .accessories import HomeAccessory, add_preload_service, debounce
|
||||
from .accessories import (
|
||||
HomeAccessory, add_preload_service, debounce, setup_char)
|
||||
from .const import (
|
||||
CATEGORY_LIGHT, SERV_LIGHTBULB, CHAR_COLOR_TEMPERATURE,
|
||||
CHAR_BRIGHTNESS, CHAR_HUE, CHAR_ON, CHAR_SATURATION)
|
||||
@ -24,12 +25,9 @@ class Light(HomeAccessory):
|
||||
Currently supports: state, brightness, color temperature, rgb_color.
|
||||
"""
|
||||
|
||||
def __init__(self, hass, entity_id, name, **kwargs):
|
||||
def __init__(self, *args, config):
|
||||
"""Initialize a new Light accessory object."""
|
||||
super().__init__(name, entity_id, CATEGORY_LIGHT, **kwargs)
|
||||
|
||||
self.hass = hass
|
||||
self.entity_id = entity_id
|
||||
super().__init__(*args, category=CATEGORY_LIGHT)
|
||||
self._flag = {CHAR_ON: False, CHAR_BRIGHTNESS: False,
|
||||
CHAR_HUE: False, CHAR_SATURATION: False,
|
||||
CHAR_COLOR_TEMPERATURE: False, RGB_COLOR: False}
|
||||
@ -49,36 +47,29 @@ class Light(HomeAccessory):
|
||||
self._saturation = None
|
||||
|
||||
serv_light = add_preload_service(self, SERV_LIGHTBULB, self.chars)
|
||||
self.char_on = serv_light.get_characteristic(CHAR_ON)
|
||||
self.char_on.setter_callback = self.set_state
|
||||
self.char_on.value = self._state
|
||||
self.char_on = setup_char(
|
||||
CHAR_ON, serv_light, value=self._state, callback=self.set_state)
|
||||
|
||||
if CHAR_BRIGHTNESS in self.chars:
|
||||
self.char_brightness = serv_light \
|
||||
.get_characteristic(CHAR_BRIGHTNESS)
|
||||
self.char_brightness.setter_callback = self.set_brightness
|
||||
self.char_brightness.value = 0
|
||||
self.char_brightness = setup_char(
|
||||
CHAR_BRIGHTNESS, serv_light, value=0,
|
||||
callback=self.set_brightness)
|
||||
if CHAR_COLOR_TEMPERATURE in self.chars:
|
||||
self.char_color_temperature = serv_light \
|
||||
.get_characteristic(CHAR_COLOR_TEMPERATURE)
|
||||
self.char_color_temperature.setter_callback = \
|
||||
self.set_color_temperature
|
||||
min_mireds = self.hass.states.get(self.entity_id) \
|
||||
.attributes.get(ATTR_MIN_MIREDS, 153)
|
||||
max_mireds = self.hass.states.get(self.entity_id) \
|
||||
.attributes.get(ATTR_MAX_MIREDS, 500)
|
||||
self.char_color_temperature.override_properties({
|
||||
'minValue': min_mireds, 'maxValue': max_mireds})
|
||||
self.char_color_temperature.value = min_mireds
|
||||
self.char_color_temperature = setup_char(
|
||||
CHAR_COLOR_TEMPERATURE, serv_light, value=min_mireds,
|
||||
properties={'minValue': min_mireds, 'maxValue': max_mireds},
|
||||
callback=self.set_color_temperature)
|
||||
if CHAR_HUE in self.chars:
|
||||
self.char_hue = serv_light.get_characteristic(CHAR_HUE)
|
||||
self.char_hue.setter_callback = self.set_hue
|
||||
self.char_hue.value = 0
|
||||
self.char_hue = setup_char(
|
||||
CHAR_HUE, serv_light, value=0, callback=self.set_hue)
|
||||
if CHAR_SATURATION in self.chars:
|
||||
self.char_saturation = serv_light \
|
||||
.get_characteristic(CHAR_SATURATION)
|
||||
self.char_saturation.setter_callback = self.set_saturation
|
||||
self.char_saturation.value = 75
|
||||
self.char_saturation = setup_char(
|
||||
CHAR_SATURATION, serv_light, value=75,
|
||||
callback=self.set_saturation)
|
||||
|
||||
def set_state(self, value):
|
||||
"""Set state if call came from HomeKit."""
|
||||
@ -136,11 +127,8 @@ class Light(HomeAccessory):
|
||||
self.hass.components.light.turn_on(
|
||||
self.entity_id, hs_color=color)
|
||||
|
||||
def update_state(self, entity_id=None, old_state=None, new_state=None):
|
||||
def update_state(self, new_state):
|
||||
"""Update light after state change."""
|
||||
if not new_state:
|
||||
return
|
||||
|
||||
# Handle State
|
||||
state = new_state.state
|
||||
if state in (STATE_ON, STATE_OFF):
|
||||
@ -162,7 +150,8 @@ class Light(HomeAccessory):
|
||||
if CHAR_COLOR_TEMPERATURE in self.chars:
|
||||
color_temperature = new_state.attributes.get(ATTR_COLOR_TEMP)
|
||||
if not self._flag[CHAR_COLOR_TEMPERATURE] \
|
||||
and isinstance(color_temperature, int):
|
||||
and isinstance(color_temperature, int) and \
|
||||
self.char_color_temperature.value != color_temperature:
|
||||
self.char_color_temperature.set_value(color_temperature)
|
||||
self._flag[CHAR_COLOR_TEMPERATURE] = False
|
||||
|
||||
|
@ -5,7 +5,7 @@ from homeassistant.components.lock import (
|
||||
ATTR_ENTITY_ID, STATE_LOCKED, STATE_UNLOCKED, STATE_UNKNOWN)
|
||||
|
||||
from . import TYPES
|
||||
from .accessories import HomeAccessory, add_preload_service
|
||||
from .accessories import HomeAccessory, add_preload_service, setup_char
|
||||
from .const import (
|
||||
CATEGORY_LOCK, SERV_LOCK, CHAR_LOCK_CURRENT_STATE, CHAR_LOCK_TARGET_STATE)
|
||||
|
||||
@ -27,25 +27,18 @@ class Lock(HomeAccessory):
|
||||
The lock entity must support: unlock and lock.
|
||||
"""
|
||||
|
||||
def __init__(self, hass, entity_id, name, **kwargs):
|
||||
def __init__(self, *args, config):
|
||||
"""Initialize a Lock accessory object."""
|
||||
super().__init__(name, entity_id, CATEGORY_LOCK, **kwargs)
|
||||
|
||||
self.hass = hass
|
||||
self.entity_id = entity_id
|
||||
|
||||
super().__init__(*args, category=CATEGORY_LOCK)
|
||||
self.flag_target_state = False
|
||||
|
||||
serv_lock_mechanism = add_preload_service(self, SERV_LOCK)
|
||||
self.char_current_state = serv_lock_mechanism. \
|
||||
get_characteristic(CHAR_LOCK_CURRENT_STATE)
|
||||
self.char_target_state = serv_lock_mechanism. \
|
||||
get_characteristic(CHAR_LOCK_TARGET_STATE)
|
||||
|
||||
self.char_current_state.value = HASS_TO_HOMEKIT[STATE_UNKNOWN]
|
||||
self.char_target_state.value = HASS_TO_HOMEKIT[STATE_LOCKED]
|
||||
|
||||
self.char_target_state.setter_callback = self.set_state
|
||||
self.char_current_state = setup_char(
|
||||
CHAR_LOCK_CURRENT_STATE, serv_lock_mechanism,
|
||||
value=HASS_TO_HOMEKIT[STATE_UNKNOWN])
|
||||
self.char_target_state = setup_char(
|
||||
CHAR_LOCK_TARGET_STATE, serv_lock_mechanism,
|
||||
value=HASS_TO_HOMEKIT[STATE_LOCKED], callback=self.set_state)
|
||||
|
||||
def set_state(self, value):
|
||||
"""Set lock state to value if call came from HomeKit."""
|
||||
@ -58,11 +51,8 @@ class Lock(HomeAccessory):
|
||||
params = {ATTR_ENTITY_ID: self.entity_id}
|
||||
self.hass.services.call('lock', service, params)
|
||||
|
||||
def update_state(self, entity_id=None, old_state=None, new_state=None):
|
||||
def update_state(self, new_state):
|
||||
"""Update lock after state changed."""
|
||||
if new_state is None:
|
||||
return
|
||||
|
||||
hass_state = new_state.state
|
||||
if hass_state in HASS_TO_HOMEKIT:
|
||||
current_lock_state = HASS_TO_HOMEKIT[hass_state]
|
||||
|
@ -7,7 +7,7 @@ from homeassistant.const import (
|
||||
ATTR_ENTITY_ID, ATTR_CODE)
|
||||
|
||||
from . import TYPES
|
||||
from .accessories import HomeAccessory, add_preload_service
|
||||
from .accessories import HomeAccessory, add_preload_service, setup_char
|
||||
from .const import (
|
||||
CATEGORY_ALARM_SYSTEM, SERV_SECURITY_SYSTEM,
|
||||
CHAR_CURRENT_SECURITY_STATE, CHAR_TARGET_SECURITY_STATE)
|
||||
@ -27,26 +27,18 @@ STATE_TO_SERVICE = {STATE_ALARM_DISARMED: 'alarm_disarm',
|
||||
class SecuritySystem(HomeAccessory):
|
||||
"""Generate an SecuritySystem accessory for an alarm control panel."""
|
||||
|
||||
def __init__(self, hass, entity_id, display_name, alarm_code, **kwargs):
|
||||
def __init__(self, *args, config):
|
||||
"""Initialize a SecuritySystem accessory object."""
|
||||
super().__init__(display_name, entity_id,
|
||||
CATEGORY_ALARM_SYSTEM, **kwargs)
|
||||
|
||||
self.hass = hass
|
||||
self.entity_id = entity_id
|
||||
self._alarm_code = alarm_code
|
||||
|
||||
super().__init__(*args, category=CATEGORY_ALARM_SYSTEM)
|
||||
self._alarm_code = config[ATTR_CODE]
|
||||
self.flag_target_state = False
|
||||
|
||||
serv_alarm = add_preload_service(self, SERV_SECURITY_SYSTEM)
|
||||
self.char_current_state = serv_alarm. \
|
||||
get_characteristic(CHAR_CURRENT_SECURITY_STATE)
|
||||
self.char_current_state.value = 3
|
||||
self.char_target_state = serv_alarm. \
|
||||
get_characteristic(CHAR_TARGET_SECURITY_STATE)
|
||||
self.char_target_state.value = 3
|
||||
|
||||
self.char_target_state.setter_callback = self.set_security_state
|
||||
self.char_current_state = setup_char(
|
||||
CHAR_CURRENT_SECURITY_STATE, serv_alarm, value=3)
|
||||
self.char_target_state = setup_char(
|
||||
CHAR_TARGET_SECURITY_STATE, serv_alarm, value=3,
|
||||
callback=self.set_security_state)
|
||||
|
||||
def set_security_state(self, value):
|
||||
"""Move security state to value if call came from HomeKit."""
|
||||
@ -61,11 +53,8 @@ class SecuritySystem(HomeAccessory):
|
||||
params[ATTR_CODE] = self._alarm_code
|
||||
self.hass.services.call('alarm_control_panel', service, params)
|
||||
|
||||
def update_state(self, entity_id=None, old_state=None, new_state=None):
|
||||
def update_state(self, new_state):
|
||||
"""Update security state after state changed."""
|
||||
if new_state is None:
|
||||
return
|
||||
|
||||
hass_state = new_state.state
|
||||
if hass_state in HASS_TO_HOMEKIT:
|
||||
current_security_state = HASS_TO_HOMEKIT[hass_state]
|
||||
|
@ -6,7 +6,7 @@ from homeassistant.const import (
|
||||
ATTR_DEVICE_CLASS, STATE_ON, STATE_HOME)
|
||||
|
||||
from . import TYPES
|
||||
from .accessories import HomeAccessory, add_preload_service
|
||||
from .accessories import HomeAccessory, add_preload_service, setup_char
|
||||
from .const import (
|
||||
CATEGORY_SENSOR, SERV_HUMIDITY_SENSOR, SERV_TEMPERATURE_SENSOR,
|
||||
CHAR_CURRENT_HUMIDITY, CHAR_CURRENT_TEMPERATURE, PROP_CELSIUS,
|
||||
@ -20,10 +20,8 @@ from .const import (
|
||||
DEVICE_CLASS_SMOKE, SERV_SMOKE_SENSOR, CHAR_SMOKE_DETECTED)
|
||||
from .util import convert_to_float, temperature_to_homekit
|
||||
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
|
||||
BINARY_SENSOR_SERVICE_MAP = {
|
||||
DEVICE_CLASS_CO2: (SERV_CARBON_DIOXIDE_SENSOR,
|
||||
CHAR_CARBON_DIOXIDE_DETECTED),
|
||||
@ -43,24 +41,17 @@ class TemperatureSensor(HomeAccessory):
|
||||
Sensor entity must return temperature in °C, °F.
|
||||
"""
|
||||
|
||||
def __init__(self, hass, entity_id, name, **kwargs):
|
||||
def __init__(self, *args, config):
|
||||
"""Initialize a TemperatureSensor accessory object."""
|
||||
super().__init__(name, entity_id, CATEGORY_SENSOR, **kwargs)
|
||||
|
||||
self.hass = hass
|
||||
self.entity_id = entity_id
|
||||
|
||||
super().__init__(*args, category=CATEGORY_SENSOR)
|
||||
serv_temp = add_preload_service(self, SERV_TEMPERATURE_SENSOR)
|
||||
self.char_temp = serv_temp.get_characteristic(CHAR_CURRENT_TEMPERATURE)
|
||||
self.char_temp.override_properties(properties=PROP_CELSIUS)
|
||||
self.char_temp.value = 0
|
||||
self.char_temp = setup_char(
|
||||
CHAR_CURRENT_TEMPERATURE, serv_temp, value=0,
|
||||
properties=PROP_CELSIUS)
|
||||
self.unit = None
|
||||
|
||||
def update_state(self, entity_id=None, old_state=None, new_state=None):
|
||||
def update_state(self, new_state):
|
||||
"""Update temperature after state changed."""
|
||||
if new_state is None:
|
||||
return
|
||||
|
||||
unit = new_state.attributes.get(ATTR_UNIT_OF_MEASUREMENT, TEMP_CELSIUS)
|
||||
temperature = convert_to_float(new_state.state)
|
||||
if temperature:
|
||||
@ -74,23 +65,15 @@ class TemperatureSensor(HomeAccessory):
|
||||
class HumiditySensor(HomeAccessory):
|
||||
"""Generate a HumiditySensor accessory as humidity sensor."""
|
||||
|
||||
def __init__(self, hass, entity_id, name, *args, **kwargs):
|
||||
def __init__(self, *args, config):
|
||||
"""Initialize a HumiditySensor accessory object."""
|
||||
super().__init__(name, entity_id, CATEGORY_SENSOR, *args, **kwargs)
|
||||
|
||||
self.hass = hass
|
||||
self.entity_id = entity_id
|
||||
|
||||
super().__init__(*args, category=CATEGORY_SENSOR)
|
||||
serv_humidity = add_preload_service(self, SERV_HUMIDITY_SENSOR)
|
||||
self.char_humidity = serv_humidity \
|
||||
.get_characteristic(CHAR_CURRENT_HUMIDITY)
|
||||
self.char_humidity.value = 0
|
||||
self.char_humidity = setup_char(
|
||||
CHAR_CURRENT_HUMIDITY, serv_humidity, value=0)
|
||||
|
||||
def update_state(self, entity_id=None, old_state=None, new_state=None):
|
||||
def update_state(self, new_state):
|
||||
"""Update accessory after state change."""
|
||||
if new_state is None:
|
||||
return
|
||||
|
||||
humidity = convert_to_float(new_state.state)
|
||||
if humidity:
|
||||
self.char_humidity.set_value(humidity)
|
||||
@ -102,28 +85,20 @@ class HumiditySensor(HomeAccessory):
|
||||
class BinarySensor(HomeAccessory):
|
||||
"""Generate a BinarySensor accessory as binary sensor."""
|
||||
|
||||
def __init__(self, hass, entity_id, name, **kwargs):
|
||||
def __init__(self, *args, config):
|
||||
"""Initialize a BinarySensor accessory object."""
|
||||
super().__init__(name, entity_id, CATEGORY_SENSOR, **kwargs)
|
||||
|
||||
self.hass = hass
|
||||
self.entity_id = entity_id
|
||||
|
||||
device_class = hass.states.get(entity_id).attributes \
|
||||
super().__init__(*args, category=CATEGORY_SENSOR)
|
||||
device_class = self.hass.states.get(self.entity_id).attributes \
|
||||
.get(ATTR_DEVICE_CLASS)
|
||||
service_char = BINARY_SENSOR_SERVICE_MAP[device_class] \
|
||||
if device_class in BINARY_SENSOR_SERVICE_MAP \
|
||||
else BINARY_SENSOR_SERVICE_MAP[DEVICE_CLASS_OCCUPANCY]
|
||||
|
||||
service = add_preload_service(self, service_char[0])
|
||||
self.char_detected = service.get_characteristic(service_char[1])
|
||||
self.char_detected.value = 0
|
||||
self.char_detected = setup_char(service_char[1], service, value=0)
|
||||
|
||||
def update_state(self, entity_id=None, old_state=None, new_state=None):
|
||||
def update_state(self, new_state):
|
||||
"""Update accessory after state change."""
|
||||
if new_state is None:
|
||||
return
|
||||
|
||||
state = new_state.state
|
||||
detected = (state == STATE_ON) or (state == STATE_HOME)
|
||||
self.char_detected.set_value(detected)
|
||||
|
@ -6,7 +6,7 @@ from homeassistant.const import (
|
||||
from homeassistant.core import split_entity_id
|
||||
|
||||
from . import TYPES
|
||||
from .accessories import HomeAccessory, add_preload_service
|
||||
from .accessories import HomeAccessory, add_preload_service, setup_char
|
||||
from .const import CATEGORY_SWITCH, SERV_SWITCH, CHAR_ON
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
@ -16,20 +16,15 @@ _LOGGER = logging.getLogger(__name__)
|
||||
class Switch(HomeAccessory):
|
||||
"""Generate a Switch accessory."""
|
||||
|
||||
def __init__(self, hass, entity_id, display_name, **kwargs):
|
||||
def __init__(self, *args, config):
|
||||
"""Initialize a Switch accessory object to represent a remote."""
|
||||
super().__init__(display_name, entity_id, CATEGORY_SWITCH, **kwargs)
|
||||
|
||||
self.hass = hass
|
||||
self.entity_id = entity_id
|
||||
self._domain = split_entity_id(entity_id)[0]
|
||||
|
||||
super().__init__(*args, category=CATEGORY_SWITCH)
|
||||
self._domain = split_entity_id(self.entity_id)[0]
|
||||
self.flag_target_state = False
|
||||
|
||||
serv_switch = add_preload_service(self, SERV_SWITCH)
|
||||
self.char_on = serv_switch.get_characteristic(CHAR_ON)
|
||||
self.char_on.value = False
|
||||
self.char_on.setter_callback = self.set_state
|
||||
self.char_on = setup_char(
|
||||
CHAR_ON, serv_switch, value=False, callback=self.set_state)
|
||||
|
||||
def set_state(self, value):
|
||||
"""Move switch state to value if call came from HomeKit."""
|
||||
@ -40,15 +35,11 @@ class Switch(HomeAccessory):
|
||||
self.hass.services.call(self._domain, service,
|
||||
{ATTR_ENTITY_ID: self.entity_id})
|
||||
|
||||
def update_state(self, entity_id=None, old_state=None, new_state=None):
|
||||
def update_state(self, new_state):
|
||||
"""Update switch state after state changed."""
|
||||
if new_state is None:
|
||||
return
|
||||
|
||||
current_state = (new_state.state == STATE_ON)
|
||||
if not self.flag_target_state:
|
||||
_LOGGER.debug('%s: Set current state to %s',
|
||||
self.entity_id, current_state)
|
||||
self.char_on.set_value(current_state)
|
||||
|
||||
self.flag_target_state = False
|
||||
|
@ -5,12 +5,15 @@ from homeassistant.components.climate import (
|
||||
ATTR_CURRENT_TEMPERATURE, ATTR_TEMPERATURE,
|
||||
ATTR_TARGET_TEMP_HIGH, ATTR_TARGET_TEMP_LOW,
|
||||
ATTR_OPERATION_MODE, ATTR_OPERATION_LIST,
|
||||
STATE_HEAT, STATE_COOL, STATE_AUTO)
|
||||
STATE_HEAT, STATE_COOL, STATE_AUTO,
|
||||
SUPPORT_TARGET_TEMPERATURE_HIGH, SUPPORT_TARGET_TEMPERATURE_LOW)
|
||||
from homeassistant.const import (
|
||||
ATTR_UNIT_OF_MEASUREMENT, STATE_OFF, TEMP_CELSIUS, TEMP_FAHRENHEIT)
|
||||
ATTR_SUPPORTED_FEATURES, ATTR_UNIT_OF_MEASUREMENT,
|
||||
STATE_OFF, TEMP_CELSIUS, TEMP_FAHRENHEIT)
|
||||
|
||||
from . import TYPES
|
||||
from .accessories import HomeAccessory, add_preload_service, debounce
|
||||
from .accessories import (
|
||||
HomeAccessory, add_preload_service, debounce, setup_char)
|
||||
from .const import (
|
||||
CATEGORY_THERMOSTAT, SERV_THERMOSTAT, CHAR_CURRENT_HEATING_COOLING,
|
||||
CHAR_TARGET_HEATING_COOLING, CHAR_CURRENT_TEMPERATURE,
|
||||
@ -26,74 +29,63 @@ HC_HASS_TO_HOMEKIT = {STATE_OFF: 0, STATE_HEAT: 1,
|
||||
STATE_COOL: 2, STATE_AUTO: 3}
|
||||
HC_HOMEKIT_TO_HASS = {c: s for s, c in HC_HASS_TO_HOMEKIT.items()}
|
||||
|
||||
SUPPORT_TEMP_RANGE = SUPPORT_TARGET_TEMPERATURE_LOW | \
|
||||
SUPPORT_TARGET_TEMPERATURE_HIGH
|
||||
|
||||
|
||||
@TYPES.register('Thermostat')
|
||||
class Thermostat(HomeAccessory):
|
||||
"""Generate a Thermostat accessory for a climate."""
|
||||
|
||||
def __init__(self, hass, entity_id, display_name, support_auto, **kwargs):
|
||||
def __init__(self, *args, config):
|
||||
"""Initialize a Thermostat accessory object."""
|
||||
super().__init__(display_name, entity_id,
|
||||
CATEGORY_THERMOSTAT, **kwargs)
|
||||
|
||||
self.hass = hass
|
||||
self.entity_id = entity_id
|
||||
self._call_timer = None
|
||||
super().__init__(*args, category=CATEGORY_THERMOSTAT)
|
||||
self._unit = TEMP_CELSIUS
|
||||
|
||||
self.heat_cool_flag_target_state = False
|
||||
self.temperature_flag_target_state = False
|
||||
self.coolingthresh_flag_target_state = False
|
||||
self.heatingthresh_flag_target_state = False
|
||||
|
||||
# Add additional characteristics if auto mode is supported
|
||||
extra_chars = [
|
||||
CHAR_COOLING_THRESHOLD_TEMPERATURE,
|
||||
CHAR_HEATING_THRESHOLD_TEMPERATURE] if support_auto else None
|
||||
self.chars = []
|
||||
features = self.hass.states.get(self.entity_id) \
|
||||
.attributes.get(ATTR_SUPPORTED_FEATURES)
|
||||
if features & SUPPORT_TEMP_RANGE:
|
||||
self.chars.extend((CHAR_COOLING_THRESHOLD_TEMPERATURE,
|
||||
CHAR_HEATING_THRESHOLD_TEMPERATURE))
|
||||
|
||||
# Preload the thermostat service
|
||||
serv_thermostat = add_preload_service(self, SERV_THERMOSTAT,
|
||||
extra_chars)
|
||||
serv_thermostat = add_preload_service(
|
||||
self, SERV_THERMOSTAT, self.chars)
|
||||
|
||||
# Current and target mode characteristics
|
||||
self.char_current_heat_cool = serv_thermostat. \
|
||||
get_characteristic(CHAR_CURRENT_HEATING_COOLING)
|
||||
self.char_current_heat_cool.value = 0
|
||||
self.char_target_heat_cool = serv_thermostat. \
|
||||
get_characteristic(CHAR_TARGET_HEATING_COOLING)
|
||||
self.char_target_heat_cool.value = 0
|
||||
self.char_target_heat_cool.setter_callback = self.set_heat_cool
|
||||
self.char_current_heat_cool = setup_char(
|
||||
CHAR_CURRENT_HEATING_COOLING, serv_thermostat, value=0)
|
||||
self.char_target_heat_cool = setup_char(
|
||||
CHAR_TARGET_HEATING_COOLING, serv_thermostat, value=0,
|
||||
callback=self.set_heat_cool)
|
||||
|
||||
# Current and target temperature characteristics
|
||||
self.char_current_temp = serv_thermostat. \
|
||||
get_characteristic(CHAR_CURRENT_TEMPERATURE)
|
||||
self.char_current_temp.value = 21.0
|
||||
self.char_target_temp = serv_thermostat. \
|
||||
get_characteristic(CHAR_TARGET_TEMPERATURE)
|
||||
self.char_target_temp.value = 21.0
|
||||
self.char_target_temp.setter_callback = self.set_target_temperature
|
||||
self.char_current_temp = setup_char(
|
||||
CHAR_CURRENT_TEMPERATURE, serv_thermostat, value=21.0)
|
||||
self.char_target_temp = setup_char(
|
||||
CHAR_TARGET_TEMPERATURE, serv_thermostat, value=21.0,
|
||||
callback=self.set_target_temperature)
|
||||
|
||||
# Display units characteristic
|
||||
self.char_display_units = serv_thermostat. \
|
||||
get_characteristic(CHAR_TEMP_DISPLAY_UNITS)
|
||||
self.char_display_units.value = 0
|
||||
self.char_display_units = setup_char(
|
||||
CHAR_TEMP_DISPLAY_UNITS, serv_thermostat, value=0)
|
||||
|
||||
# If the device supports it: high and low temperature characteristics
|
||||
if support_auto:
|
||||
self.char_cooling_thresh_temp = serv_thermostat. \
|
||||
get_characteristic(CHAR_COOLING_THRESHOLD_TEMPERATURE)
|
||||
self.char_cooling_thresh_temp.value = 23.0
|
||||
self.char_cooling_thresh_temp.setter_callback = \
|
||||
self.set_cooling_threshold
|
||||
|
||||
self.char_heating_thresh_temp = serv_thermostat. \
|
||||
get_characteristic(CHAR_HEATING_THRESHOLD_TEMPERATURE)
|
||||
self.char_heating_thresh_temp.value = 19.0
|
||||
self.char_heating_thresh_temp.setter_callback = \
|
||||
self.set_heating_threshold
|
||||
else:
|
||||
self.char_cooling_thresh_temp = None
|
||||
self.char_heating_thresh_temp = None
|
||||
self.char_cooling_thresh_temp = None
|
||||
self.char_heating_thresh_temp = None
|
||||
if CHAR_COOLING_THRESHOLD_TEMPERATURE in self.chars:
|
||||
self.char_cooling_thresh_temp = setup_char(
|
||||
CHAR_COOLING_THRESHOLD_TEMPERATURE, serv_thermostat,
|
||||
value=23.0, callback=self.set_cooling_threshold)
|
||||
if CHAR_HEATING_THRESHOLD_TEMPERATURE in self.chars:
|
||||
self.char_heating_thresh_temp = setup_char(
|
||||
CHAR_HEATING_THRESHOLD_TEMPERATURE, serv_thermostat,
|
||||
value=19.0, callback=self.set_heating_threshold)
|
||||
|
||||
def set_heat_cool(self, value):
|
||||
"""Move operation mode to value if call came from HomeKit."""
|
||||
@ -141,11 +133,8 @@ class Thermostat(HomeAccessory):
|
||||
self.hass.components.climate.set_temperature(
|
||||
temperature=value, entity_id=self.entity_id)
|
||||
|
||||
def update_state(self, entity_id=None, old_state=None, new_state=None):
|
||||
def update_state(self, new_state):
|
||||
"""Update security state after state changed."""
|
||||
if new_state is None:
|
||||
return
|
||||
|
||||
self._unit = new_state.attributes.get(ATTR_UNIT_OF_MEASUREMENT,
|
||||
TEMP_CELSIUS)
|
||||
|
||||
|
@ -33,7 +33,7 @@ def validate_entity_config(values):
|
||||
return entities
|
||||
|
||||
|
||||
def show_setup_message(bridge, hass):
|
||||
def show_setup_message(hass, bridge):
|
||||
"""Display persistent notification with setup information."""
|
||||
pin = bridge.pincode.decode()
|
||||
_LOGGER.info('Pincode: %s', pin)
|
||||
|
@ -10,9 +10,8 @@ from homeassistant.components.homekit.accessories import (
|
||||
add_preload_service, set_accessory_info,
|
||||
debounce, HomeAccessory, HomeBridge, HomeDriver)
|
||||
from homeassistant.components.homekit.const import (
|
||||
ACCESSORY_MODEL, ACCESSORY_NAME, BRIDGE_MODEL, BRIDGE_NAME,
|
||||
SERV_ACCESSORY_INFO, CHAR_MANUFACTURER, CHAR_MODEL,
|
||||
CHAR_NAME, CHAR_SERIAL_NUMBER)
|
||||
BRIDGE_MODEL, BRIDGE_NAME, SERV_ACCESSORY_INFO,
|
||||
CHAR_MANUFACTURER, CHAR_MODEL, CHAR_NAME, CHAR_SERIAL_NUMBER)
|
||||
from homeassistant.const import ATTR_NOW, EVENT_TIME_CHANGED
|
||||
import homeassistant.util.dt as dt_util
|
||||
|
||||
@ -92,7 +91,7 @@ class TestAccessories(unittest.TestCase):
|
||||
def test_set_accessory_info(self):
|
||||
"""Test setting the basic accessory information."""
|
||||
# Test HomeAccessory
|
||||
acc = HomeAccessory()
|
||||
acc = HomeAccessory('HA', 'Home Accessory', 'homekit.accessory', 2, '')
|
||||
set_accessory_info(acc, 'name', 'model', 'manufacturer', '0000')
|
||||
|
||||
serv = acc.get_service(SERV_ACCESSORY_INFO)
|
||||
@ -104,7 +103,7 @@ class TestAccessories(unittest.TestCase):
|
||||
serv.get_characteristic(CHAR_SERIAL_NUMBER).value, '0000')
|
||||
|
||||
# Test HomeBridge
|
||||
acc = HomeBridge(None)
|
||||
acc = HomeBridge('hass')
|
||||
set_accessory_info(acc, 'name', 'model', 'manufacturer', '0000')
|
||||
|
||||
serv = acc.get_service(SERV_ACCESSORY_INFO)
|
||||
@ -116,26 +115,37 @@ class TestAccessories(unittest.TestCase):
|
||||
|
||||
def test_home_accessory(self):
|
||||
"""Test HomeAccessory class."""
|
||||
acc = HomeAccessory()
|
||||
self.assertEqual(acc.display_name, ACCESSORY_NAME)
|
||||
hass = get_test_home_assistant()
|
||||
|
||||
acc = HomeAccessory(hass, 'Home Accessory', 'homekit.accessory', 2, '')
|
||||
self.assertEqual(acc.hass, hass)
|
||||
self.assertEqual(acc.display_name, 'Home Accessory')
|
||||
self.assertEqual(acc.category, 1) # Category.OTHER
|
||||
self.assertEqual(len(acc.services), 1)
|
||||
serv = acc.services[0] # SERV_ACCESSORY_INFO
|
||||
self.assertEqual(
|
||||
serv.get_characteristic(CHAR_MODEL).value, ACCESSORY_MODEL)
|
||||
serv.get_characteristic(CHAR_MODEL).value, 'homekit.accessory')
|
||||
|
||||
acc = HomeAccessory('test_name', 'test_model', 'FAN', aid=2)
|
||||
hass.states.set('homekit.accessory', 'on')
|
||||
hass.block_till_done()
|
||||
acc.run()
|
||||
hass.states.set('homekit.accessory', 'off')
|
||||
hass.block_till_done()
|
||||
|
||||
acc = HomeAccessory('hass', 'test_name', 'test_model', 2, '')
|
||||
self.assertEqual(acc.display_name, 'test_name')
|
||||
self.assertEqual(acc.category, 3) # Category.FAN
|
||||
self.assertEqual(acc.aid, 2)
|
||||
self.assertEqual(len(acc.services), 1)
|
||||
serv = acc.services[0] # SERV_ACCESSORY_INFO
|
||||
self.assertEqual(
|
||||
serv.get_characteristic(CHAR_MODEL).value, 'test_model')
|
||||
|
||||
hass.stop()
|
||||
|
||||
def test_home_bridge(self):
|
||||
"""Test HomeBridge class."""
|
||||
bridge = HomeBridge(None)
|
||||
bridge = HomeBridge('hass')
|
||||
self.assertEqual(bridge.hass, 'hass')
|
||||
self.assertEqual(bridge.display_name, BRIDGE_NAME)
|
||||
self.assertEqual(bridge.category, 2) # Category.BRIDGE
|
||||
self.assertEqual(len(bridge.services), 1)
|
||||
@ -144,12 +154,10 @@ class TestAccessories(unittest.TestCase):
|
||||
self.assertEqual(
|
||||
serv.get_characteristic(CHAR_MODEL).value, BRIDGE_MODEL)
|
||||
|
||||
bridge = HomeBridge('hass', 'test_name', 'test_model')
|
||||
bridge = HomeBridge('hass', 'test_name')
|
||||
self.assertEqual(bridge.display_name, 'test_name')
|
||||
self.assertEqual(len(bridge.services), 1)
|
||||
serv = bridge.services[0] # SERV_ACCESSORY_INFO
|
||||
self.assertEqual(
|
||||
serv.get_characteristic(CHAR_MODEL).value, 'test_model')
|
||||
|
||||
# setup_message
|
||||
bridge.setup_message()
|
||||
@ -174,11 +182,11 @@ class TestAccessories(unittest.TestCase):
|
||||
|
||||
self.assertEqual(
|
||||
mock_remove_paired_client.call_args, call('client_uuid'))
|
||||
self.assertEqual(mock_show_msg.call_args, call(bridge, 'hass'))
|
||||
self.assertEqual(mock_show_msg.call_args, call('hass', bridge))
|
||||
|
||||
def test_home_driver(self):
|
||||
"""Test HomeDriver class."""
|
||||
bridge = HomeBridge(None)
|
||||
bridge = HomeBridge('hass')
|
||||
ip_address = '127.0.0.1'
|
||||
port = 51826
|
||||
path = '.homekit.state'
|
||||
|
@ -19,14 +19,14 @@ CONFIG = {}
|
||||
def test_get_accessory_invalid_aid(caplog):
|
||||
"""Test with unsupported component."""
|
||||
assert get_accessory(None, State('light.demo', 'on'),
|
||||
aid=None, config=None) is None
|
||||
None, config=None) is None
|
||||
assert caplog.records[0].levelname == 'WARNING'
|
||||
assert 'invalid aid' in caplog.records[0].msg
|
||||
|
||||
|
||||
def test_not_supported():
|
||||
"""Test if none is returned if entity isn't supported."""
|
||||
assert get_accessory(None, State('demo.demo', 'on'), aid=2, config=None) \
|
||||
assert get_accessory(None, State('demo.demo', 'on'), 2, config=None) \
|
||||
is None
|
||||
|
||||
|
||||
@ -48,7 +48,6 @@ class TestGetAccessories(unittest.TestCase):
|
||||
{ATTR_UNIT_OF_MEASUREMENT: TEMP_CELSIUS})
|
||||
get_accessory(None, state, 2, {})
|
||||
|
||||
# pylint: disable=invalid-name
|
||||
def test_sensor_temperature_fahrenheit(self):
|
||||
"""Test temperature sensor with Fahrenheit as unit."""
|
||||
with patch.dict(TYPES, {'TemperatureSensor': self.mock_type}):
|
||||
@ -91,8 +90,9 @@ class TestGetAccessories(unittest.TestCase):
|
||||
get_accessory(None, state, 2, config)
|
||||
|
||||
# pylint: disable=unsubscriptable-object
|
||||
print(self.mock_type.call_args[1])
|
||||
self.assertEqual(
|
||||
self.mock_type.call_args[1].get('alarm_code'), '1234')
|
||||
self.mock_type.call_args[1]['config'][ATTR_CODE], '1234')
|
||||
|
||||
def test_climate(self):
|
||||
"""Test climate devices."""
|
||||
@ -100,10 +100,6 @@ class TestGetAccessories(unittest.TestCase):
|
||||
state = State('climate.test', 'auto')
|
||||
get_accessory(None, state, 2, {})
|
||||
|
||||
# pylint: disable=unsubscriptable-object
|
||||
self.assertEqual(
|
||||
self.mock_type.call_args[0][-1], False) # support_auto
|
||||
|
||||
def test_light(self):
|
||||
"""Test light devices."""
|
||||
with patch.dict(TYPES, {'Light': self.mock_type}):
|
||||
@ -119,10 +115,6 @@ class TestGetAccessories(unittest.TestCase):
|
||||
SUPPORT_TARGET_TEMPERATURE_HIGH})
|
||||
get_accessory(None, state, 2, {})
|
||||
|
||||
# pylint: disable=unsubscriptable-object
|
||||
self.assertEqual(
|
||||
self.mock_type.call_args[0][-1], True) # support_auto
|
||||
|
||||
def test_switch(self):
|
||||
"""Test switch."""
|
||||
with patch.dict(TYPES, {'Switch': self.mock_type}):
|
||||
@ -140,3 +132,9 @@ class TestGetAccessories(unittest.TestCase):
|
||||
with patch.dict(TYPES, {'Switch': self.mock_type}):
|
||||
state = State('input_boolean.test', 'on')
|
||||
get_accessory(None, state, 2, {})
|
||||
|
||||
def test_lock(self):
|
||||
"""Test lock."""
|
||||
with patch.dict(TYPES, {'Lock': self.mock_type}):
|
||||
state = State('lock.test', 'locked')
|
||||
get_accessory(None, state, 2, {})
|
||||
|
@ -173,7 +173,7 @@ class TestHomeKit(unittest.TestCase):
|
||||
|
||||
self.assertEqual(mock_add_bridge_acc.mock_calls, [call(state)])
|
||||
self.assertEqual(mock_show_setup_msg.mock_calls, [
|
||||
call(homekit.bridge, self.hass)])
|
||||
call(self.hass, homekit.bridge)])
|
||||
self.assertEqual(homekit.driver.mock_calls, [call.start()])
|
||||
self.assertTrue(homekit.started)
|
||||
|
||||
|
@ -35,7 +35,7 @@ class TestHomekitSensors(unittest.TestCase):
|
||||
"""Test if accessory and HA are updated accordingly."""
|
||||
window_cover = 'cover.window'
|
||||
|
||||
acc = WindowCovering(self.hass, window_cover, 'Cover', aid=2)
|
||||
acc = WindowCovering(self.hass, 'Cover', window_cover, 2, config=None)
|
||||
acc.run()
|
||||
|
||||
self.assertEqual(acc.aid, 2)
|
||||
|
@ -50,9 +50,11 @@ class TestHomekitLights(unittest.TestCase):
|
||||
def test_light_basic(self):
|
||||
"""Test light with char state."""
|
||||
entity_id = 'light.demo'
|
||||
|
||||
self.hass.states.set(entity_id, STATE_ON,
|
||||
{ATTR_SUPPORTED_FEATURES: 0})
|
||||
acc = self.light_cls(self.hass, entity_id, 'Light', aid=2)
|
||||
self.hass.block_till_done()
|
||||
acc = self.light_cls(self.hass, 'Light', entity_id, 2, config=None)
|
||||
self.assertEqual(acc.aid, 2)
|
||||
self.assertEqual(acc.category, 5) # Lightbulb
|
||||
self.assertEqual(acc.char_on.value, 0)
|
||||
@ -94,9 +96,11 @@ class TestHomekitLights(unittest.TestCase):
|
||||
def test_light_brightness(self):
|
||||
"""Test light with brightness."""
|
||||
entity_id = 'light.demo'
|
||||
|
||||
self.hass.states.set(entity_id, STATE_ON, {
|
||||
ATTR_SUPPORTED_FEATURES: SUPPORT_BRIGHTNESS, ATTR_BRIGHTNESS: 255})
|
||||
acc = self.light_cls(self.hass, entity_id, 'Light', aid=2)
|
||||
self.hass.block_till_done()
|
||||
acc = self.light_cls(self.hass, 'Light', entity_id, 2, config=None)
|
||||
self.assertEqual(acc.char_brightness.value, 0)
|
||||
|
||||
acc.run()
|
||||
@ -135,10 +139,12 @@ class TestHomekitLights(unittest.TestCase):
|
||||
def test_light_color_temperature(self):
|
||||
"""Test light with color temperature."""
|
||||
entity_id = 'light.demo'
|
||||
|
||||
self.hass.states.set(entity_id, STATE_ON, {
|
||||
ATTR_SUPPORTED_FEATURES: SUPPORT_COLOR_TEMP,
|
||||
ATTR_COLOR_TEMP: 190})
|
||||
acc = self.light_cls(self.hass, entity_id, 'Light', aid=2)
|
||||
self.hass.block_till_done()
|
||||
acc = self.light_cls(self.hass, 'Light', entity_id, 2, config=None)
|
||||
self.assertEqual(acc.char_color_temperature.value, 153)
|
||||
|
||||
acc.run()
|
||||
@ -157,10 +163,12 @@ class TestHomekitLights(unittest.TestCase):
|
||||
def test_light_rgb_color(self):
|
||||
"""Test light with rgb_color."""
|
||||
entity_id = 'light.demo'
|
||||
|
||||
self.hass.states.set(entity_id, STATE_ON, {
|
||||
ATTR_SUPPORTED_FEATURES: SUPPORT_COLOR,
|
||||
ATTR_HS_COLOR: (260, 90)})
|
||||
acc = self.light_cls(self.hass, entity_id, 'Light', aid=2)
|
||||
self.hass.block_till_done()
|
||||
acc = self.light_cls(self.hass, 'Light', entity_id, 2, config=None)
|
||||
self.assertEqual(acc.char_hue.value, 0)
|
||||
self.assertEqual(acc.char_saturation.value, 75)
|
||||
|
||||
|
@ -33,7 +33,7 @@ class TestHomekitSensors(unittest.TestCase):
|
||||
"""Test if accessory and HA are updated accordingly."""
|
||||
kitchen_lock = 'lock.kitchen_door'
|
||||
|
||||
acc = Lock(self.hass, kitchen_lock, 'Lock', aid=2)
|
||||
acc = Lock(self.hass, 'Lock', kitchen_lock, 2, config=None)
|
||||
acc.run()
|
||||
|
||||
self.assertEqual(acc.aid, 2)
|
||||
|
@ -35,8 +35,8 @@ class TestHomekitSecuritySystems(unittest.TestCase):
|
||||
"""Test if accessory and HA are updated accordingly."""
|
||||
acp = 'alarm_control_panel.test'
|
||||
|
||||
acc = SecuritySystem(self.hass, acp, 'SecuritySystem',
|
||||
alarm_code='1234', aid=2)
|
||||
acc = SecuritySystem(self.hass, 'SecuritySystem', acp,
|
||||
2, config={ATTR_CODE: '1234'})
|
||||
acc.run()
|
||||
|
||||
self.assertEqual(acc.aid, 2)
|
||||
@ -107,8 +107,8 @@ class TestHomekitSecuritySystems(unittest.TestCase):
|
||||
"""Test accessory if security_system doesn't require a alarm_code."""
|
||||
acp = 'alarm_control_panel.test'
|
||||
|
||||
acc = SecuritySystem(self.hass, acp, 'SecuritySystem',
|
||||
alarm_code=None, aid=2)
|
||||
acc = SecuritySystem(self.hass, 'SecuritySystem', acp,
|
||||
2, config={ATTR_CODE: None})
|
||||
acc.run()
|
||||
|
||||
# Set from HomeKit
|
||||
|
@ -26,7 +26,8 @@ class TestHomekitSensors(unittest.TestCase):
|
||||
"""Test if accessory is updated after state change."""
|
||||
entity_id = 'sensor.temperature'
|
||||
|
||||
acc = TemperatureSensor(self.hass, entity_id, 'Temperature', aid=2)
|
||||
acc = TemperatureSensor(self.hass, 'Temperature', entity_id,
|
||||
2, config=None)
|
||||
acc.run()
|
||||
|
||||
self.assertEqual(acc.aid, 2)
|
||||
@ -54,7 +55,7 @@ class TestHomekitSensors(unittest.TestCase):
|
||||
"""Test if accessory is updated after state change."""
|
||||
entity_id = 'sensor.humidity'
|
||||
|
||||
acc = HumiditySensor(self.hass, entity_id, 'Humidity', aid=2)
|
||||
acc = HumiditySensor(self.hass, 'Humidity', entity_id, 2, config=None)
|
||||
acc.run()
|
||||
|
||||
self.assertEqual(acc.aid, 2)
|
||||
@ -78,7 +79,8 @@ class TestHomekitSensors(unittest.TestCase):
|
||||
{ATTR_DEVICE_CLASS: "opening"})
|
||||
self.hass.block_till_done()
|
||||
|
||||
acc = BinarySensor(self.hass, entity_id, 'Window Opening', aid=2)
|
||||
acc = BinarySensor(self.hass, 'Window Opening', entity_id,
|
||||
2, config=None)
|
||||
acc.run()
|
||||
|
||||
self.assertEqual(acc.aid, 2)
|
||||
@ -118,6 +120,7 @@ class TestHomekitSensors(unittest.TestCase):
|
||||
{ATTR_DEVICE_CLASS: device_class})
|
||||
self.hass.block_till_done()
|
||||
|
||||
acc = BinarySensor(self.hass, entity_id, 'Binary Sensor', aid=2)
|
||||
acc = BinarySensor(self.hass, 'Binary Sensor', entity_id,
|
||||
2, config=None)
|
||||
self.assertEqual(acc.get_service(service).display_name, service)
|
||||
self.assertEqual(acc.char_detected.display_name, char)
|
||||
|
@ -34,7 +34,7 @@ class TestHomekitSwitches(unittest.TestCase):
|
||||
entity_id = 'switch.test'
|
||||
domain = split_entity_id(entity_id)[0]
|
||||
|
||||
acc = Switch(self.hass, entity_id, 'Switch', aid=2)
|
||||
acc = Switch(self.hass, 'Switch', entity_id, 2, config=None)
|
||||
acc.run()
|
||||
|
||||
self.assertEqual(acc.aid, 2)
|
||||
@ -70,7 +70,7 @@ class TestHomekitSwitches(unittest.TestCase):
|
||||
entity_id = 'remote.test'
|
||||
domain = split_entity_id(entity_id)[0]
|
||||
|
||||
acc = Switch(self.hass, entity_id, 'Switch', aid=2)
|
||||
acc = Switch(self.hass, 'Switch', entity_id, 2, config=None)
|
||||
acc.run()
|
||||
|
||||
self.assertEqual(acc.char_on.value, False)
|
||||
@ -89,7 +89,7 @@ class TestHomekitSwitches(unittest.TestCase):
|
||||
entity_id = 'input_boolean.test'
|
||||
domain = split_entity_id(entity_id)[0]
|
||||
|
||||
acc = Switch(self.hass, entity_id, 'Switch', aid=2)
|
||||
acc = Switch(self.hass, 'Switch', entity_id, 2, config=None)
|
||||
acc.run()
|
||||
|
||||
self.assertEqual(acc.char_on.value, False)
|
||||
|
@ -7,8 +7,9 @@ from homeassistant.components.climate import (
|
||||
ATTR_TARGET_TEMP_LOW, ATTR_TARGET_TEMP_HIGH, ATTR_OPERATION_MODE,
|
||||
ATTR_OPERATION_LIST, STATE_COOL, STATE_HEAT, STATE_AUTO)
|
||||
from homeassistant.const import (
|
||||
ATTR_SERVICE, EVENT_CALL_SERVICE, ATTR_SERVICE_DATA,
|
||||
ATTR_UNIT_OF_MEASUREMENT, STATE_OFF, TEMP_CELSIUS, TEMP_FAHRENHEIT)
|
||||
ATTR_SERVICE, ATTR_SERVICE_DATA, ATTR_SUPPORTED_FEATURES,
|
||||
ATTR_UNIT_OF_MEASUREMENT, EVENT_CALL_SERVICE,
|
||||
STATE_OFF, TEMP_CELSIUS, TEMP_FAHRENHEIT)
|
||||
|
||||
from tests.common import get_test_home_assistant
|
||||
from tests.components.homekit.test_accessories import patch_debounce
|
||||
@ -52,7 +53,10 @@ class TestHomekitThermostats(unittest.TestCase):
|
||||
"""Test if accessory and HA are updated accordingly."""
|
||||
climate = 'climate.test'
|
||||
|
||||
acc = self.thermostat_cls(self.hass, climate, 'Climate', False, aid=2)
|
||||
self.hass.states.set(climate, STATE_OFF, {ATTR_SUPPORTED_FEATURES: 0})
|
||||
self.hass.block_till_done()
|
||||
acc = self.thermostat_cls(self.hass, 'Climate', climate,
|
||||
2, config=None)
|
||||
acc.run()
|
||||
|
||||
self.assertEqual(acc.aid, 2)
|
||||
@ -187,7 +191,11 @@ class TestHomekitThermostats(unittest.TestCase):
|
||||
"""Test if accessory and HA are updated accordingly."""
|
||||
climate = 'climate.test'
|
||||
|
||||
acc = self.thermostat_cls(self.hass, climate, 'Climate', True)
|
||||
# support_auto = True
|
||||
self.hass.states.set(climate, STATE_OFF, {ATTR_SUPPORTED_FEATURES: 6})
|
||||
self.hass.block_till_done()
|
||||
acc = self.thermostat_cls(self.hass, 'Climate', climate,
|
||||
2, config=None)
|
||||
acc.run()
|
||||
|
||||
self.assertEqual(acc.char_cooling_thresh_temp.value, 23.0)
|
||||
@ -257,7 +265,11 @@ class TestHomekitThermostats(unittest.TestCase):
|
||||
"""Test if accessory and HA are updated accordingly."""
|
||||
climate = 'climate.test'
|
||||
|
||||
acc = self.thermostat_cls(self.hass, climate, 'Climate', True)
|
||||
# support_auto = True
|
||||
self.hass.states.set(climate, STATE_OFF, {ATTR_SUPPORTED_FEATURES: 6})
|
||||
self.hass.block_till_done()
|
||||
acc = self.thermostat_cls(self.hass, 'Climate', climate,
|
||||
2, config=None)
|
||||
acc.run()
|
||||
|
||||
self.hass.states.set(climate, STATE_AUTO,
|
||||
|
@ -58,7 +58,7 @@ class TestUtil(unittest.TestCase):
|
||||
"""Test show setup message as persistence notification."""
|
||||
bridge = HomeBridge(self.hass)
|
||||
|
||||
show_setup_message(bridge, self.hass)
|
||||
show_setup_message(self.hass, bridge)
|
||||
self.hass.block_till_done()
|
||||
|
||||
data = self.events[0].data
|
||||
|
Loading…
x
Reference in New Issue
Block a user