Refactor homekit_controller to be fully asynchronous (#32111)
* Port homekit_controller to aiohomekit * Remove succeed() test helper * Remove fail() test helper
This commit is contained in:
parent
a1a835cf54
commit
df9363610c
31 changed files with 560 additions and 583 deletions
|
@ -4,14 +4,10 @@ import json
|
|||
import os
|
||||
from unittest import mock
|
||||
|
||||
from homekit.exceptions import AccessoryNotFoundError
|
||||
from homekit.model import Accessory, get_id
|
||||
from homekit.model.characteristics import (
|
||||
AbstractCharacteristic,
|
||||
CharacteristicPermissions,
|
||||
CharacteristicsTypes,
|
||||
)
|
||||
from homekit.model.services import AbstractService, ServicesTypes
|
||||
from aiohomekit.exceptions import AccessoryNotFoundError
|
||||
from aiohomekit.model import Accessory
|
||||
from aiohomekit.model.characteristics import CharacteristicsTypes
|
||||
from aiohomekit.model.services import ServicesTypes
|
||||
|
||||
from homeassistant import config_entries
|
||||
from homeassistant.components.homekit_controller import config_flow
|
||||
|
@ -40,14 +36,14 @@ class FakePairing:
|
|||
self.pairing_data = {}
|
||||
self.available = True
|
||||
|
||||
def list_accessories_and_characteristics(self):
|
||||
async def list_accessories_and_characteristics(self):
|
||||
"""Fake implementation of list_accessories_and_characteristics."""
|
||||
accessories = [a.to_accessory_and_service_list() for a in self.accessories]
|
||||
# replicate what happens upstream right now
|
||||
self.pairing_data["accessories"] = accessories
|
||||
return accessories
|
||||
|
||||
def get_characteristics(self, characteristics):
|
||||
async def get_characteristics(self, characteristics):
|
||||
"""Fake implementation of get_characteristics."""
|
||||
if not self.available:
|
||||
raise AccessoryNotFoundError("Accessory not found")
|
||||
|
@ -64,7 +60,7 @@ class FakePairing:
|
|||
results[(aid, cid)] = {"value": char.get_value()}
|
||||
return results
|
||||
|
||||
def put_characteristics(self, characteristics):
|
||||
async def put_characteristics(self, characteristics):
|
||||
"""Fake implementation of put_characteristics."""
|
||||
for aid, cid, new_val in characteristics:
|
||||
for accessory in self.accessories:
|
||||
|
@ -124,45 +120,6 @@ class Helper:
|
|||
return state
|
||||
|
||||
|
||||
class FakeCharacteristic(AbstractCharacteristic):
|
||||
"""
|
||||
A model of a generic HomeKit characteristic.
|
||||
|
||||
Base is abstract and can't be instanced directly so this subclass is
|
||||
needed even though it doesn't add any methods.
|
||||
"""
|
||||
|
||||
def to_accessory_and_service_list(self):
|
||||
"""Serialize the characteristic."""
|
||||
# Upstream doesn't correctly serialize valid_values
|
||||
# This fix will be upstreamed and this function removed when it
|
||||
# is fixed.
|
||||
record = super().to_accessory_and_service_list()
|
||||
if self.valid_values:
|
||||
record["valid-values"] = self.valid_values
|
||||
return record
|
||||
|
||||
|
||||
class FakeService(AbstractService):
|
||||
"""A model of a generic HomeKit service."""
|
||||
|
||||
def __init__(self, service_name):
|
||||
"""Create a fake service by its short form HAP spec name."""
|
||||
char_type = ServicesTypes.get_uuid(service_name)
|
||||
super().__init__(char_type, get_id())
|
||||
|
||||
def add_characteristic(self, name):
|
||||
"""Add a characteristic to this service by name."""
|
||||
full_name = "public.hap.characteristic." + name
|
||||
char = FakeCharacteristic(get_id(), full_name, None)
|
||||
char.perms = [
|
||||
CharacteristicPermissions.paired_read,
|
||||
CharacteristicPermissions.paired_write,
|
||||
]
|
||||
self.characteristics.append(char)
|
||||
return char
|
||||
|
||||
|
||||
async def time_changed(hass, seconds):
|
||||
"""Trigger time changed."""
|
||||
next_update = dt_util.utcnow() + timedelta(seconds)
|
||||
|
@ -176,40 +133,7 @@ async def setup_accessories_from_file(hass, path):
|
|||
load_fixture, os.path.join("homekit_controller", path)
|
||||
)
|
||||
accessories_json = json.loads(accessories_fixture)
|
||||
|
||||
accessories = []
|
||||
|
||||
for accessory_data in accessories_json:
|
||||
accessory = Accessory("Name", "Mfr", "Model", "0001", "0.1")
|
||||
accessory.services = []
|
||||
accessory.aid = accessory_data["aid"]
|
||||
for service_data in accessory_data["services"]:
|
||||
service = FakeService("public.hap.service.accessory-information")
|
||||
service.type = service_data["type"]
|
||||
service.iid = service_data["iid"]
|
||||
|
||||
for char_data in service_data["characteristics"]:
|
||||
char = FakeCharacteristic(1, "23", None)
|
||||
char.type = char_data["type"]
|
||||
char.iid = char_data["iid"]
|
||||
char.perms = char_data["perms"]
|
||||
char.format = char_data["format"]
|
||||
if "description" in char_data:
|
||||
char.description = char_data["description"]
|
||||
if "value" in char_data:
|
||||
char.value = char_data["value"]
|
||||
if "minValue" in char_data:
|
||||
char.minValue = char_data["minValue"]
|
||||
if "maxValue" in char_data:
|
||||
char.maxValue = char_data["maxValue"]
|
||||
if "valid-values" in char_data:
|
||||
char.valid_values = char_data["valid-values"]
|
||||
service.characteristics.append(char)
|
||||
|
||||
accessory.services.append(service)
|
||||
|
||||
accessories.append(accessory)
|
||||
|
||||
accessories = Accessory.setup_accessories_from_list(accessories_json)
|
||||
return accessories
|
||||
|
||||
|
||||
|
@ -217,7 +141,7 @@ async def setup_platform(hass):
|
|||
"""Load the platform but with a fake Controller API."""
|
||||
config = {"discovery": {}}
|
||||
|
||||
with mock.patch("homekit.Controller") as controller:
|
||||
with mock.patch("aiohomekit.Controller") as controller:
|
||||
fake_controller = controller.return_value = FakeController()
|
||||
await async_setup_component(hass, DOMAIN, config)
|
||||
|
||||
|
@ -293,15 +217,18 @@ async def device_config_changed(hass, accessories):
|
|||
await hass.async_block_till_done()
|
||||
|
||||
|
||||
async def setup_test_component(hass, services, capitalize=False, suffix=None):
|
||||
async def setup_test_component(hass, setup_accessory, capitalize=False, suffix=None):
|
||||
"""Load a fake homekit accessory based on a homekit accessory model.
|
||||
|
||||
If capitalize is True, property names will be in upper case.
|
||||
|
||||
If suffix is set, entityId will include the suffix
|
||||
"""
|
||||
accessory = Accessory("TestDevice", "example.com", "Test", "0001", "0.1")
|
||||
setup_accessory(accessory)
|
||||
|
||||
domain = None
|
||||
for service in services:
|
||||
for service in accessory.services:
|
||||
service_name = ServicesTypes.get_short(service.type)
|
||||
if service_name in HOMEKIT_ACCESSORY_DISPATCH:
|
||||
domain = HOMEKIT_ACCESSORY_DISPATCH[service_name]
|
||||
|
@ -309,9 +236,6 @@ async def setup_test_component(hass, services, capitalize=False, suffix=None):
|
|||
|
||||
assert domain, "Cannot map test homekit services to Home Assistant domain"
|
||||
|
||||
accessory = Accessory("TestDevice", "example.com", "Test", "0001", "0.1")
|
||||
accessory.services.extend(services)
|
||||
|
||||
config_entry, pairing = await setup_test_accessories(hass, [accessory])
|
||||
entity = "testdevice" if suffix is None else "testdevice_{}".format(suffix)
|
||||
return Helper(hass, ".".join((domain, entity)), pairing, accessory, config_entry)
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue