diff --git a/homeassistant/components/homekit_controller/air_quality.py b/homeassistant/components/homekit_controller/air_quality.py index 2a162eb2b2a37..b4ca2f4918af3 100644 --- a/homeassistant/components/homekit_controller/air_quality.py +++ b/homeassistant/components/homekit_controller/air_quality.py @@ -1,4 +1,6 @@ """Support for HomeKit Controller air quality sensors.""" +import logging + from aiohomekit.model.characteristics import CharacteristicsTypes from aiohomekit.model.services import ServicesTypes @@ -7,6 +9,8 @@ from . import KNOWN_DEVICES, HomeKitEntity +_LOGGER = logging.getLogger(__name__) + AIR_QUALITY_TEXT = { 0: "unknown", 1: "excellent", @@ -20,6 +24,20 @@ class HomeAirQualitySensor(HomeKitEntity, AirQualityEntity): """Representation of a HomeKit Controller Air Quality sensor.""" + async def async_added_to_hass(self): + """Call when entity is added to hass.""" + _LOGGER.warning( + "The homekit_controller air_quality entity has been " + "deprecated and will be removed in 2021.12.0" + ) + await super().async_added_to_hass() + + @property + def entity_registry_enabled_default(self) -> bool: + """Whether or not to enable this entity by default.""" + # This entity is deprecated, so don't enable by default + return False + def get_characteristic_types(self): """Define the homekit characteristics the entity cares about.""" return [ diff --git a/homeassistant/components/homekit_controller/sensor.py b/homeassistant/components/homekit_controller/sensor.py index b599e7263c8d1..ac4f19dadb433 100644 --- a/homeassistant/components/homekit_controller/sensor.py +++ b/homeassistant/components/homekit_controller/sensor.py @@ -4,12 +4,19 @@ from homeassistant.components.sensor import STATE_CLASS_MEASUREMENT, SensorEntity from homeassistant.const import ( + CONCENTRATION_MICROGRAMS_PER_CUBIC_METER, CONCENTRATION_PARTS_PER_MILLION, + DEVICE_CLASS_AQI, DEVICE_CLASS_BATTERY, DEVICE_CLASS_HUMIDITY, DEVICE_CLASS_ILLUMINANCE, + DEVICE_CLASS_NITROGEN_DIOXIDE, + DEVICE_CLASS_OZONE, + DEVICE_CLASS_PM10, + DEVICE_CLASS_PM25, DEVICE_CLASS_POWER, DEVICE_CLASS_PRESSURE, + DEVICE_CLASS_SULPHUR_DIOXIDE, DEVICE_CLASS_TEMPERATURE, LIGHT_LUX, PERCENTAGE, @@ -52,7 +59,7 @@ "state_class": STATE_CLASS_MEASUREMENT, "unit": PRESSURE_HPA, }, - CharacteristicsTypes.get_uuid(CharacteristicsTypes.TEMPERATURE_CURRENT): { + CharacteristicsTypes.TEMPERATURE_CURRENT: { "name": "Current Temperature", "device_class": DEVICE_CLASS_TEMPERATURE, "state_class": STATE_CLASS_MEASUREMENT, @@ -62,7 +69,7 @@ "probe": lambda char: char.service.type != ServicesTypes.get_uuid(ServicesTypes.TEMPERATURE_SENSOR), }, - CharacteristicsTypes.get_uuid(CharacteristicsTypes.RELATIVE_HUMIDITY_CURRENT): { + CharacteristicsTypes.RELATIVE_HUMIDITY_CURRENT: { "name": "Current Humidity", "device_class": DEVICE_CLASS_HUMIDITY, "state_class": STATE_CLASS_MEASUREMENT, @@ -72,8 +79,52 @@ "probe": lambda char: char.service.type != ServicesTypes.get_uuid(ServicesTypes.HUMIDITY_SENSOR), }, + CharacteristicsTypes.AIR_QUALITY: { + "name": "Air Quality", + "device_class": DEVICE_CLASS_AQI, + "state_class": STATE_CLASS_MEASUREMENT, + }, + CharacteristicsTypes.DENSITY_PM25: { + "name": "PM2.5 Density", + "device_class": DEVICE_CLASS_PM25, + "state_class": STATE_CLASS_MEASUREMENT, + "unit": CONCENTRATION_MICROGRAMS_PER_CUBIC_METER, + }, + CharacteristicsTypes.DENSITY_PM10: { + "name": "PM10 Density", + "device_class": DEVICE_CLASS_PM10, + "state_class": STATE_CLASS_MEASUREMENT, + "unit": CONCENTRATION_MICROGRAMS_PER_CUBIC_METER, + }, + CharacteristicsTypes.DENSITY_OZONE: { + "name": "Ozone Density", + "device_class": DEVICE_CLASS_OZONE, + "state_class": STATE_CLASS_MEASUREMENT, + "unit": CONCENTRATION_MICROGRAMS_PER_CUBIC_METER, + }, + CharacteristicsTypes.DENSITY_NO2: { + "name": "Nitrogen Dioxide Density", + "device_class": DEVICE_CLASS_NITROGEN_DIOXIDE, + "state_class": STATE_CLASS_MEASUREMENT, + "unit": CONCENTRATION_MICROGRAMS_PER_CUBIC_METER, + }, + CharacteristicsTypes.DENSITY_SO2: { + "name": "Sulphur Dioxide Density", + "device_class": DEVICE_CLASS_SULPHUR_DIOXIDE, + "state_class": STATE_CLASS_MEASUREMENT, + "unit": CONCENTRATION_MICROGRAMS_PER_CUBIC_METER, + }, } +# For legacy reasons, "built-in" characteristic types are in their short form +# And vendor types don't have a short form +# This means long and short forms get mixed up in this dict, and comparisons +# don't work! +# We call get_uuid on *every* type to normalise them to the long form +# Eventually aiohomekit will use the long form exclusively amd this can be removed. +for k, v in list(SIMPLE_SENSOR.items()): + SIMPLE_SENSOR[CharacteristicsTypes.get_uuid(k)] = SIMPLE_SENSOR.pop(k) + class HomeKitHumiditySensor(HomeKitEntity, SensorEntity): """Representation of a Homekit humidity sensor.""" diff --git a/tests/components/homekit_controller/specific_devices/test_arlo_baby.py b/tests/components/homekit_controller/specific_devices/test_arlo_baby.py new file mode 100644 index 0000000000000..86fb9f65f11e7 --- /dev/null +++ b/tests/components/homekit_controller/specific_devices/test_arlo_baby.py @@ -0,0 +1,84 @@ +"""Make sure that an Arlo Baby can be setup.""" + +from homeassistant.helpers import device_registry as dr, entity_registry as er + +from tests.components.homekit_controller.common import ( + Helper, + setup_accessories_from_file, + setup_test_accessories, +) + + +async def test_arlo_baby_setup(hass): + """Test that an Arlo Baby can be correctly setup in HA.""" + accessories = await setup_accessories_from_file(hass, "arlo_baby.json") + config_entry, pairing = await setup_test_accessories(hass, accessories) + + entity_registry = er.async_get(hass) + device_registry = dr.async_get(hass) + + sensors = [ + ( + "camera.arlobabya0", + "homekit-00A0000000000-aid:1", + "ArloBabyA0", + ), + ( + "binary_sensor.arlobabya0", + "homekit-00A0000000000-500", + "ArloBabyA0", + ), + ( + "sensor.arlobabya0_battery", + "homekit-00A0000000000-700", + "ArloBabyA0 Battery", + ), + ( + "sensor.arlobabya0_humidity", + "homekit-00A0000000000-900", + "ArloBabyA0 Humidity", + ), + ( + "sensor.arlobabya0_temperature", + "homekit-00A0000000000-1000", + "ArloBabyA0 Temperature", + ), + ( + "sensor.arlobabya0_air_quality", + "homekit-00A0000000000-aid:1-sid:800-cid:802", + "ArloBabyA0 - Air Quality", + ), + ( + "light.arlobabya0", + "homekit-00A0000000000-1100", + "ArloBabyA0", + ), + ] + + device_ids = set() + + for (entity_id, unique_id, friendly_name) in sensors: + entry = entity_registry.async_get(entity_id) + assert entry.unique_id == unique_id + + helper = Helper( + hass, + entity_id, + pairing, + accessories[0], + config_entry, + ) + state = await helper.poll_and_get_state() + assert state.attributes["friendly_name"] == friendly_name + + device = device_registry.async_get(entry.device_id) + assert device.manufacturer == "Netgear, Inc" + assert device.name == "ArloBabyA0" + assert device.model == "ABC1000" + assert device.sw_version == "1.10.931" + assert device.via_device_id is None + + device_ids.add(entry.device_id) + + # All entities should be part of same device + assert len(device_ids) == 1 diff --git a/tests/components/homekit_controller/test_air_quality.py b/tests/components/homekit_controller/test_air_quality.py index 52c79f2b28a86..f75335ca357bc 100644 --- a/tests/components/homekit_controller/test_air_quality.py +++ b/tests/components/homekit_controller/test_air_quality.py @@ -2,6 +2,8 @@ from aiohomekit.model.characteristics import CharacteristicsTypes from aiohomekit.model.services import ServicesTypes +from homeassistant.helpers import entity_registry as er + from tests.components.homekit_controller.common import setup_test_component @@ -35,6 +37,12 @@ async def test_air_quality_sensor_read_state(hass, utcnow): """Test reading the state of a HomeKit temperature sensor accessory.""" helper = await setup_test_component(hass, create_air_quality_sensor_service) + entity_registry = er.async_get(hass) + entity_registry.async_update_entity( + entity_id="air_quality.testdevice", disabled_by=None + ) + await hass.async_block_till_done() + state = await helper.poll_and_get_state() assert state.state == "4444" diff --git a/tests/fixtures/homekit_controller/arlo_baby.json b/tests/fixtures/homekit_controller/arlo_baby.json new file mode 100644 index 0000000000000..6a124a5f56f64 --- /dev/null +++ b/tests/fixtures/homekit_controller/arlo_baby.json @@ -0,0 +1,484 @@ +[ + { + "aid": 1, + "services": [ + { + "type": "0000003E-0000-1000-8000-0026BB765291", + "iid": 1, + "characteristics": [ + { + "type": "00000023-0000-1000-8000-0026BB765291", + "iid": 2, + "value": "ArloBabyA0", + "perms": [ + "pr" + ], + "format": "string" + }, + { + "type": "00000020-0000-1000-8000-0026BB765291", + "iid": 3, + "value": "Netgear, Inc", + "perms": [ + "pr" + ], + "format": "string" + }, + { + "type": "00000030-0000-1000-8000-0026BB765291", + "iid": 4, + "value": "00A0000000000", + "perms": [ + "pr" + ], + "format": "string" + }, + { + "type": "00000021-0000-1000-8000-0026BB765291", + "iid": 5, + "value": "ABC1000", + "perms": [ + "pr" + ], + "format": "string" + }, + { + "type": "00000052-0000-1000-8000-0026BB765291", + "iid": 7, + "value": "1.10.931", + "perms": [ + "pr" + ], + "format": "string" + }, + { + "type": "00000014-0000-1000-8000-0026BB765291", + "iid": 6, + "perms": [ + "pw" + ], + "format": "bool" + } + ] + }, + { + "type": "000000A2-0000-1000-8000-0026BB765291", + "iid": 20, + "characteristics": [ + { + "type": "00000037-0000-1000-8000-0026BB765291", + "iid": 21, + "value": "1.1.0", + "perms": [ + "pr" + ], + "format": "string" + } + ] + }, + { + "type": "00000110-0000-1000-8000-0026BB765291", + "iid": 100, + "characteristics": [ + { + "type": "00000120-0000-1000-8000-0026BB765291", + "iid": 106, + "value": "AQEB", + "perms": [ + "pr", + "ev" + ], + "format": "tlv8" + }, + { + "type": "00000114-0000-1000-8000-0026BB765291", + "iid": 101, + "value": "AY8BAQACFQEBAAEBAQEBAQIBAAMBAAQBAAUBAQMLAQKABwICOAQDAR4DCwECAAUCAsADAwEeAwsBAgAEAgIAAwMBHgMLAQIABQIC0AIDAR4DCwECgAICAmgBAwEeAwsBAuABAgIOAQMBHgMLAQKAAgIC4AEDAR4DCwEC4AECAmgBAwEeAwsBAkABAgLwAAMBHg==", + "perms": [ + "pr" + ], + "format": "tlv8" + }, + { + "type": "00000115-0000-1000-8000-0026BB765291", + "iid": 102, + "value": "AQ4BAQMCCQEBAQIBAAMBAQIBAA==", + "perms": [ + "pr" + ], + "format": "tlv8" + }, + { + "type": "00000116-0000-1000-8000-0026BB765291", + "iid": 103, + "value": "AgEAAgEBAgEC", + "perms": [ + "pr" + ], + "format": "tlv8" + }, + { + "type": "00000117-0000-1000-8000-0026BB765291", + "iid": 104, + "value": "", + "perms": [ + "pr", + "pw" + ], + "format": "tlv8" + }, + { + "type": "00000118-0000-1000-8000-0026BB765291", + "iid": 108, + "value": "", + "perms": [ + "pr", + "pw" + ], + "format": "tlv8" + } + ] + }, + { + "type": "00000110-0000-1000-8000-0026BB765291", + "iid": 110, + "characteristics": [ + { + "type": "00000120-0000-1000-8000-0026BB765291", + "iid": 116, + "value": "AQEA", + "perms": [ + "pr", + "ev" + ], + "format": "tlv8" + }, + { + "type": "00000114-0000-1000-8000-0026BB765291", + "iid": 111, + "value": "AWgBAQACFQEBAAEBAQEBAQIBAAMBAAQBAAUBAQMLAQIABQIC0AIDAR4DCwECgAICAmgBAwEeAwsBAuABAgIOAQMBHgMLAQKAAgIC4AEDAR4DCwEC4AECAmgBAwEeAwsBAkABAgLwAAMBHg==", + "perms": [ + "pr" + ], + "format": "tlv8" + }, + { + "type": "00000115-0000-1000-8000-0026BB765291", + "iid": 112, + "value": "AQ4BAQMCCQEBAQIBAAMBAQIBAA==", + "perms": [ + "pr" + ], + "format": "tlv8" + }, + { + "type": "00000116-0000-1000-8000-0026BB765291", + "iid": 113, + "value": "AgEAAgEBAgEC", + "perms": [ + "pr" + ], + "format": "tlv8" + }, + { + "type": "00000117-0000-1000-8000-0026BB765291", + "iid": 114, + "value": "", + "perms": [ + "pr", + "pw" + ], + "format": "tlv8" + }, + { + "type": "00000118-0000-1000-8000-0026BB765291", + "iid": 118, + "value": "", + "perms": [ + "pr", + "pw" + ], + "format": "tlv8" + } + ] + }, + { + "type": "00000112-0000-1000-8000-0026BB765291", + "iid": 300, + "characteristics": [ + { + "type": "0000011A-0000-1000-8000-0026BB765291", + "iid": 302, + "value": false, + "perms": [ + "pr", + "pw", + "ev" + ], + "format": "bool" + } + ] + }, + { + "type": "00000113-0000-1000-8000-0026BB765291", + "iid": 400, + "characteristics": [ + { + "type": "0000011A-0000-1000-8000-0026BB765291", + "iid": 402, + "value": false, + "perms": [ + "pr", + "pw", + "ev" + ], + "format": "bool" + }, + { + "type": "00000119-0000-1000-8000-0026BB765291", + "iid": 403, + "value": 50, + "perms": [ + "pr", + "pw", + "ev" + ], + "format": "uint8", + "minValue": 0, + "maxValue": 100, + "minStep": 1, + "unit": "percentage" + } + ] + }, + { + "type": "00000085-0000-1000-8000-0026BB765291", + "iid": 500, + "characteristics": [ + { + "type": "00000023-0000-1000-8000-0026BB765291", + "iid": 501, + "value": "Motion", + "perms": [ + "pr" + ], + "format": "string" + }, + { + "type": "00000022-0000-1000-8000-0026BB765291", + "iid": 502, + "value": false, + "perms": [ + "pr", + "ev" + ], + "format": "bool" + } + ] + }, + { + "type": "00000096-0000-1000-8000-0026BB765291", + "iid": 700, + "characteristics": [ + { + "type": "00000068-0000-1000-8000-0026BB765291", + "iid": 701, + "value": 82, + "perms": [ + "pr", + "ev" + ], + "format": "uint8", + "minValue": 0, + "maxValue": 100, + "minStep": 1, + "unit": "percentage" + }, + { + "type": "0000008F-0000-1000-8000-0026BB765291", + "iid": 702, + "value": 0, + "perms": [ + "pr", + "ev" + ], + "format": "uint8", + "minValue": 0, + "maxValue": 2, + "minStep": 1 + }, + { + "type": "00000079-0000-1000-8000-0026BB765291", + "iid": 703, + "value": 0, + "perms": [ + "pr", + "ev" + ], + "format": "uint8", + "minValue": 0, + "maxValue": 2, + "minStep": 1 + } + ] + }, + { + "type": "0000008D-0000-1000-8000-0026BB765291", + "iid": 800, + "characteristics": [ + { + "type": "00000023-0000-1000-8000-0026BB765291", + "iid": 801, + "value": "Air Quality", + "perms": [ + "pr" + ], + "format": "string" + }, + { + "type": "00000095-0000-1000-8000-0026BB765291", + "iid": 802, + "value": 1, + "perms": [ + "pr", + "ev" + ], + "format": "uint8", + "minValue": 0, + "maxValue": 5, + "minStep": 1 + } + ] + }, + { + "type": "00000082-0000-1000-8000-0026BB765291", + "iid": 900, + "characteristics": [ + { + "type": "00000023-0000-1000-8000-0026BB765291", + "iid": 901, + "value": "Humidity", + "perms": [ + "pr" + ], + "format": "string" + }, + { + "type": "00000010-0000-1000-8000-0026BB765291", + "iid": 902, + "value": 60.099998, + "perms": [ + "pr", + "ev" + ], + "format": "float", + "minValue": 0.0, + "maxValue": 100.0, + "minStep": 1.0, + "unit": "percentage" + } + ] + }, + { + "type": "0000008A-0000-1000-8000-0026BB765291", + "iid": 1000, + "characteristics": [ + { + "type": "00000023-0000-1000-8000-0026BB765291", + "iid": 1001, + "value": "Temperature", + "perms": [ + "pr" + ], + "format": "string" + }, + { + "type": "00000011-0000-1000-8000-0026BB765291", + "iid": 1002, + "value": 24.0, + "perms": [ + "pr", + "ev" + ], + "format": "float", + "minValue": 0.0, + "maxValue": 100.0, + "minStep": 0.1, + "unit": "celsius" + } + ] + }, + { + "type": "00000043-0000-1000-8000-0026BB765291", + "iid": 1100, + "characteristics": [ + { + "type": "00000023-0000-1000-8000-0026BB765291", + "iid": 1101, + "value": "Nightlight", + "perms": [ + "pr" + ], + "format": "string" + }, + { + "type": "00000025-0000-1000-8000-0026BB765291", + "iid": 1102, + "value": false, + "perms": [ + "pr", + "pw", + "ev" + ], + "format": "bool" + }, + { + "type": "00000008-0000-1000-8000-0026BB765291", + "iid": 1103, + "value": 100, + "perms": [ + "pr", + "pw", + "ev" + ], + "format": "int", + "minValue": 0, + "maxValue": 100, + "minStep": 1, + "unit": "percentage" + }, + { + "type": "00000013-0000-1000-8000-0026BB765291", + "iid": 1104, + "value": 0.0, + "perms": [ + "pr", + "pw", + "ev" + ], + "format": "float", + "minValue": 0.0, + "maxValue": 360.0, + "minStep": 1.0, + "unit": "arcdegrees" + }, + { + "type": "0000002F-0000-1000-8000-0026BB765291", + "iid": 1105, + "value": 0.0, + "perms": [ + "pr", + "pw", + "ev" + ], + "format": "float", + "minValue": 0.0, + "maxValue": 100.0, + "minStep": 1.0, + "unit": "percentage" + } + ] + } + ] + } +] \ No newline at end of file