Update HAP-python to 2.0.0 (#14278)

* Fixed async (added 'async_add_job' and 'add_job')

* Driver status

* Use pyhap category constants

* Changed 'set_broker' to 'set_driver'

* Changed loader method names

* Use 'serv.configure_char'

* Use 'self.set_info_service'

* Use 'self.add_preload_service'

* Fix hound issue

* Updated HAP-python to 2.0.0
This commit is contained in:
cdce8p 2018-05-04 16:46:00 +02:00 committed by Pascal Vizeli
parent f98525acbf
commit e37fd5b132
14 changed files with 203 additions and 272 deletions

View file

@ -30,7 +30,13 @@ from .util import (
TYPES = Registry()
_LOGGER = logging.getLogger(__name__)
REQUIREMENTS = ['HAP-python==1.1.9']
REQUIREMENTS = ['HAP-python==2.0.0']
# #### Driver Status ####
STATUS_READY = 0
STATUS_RUNNING = 1
STATUS_STOPPED = 2
STATUS_WAIT = 3
CONFIG_SCHEMA = vol.Schema({
@ -57,7 +63,7 @@ async def async_setup(hass, config):
entity_config = conf[CONF_ENTITY_CONFIG]
homekit = HomeKit(hass, port, ip_address, entity_filter, entity_config)
homekit.setup()
await hass.async_add_job(homekit.setup)
if auto_start:
hass.bus.async_listen_once(EVENT_HOMEASSISTANT_START, homekit.start)
@ -65,8 +71,10 @@ async def async_setup(hass, config):
def handle_homekit_service_start(service):
"""Handle start HomeKit service call."""
if homekit.started:
_LOGGER.warning('HomeKit is already running')
if homekit.status != STATUS_READY:
_LOGGER.warning(
'HomeKit is not ready. Either it is already running or has '
'been stopped.')
return
homekit.start()
@ -162,7 +170,7 @@ class HomeKit():
self._ip_address = ip_address
self._filter = entity_filter
self._config = entity_config
self.started = False
self.status = STATUS_READY
self.bridge = None
self.driver = None
@ -191,9 +199,9 @@ class HomeKit():
def start(self, *args):
"""Start the accessory driver."""
if self.started:
if self.status != STATUS_READY:
return
self.started = True
self.status = STATUS_WAIT
# pylint: disable=unused-variable
from . import ( # noqa F401
@ -202,19 +210,20 @@ class HomeKit():
for state in self.hass.states.all():
self.add_bridge_accessory(state)
self.bridge.set_broker(self.driver)
self.bridge.set_driver(self.driver)
if not self.bridge.paired:
show_setup_message(self.hass, self.bridge)
_LOGGER.debug('Driver start')
self.driver.start()
self.hass.add_job(self.driver.start)
self.status = STATUS_RUNNING
def stop(self, *args):
"""Stop the accessory driver."""
if not self.started:
if self.status != STATUS_RUNNING:
return
self.status = STATUS_STOPPED
_LOGGER.debug('Driver stop')
if self.driver and self.driver.run_sentinel:
self.driver.stop()
self.hass.add_job(self.driver.stop)

View file

@ -4,8 +4,9 @@ from functools import wraps
from inspect import getmodule
import logging
from pyhap.accessory import Accessory, Bridge, Category
from pyhap.accessory import Accessory, Bridge
from pyhap.accessory_driver import AccessoryDriver
from pyhap.const import CATEGORY_OTHER
from homeassistant.const import __version__
from homeassistant.core import callback as ha_callback
@ -15,9 +16,8 @@ from homeassistant.helpers.event import (
from homeassistant.util import dt as dt_util
from .const import (
DEBOUNCE_TIMEOUT, BRIDGE_MODEL, BRIDGE_NAME, BRIDGE_SERIAL_NUMBER,
MANUFACTURER, SERV_ACCESSORY_INFO, CHAR_FIRMWARE_REVISION,
CHAR_MANUFACTURER, CHAR_MODEL, CHAR_NAME, CHAR_SERIAL_NUMBER)
DEBOUNCE_TIMEOUT, BRIDGE_MODEL, BRIDGE_NAME,
BRIDGE_SERIAL_NUMBER, MANUFACTURER)
from .util import (
show_setup_message, dismiss_setup_message)
@ -61,59 +61,20 @@ def debounce(func):
return wrapper
def add_preload_service(acc, service, chars=None):
"""Define and return a service to be available for the accessory."""
from pyhap.loader import get_serv_loader, get_char_loader
service = get_serv_loader().get(service)
if chars:
chars = chars if isinstance(chars, list) else [chars]
for char_name in chars:
char = get_char_loader().get(char_name)
service.add_characteristic(char)
acc.add_service(service)
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, serial_number,
manufacturer=MANUFACTURER,
firmware_revision=__version__):
"""Set the default accessory information."""
service = acc.get_service(SERV_ACCESSORY_INFO)
service.get_characteristic(CHAR_NAME).set_value(name)
service.get_characteristic(CHAR_MODEL).set_value(model)
service.get_characteristic(CHAR_MANUFACTURER).set_value(manufacturer)
service.get_characteristic(CHAR_SERIAL_NUMBER).set_value(serial_number)
service.get_characteristic(CHAR_FIRMWARE_REVISION) \
.set_value(firmware_revision)
class HomeAccessory(Accessory):
"""Adapter class for Accessory."""
def __init__(self, hass, name, entity_id, aid, category):
def __init__(self, hass, name, entity_id, aid, category=CATEGORY_OTHER):
"""Initialize a Accessory object."""
super().__init__(name, aid=aid)
domain = split_entity_id(entity_id)[0].replace("_", " ").title()
set_accessory_info(self, name, model=domain, serial_number=entity_id)
self.category = getattr(Category, category, Category.OTHER)
self.set_info_service(
firmware_revision=__version__, manufacturer=MANUFACTURER,
model=domain, serial_number=entity_id)
self.category = category
self.entity_id = entity_id
self.hass = hass
def _set_services(self):
add_preload_service(self, SERV_ACCESSORY_INFO)
def run(self):
"""Method called by accessory after driver is started."""
state = self.hass.states.get(self.entity_id)
@ -143,13 +104,11 @@ class HomeBridge(Bridge):
def __init__(self, hass, name=BRIDGE_NAME):
"""Initialize a Bridge object."""
super().__init__(name)
set_accessory_info(self, name, model=BRIDGE_MODEL,
serial_number=BRIDGE_SERIAL_NUMBER)
self.set_info_service(
firmware_revision=__version__, manufacturer=MANUFACTURER,
model=BRIDGE_MODEL, serial_number=BRIDGE_SERIAL_NUMBER)
self.hass = hass
def _set_services(self):
add_preload_service(self, SERV_ACCESSORY_INFO)
def setup_message(self):
"""Prevent print of pyhap setup message to terminal."""
pass

View file

@ -23,17 +23,6 @@ BRIDGE_NAME = 'Home Assistant Bridge'
BRIDGE_SERIAL_NUMBER = 'homekit.bridge'
MANUFACTURER = 'Home Assistant'
# #### Categories ####
CATEGORY_ALARM_SYSTEM = 'ALARM_SYSTEM'
CATEGORY_GARAGE_DOOR_OPENER = 'GARAGE_DOOR_OPENER'
CATEGORY_LIGHT = 'LIGHTBULB'
CATEGORY_LOCK = 'DOOR_LOCK'
CATEGORY_SENSOR = 'SENSOR'
CATEGORY_SWITCH = 'SWITCH'
CATEGORY_THERMOSTAT = 'THERMOSTAT'
CATEGORY_WINDOW_COVERING = 'WINDOW_COVERING'
# #### Services ####
SERV_ACCESSORY_INFO = 'AccessoryInformation'
SERV_AIR_QUALITY_SENSOR = 'AirQualitySensor'
@ -56,7 +45,6 @@ SERV_THERMOSTAT = 'Thermostat'
SERV_WINDOW_COVERING = 'WindowCovering'
# CurrentPosition, TargetPosition, PositionState
# #### Characteristics ####
CHAR_AIR_PARTICULATE_DENSITY = 'AirParticulateDensity'
CHAR_AIR_QUALITY = 'AirQuality'

View file

@ -1,6 +1,8 @@
"""Class to hold all cover accessories."""
import logging
from pyhap.const import CATEGORY_WINDOW_COVERING, CATEGORY_GARAGE_DOOR_OPENER
from homeassistant.components.cover import (
ATTR_CURRENT_POSITION, ATTR_POSITION, DOMAIN, SUPPORT_STOP)
from homeassistant.const import (
@ -9,12 +11,11 @@ from homeassistant.const import (
ATTR_SUPPORTED_FEATURES)
from . import TYPES
from .accessories import HomeAccessory, add_preload_service, setup_char
from .accessories import HomeAccessory
from .const import (
CATEGORY_WINDOW_COVERING, SERV_WINDOW_COVERING,
CHAR_CURRENT_POSITION, CHAR_TARGET_POSITION, CHAR_POSITION_STATE,
CATEGORY_GARAGE_DOOR_OPENER, SERV_GARAGE_DOOR_OPENER,
CHAR_CURRENT_DOOR_STATE, CHAR_TARGET_DOOR_STATE)
SERV_WINDOW_COVERING, CHAR_CURRENT_POSITION,
CHAR_TARGET_POSITION, CHAR_POSITION_STATE,
SERV_GARAGE_DOOR_OPENER, CHAR_CURRENT_DOOR_STATE, CHAR_TARGET_DOOR_STATE)
_LOGGER = logging.getLogger(__name__)
@ -32,12 +33,11 @@ class GarageDoorOpener(HomeAccessory):
super().__init__(*args, category=CATEGORY_GARAGE_DOOR_OPENER)
self.flag_target_state = False
serv_garage_door = add_preload_service(self, SERV_GARAGE_DOOR_OPENER)
self.char_current_state = setup_char(
CHAR_CURRENT_DOOR_STATE, serv_garage_door, value=0)
self.char_target_state = setup_char(
CHAR_TARGET_DOOR_STATE, serv_garage_door, value=0,
callback=self.set_state)
serv_garage_door = self.add_preload_service(SERV_GARAGE_DOOR_OPENER)
self.char_current_state = serv_garage_door.configure_char(
CHAR_CURRENT_DOOR_STATE, value=0)
self.char_target_state = serv_garage_door.configure_char(
CHAR_TARGET_DOOR_STATE, value=0, setter_callback=self.set_state)
def set_state(self, value):
"""Change garage state if call came from HomeKit."""
@ -74,12 +74,11 @@ class WindowCovering(HomeAccessory):
super().__init__(*args, category=CATEGORY_WINDOW_COVERING)
self.homekit_target = None
serv_cover = add_preload_service(self, SERV_WINDOW_COVERING)
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)
serv_cover = self.add_preload_service(SERV_WINDOW_COVERING)
self.char_current_position = serv_cover.configure_char(
CHAR_CURRENT_POSITION, value=0)
self.char_target_position = serv_cover.configure_char(
CHAR_TARGET_POSITION, value=0, setter_callback=self.move_cover)
def move_cover(self, value):
"""Move cover to value if call came from HomeKit."""
@ -115,14 +114,13 @@ class WindowCoveringBasic(HomeAccessory):
.attributes.get(ATTR_SUPPORTED_FEATURES)
self.supports_stop = features & SUPPORT_STOP
serv_cover = add_preload_service(self, SERV_WINDOW_COVERING)
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=2)
serv_cover = self.add_preload_service(SERV_WINDOW_COVERING)
self.char_current_position = serv_cover.configure_char(
CHAR_CURRENT_POSITION, value=0)
self.char_target_position = serv_cover.configure_char(
CHAR_TARGET_POSITION, value=0, setter_callback=self.move_cover)
self.char_position_state = serv_cover.configure_char(
CHAR_POSITION_STATE, value=2)
def move_cover(self, value):
"""Move cover to value if call came from HomeKit."""

View file

@ -1,16 +1,17 @@
"""Class to hold all light accessories."""
import logging
from pyhap.const import CATEGORY_LIGHTBULB
from homeassistant.components.light import (
ATTR_HS_COLOR, ATTR_COLOR_TEMP, ATTR_BRIGHTNESS, ATTR_MIN_MIREDS,
ATTR_MAX_MIREDS, SUPPORT_COLOR, SUPPORT_COLOR_TEMP, SUPPORT_BRIGHTNESS)
from homeassistant.const import ATTR_SUPPORTED_FEATURES, STATE_ON, STATE_OFF
from . import TYPES
from .accessories import (
HomeAccessory, add_preload_service, debounce, setup_char)
from .accessories import HomeAccessory, debounce
from .const import (
CATEGORY_LIGHT, SERV_LIGHTBULB, CHAR_COLOR_TEMPERATURE,
SERV_LIGHTBULB, CHAR_COLOR_TEMPERATURE,
CHAR_BRIGHTNESS, CHAR_HUE, CHAR_ON, CHAR_SATURATION)
_LOGGER = logging.getLogger(__name__)
@ -27,7 +28,7 @@ class Light(HomeAccessory):
def __init__(self, *args, config):
"""Initialize a new Light accessory object."""
super().__init__(*args, category=CATEGORY_LIGHT)
super().__init__(*args, category=CATEGORY_LIGHTBULB)
self._flag = {CHAR_ON: False, CHAR_BRIGHTNESS: False,
CHAR_HUE: False, CHAR_SATURATION: False,
CHAR_COLOR_TEMPERATURE: False, RGB_COLOR: False}
@ -46,30 +47,28 @@ class Light(HomeAccessory):
self._hue = None
self._saturation = None
serv_light = add_preload_service(self, SERV_LIGHTBULB, self.chars)
self.char_on = setup_char(
CHAR_ON, serv_light, value=self._state, callback=self.set_state)
serv_light = self.add_preload_service(SERV_LIGHTBULB, self.chars)
self.char_on = serv_light.configure_char(
CHAR_ON, value=self._state, setter_callback=self.set_state)
if CHAR_BRIGHTNESS in self.chars:
self.char_brightness = setup_char(
CHAR_BRIGHTNESS, serv_light, value=0,
callback=self.set_brightness)
self.char_brightness = serv_light.configure_char(
CHAR_BRIGHTNESS, value=0, setter_callback=self.set_brightness)
if CHAR_COLOR_TEMPERATURE in self.chars:
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 = setup_char(
CHAR_COLOR_TEMPERATURE, serv_light, value=min_mireds,
self.char_color_temperature = serv_light.configure_char(
CHAR_COLOR_TEMPERATURE, value=min_mireds,
properties={'minValue': min_mireds, 'maxValue': max_mireds},
callback=self.set_color_temperature)
setter_callback=self.set_color_temperature)
if CHAR_HUE in self.chars:
self.char_hue = setup_char(
CHAR_HUE, serv_light, value=0, callback=self.set_hue)
self.char_hue = serv_light.configure_char(
CHAR_HUE, value=0, setter_callback=self.set_hue)
if CHAR_SATURATION in self.chars:
self.char_saturation = setup_char(
CHAR_SATURATION, serv_light, value=75,
callback=self.set_saturation)
self.char_saturation = serv_light.configure_char(
CHAR_SATURATION, value=75, setter_callback=self.set_saturation)
def set_state(self, value):
"""Set state if call came from HomeKit."""

View file

@ -1,13 +1,15 @@
"""Class to hold all lock accessories."""
import logging
from pyhap.const import CATEGORY_DOOR_LOCK
from homeassistant.components.lock import (
ATTR_ENTITY_ID, STATE_LOCKED, STATE_UNLOCKED, STATE_UNKNOWN)
from . import TYPES
from .accessories import HomeAccessory, add_preload_service, setup_char
from .accessories import HomeAccessory
from .const import (
CATEGORY_LOCK, SERV_LOCK, CHAR_LOCK_CURRENT_STATE, CHAR_LOCK_TARGET_STATE)
SERV_LOCK, CHAR_LOCK_CURRENT_STATE, CHAR_LOCK_TARGET_STATE)
_LOGGER = logging.getLogger(__name__)
@ -29,16 +31,16 @@ class Lock(HomeAccessory):
def __init__(self, *args, config):
"""Initialize a Lock accessory object."""
super().__init__(*args, category=CATEGORY_LOCK)
super().__init__(*args, category=CATEGORY_DOOR_LOCK)
self.flag_target_state = False
serv_lock_mechanism = add_preload_service(self, SERV_LOCK)
self.char_current_state = setup_char(
CHAR_LOCK_CURRENT_STATE, serv_lock_mechanism,
serv_lock_mechanism = self.add_preload_service(SERV_LOCK)
self.char_current_state = serv_lock_mechanism.configure_char(
CHAR_LOCK_CURRENT_STATE,
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)
self.char_target_state = serv_lock_mechanism.configure_char(
CHAR_LOCK_TARGET_STATE, value=HASS_TO_HOMEKIT[STATE_LOCKED],
setter_callback=self.set_state)
def set_state(self, value):
"""Set lock state to value if call came from HomeKit."""

View file

@ -1,16 +1,18 @@
"""Class to hold all alarm control panel accessories."""
import logging
from pyhap.const import CATEGORY_ALARM_SYSTEM
from homeassistant.const import (
STATE_ALARM_ARMED_AWAY, STATE_ALARM_ARMED_HOME,
STATE_ALARM_ARMED_NIGHT, STATE_ALARM_DISARMED,
ATTR_ENTITY_ID, ATTR_CODE)
from . import TYPES
from .accessories import HomeAccessory, add_preload_service, setup_char
from .accessories import HomeAccessory
from .const import (
CATEGORY_ALARM_SYSTEM, SERV_SECURITY_SYSTEM,
CHAR_CURRENT_SECURITY_STATE, CHAR_TARGET_SECURITY_STATE)
SERV_SECURITY_SYSTEM, CHAR_CURRENT_SECURITY_STATE,
CHAR_TARGET_SECURITY_STATE)
_LOGGER = logging.getLogger(__name__)
@ -33,12 +35,12 @@ class SecuritySystem(HomeAccessory):
self._alarm_code = config.get(ATTR_CODE)
self.flag_target_state = False
serv_alarm = add_preload_service(self, SERV_SECURITY_SYSTEM)
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)
serv_alarm = self.add_preload_service(SERV_SECURITY_SYSTEM)
self.char_current_state = serv_alarm.configure_char(
CHAR_CURRENT_SECURITY_STATE, value=3)
self.char_target_state = serv_alarm.configure_char(
CHAR_TARGET_SECURITY_STATE, value=3,
setter_callback=self.set_security_state)
def set_security_state(self, value):
"""Move security state to value if call came from HomeKit."""

