From 004ff8fb30b0dbf22abab9dbcd94304954431e27 Mon Sep 17 00:00:00 2001 From: Christopher Bailey Date: Wed, 8 Jun 2022 20:13:56 -0400 Subject: [PATCH] Overhaul UniFi Protect NVR Disk sensors (#73197) * Overhauls NVR Disk sensors * Updates from latest version of pyunifiprotect --- .../components/unifiprotect/binary_sensor.py | 47 ++++-- .../unifiprotect/fixtures/sample_nvr.json | 152 ++++++++++++++++++ .../unifiprotect/test_binary_sensor.py | 10 +- 3 files changed, 187 insertions(+), 22 deletions(-) diff --git a/homeassistant/components/unifiprotect/binary_sensor.py b/homeassistant/components/unifiprotect/binary_sensor.py index 7613ffb8ebf5..34c1119eb608 100644 --- a/homeassistant/components/unifiprotect/binary_sensor.py +++ b/homeassistant/components/unifiprotect/binary_sensor.py @@ -6,6 +6,7 @@ from dataclasses import dataclass import logging from pyunifiprotect.data import NVR, Camera, Event, Light, MountType, Sensor +from pyunifiprotect.data.nvr import UOSDisk from homeassistant.components.binary_sensor import ( BinarySensorDeviceClass, @@ -13,7 +14,6 @@ from homeassistant.components.binary_sensor import ( BinarySensorEntityDescription, ) from homeassistant.config_entries import ConfigEntry -from homeassistant.const import ATTR_MODEL from homeassistant.core import HomeAssistant, callback from homeassistant.helpers.entity import EntityCategory from homeassistant.helpers.entity_platform import AddEntitiesCallback @@ -131,7 +131,6 @@ DOORLOCK_SENSORS: tuple[ProtectBinaryEntityDescription, ...] = ( DISK_SENSORS: tuple[ProtectBinaryEntityDescription, ...] = ( ProtectBinaryEntityDescription( key="disk_health", - name="Disk {index} Health", device_class=BinarySensorDeviceClass.PROBLEM, entity_category=EntityCategory.DIAGNOSTIC, ), @@ -182,14 +181,18 @@ def _async_nvr_entities( ) -> list[ProtectDeviceEntity]: entities: list[ProtectDeviceEntity] = [] device = data.api.bootstrap.nvr - for index, _ in enumerate(device.system_info.storage.devices): + if device.system_info.ustorage is None: + return entities + + for disk in device.system_info.ustorage.disks: for description in DISK_SENSORS: - entities.append( - ProtectDiskBinarySensor(data, device, description, index=index) - ) + if not disk.has_disk: + continue + + entities.append(ProtectDiskBinarySensor(data, device, description, disk)) _LOGGER.debug( "Adding binary sensor entity %s", - (description.name or "{index}").format(index=index), + f"{disk.type} {disk.slot}", ) return entities @@ -216,6 +219,7 @@ class ProtectDeviceBinarySensor(ProtectDeviceEntity, BinarySensorEntity): class ProtectDiskBinarySensor(ProtectNVREntity, BinarySensorEntity): """A UniFi Protect NVR Disk Binary Sensor.""" + _disk: UOSDisk entity_description: ProtectBinaryEntityDescription def __init__( @@ -223,26 +227,35 @@ class ProtectDiskBinarySensor(ProtectNVREntity, BinarySensorEntity): data: ProtectData, device: NVR, description: ProtectBinaryEntityDescription, - index: int, + disk: UOSDisk, ) -> None: """Initialize the Binary Sensor.""" + self._disk = disk + # backwards compat with old unique IDs + index = self._disk.slot - 1 + description = copy(description) description.key = f"{description.key}_{index}" - description.name = (description.name or "{index}").format(index=index) - self._index = index + description.name = f"{disk.type} {disk.slot}" super().__init__(data, device, description) @callback def _async_update_device_from_protect(self) -> None: super()._async_update_device_from_protect() - disks = self.device.system_info.storage.devices - disk_available = len(disks) > self._index - self._attr_available = self._attr_available and disk_available - if disk_available: - disk = disks[self._index] - self._attr_is_on = not disk.healthy - self._attr_extra_state_attributes = {ATTR_MODEL: disk.model} + slot = self._disk.slot + self._attr_available = False + + if self.device.system_info.ustorage is None: + return + + for disk in self.device.system_info.ustorage.disks: + if disk.slot == slot: + self._disk = disk + self._attr_available = True + break + + self._attr_is_on = not self._disk.is_healthy class ProtectEventBinarySensor(EventThumbnailMixin, ProtectDeviceBinarySensor): diff --git a/tests/components/unifiprotect/fixtures/sample_nvr.json b/tests/components/unifiprotect/fixtures/sample_nvr.json index 728f92c3e32c..507e75fec09d 100644 --- a/tests/components/unifiprotect/fixtures/sample_nvr.json +++ b/tests/components/unifiprotect/fixtures/sample_nvr.json @@ -117,6 +117,158 @@ } ] }, + "ustorage": { + "disks": [ + { + "slot": 1, + "type": "HDD", + "model": "ST16000VE000-2L2103", + "serial": "ABCD1234", + "firmware": "EV02", + "rpm": 7200, + "ata": "ACS-4", + "sata": "SATA 3.3", + "action": "expanding", + "healthy": "good", + "state": "expanding", + "reason": null, + "temperature": 52, + "poweronhrs": 4242, + "life_span": null, + "bad_sector": 0, + "threshold": 10, + "progress": 21.390607518939174, + "estimate": 234395.73300748435 + }, + { + "slot": 2, + "type": "HDD", + "model": "ST16000VE000-2L2103", + "serial": "ABCD1234", + "firmware": "EV02", + "rpm": 7200, + "ata": "ACS-4", + "sata": "SATA 3.3", + "action": "expanding", + "healthy": "good", + "state": "expanding", + "reason": null, + "temperature": 52, + "poweronhrs": 4242, + "life_span": null, + "bad_sector": 0, + "threshold": 10, + "progress": 21.390607518939174, + "estimate": 234395.73300748435 + }, + { + "slot": 3, + "type": "HDD", + "model": "ST16000VE000-2L2103", + "serial": "ABCD1234", + "firmware": "EV02", + "rpm": 7200, + "ata": "ACS-4", + "sata": "SATA 3.3", + "action": "expanding", + "healthy": "good", + "state": "expanding", + "reason": null, + "temperature": 51, + "poweronhrs": 4242, + "life_span": null, + "bad_sector": 0, + "threshold": 10, + "progress": 21.390607518939174, + "estimate": 234395.73300748435 + }, + { + "slot": 4, + "type": "HDD", + "model": "ST16000VE000-2L2103", + "serial": "ABCD1234", + "firmware": "EV02", + "rpm": 7200, + "ata": "ACS-4", + "sata": "SATA 3.3", + "action": "expanding", + "healthy": "good", + "state": "expanding", + "reason": null, + "temperature": 50, + "poweronhrs": 2443, + "life_span": null, + "bad_sector": 0, + "threshold": 10, + "progress": 21.390607518939174, + "estimate": 234395.73300748435 + }, + { + "slot": 5, + "type": "HDD", + "model": "ST16000VE000-2L2103", + "serial": "ABCD1234", + "firmware": "EV02", + "rpm": 7200, + "ata": "ACS-4", + "sata": "SATA 3.3", + "action": "expanding", + "healthy": "good", + "state": "expanding", + "reason": null, + "temperature": 50, + "poweronhrs": 783, + "life_span": null, + "bad_sector": 0, + "threshold": 10, + "progress": 21.390607518939174, + "estimate": 234395.73300748435 + }, + { + "slot": 6, + "state": "nodisk" + }, + { + "slot": 7, + "type": "HDD", + "model": "ST16000VE002-3BR101", + "serial": "ABCD1234", + "firmware": "EV01", + "rpm": 7200, + "ata": "ACS-4", + "sata": "SATA 3.3", + "action": "expanding", + "healthy": "good", + "state": "expanding", + "reason": null, + "temperature": 45, + "poweronhrs": 18, + "life_span": null, + "bad_sector": 0, + "threshold": 10, + "progress": 21.390607518939174, + "estimate": 234395.73300748435 + } + ], + "space": [ + { + "device": "md3", + "total_bytes": 63713403555840, + "used_bytes": 57006577086464, + "action": "expanding", + "progress": 21.390607518939174, + "estimate": 234395.73300748435 + }, + { + "device": "md0", + "total_bytes": 0, + "used_bytes": 0, + "action": "syncing", + "progress": 0, + "estimate": null + } + ] + }, "tmpfs": { "available": 934204, "total": 1048576, diff --git a/tests/components/unifiprotect/test_binary_sensor.py b/tests/components/unifiprotect/test_binary_sensor.py index 88b42d36994d..834f8634ee14 100644 --- a/tests/components/unifiprotect/test_binary_sensor.py +++ b/tests/components/unifiprotect/test_binary_sensor.py @@ -71,7 +71,7 @@ async def camera_fixture( await hass.config_entries.async_setup(mock_entry.entry.entry_id) await hass.async_block_till_done() - assert_entity_counts(hass, Platform.BINARY_SENSOR, 3, 3) + assert_entity_counts(hass, Platform.BINARY_SENSOR, 9, 9) yield camera_obj @@ -103,7 +103,7 @@ async def light_fixture( await hass.config_entries.async_setup(mock_entry.entry.entry_id) await hass.async_block_till_done() - assert_entity_counts(hass, Platform.BINARY_SENSOR, 2, 2) + assert_entity_counts(hass, Platform.BINARY_SENSOR, 8, 8) yield light_obj @@ -138,7 +138,7 @@ async def camera_none_fixture( await hass.config_entries.async_setup(mock_entry.entry.entry_id) await hass.async_block_till_done() - assert_entity_counts(hass, Platform.BINARY_SENSOR, 2, 2) + assert_entity_counts(hass, Platform.BINARY_SENSOR, 8, 8) yield camera_obj @@ -179,7 +179,7 @@ async def sensor_fixture( await hass.config_entries.async_setup(mock_entry.entry.entry_id) await hass.async_block_till_done() - assert_entity_counts(hass, Platform.BINARY_SENSOR, 4, 4) + assert_entity_counts(hass, Platform.BINARY_SENSOR, 10, 10) yield sensor_obj @@ -215,7 +215,7 @@ async def sensor_none_fixture( await hass.config_entries.async_setup(mock_entry.entry.entry_id) await hass.async_block_till_done() - assert_entity_counts(hass, Platform.BINARY_SENSOR, 4, 4) + assert_entity_counts(hass, Platform.BINARY_SENSOR, 10, 10) yield sensor_obj