generated from ludeeus/integration_blueprint
-
Notifications
You must be signed in to change notification settings - Fork 132
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add an 'update' entity to report new versions of the Frigate backend (#…
…237) * Add update entity * Add update tests * Add update to supported entities * Update testing values * Fixing tests * Fix tests * Use property not attr * use attributes * use attributes * Adjust correct test * Adjust correct test * Set update entity category as system not diagnostic * Revert * Update name to fit all use cases * Make unique_id not break redundancy * Via device * Add constant for tag release url * Inherit from frigate entity * Cleanup tests * Cleanup tests * Update to name attr * Remove unnecessary log error * Update test update entity name * remove name property * Fix inheritence * Use better string templating * Use better string templating * Update unique id * Improve string parsing * Update with bad data is unknown
- Loading branch information
Showing
4 changed files
with
213 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,100 @@ | ||
"""Update platform for frigate.""" | ||
from __future__ import annotations | ||
|
||
import logging | ||
|
||
from homeassistant.components.update import UpdateEntity | ||
from homeassistant.config_entries import ConfigEntry | ||
from homeassistant.const import CONF_URL | ||
from homeassistant.core import HomeAssistant | ||
from homeassistant.helpers.entity import DeviceInfo | ||
from homeassistant.helpers.entity_platform import AddEntitiesCallback | ||
from homeassistant.helpers.update_coordinator import CoordinatorEntity | ||
|
||
from . import ( | ||
FrigateDataUpdateCoordinator, | ||
FrigateEntity, | ||
get_frigate_device_identifier, | ||
get_frigate_entity_unique_id, | ||
) | ||
from .const import ATTR_COORDINATOR, DOMAIN, FRIGATE_RELEASE_TAG_URL, NAME | ||
|
||
_LOGGER: logging.Logger = logging.getLogger(__name__) | ||
|
||
|
||
async def async_setup_entry( | ||
hass: HomeAssistant, entry: ConfigEntry, async_add_entities: AddEntitiesCallback | ||
) -> None: | ||
"""Sensor entry setup.""" | ||
coordinator = hass.data[DOMAIN][entry.entry_id][ATTR_COORDINATOR] | ||
|
||
entities = [] | ||
entities.append(FrigateContainerUpdate(coordinator, entry)) | ||
async_add_entities(entities) | ||
|
||
|
||
class FrigateContainerUpdate(FrigateEntity, UpdateEntity, CoordinatorEntity): # type: ignore[misc] | ||
"""Frigate container update.""" | ||
|
||
_attr_name = "Frigate Server" | ||
|
||
def __init__( | ||
self, | ||
coordinator: FrigateDataUpdateCoordinator, | ||
config_entry: ConfigEntry, | ||
) -> None: | ||
"""Construct a FrigateContainerUpdate.""" | ||
FrigateEntity.__init__(self, config_entry) | ||
CoordinatorEntity.__init__(self, coordinator) | ||
|
||
@property | ||
def unique_id(self) -> str: | ||
"""Return a unique ID to use for this entity.""" | ||
return get_frigate_entity_unique_id( | ||
self._config_entry.entry_id, "update", "frigate_server" | ||
) | ||
|
||
@property | ||
def device_info(self) -> DeviceInfo: | ||
"""Get device information.""" | ||
return { | ||
"identifiers": {get_frigate_device_identifier(self._config_entry)}, | ||
"via_device": get_frigate_device_identifier(self._config_entry), | ||
"name": NAME, | ||
"model": self._get_model(), | ||
"configuration_url": self._config_entry.data.get(CONF_URL), | ||
"manufacturer": NAME, | ||
} | ||
|
||
@property | ||
def installed_version(self) -> str | None: | ||
"""Version currently in use.""" | ||
|
||
version_hash = self.coordinator.data.get("service", {}).get("version") | ||
|
||
if not version_hash: | ||
return None | ||
|
||
version = str(version_hash).split("-")[0] | ||
|
||
return version | ||
|
||
@property | ||
def latest_version(self) -> str | None: | ||
"""Latest version available for install.""" | ||
|
||
version = self.coordinator.data.get("service", {}).get("latest_version") | ||
|
||
if not version or version == "unknown": | ||
return None | ||
|
||
return str(version) | ||
|
||
@property | ||
def release_url(self) -> str | None: | ||
"""URL to the full release notes of the latest version available.""" | ||
|
||
if (version := self.latest_version) is None: | ||
return None | ||
|
||
return f"{FRIGATE_RELEASE_TAG_URL}/v{version}" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,108 @@ | ||
"""Test the frigate updaters.""" | ||
from __future__ import annotations | ||
|
||
import copy | ||
import logging | ||
from typing import Any | ||
from unittest.mock import AsyncMock | ||
|
||
from pytest_homeassistant_custom_component.common import async_fire_time_changed | ||
|
||
from custom_components.frigate import SCAN_INTERVAL | ||
from custom_components.frigate.const import FRIGATE_RELEASE_TAG_URL | ||
from homeassistant.components.update.const import ( | ||
ATTR_INSTALLED_VERSION, | ||
ATTR_LATEST_VERSION, | ||
ATTR_RELEASE_URL, | ||
) | ||
from homeassistant.core import HomeAssistant | ||
import homeassistant.util.dt as dt_util | ||
|
||
from . import ( | ||
TEST_STATS, | ||
TEST_UPDATE_FRIGATE_CONTAINER_ENTITY_ID, | ||
create_mock_frigate_client, | ||
setup_mock_frigate_config_entry, | ||
) | ||
|
||
_LOGGER = logging.getLogger(__name__) | ||
|
||
|
||
async def test_update_sensor_new_update(hass: HomeAssistant) -> None: | ||
"""Test FrigateUpdateSensor state.""" | ||
|
||
client = create_mock_frigate_client() | ||
await setup_mock_frigate_config_entry(hass, client=client) | ||
|
||
entity_state = hass.states.get(TEST_UPDATE_FRIGATE_CONTAINER_ENTITY_ID) | ||
assert entity_state | ||
assert entity_state.state == "on" | ||
assert ( | ||
entity_state.attributes[ATTR_RELEASE_URL] | ||
== f"{FRIGATE_RELEASE_TAG_URL}/v0.10.1" | ||
) | ||
|
||
|
||
async def test_update_sensor_same_version(hass: HomeAssistant) -> None: | ||
"""Test FrigateUpdateSensor state.""" | ||
|
||
client = create_mock_frigate_client() | ||
await setup_mock_frigate_config_entry(hass, client=client) | ||
|
||
stats: dict[str, Any] = copy.deepcopy(TEST_STATS) | ||
client.async_get_stats = AsyncMock(return_value=stats) | ||
|
||
stats["service"]["version"] = stats["service"]["latest_version"] | ||
async_fire_time_changed(hass, dt_util.utcnow() + SCAN_INTERVAL) | ||
await hass.async_block_till_done() | ||
|
||
entity_state = hass.states.get(TEST_UPDATE_FRIGATE_CONTAINER_ENTITY_ID) | ||
assert entity_state | ||
assert entity_state.state == "off" | ||
assert entity_state.attributes[ATTR_INSTALLED_VERSION] == "0.10.1" | ||
assert entity_state.attributes[ATTR_LATEST_VERSION] == "0.10.1" | ||
assert ( | ||
entity_state.attributes[ATTR_RELEASE_URL] | ||
== f"{FRIGATE_RELEASE_TAG_URL}/v0.10.1" | ||
) | ||
|
||
|
||
async def test_update_sensor_bad_current(hass: HomeAssistant) -> None: | ||
"""Test FrigateUpdateSensor state.""" | ||
|
||
client = create_mock_frigate_client() | ||
await setup_mock_frigate_config_entry(hass, client=client) | ||
|
||
stats: dict[str, Any] = copy.deepcopy(TEST_STATS) | ||
client.async_get_stats = AsyncMock(return_value=stats) | ||
|
||
stats["service"]["version"] = "" | ||
async_fire_time_changed(hass, dt_util.utcnow() + SCAN_INTERVAL) | ||
await hass.async_block_till_done() | ||
|
||
entity_state = hass.states.get(TEST_UPDATE_FRIGATE_CONTAINER_ENTITY_ID) | ||
assert entity_state | ||
assert entity_state.state == "unknown" | ||
assert entity_state.attributes[ATTR_INSTALLED_VERSION] is None | ||
assert entity_state.attributes[ATTR_LATEST_VERSION] == "0.10.1" | ||
|
||
|
||
async def test_update_sensor_bad_latest(hass: HomeAssistant) -> None: | ||
"""Test FrigateUpdateSensor state.""" | ||
|
||
client = create_mock_frigate_client() | ||
await setup_mock_frigate_config_entry(hass, client=client) | ||
|
||
stats: dict[str, Any] = copy.deepcopy(TEST_STATS) | ||
client.async_get_stats = AsyncMock(return_value=stats) | ||
|
||
stats["service"]["latest_version"] = "unknown" | ||
async_fire_time_changed(hass, dt_util.utcnow() + SCAN_INTERVAL) | ||
await hass.async_block_till_done() | ||
|
||
entity_state = hass.states.get(TEST_UPDATE_FRIGATE_CONTAINER_ENTITY_ID) | ||
assert entity_state | ||
assert entity_state.state == "unknown" | ||
assert entity_state.attributes[ATTR_INSTALLED_VERSION] == "0.8.4" | ||
assert entity_state.attributes[ATTR_LATEST_VERSION] is None | ||
assert entity_state.attributes[ATTR_RELEASE_URL] is None |