async def list_accessories_and_characteristics(self): """ This retrieves a current set of accessories and characteristics behind this pairing. :return: the accessory data as described in the spec on page 73 and following :raises AccessoryNotFoundError: if the device can not be found via zeroconf """ await self._ensure_connected() response = await self.connection.get_json("/accessories") accessories = response["accessories"] for accessory in accessories: for service in accessory["services"]: service["type"] = service["type"].upper() try: service["type"] = ServicesTypes.get_uuid(service["type"]) except KeyError: pass for characteristic in service["characteristics"]: characteristic["type"] = characteristic["type"].upper() try: characteristic["type"] = CharacteristicsTypes.get_uuid( characteristic["type"]) except KeyError: pass self.pairing_data["accessories"] = accessories return accessories
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.create_with_info("TestDevice", "example.com", "Test", "0001", "0.1") setup_accessory(accessory) domain = None 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] break assert domain, "Cannot map test homekit services to Home Assistant domain" config_entry, pairing = await setup_test_accessories(hass, [accessory]) entity = "testdevice" if suffix is None else f"testdevice_{suffix}" return Helper(hass, ".".join((domain, entity)), pairing, accessory, config_entry)
def get_accessory_information(accessory): """Obtain the accessory information service of a HomeKit device.""" result = {} for service in accessory["services"]: stype = service["type"].upper() if ServicesTypes.get_short(stype) != "accessory-information": continue for characteristic in service["characteristics"]: ctype = CharacteristicsTypes.get_short(characteristic["type"]) if "value" in characteristic: result[ctype] = characteristic["value"] return result
async def async_load_platforms(self): """Load any platforms needed by this HomeKit device.""" for accessory in self.accessories: for service in accessory["services"]: stype = ServicesTypes.get_short(service["type"].upper()) if stype in HOMEKIT_ACCESSORY_DISPATCH: platform = HOMEKIT_ACCESSORY_DISPATCH[stype] await self.async_load_platform(platform) for char in service["characteristics"]: if char["type"].upper() in CHARACTERISTIC_PLATFORMS: platform = CHARACTERISTIC_PLATFORMS[ char["type"].upper()] await self.async_load_platform(platform)
def __init__(self, hass, entity_id, pairing, accessory, config_entry): """Create a helper for a given accessory/entity.""" self.hass = hass self.entity_id = entity_id self.pairing = pairing self.accessory = accessory self.config_entry = config_entry self.characteristics = {} for service in self.accessory.services: service_name = ServicesTypes.get_short(service.type) for char in service.characteristics: char_name = CharacteristicsTypes.get_short(char.type) self.characteristics[(service_name, char_name)] = char
def _add_new_entities(self, callbacks): for accessory in self.accessories: aid = accessory["aid"] for service in accessory["services"]: iid = service["iid"] stype = ServicesTypes.get_short(service["type"].upper()) service["stype"] = stype if (aid, iid) in self.entities: # Don't add the same entity again continue for listener in callbacks: if listener(aid, service): self.entities.append((aid, iid)) break
async def async_load_platforms(self): """Load any platforms needed by this HomeKit device.""" for accessory in self.accessories: for service in accessory["services"]: stype = ServicesTypes.get_short(service["type"].upper()) if stype not in HOMEKIT_ACCESSORY_DISPATCH: continue platform = HOMEKIT_ACCESSORY_DISPATCH[stype] if platform in self.platforms: continue self.platforms.add(platform) try: await self.hass.config_entries.async_forward_entry_setup( self.config_entry, platform) except Exception: self.platforms.remove(platform) raise
}, CharacteristicsTypes.Vendor.EVE_DEGREE_AIR_PRESSURE: { "name": "Air Pressure", "device_class": DEVICE_CLASS_PRESSURE, "state_class": STATE_CLASS_MEASUREMENT, "unit": PRESSURE_HPA, }, CharacteristicsTypes.get_uuid(CharacteristicsTypes.TEMPERATURE_CURRENT): { "name": "Current Temperature", "device_class": DEVICE_CLASS_TEMPERATURE, "state_class": STATE_CLASS_MEASUREMENT, "unit": TEMP_CELSIUS, # This sensor is only for temperature characteristics that are not part # of a temperature sensor service. "probe": lambda char: char.service.type != ServicesTypes.get_uuid(ServicesTypes.TEMPERATURE_SENSOR), }, CharacteristicsTypes.get_uuid(CharacteristicsTypes.RELATIVE_HUMIDITY_CURRENT): { "name": "Current Humidity", "device_class": DEVICE_CLASS_HUMIDITY, "state_class": STATE_CLASS_MEASUREMENT, "unit": PERCENTAGE, # This sensor is only for humidity characteristics that are not part # of a humidity sensor service. "probe": lambda char: char.service.type != ServicesTypes.get_uuid(ServicesTypes.HUMIDITY_SENSOR), }, } class HomeKitHumiditySensor(HomeKitEntity, SensorEntity):
def test_get_short_no_service(): assert ( ServicesTypes.get_short("00000023-0000-1000-8000-0026BB765291") == "Unknown Service: 00000023-0000-1000-8000-0026BB765291" )
def test_get_short_no_baseid(): assert ( ServicesTypes.get_short("00000023-0000-1000-8000-NOTBASEID") == "Unknown Service: 00000023-0000-1000-8000-NOTBASEID" )
def test_get_uuid(): assert ( ServicesTypes.get_uuid("public.hap.service.doorbell") == "00000121-0000-1000-8000-0026BB765291" )
key=CharacteristicsTypes.Vendor.VOCOLINC_OUTLET_ENERGY, name="Power", device_class=SensorDeviceClass.POWER, state_class=SensorStateClass.MEASUREMENT, native_unit_of_measurement=POWER_WATT, ), CharacteristicsTypes.TEMPERATURE_CURRENT: HomeKitSensorEntityDescription( key=CharacteristicsTypes.TEMPERATURE_CURRENT, name="Current Temperature", device_class=SensorDeviceClass.TEMPERATURE, state_class=SensorStateClass.MEASUREMENT, native_unit_of_measurement=TEMP_CELSIUS, # This sensor is only for temperature characteristics that are not part # of a temperature sensor service. probe=(lambda char: char.service.type != ServicesTypes.get_uuid( ServicesTypes.TEMPERATURE_SENSOR)), ), CharacteristicsTypes.RELATIVE_HUMIDITY_CURRENT: HomeKitSensorEntityDescription( key=CharacteristicsTypes.RELATIVE_HUMIDITY_CURRENT, name="Current Humidity", device_class=SensorDeviceClass.HUMIDITY, state_class=SensorStateClass.MEASUREMENT, native_unit_of_measurement=PERCENTAGE, # This sensor is only for humidity characteristics that are not part # of a humidity sensor service. probe=(lambda char: char.service.type != ServicesTypes.get_uuid( ServicesTypes.HUMIDITY_SENSOR)), ), CharacteristicsTypes.AIR_QUALITY: HomeKitSensorEntityDescription(
}, CharacteristicsTypes.Vendor.KOOGEEK_REALTIME_ENERGY: { "name": "Real Time Energy", "device_class": DEVICE_CLASS_POWER, "state_class": STATE_CLASS_MEASUREMENT, "unit": "watts", }, CharacteristicsTypes.get_uuid(CharacteristicsTypes.TEMPERATURE_CURRENT): { "name": "Current Temperature", "device_class": DEVICE_CLASS_TEMPERATURE, "state_class": STATE_CLASS_MEASUREMENT, "unit": TEMP_CELSIUS, # This sensor is only for temperature characteristics that are not part # of a temperature sensor service. "probe": lambda char: char.service.type != ServicesTypes.get_uuid(ServicesTypes.TEMPERATURE_SENSOR), }, } class HomeKitHumiditySensor(HomeKitEntity, SensorEntity): """Representation of a Homekit humidity sensor.""" _attr_device_class = DEVICE_CLASS_HUMIDITY _attr_unit_of_measurement = PERCENTAGE def get_characteristic_types(self): """Define the homekit characteristics the entity is tracking.""" return [CharacteristicsTypes.RELATIVE_HUMIDITY_CURRENT] @property
def test_get_uuid_no_service(): with pytest.raises(Exception): ServicesTypes.get_uuid("public.hap.service.NO_A_SERVICE")
def test_get_short_lowercase(): assert ( ServicesTypes.get_short("00000086-0000-1000-8000-0026bb765291") == "occupancy" )
def test_get_short_uuid_from_uuid(): assert ServicesTypes.get_short_uuid("00000086-0000-1000-8000-0026BB765291") == "86"
def test_get_short_uuid_from_name(): assert ServicesTypes.get_short_uuid("public.hap.service.doorbell") == "121"
with open("aiohomekit/model/characteristics/data.py", "w") as fp: fp.write( textwrap.dedent(""" # AUTOGENERATED, DO NOT EDIT characteristics = """).strip()) fp.write(" " + json.dumps(characteristics, indent=4)) fp.write("\n") from aiohomekit.model.services import ServicesTypes for serv in data.get('Services', []): name = serv['Name'].replace(" ", "_").upper() short = ServicesTypes.get_short_uuid(serv['UUID']) print(f'{name} = "{short}"') services = {} for serv in data.get('Services', []): name = serv['Name'].replace(" ", "_").upper() s = services[serv['UUID']] = { 'name': name, 'description': serv['Name'], 'required': serv.get("RequiredCharacteristics", []), 'optional': serv.get("OptionalCharacteristics", []), } with open("aiohomekit/model/services/data.py", "w") as fp:
def test_get_short(): assert ( ServicesTypes.get_short("00000086-0000-1000-8000-0026BB765291") == "occupancy" )