View file

@ -1,14 +1,16 @@
"""Class to hold all sensor accessories."""
import logging
from pyhap.const import CATEGORY_SENSOR
from homeassistant.const import (
ATTR_UNIT_OF_MEASUREMENT, TEMP_CELSIUS,
ATTR_DEVICE_CLASS, STATE_ON, STATE_HOME)
from . import TYPES
from .accessories import HomeAccessory, add_preload_service, setup_char
from .accessories import HomeAccessory
from .const import (
CATEGORY_SENSOR, SERV_HUMIDITY_SENSOR, SERV_TEMPERATURE_SENSOR,
SERV_HUMIDITY_SENSOR, SERV_TEMPERATURE_SENSOR,
CHAR_CURRENT_HUMIDITY, CHAR_CURRENT_TEMPERATURE, PROP_CELSIUS,
SERV_AIR_QUALITY_SENSOR, CHAR_AIR_QUALITY, CHAR_AIR_PARTICULATE_DENSITY,
CHAR_CARBON_DIOXIDE_LEVEL, CHAR_CARBON_DIOXIDE_PEAK_LEVEL,
@ -52,10 +54,9 @@ class TemperatureSensor(HomeAccessory):
def __init__(self, *args, config):
"""Initialize a TemperatureSensor accessory object."""
super().__init__(*args, category=CATEGORY_SENSOR)
serv_temp = add_preload_service(self, SERV_TEMPERATURE_SENSOR)
self.char_temp = setup_char(
CHAR_CURRENT_TEMPERATURE, serv_temp, value=0,
properties=PROP_CELSIUS)
serv_temp = self.add_preload_service(SERV_TEMPERATURE_SENSOR)
self.char_temp = serv_temp.configure_char(
CHAR_CURRENT_TEMPERATURE, value=0, properties=PROP_CELSIUS)
self.unit = None
def update_state(self, new_state):
@ -76,9 +77,9 @@ class HumiditySensor(HomeAccessory):
def __init__(self, *args, config):
"""Initialize a HumiditySensor accessory object."""
super().__init__(*args, category=CATEGORY_SENSOR)
serv_humidity = add_preload_service(self, SERV_HUMIDITY_SENSOR)
self.char_humidity = setup_char(
CHAR_CURRENT_HUMIDITY, serv_humidity, value=0)
serv_humidity = self.add_preload_service(SERV_HUMIDITY_SENSOR)
self.char_humidity = serv_humidity.configure_char(
CHAR_CURRENT_HUMIDITY, value=0)
def update_state(self, new_state):
"""Update accessory after state change."""
@ -97,12 +98,12 @@ class AirQualitySensor(HomeAccessory):
"""Initialize a AirQualitySensor accessory object."""
super().__init__(*args, category=CATEGORY_SENSOR)
serv_air_quality = add_preload_service(self, SERV_AIR_QUALITY_SENSOR,
[CHAR_AIR_PARTICULATE_DENSITY])
self.char_quality = setup_char(
CHAR_AIR_QUALITY, serv_air_quality, value=0)
self.char_density = setup_char(
CHAR_AIR_PARTICULATE_DENSITY, serv_air_quality, value=0)
serv_air_quality = self.add_preload_service(
SERV_AIR_QUALITY_SENSOR, [CHAR_AIR_PARTICULATE_DENSITY])
self.char_quality = serv_air_quality.configure_char(
CHAR_AIR_QUALITY, value=0)
self.char_density = serv_air_quality.configure_char(
CHAR_AIR_PARTICULATE_DENSITY, value=0)
def update_state(self, new_state):
"""Update accessory after state change."""
@ -121,14 +122,14 @@ class CarbonDioxideSensor(HomeAccessory):
"""Initialize a CarbonDioxideSensor accessory object."""
super().__init__(*args, category=CATEGORY_SENSOR)
serv_co2 = add_preload_service(self, SERV_CARBON_DIOXIDE_SENSOR, [
serv_co2 = self.add_preload_service(SERV_CARBON_DIOXIDE_SENSOR, [
CHAR_CARBON_DIOXIDE_LEVEL, CHAR_CARBON_DIOXIDE_PEAK_LEVEL])
self.char_co2 = setup_char(
CHAR_CARBON_DIOXIDE_LEVEL, serv_co2, value=0)
self.char_peak = setup_char(
CHAR_CARBON_DIOXIDE_PEAK_LEVEL, serv_co2, value=0)
self.char_detected = setup_char(
CHAR_CARBON_DIOXIDE_DETECTED, serv_co2, value=0)
self.char_co2 = serv_co2.configure_char(
CHAR_CARBON_DIOXIDE_LEVEL, value=0)
self.char_peak = serv_co2.configure_char(
CHAR_CARBON_DIOXIDE_PEAK_LEVEL, value=0)
self.char_detected = serv_co2.configure_char(
CHAR_CARBON_DIOXIDE_DETECTED, value=0)
def update_state(self, new_state):
"""Update accessory after state change."""
@ -149,9 +150,9 @@ class LightSensor(HomeAccessory):
"""Initialize a LightSensor accessory object."""
super().__init__(*args, category=CATEGORY_SENSOR)
serv_light = add_preload_service(self, SERV_LIGHT_SENSOR)
self.char_light = setup_char(
CHAR_CURRENT_AMBIENT_LIGHT_LEVEL, serv_light, value=0)
serv_light = self.add_preload_service(SERV_LIGHT_SENSOR)
self.char_light = serv_light.configure_char(
CHAR_CURRENT_AMBIENT_LIGHT_LEVEL, value=0)
def update_state(self, new_state):
"""Update accessory after state change."""
@ -174,8 +175,8 @@ class BinarySensor(HomeAccessory):
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 = setup_char(service_char[1], service, value=0)
service = self.add_preload_service(service_char[0])
self.char_detected = service.configure_char(service_char[1], value=0)
def update_state(self, new_state):
"""Update accessory after state change."""

View file

@ -1,13 +1,15 @@
"""Class to hold all switch accessories."""
import logging
from pyhap.const import CATEGORY_SWITCH
from homeassistant.const import (
ATTR_ENTITY_ID, SERVICE_TURN_ON, SERVICE_TURN_OFF, STATE_ON)
from homeassistant.core import split_entity_id
from . import TYPES
from .accessories import HomeAccessory, add_preload_service, setup_char
from .const import CATEGORY_SWITCH, SERV_SWITCH, CHAR_ON
from .accessories import HomeAccessory
from .const import SERV_SWITCH, CHAR_ON
_LOGGER = logging.getLogger(__name__)
@ -22,9 +24,9 @@ class Switch(HomeAccessory):
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 = setup_char(
CHAR_ON, serv_switch, value=False, callback=self.set_state)
serv_switch = self.add_preload_service(SERV_SWITCH)
self.char_on = serv_switch.configure_char(
CHAR_ON, value=False, setter_callback=self.set_state)
def set_state(self, value):
"""Move switch state to value if call came from HomeKit."""

View file

@ -1,6 +1,8 @@
"""Class to hold all thermostat accessories."""
import logging
from pyhap.const import CATEGORY_THERMOSTAT
from homeassistant.components.climate import (
ATTR_CURRENT_TEMPERATURE, ATTR_TEMPERATURE,
ATTR_TARGET_TEMP_HIGH, ATTR_TARGET_TEMP_LOW,
@ -12,10 +14,9 @@ from homeassistant.const import (
STATE_OFF, TEMP_CELSIUS, TEMP_FAHRENHEIT)
from . import TYPES
from .accessories import (
HomeAccessory, add_preload_service, debounce, setup_char)
from .accessories import HomeAccessory, debounce
from .const import (
CATEGORY_THERMOSTAT, SERV_THERMOSTAT, CHAR_CURRENT_HEATING_COOLING,
SERV_THERMOSTAT, CHAR_CURRENT_HEATING_COOLING,
CHAR_TARGET_HEATING_COOLING, CHAR_CURRENT_TEMPERATURE,
CHAR_TARGET_TEMPERATURE, CHAR_TEMP_DISPLAY_UNITS,
CHAR_COOLING_THRESHOLD_TEMPERATURE, CHAR_HEATING_THRESHOLD_TEMPERATURE)
@ -57,38 +58,37 @@ class Thermostat(HomeAccessory):
self.chars.extend((CHAR_COOLING_THRESHOLD_TEMPERATURE,
CHAR_HEATING_THRESHOLD_TEMPERATURE))
serv_thermostat = add_preload_service(
self, SERV_THERMOSTAT, self.chars)
serv_thermostat = self.add_preload_service(SERV_THERMOSTAT, self.chars)
# Current and target mode characteristics
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)
self.char_current_heat_cool = serv_thermostat.configure_char(
CHAR_CURRENT_HEATING_COOLING, value=0)
self.char_target_heat_cool = serv_thermostat.configure_char(
CHAR_TARGET_HEATING_COOLING, value=0,
setter_callback=self.set_heat_cool)
# Current and target temperature characteristics
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)
self.char_current_temp = serv_thermostat.configure_char(
CHAR_CURRENT_TEMPERATURE, value=21.0)
self.char_target_temp = serv_thermostat.configure_char(
CHAR_TARGET_TEMPERATURE, value=21.0,
setter_callback=self.set_target_temperature)
# Display units characteristic
self.char_display_units = setup_char(
CHAR_TEMP_DISPLAY_UNITS, serv_thermostat, value=0)
self.char_display_units = serv_thermostat.configure_char(
CHAR_TEMP_DISPLAY_UNITS, value=0)
# If the device supports it: high and low temperature characteristics
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)
self.char_cooling_thresh_temp = serv_thermostat.configure_char(
CHAR_COOLING_THRESHOLD_TEMPERATURE, value=23.0,
setter_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)
self.char_heating_thresh_temp = serv_thermostat.configure_char(
CHAR_HEATING_THRESHOLD_TEMPERATURE, value=19.0,
setter_callback=self.set_heating_threshold)
def set_heat_cool(self, value):
"""Move operation mode to value if call came from HomeKit."""

View file

@ -28,7 +28,7 @@ Adafruit-SHT31==1.0.2
DoorBirdPy==0.1.3
# homeassistant.components.homekit
HAP-python==1.1.9
HAP-python==2.0.0
# homeassistant.components.notify.mastodon
Mastodon.py==1.2.2

View file

@ -19,7 +19,7 @@ requests_mock==1.4
# homeassistant.components.homekit
HAP-python==1.1.9
HAP-python==2.0.0
# homeassistant.components.notify.html5
PyJWT==1.6.0

View file

@ -7,12 +7,12 @@ import unittest
from unittest.mock import call, patch, Mock
from homeassistant.components.homekit.accessories import (
add_preload_service, set_accessory_info,
debounce, HomeAccessory, HomeBridge, HomeDriver)
from homeassistant.components.homekit.const import (
BRIDGE_MODEL, BRIDGE_NAME, SERV_ACCESSORY_INFO, CHAR_FIRMWARE_REVISION,
CHAR_MANUFACTURER, CHAR_MODEL, CHAR_NAME, CHAR_SERIAL_NUMBER, MANUFACTURER)
from homeassistant.const import ATTR_NOW, EVENT_TIME_CHANGED
BRIDGE_MODEL, BRIDGE_NAME, BRIDGE_SERIAL_NUMBER, SERV_ACCESSORY_INFO,
CHAR_FIRMWARE_REVISION, CHAR_MANUFACTURER, CHAR_MODEL, CHAR_NAME,
CHAR_SERIAL_NUMBER, MANUFACTURER)
from homeassistant.const import __version__, ATTR_NOW, EVENT_TIME_CHANGED
import homeassistant.util.dt as dt_util
from tests.common import get_test_home_assistant
@ -62,73 +62,25 @@ class TestAccessories(unittest.TestCase):
hass.stop()
def test_add_preload_service(self):
"""Test add_preload_service without additional characteristics."""
acc = Mock()
serv = add_preload_service(acc, 'AirPurifier')
self.assertEqual(acc.mock_calls, [call.add_service(serv)])
with self.assertRaises(ValueError):
serv.get_characteristic('Name')
# Test with typo in service name
with self.assertRaises(KeyError):
add_preload_service(Mock(), 'AirPurifierTypo')
# Test adding additional characteristic as string
serv = add_preload_service(Mock(), 'AirPurifier', 'Name')
serv.get_characteristic('Name')
# Test adding additional characteristics as list
serv = add_preload_service(Mock(), 'AirPurifier',
['Name', 'RotationSpeed'])
serv.get_characteristic('Name')
serv.get_characteristic('RotationSpeed')
# Test adding additional characteristic with typo
with self.assertRaises(KeyError):
add_preload_service(Mock(), 'AirPurifier', 'NameTypo')
def test_set_accessory_info(self):
"""Test setting the basic accessory information."""
# Test HomeAccessory
acc = HomeAccessory('HA', 'Home Accessory', 'homekit.accessory', 2, '')
set_accessory_info(acc, 'name', 'model', '0000', MANUFACTURER, '1.2.3')
serv = acc.get_service(SERV_ACCESSORY_INFO)
self.assertEqual(serv.get_characteristic(CHAR_NAME).value, 'name')
self.assertEqual(serv.get_characteristic(CHAR_MODEL).value, 'model')
self.assertEqual(
serv.get_characteristic(CHAR_SERIAL_NUMBER).value, '0000')
self.assertEqual(
serv.get_characteristic(CHAR_MANUFACTURER).value, MANUFACTURER)
self.assertEqual(
serv.get_characteristic(CHAR_FIRMWARE_REVISION).value, '1.2.3')
# Test HomeBridge
acc = HomeBridge('hass')
set_accessory_info(acc, 'name', 'model', '0000', MANUFACTURER, '1.2.3')
serv = acc.get_service(SERV_ACCESSORY_INFO)
self.assertEqual(serv.get_characteristic(CHAR_MODEL).value, 'model')
self.assertEqual(
serv.get_characteristic(CHAR_SERIAL_NUMBER).value, '0000')
self.assertEqual(
serv.get_characteristic(CHAR_MANUFACTURER).value, MANUFACTURER)
self.assertEqual(
serv.get_characteristic(CHAR_FIRMWARE_REVISION).value, '1.2.3')
def test_home_accessory(self):
"""Test HomeAccessory class."""
hass = get_test_home_assistant()
acc = HomeAccessory(hass, 'Home Accessory', 'homekit.accessory', 2, '')
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.display_name, SERV_ACCESSORY_INFO)
self.assertEqual(
serv.get_characteristic(CHAR_NAME).value, 'Home Accessory')
self.assertEqual(
serv.get_characteristic(CHAR_MANUFACTURER).value, MANUFACTURER)
self.assertEqual(
serv.get_characteristic(CHAR_MODEL).value, 'Homekit')
self.assertEqual(serv.get_characteristic(CHAR_SERIAL_NUMBER).value,
'homekit.accessory')
hass.states.set('homekit.accessory', 'on')
hass.block_till_done()
@ -136,7 +88,7 @@ class TestAccessories(unittest.TestCase):
hass.states.set('homekit.accessory', 'off')
hass.block_till_done()
acc = HomeAccessory('hass', 'test_name', 'test_model.demo', 2, '')
acc = HomeAccessory('hass', 'test_name', 'test_model.demo', 2)
self.assertEqual(acc.display_name, 'test_name')
self.assertEqual(acc.aid, 2)
self.assertEqual(len(acc.services), 1)
@ -155,8 +107,17 @@ class TestAccessories(unittest.TestCase):
self.assertEqual(len(bridge.services), 1)
serv = bridge.services[0] # SERV_ACCESSORY_INFO
self.assertEqual(serv.display_name, SERV_ACCESSORY_INFO)
self.assertEqual(
serv.get_characteristic(CHAR_NAME).value, BRIDGE_NAME)
self.assertEqual(
serv.get_characteristic(CHAR_FIRMWARE_REVISION).value, __version__)
self.assertEqual(
serv.get_characteristic(CHAR_MANUFACTURER).value, MANUFACTURER)
self.assertEqual(
serv.get_characteristic(CHAR_MODEL).value, BRIDGE_MODEL)
self.assertEqual(
serv.get_characteristic(CHAR_SERIAL_NUMBER).value,
BRIDGE_SERIAL_NUMBER)
bridge = HomeBridge('hass', 'test_name')
self.assertEqual(bridge.display_name, 'test_name')

