diff --git a/custom_components/vivint/__init__.py b/custom_components/vivint/__init__.py index 2394f8d..2f998e6 100644 --- a/custom_components/vivint/__init__.py +++ b/custom_components/vivint/__init__.py @@ -36,6 +36,7 @@ Platform.LOCK, Platform.SENSOR, Platform.SWITCH, + Platform.UPDATE, ] ATTR_TYPE = "type" diff --git a/custom_components/vivint/hub.py b/custom_components/vivint/hub.py index 960ee49..25b3810 100644 --- a/custom_components/vivint/hub.py +++ b/custom_components/vivint/hub.py @@ -160,9 +160,8 @@ def __init__( self.hub = hub self.entity_description = entity_description - self._attr_unique_id = ( - f"{device.alarm_panel.id}-{device.id}-{entity_description.key}" - ) + prefix = f"{device.alarm_panel.id}-" if device.alarm_panel else "" + self._attr_unique_id = f"{prefix}{device.id}-{entity_description.key}" device = self.device.parent if self.device.is_subdevice else self.device self._attr_device_info = DeviceInfo( identifiers={get_device_id(device)}, diff --git a/custom_components/vivint/manifest.json b/custom_components/vivint/manifest.json index 33919c0..be2a0c6 100644 --- a/custom_components/vivint/manifest.json +++ b/custom_components/vivint/manifest.json @@ -9,7 +9,7 @@ "iot_class": "cloud_push", "issue_tracker": "https://github.com/natekspencer/hacs-vivint/issues", "loggers": ["custom_components.vivint", "vivintpy"], - "requirements": ["vivintpy==2023.3.5"], - "version": "2023.8.0", + "requirements": ["vivintpy==2023.3.6"], + "version": "2023.8.1", "zeroconf": ["_vivint-ODC300._tcp.local.", "_vivint-DBC350._tcp.local."] } diff --git a/custom_components/vivint/sensor.py b/custom_components/vivint/sensor.py index a11f64d..3654f95 100644 --- a/custom_components/vivint/sensor.py +++ b/custom_components/vivint/sensor.py @@ -5,6 +5,7 @@ DOMAIN as SENSOR_DOMAIN, SensorDeviceClass, SensorEntity, + SensorStateClass, ) from homeassistant.config_entries import ConfigEntry from homeassistant.const import PERCENTAGE @@ -68,6 +69,7 @@ class VivintBatterySensorEntity(VivintEntity, SensorEntity): _attr_device_class = SensorDeviceClass.BATTERY _attr_entity_category = EntityCategory.DIAGNOSTIC _attr_native_unit_of_measurement = PERCENTAGE + _attr_state_class = SensorStateClass.MEASUREMENT @property def name(self) -> str: diff --git a/custom_components/vivint/update.py b/custom_components/vivint/update.py new file mode 100644 index 0000000..ebbd1ba --- /dev/null +++ b/custom_components/vivint/update.py @@ -0,0 +1,96 @@ +"""Support for Vivint updates.""" +from __future__ import annotations + +from datetime import timedelta +from typing import Any + +from vivintpy.devices.alarm_panel import AlarmPanel +from vivintpy.entity import UPDATE + +from homeassistant.components.update import ( + UpdateDeviceClass, + UpdateEntity, + UpdateEntityDescription, + UpdateEntityFeature as Feature, +) +from homeassistant.config_entries import ConfigEntry +from homeassistant.core import HomeAssistant +from homeassistant.exceptions import HomeAssistantError +from homeassistant.helpers.entity_platform import AddEntitiesCallback + +from .const import DOMAIN +from .hub import VivintBaseEntity, VivintHub + +SCAN_INTERVAL = timedelta(days=1) + +FIRMWARE_UPDATE_ENTITY = UpdateEntityDescription( + key="firmware", name="Firmware", device_class=UpdateDeviceClass.FIRMWARE +) + + +async def async_setup_entry( + hass: HomeAssistant, + entry: ConfigEntry, + async_add_entities: AddEntitiesCallback, +) -> None: + """Set up Litter-Robot update platform.""" + hub: VivintHub = hass.data[DOMAIN][entry.entry_id] + entities = [ + VivintUpdateEntity( + device=alarm_panel, hub=hub, entity_description=FIRMWARE_UPDATE_ENTITY + ) + for system in hub.account.systems + if system.is_admin + for alarm_panel in system.alarm_panels + ] + async_add_entities(entities, True) + + +class VivintUpdateEntity(VivintBaseEntity, UpdateEntity): + """A class that describes device update entities.""" + + device: AlarmPanel + + _attr_supported_features = Feature.INSTALL | Feature.PROGRESS + + @property + def in_progress(self) -> bool: + """Update installation progress.""" + return self.device._AlarmPanel__panel.data["sus"] != "Idle" + + @property + def installed_version(self) -> str: + """Version installed and in use.""" + return self.device.software_version + + @property + def should_poll(self) -> bool: + """Set polling to True.""" + return True + + async def async_update(self) -> None: + """Update the entity.""" + software_update = await self.device.get_software_update_details() + if software_update.get("available"): + latest_version = software_update["available_version"] + else: + latest_version = self.device.software_version + self._attr_latest_version = latest_version + + async def async_install( + self, version: str | None, backup: bool, **kwargs: Any + ) -> None: + """Install an update.""" + if (await self.device.get_software_update_details()).get("available"): + if not await self.device.update_software(): + message = f"Unable to start firmware update on {self.device.name}" + raise HomeAssistantError(message) + + async def async_added_to_hass(self) -> None: + """Set up a listener for the entity.""" + await super().async_added_to_hass() + self.async_on_remove( + self.device._AlarmPanel__panel.on( + UPDATE, lambda _: self.async_write_ha_state() + ) + )