From c157a582b85347d9684105e3f4bd46dac20e6142 Mon Sep 17 00:00:00 2001 From: epenet <6771947+epenet@users.noreply.github.com> Date: Sun, 25 Oct 2020 18:05:02 +0100 Subject: [PATCH] Add device_info to onewire sensors (#42309) * Add device info * Cleanup log --- homeassistant/components/onewire/sensor.py | 35 ++++- .../onewire/test_entity_owserver.py | 128 ++++++++++++++++-- .../components/onewire/test_entity_sysbus.py | 57 +++++++- 3 files changed, 193 insertions(+), 27 deletions(-) diff --git a/homeassistant/components/onewire/sensor.py b/homeassistant/components/onewire/sensor.py index 510d8601fbf..31bafdb483a 100644 --- a/homeassistant/components/onewire/sensor.py +++ b/homeassistant/components/onewire/sensor.py @@ -186,10 +186,12 @@ def get_entities(config): for device in devices: _LOGGER.debug("Found device: %s", device) family = owproxy.read(f"{device}family").decode() + device_type = owproxy.read(f"{device}type").decode() + sensor_id = os.path.split(os.path.split(device)[0])[1] dev_type = "std" if "EF" in family: dev_type = "HobbyBoard" - family = owproxy.read(f"{device}type").decode() + family = device_type if family not in hb_info_from_type(dev_type): _LOGGER.warning( @@ -198,6 +200,12 @@ def get_entities(config): device, ) continue + device_info = { + "identifiers": {(DOMAIN, sensor_id)}, + "manufacturer": "Maxim Integrated", + "model": device_type, + "name": sensor_id, + } for sensor_key, sensor_value in hb_info_from_type(dev_type)[family].items(): if "moisture" in sensor_key: s_id = sensor_key.split("_")[1] @@ -206,13 +214,13 @@ def get_entities(config): ) if is_leaf: sensor_key = f"wetness_{s_id}" - sensor_id = os.path.split(os.path.split(device)[0])[1] device_file = os.path.join(os.path.split(device)[0], sensor_value) entities.append( OneWireProxy( device_names.get(sensor_id, sensor_id), device_file, sensor_key, + device_info, owproxy, ) ) @@ -232,12 +240,19 @@ def get_entities(config): ) continue + device_info = { + "identifiers": {(DOMAIN, sensor_id)}, + "manufacturer": "Maxim Integrated", + "model": family, + "name": sensor_id, + } device_file = f"/sys/bus/w1/devices/{sensor_id}/w1_slave" entities.append( OneWireDirect( device_names.get(sensor_id, sensor_id), device_file, "temperature", + device_info, p1sensor, ) ) @@ -286,12 +301,13 @@ def get_entities(config): class OneWire(Entity): """Implementation of a 1-Wire sensor.""" - def __init__(self, name, device_file, sensor_type): + def __init__(self, name, device_file, sensor_type, device_info=None): """Initialize the sensor.""" self._name = f"{name} {sensor_type.capitalize()}" self._device_file = device_file self._device_class = SENSOR_TYPES[sensor_type][2] self._unit_of_measurement = SENSOR_TYPES[sensor_type][1] + self._device_info = device_info self._state = None self._value_raw = None @@ -327,13 +343,18 @@ class OneWire(Entity): """Return a unique ID.""" return self._device_file + @property + def device_info(self) -> Optional[Dict[str, Any]]: + """Return device specific attributes.""" + return self._device_info + class OneWireProxy(OneWire): """Implementation of a 1-Wire sensor through owserver.""" - def __init__(self, name, device_file, sensor_type, owproxy): + def __init__(self, name, device_file, sensor_type, device_info, owproxy): """Initialize the sensor.""" - super().__init__(name, device_file, sensor_type) + super().__init__(name, device_file, sensor_type, device_info) self._owproxy = owproxy def _read_value_ownet(self): @@ -358,9 +379,9 @@ class OneWireProxy(OneWire): class OneWireDirect(OneWire): """Implementation of a 1-Wire sensor directly connected to RPI GPIO.""" - def __init__(self, name, device_file, sensor_type, owsensor): + def __init__(self, name, device_file, sensor_type, device_info, owsensor): """Initialize the sensor.""" - super().__init__(name, device_file, sensor_type) + super().__init__(name, device_file, sensor_type, device_info) self._owsensor = owsensor def update(self): diff --git a/tests/components/onewire/test_entity_owserver.py b/tests/components/onewire/test_entity_owserver.py index 517bf81ca18..26668e105ca 100644 --- a/tests/components/onewire/test_entity_owserver.py +++ b/tests/components/onewire/test_entity_owserver.py @@ -25,7 +25,7 @@ from homeassistant.const import ( from homeassistant.setup import async_setup_component from tests.async_mock import patch -from tests.common import mock_registry +from tests.common import mock_device_registry, mock_registry MOCK_CONFIG = { SENSOR_DOMAIN: { @@ -39,8 +39,22 @@ MOCK_CONFIG = { } MOCK_DEVICE_SENSORS = { - "00.111111111111": {"sensors": []}, + "00.111111111111": { + "inject_reads": [ + b"", # read device type + ], + "sensors": [], + }, "10.111111111111": { + "inject_reads": [ + b"DS18S20", # read device type + ], + "device_info": { + "identifiers": {(DOMAIN, "10.111111111111")}, + "manufacturer": "Maxim Integrated", + "model": "DS18S20", + "name": "10.111111111111", + }, "sensors": [ { "entity_id": "sensor.my_ds18b20_temperature", @@ -50,9 +64,18 @@ MOCK_DEVICE_SENSORS = { "unit": TEMP_CELSIUS, "class": DEVICE_CLASS_TEMPERATURE, }, - ] + ], }, "12.111111111111": { + "inject_reads": [ + b"DS2406", # read device type + ], + "device_info": { + "identifiers": {(DOMAIN, "12.111111111111")}, + "manufacturer": "Maxim Integrated", + "model": "DS2406", + "name": "12.111111111111", + }, "sensors": [ { "entity_id": "sensor.12_111111111111_temperature", @@ -70,9 +93,18 @@ MOCK_DEVICE_SENSORS = { "unit": PRESSURE_MBAR, "class": DEVICE_CLASS_PRESSURE, }, - ] + ], }, "1D.111111111111": { + "inject_reads": [ + b"DS2423", # read device type + ], + "device_info": { + "identifiers": {(DOMAIN, "1D.111111111111")}, + "manufacturer": "Maxim Integrated", + "model": "DS2423", + "name": "1D.111111111111", + }, "sensors": [ { "entity_id": "sensor.1d_111111111111_counter_a", @@ -90,9 +122,18 @@ MOCK_DEVICE_SENSORS = { "unit": "count", "class": None, }, - ] + ], }, "22.111111111111": { + "inject_reads": [ + b"DS1822", # read device type + ], + "device_info": { + "identifiers": {(DOMAIN, "22.111111111111")}, + "manufacturer": "Maxim Integrated", + "model": "DS1822", + "name": "22.111111111111", + }, "sensors": [ { "entity_id": "sensor.22_111111111111_temperature", @@ -102,9 +143,18 @@ MOCK_DEVICE_SENSORS = { "unit": TEMP_CELSIUS, "class": DEVICE_CLASS_TEMPERATURE, }, - ] + ], }, "26.111111111111": { + "inject_reads": [ + b"DS2438", # read device type + ], + "device_info": { + "identifiers": {(DOMAIN, "26.111111111111")}, + "manufacturer": "Maxim Integrated", + "model": "DS2438", + "name": "26.111111111111", + }, "sensors": [ { "entity_id": "sensor.26_111111111111_temperature", @@ -194,9 +244,18 @@ MOCK_DEVICE_SENSORS = { "unit": ELECTRICAL_CURRENT_AMPERE, "class": DEVICE_CLASS_CURRENT, }, - ] + ], }, "28.111111111111": { + "inject_reads": [ + b"DS18B20", # read device type + ], + "device_info": { + "identifiers": {(DOMAIN, "28.111111111111")}, + "manufacturer": "Maxim Integrated", + "model": "DS18B20", + "name": "28.111111111111", + }, "sensors": [ { "entity_id": "sensor.28_111111111111_temperature", @@ -206,9 +265,18 @@ MOCK_DEVICE_SENSORS = { "unit": TEMP_CELSIUS, "class": DEVICE_CLASS_TEMPERATURE, }, - ] + ], }, "3B.111111111111": { + "inject_reads": [ + b"DS1825", # read device type + ], + "device_info": { + "identifiers": {(DOMAIN, "3B.111111111111")}, + "manufacturer": "Maxim Integrated", + "model": "DS1825", + "name": "3B.111111111111", + }, "sensors": [ { "entity_id": "sensor.3b_111111111111_temperature", @@ -218,9 +286,18 @@ MOCK_DEVICE_SENSORS = { "unit": TEMP_CELSIUS, "class": DEVICE_CLASS_TEMPERATURE, }, - ] + ], }, "42.111111111111": { + "inject_reads": [ + b"DS28EA00", # read device type + ], + "device_info": { + "identifiers": {(DOMAIN, "42.111111111111")}, + "manufacturer": "Maxim Integrated", + "model": "DS28EA00", + "name": "42.111111111111", + }, "sensors": [ { "entity_id": "sensor.42_111111111111_temperature", @@ -230,12 +307,18 @@ MOCK_DEVICE_SENSORS = { "unit": TEMP_CELSIUS, "class": DEVICE_CLASS_TEMPERATURE, }, - ] + ], }, "EF.111111111111": { "inject_reads": [ b"HobbyBoards_EF", # read type ], + "device_info": { + "identifiers": {(DOMAIN, "EF.111111111111")}, + "manufacturer": "Maxim Integrated", + "model": "HobbyBoards_EF", + "name": "EF.111111111111", + }, "sensors": [ { "entity_id": "sensor.ef_111111111111_humidity", @@ -271,6 +354,12 @@ MOCK_DEVICE_SENSORS = { b" 0", # read is_leaf_2 b" 0", # read is_leaf_3 ], + "device_info": { + "identifiers": {(DOMAIN, "EF.111111111112")}, + "manufacturer": "Maxim Integrated", + "model": "HB_MOISTURE_METER", + "name": "EF.111111111112", + }, "sensors": [ { "entity_id": "sensor.ef_111111111112_wetness_0", @@ -313,13 +402,16 @@ MOCK_DEVICE_SENSORS = { async def test_owserver_setup_valid_device(hass, device_id): """Test for 1-Wire device.""" entity_registry = mock_registry(hass) + device_registry = mock_device_registry(hass) + + mock_device_sensor = MOCK_DEVICE_SENSORS[device_id] dir_return_value = [f"/{device_id}/"] read_side_effect = [device_id[0:2].encode()] - if "inject_reads" in MOCK_DEVICE_SENSORS[device_id]: - read_side_effect += MOCK_DEVICE_SENSORS[device_id]["inject_reads"] + if "inject_reads" in mock_device_sensor: + read_side_effect += mock_device_sensor["inject_reads"] - expected_sensors = MOCK_DEVICE_SENSORS[device_id]["sensors"] + expected_sensors = mock_device_sensor["sensors"] for expected_sensor in expected_sensors: read_side_effect.append(expected_sensor["injected_value"]) @@ -335,6 +427,16 @@ async def test_owserver_setup_valid_device(hass, device_id): assert len(entity_registry.entities) == len(expected_sensors) + if len(expected_sensors) > 0: + device_info = mock_device_sensor["device_info"] + assert len(device_registry.devices) == 1 + registry_entry = device_registry.async_get_device({(DOMAIN, device_id)}, set()) + assert registry_entry is not None + assert registry_entry.identifiers == {(DOMAIN, device_id)} + assert registry_entry.manufacturer == device_info["manufacturer"] + assert registry_entry.name == device_info["name"] + assert registry_entry.model == device_info["model"] + for expected_sensor in expected_sensors: entity_id = expected_sensor["entity_id"] registry_entry = entity_registry.entities.get(entity_id) diff --git a/tests/components/onewire/test_entity_sysbus.py b/tests/components/onewire/test_entity_sysbus.py index dc4673e42a7..f1fcc90e96e 100644 --- a/tests/components/onewire/test_entity_sysbus.py +++ b/tests/components/onewire/test_entity_sysbus.py @@ -8,7 +8,7 @@ from homeassistant.const import DEVICE_CLASS_TEMPERATURE, TEMP_CELSIUS from homeassistant.setup import async_setup_component from tests.async_mock import patch -from tests.common import mock_registry +from tests.common import mock_device_registry, mock_registry MOCK_CONFIG = { SENSOR_DOMAIN: { @@ -23,6 +23,12 @@ MOCK_CONFIG = { MOCK_DEVICE_SENSORS = { "00-111111111111": {"sensors": []}, "10-111111111111": { + "device_info": { + "identifiers": {(DOMAIN, "10-111111111111")}, + "manufacturer": "Maxim Integrated", + "model": "10", + "name": "10-111111111111", + }, "sensors": [ { "entity_id": "sensor.my_ds18b20_temperature", @@ -32,11 +38,17 @@ MOCK_DEVICE_SENSORS = { "unit": TEMP_CELSIUS, "class": DEVICE_CLASS_TEMPERATURE, }, - ] + ], }, "12-111111111111": {"sensors": []}, "1D-111111111111": {"sensors": []}, "22-111111111111": { + "device_info": { + "identifiers": {(DOMAIN, "22-111111111111")}, + "manufacturer": "Maxim Integrated", + "model": "22", + "name": "22-111111111111", + }, "sensors": [ { "entity_id": "sensor.22_111111111111_temperature", @@ -46,10 +58,16 @@ MOCK_DEVICE_SENSORS = { "unit": TEMP_CELSIUS, "class": DEVICE_CLASS_TEMPERATURE, }, - ] + ], }, "26-111111111111": {"sensors": []}, "28-111111111111": { + "device_info": { + "identifiers": {(DOMAIN, "28-111111111111")}, + "manufacturer": "Maxim Integrated", + "model": "28", + "name": "28-111111111111", + }, "sensors": [ { "entity_id": "sensor.28_111111111111_temperature", @@ -59,9 +77,15 @@ MOCK_DEVICE_SENSORS = { "unit": TEMP_CELSIUS, "class": DEVICE_CLASS_TEMPERATURE, }, - ] + ], }, "3B-111111111111": { + "device_info": { + "identifiers": {(DOMAIN, "3B-111111111111")}, + "manufacturer": "Maxim Integrated", + "model": "3B", + "name": "3B-111111111111", + }, "sensors": [ { "entity_id": "sensor.3b_111111111111_temperature", @@ -71,9 +95,15 @@ MOCK_DEVICE_SENSORS = { "unit": TEMP_CELSIUS, "class": DEVICE_CLASS_TEMPERATURE, }, - ] + ], }, "42-111111111111": { + "device_info": { + "identifiers": {(DOMAIN, "42-111111111111")}, + "manufacturer": "Maxim Integrated", + "model": "42", + "name": "42-111111111111", + }, "sensors": [ { "entity_id": "sensor.42_111111111111_temperature", @@ -83,7 +113,7 @@ MOCK_DEVICE_SENSORS = { "unit": TEMP_CELSIUS, "class": DEVICE_CLASS_TEMPERATURE, }, - ] + ], }, "EF-111111111111": { "sensors": [], @@ -98,10 +128,13 @@ MOCK_DEVICE_SENSORS = { async def test_onewiredirect_setup_valid_device(hass, device_id): """Test that sysbus config entry works correctly.""" entity_registry = mock_registry(hass) + device_registry = mock_device_registry(hass) + + mock_device_sensor = MOCK_DEVICE_SENSORS[device_id] glob_result = [f"/{DEFAULT_SYSBUS_MOUNT_DIR}/{device_id}"] read_side_effect = [] - expected_sensors = MOCK_DEVICE_SENSORS[device_id]["sensors"] + expected_sensors = mock_device_sensor["sensors"] for expected_sensor in expected_sensors: read_side_effect.append(expected_sensor["injected_value"]) @@ -119,6 +152,16 @@ async def test_onewiredirect_setup_valid_device(hass, device_id): assert len(entity_registry.entities) == len(expected_sensors) + if len(expected_sensors) > 0: + device_info = mock_device_sensor["device_info"] + assert len(device_registry.devices) == 1 + registry_entry = device_registry.async_get_device({(DOMAIN, device_id)}, set()) + assert registry_entry is not None + assert registry_entry.identifiers == {(DOMAIN, device_id)} + assert registry_entry.manufacturer == device_info["manufacturer"] + assert registry_entry.name == device_info["name"] + assert registry_entry.model == device_info["model"] + for expected_sensor in expected_sensors: entity_id = expected_sensor["entity_id"] registry_entry = entity_registry.entities.get(entity_id)