View file

@ -4,7 +4,9 @@ from unittest.mock import call, patch, ANY, Mock
from homeassistant import setup
from homeassistant.core import State
from homeassistant.components.homekit import HomeKit, generate_aid
from homeassistant.components.homekit import (
HomeKit, generate_aid,
STATUS_READY, STATUS_RUNNING, STATUS_STOPPED, STATUS_WAIT)
from homeassistant.components.homekit.accessories import HomeBridge
from homeassistant.components.homekit.const import (
DOMAIN, HOMEKIT_FILE, CONF_AUTO_START,
@ -79,24 +81,28 @@ class TestHomeKit(unittest.TestCase):
CONF_IP_ADDRESS: '172.0.0.0'}}
self.assertTrue(setup.setup_component(
self.hass, DOMAIN, config))
self.hass.bus.fire(EVENT_HOMEASSISTANT_START)
self.hass.block_till_done()
self.assertEqual(mock_homekit.mock_calls, [
call(self.hass, 11111, '172.0.0.0', ANY, {}),
call().setup()])
# Test start call with driver stopped.
# Test auto_start disabled
homekit.reset_mock()
homekit.configure_mock(**{'started': False})
self.hass.bus.fire(EVENT_HOMEASSISTANT_START)
self.hass.block_till_done()
self.assertEqual(homekit.mock_calls, [])
# Test start call with driver is ready
homekit.reset_mock()
homekit.status = STATUS_READY
self.hass.services.call('homekit', 'start')
self.assertEqual(homekit.mock_calls, [call.start()])
# Test start call with driver started.
# Test start call with driver started
homekit.reset_mock()
homekit.configure_mock(**{'started': True})
homekit.status = STATUS_STOPPED
self.hass.services.call(DOMAIN, SERVICE_HOMEKIT_START)
self.assertEqual(homekit.mock_calls, [])
@ -180,34 +186,38 @@ class TestHomeKit(unittest.TestCase):
state = self.hass.states.all()[0]
homekit.start()
self.hass.block_till_done()
self.assertEqual(mock_add_bridge_acc.mock_calls, [call(state)])
self.assertEqual(mock_show_setup_msg.mock_calls, [
call(self.hass, homekit.bridge)])
self.assertEqual(homekit.driver.mock_calls, [call.start()])
self.assertTrue(homekit.started)
self.assertEqual(homekit.status, STATUS_RUNNING)
# Test start() if already started
homekit.driver.reset_mock()
homekit.start()
self.hass.block_till_done()
self.assertEqual(homekit.driver.mock_calls, [])
def test_homekit_stop(self):
"""Test HomeKit stop method."""
homekit = HomeKit(None, None, None, None, None)
homekit = HomeKit(self.hass, None, None, None, None)
homekit.driver = Mock()
# Test if started = False
self.assertEqual(homekit.status, STATUS_READY)
homekit.stop()
self.assertFalse(homekit.driver.stop.called)
# Test if driver not started
homekit.started = True
homekit.driver.configure_mock(**{'run_sentinel': None})
self.hass.block_till_done()
homekit.status = STATUS_WAIT
homekit.stop()
self.hass.block_till_done()
homekit.status = STATUS_STOPPED
homekit.stop()
self.hass.block_till_done()
self.assertFalse(homekit.driver.stop.called)
# Test if driver is started
homekit.driver.configure_mock(**{'run_sentinel': 'sentinel'})
homekit.status = STATUS_RUNNING
homekit.stop()
self.hass.block_till_done()
self.assertTrue(homekit.driver.stop.called)