diff --git a/homeassistant/components/homeassistant_hardware/coordinator.py b/homeassistant/components/homeassistant_hardware/coordinator.py index 9eb900b13fd..c9a5c891328 100644 --- a/homeassistant/components/homeassistant_hardware/coordinator.py +++ b/homeassistant/components/homeassistant_hardware/coordinator.py @@ -31,7 +31,6 @@ class FirmwareUpdateCoordinator(DataUpdateCoordinator[FirmwareManifest]): _LOGGER, name="firmware update coordinator", update_interval=FIRMWARE_REFRESH_INTERVAL, - always_update=False, ) self.hass = hass self.session = session diff --git a/homeassistant/components/homeassistant_sky_connect/__init__.py b/homeassistant/components/homeassistant_sky_connect/__init__.py index b3af47df61d..e8b8c3bb433 100644 --- a/homeassistant/components/homeassistant_sky_connect/__init__.py +++ b/homeassistant/components/homeassistant_sky_connect/__init__.py @@ -15,14 +15,13 @@ _LOGGER = logging.getLogger(__name__) async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: """Set up a Home Assistant SkyConnect config entry.""" - await hass.config_entries.async_forward_entry_setups(entry, ["update"]) - return True async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: """Unload a config entry.""" + await hass.config_entries.async_unload_platforms(entry, ["update"]) return True diff --git a/homeassistant/components/homeassistant_sky_connect/update.py b/homeassistant/components/homeassistant_sky_connect/update.py index 43e3f1ca255..96978eb4562 100644 --- a/homeassistant/components/homeassistant_sky_connect/update.py +++ b/homeassistant/components/homeassistant_sky_connect/update.py @@ -21,11 +21,20 @@ from homeassistant.components.update import UpdateDeviceClass from homeassistant.config_entries import ConfigEntry from homeassistant.const import EntityCategory from homeassistant.core import HomeAssistant, callback -from homeassistant.helpers import entity_registry as er +from homeassistant.helpers import device_registry as dr, entity_registry as er from homeassistant.helpers.aiohttp_client import async_get_clientsession +from homeassistant.helpers.device_registry import DeviceInfo from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback -from .const import FIRMWARE, FIRMWARE_VERSION, NABU_CASA_FIRMWARE_RELEASES_URL +from .const import ( + DOMAIN, + FIRMWARE, + FIRMWARE_VERSION, + NABU_CASA_FIRMWARE_RELEASES_URL, + PRODUCT, + SERIAL_NUMBER, + HardwareVariant, +) _LOGGER = logging.getLogger(__name__) @@ -42,7 +51,7 @@ FIRMWARE_ENTITY_DESCRIPTIONS: dict[ fw_type="skyconnect_zigbee_ncp", version_key="ezsp_version", expected_firmware_type=ApplicationType.EZSP, - firmware_name="EmberZNet", + firmware_name="EmberZNet Zigbee", ), ApplicationType.SPINEL: FirmwareUpdateEntityDescription( key="firmware", @@ -130,6 +139,7 @@ class FirmwareUpdateEntity(BaseFirmwareUpdateEntity): """SkyConnect firmware update entity.""" bootloader_reset_type = None + _attr_has_entity_name = True def __init__( self, @@ -141,8 +151,18 @@ class FirmwareUpdateEntity(BaseFirmwareUpdateEntity): """Initialize the SkyConnect firmware update entity.""" super().__init__(device, config_entry, update_coordinator, entity_description) - self._attr_unique_id = ( - f"{self._config_entry.data['serial_number']}_{self.entity_description.key}" + variant = HardwareVariant.from_usb_product_name( + self._config_entry.data[PRODUCT] + ) + serial_number = self._config_entry.data[SERIAL_NUMBER] + + self._attr_unique_id = f"{serial_number}_{self.entity_description.key}" + self._attr_device_info = DeviceInfo( + identifiers={(DOMAIN, serial_number)}, + name=f"{variant.full_name} ({serial_number[:8]})", + model=variant.full_name, + manufacturer="Nabu Casa", + serial_number=serial_number, ) # Use the cached firmware info if it exists @@ -155,6 +175,17 @@ class FirmwareUpdateEntity(BaseFirmwareUpdateEntity): source="homeassistant_sky_connect", ) + def _update_attributes(self) -> None: + """Recompute the attributes of the entity.""" + super()._update_attributes() + + assert self.device_entry is not None + device_registry = dr.async_get(self.hass) + device_registry.async_update_device( + device_id=self.device_entry.id, + sw_version=f"{self.entity_description.firmware_name} {self._attr_installed_version}", + ) + @callback def _firmware_info_callback(self, firmware_info: FirmwareInfo) -> None: """Handle updated firmware info being pushed by an integration.""" diff --git a/homeassistant/components/homeassistant_yellow/__init__.py b/homeassistant/components/homeassistant_yellow/__init__.py index 06f908ab61e..71aa8ef99b7 100644 --- a/homeassistant/components/homeassistant_yellow/__init__.py +++ b/homeassistant/components/homeassistant_yellow/__init__.py @@ -62,6 +62,7 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: """Unload a config entry.""" + await hass.config_entries.async_unload_platforms(entry, ["update"]) return True diff --git a/homeassistant/components/homeassistant_yellow/const.py b/homeassistant/components/homeassistant_yellow/const.py index b98b1133d01..b8bf17391f9 100644 --- a/homeassistant/components/homeassistant_yellow/const.py +++ b/homeassistant/components/homeassistant_yellow/const.py @@ -2,8 +2,9 @@ DOMAIN = "homeassistant_yellow" -RADIO_MODEL = "Home Assistant Yellow" -RADIO_MANUFACTURER = "Nabu Casa" +MODEL = "Home Assistant Yellow" +MANUFACTURER = "Nabu Casa" + RADIO_DEVICE = "/dev/ttyAMA1" ZHA_HW_DISCOVERY_DATA = { diff --git a/homeassistant/components/homeassistant_yellow/strings.json b/homeassistant/components/homeassistant_yellow/strings.json index b089e483899..ddff5fd9b6d 100644 --- a/homeassistant/components/homeassistant_yellow/strings.json +++ b/homeassistant/components/homeassistant_yellow/strings.json @@ -149,5 +149,12 @@ "run_zigbee_flasher_addon": "[%key:component::homeassistant_hardware::firmware_picker::options::progress::run_zigbee_flasher_addon%]", "uninstall_zigbee_flasher_addon": "[%key:component::homeassistant_hardware::firmware_picker::options::progress::uninstall_zigbee_flasher_addon%]" } + }, + "entity": { + "update": { + "firmware": { + "name": "Radio firmware" + } + } } } diff --git a/homeassistant/components/homeassistant_yellow/update.py b/homeassistant/components/homeassistant_yellow/update.py index 88d4f2912d3..71913dc9923 100644 --- a/homeassistant/components/homeassistant_yellow/update.py +++ b/homeassistant/components/homeassistant_yellow/update.py @@ -21,13 +21,17 @@ from homeassistant.components.update import UpdateDeviceClass from homeassistant.config_entries import ConfigEntry from homeassistant.const import EntityCategory from homeassistant.core import HomeAssistant, callback -from homeassistant.helpers import entity_registry as er +from homeassistant.helpers import device_registry as dr, entity_registry as er from homeassistant.helpers.aiohttp_client import async_get_clientsession +from homeassistant.helpers.device_registry import DeviceInfo from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback from .const import ( + DOMAIN, FIRMWARE, FIRMWARE_VERSION, + MANUFACTURER, + MODEL, NABU_CASA_FIRMWARE_RELEASES_URL, RADIO_DEVICE, ) @@ -39,7 +43,7 @@ FIRMWARE_ENTITY_DESCRIPTIONS: dict[ ApplicationType | None, FirmwareUpdateEntityDescription ] = { ApplicationType.EZSP: FirmwareUpdateEntityDescription( - key="firmware", + key="radio_firmware", display_precision=0, device_class=UpdateDeviceClass.FIRMWARE, entity_category=EntityCategory.CONFIG, @@ -47,10 +51,10 @@ FIRMWARE_ENTITY_DESCRIPTIONS: dict[ fw_type="yellow_zigbee_ncp", version_key="ezsp_version", expected_firmware_type=ApplicationType.EZSP, - firmware_name="EmberZNet", + firmware_name="EmberZNet Zigbee", ), ApplicationType.SPINEL: FirmwareUpdateEntityDescription( - key="firmware", + key="radio_firmware", display_precision=0, device_class=UpdateDeviceClass.FIRMWARE, entity_category=EntityCategory.CONFIG, @@ -61,7 +65,7 @@ FIRMWARE_ENTITY_DESCRIPTIONS: dict[ firmware_name="OpenThread RCP", ), None: FirmwareUpdateEntityDescription( - key="firmware", + key="radio_firmware", display_precision=0, device_class=UpdateDeviceClass.FIRMWARE, entity_category=EntityCategory.CONFIG, @@ -135,6 +139,7 @@ class FirmwareUpdateEntity(BaseFirmwareUpdateEntity): """Yellow firmware update entity.""" bootloader_reset_type = "yellow" # Triggers a GPIO reset + _attr_has_entity_name = True def __init__( self, @@ -145,8 +150,13 @@ class FirmwareUpdateEntity(BaseFirmwareUpdateEntity): ) -> None: """Initialize the Yellow firmware update entity.""" super().__init__(device, config_entry, update_coordinator, entity_description) - self._attr_unique_id = self.entity_description.key + self._attr_device_info = DeviceInfo( + identifiers={(DOMAIN, "yellow")}, + name=MODEL, + model=MODEL, + manufacturer=MANUFACTURER, + ) # Use the cached firmware info if it exists if self._config_entry.data[FIRMWARE] is not None: @@ -158,6 +168,17 @@ class FirmwareUpdateEntity(BaseFirmwareUpdateEntity): source="homeassistant_yellow", ) + def _update_attributes(self) -> None: + """Recompute the attributes of the entity.""" + super()._update_attributes() + + assert self.device_entry is not None + device_registry = dr.async_get(self.hass) + device_registry.async_update_device( + device_id=self.device_entry.id, + sw_version=f"{self.entity_description.firmware_name} {self._attr_installed_version}", + ) + @callback def _firmware_info_callback(self, firmware_info: FirmwareInfo) -> None: """Handle updated firmware info being pushed by an integration.""" diff --git a/tests/components/homeassistant_sky_connect/test_update.py b/tests/components/homeassistant_sky_connect/test_update.py index 9fb7528987e..7ad0099785b 100644 --- a/tests/components/homeassistant_sky_connect/test_update.py +++ b/tests/components/homeassistant_sky_connect/test_update.py @@ -14,9 +14,7 @@ from .common import USB_DATA_ZBT1 from tests.common import MockConfigEntry -UPDATE_ENTITY_ID = ( - "update.homeassistant_sky_connect_9e2adbd75b8beb119fe564a0f320645d_firmware" -) +UPDATE_ENTITY_ID = "update.home_assistant_connect_zbt_1_9e2adbd7_firmware" async def test_zbt1_update_entity(hass: HomeAssistant) -> None: @@ -59,8 +57,9 @@ async def test_zbt1_update_entity(hass: HomeAssistant) -> None: await hass.async_block_till_done() state_ezsp = hass.states.get(UPDATE_ENTITY_ID) + assert state_ezsp is not None assert state_ezsp.state == "unknown" - assert state_ezsp.attributes["title"] == "EmberZNet" + assert state_ezsp.attributes["title"] == "EmberZNet Zigbee" assert state_ezsp.attributes["installed_version"] == "7.3.1.0" assert state_ezsp.attributes["latest_version"] is None @@ -80,6 +79,7 @@ async def test_zbt1_update_entity(hass: HomeAssistant) -> None: # After the firmware update, the entity has the new version and the correct state state_spinel = hass.states.get(UPDATE_ENTITY_ID) + assert state_spinel is not None assert state_spinel.state == "unknown" assert state_spinel.attributes["title"] == "OpenThread RCP" assert state_spinel.attributes["installed_version"] == "2.4.4.0" diff --git a/tests/components/homeassistant_yellow/test_update.py b/tests/components/homeassistant_yellow/test_update.py index 269ff2afc49..2ce66b95137 100644 --- a/tests/components/homeassistant_yellow/test_update.py +++ b/tests/components/homeassistant_yellow/test_update.py @@ -15,7 +15,7 @@ from homeassistant.setup import async_setup_component from tests.common import MockConfigEntry -UPDATE_ENTITY_ID = "update.homeassistant_yellow_firmware" +UPDATE_ENTITY_ID = "update.home_assistant_yellow_firmware" async def test_yellow_update_entity(hass: HomeAssistant) -> None: @@ -24,6 +24,7 @@ async def test_yellow_update_entity(hass: HomeAssistant) -> None: # Set up the Yellow integration yellow_config_entry = MockConfigEntry( + title="Home Assistant Yellow", domain="homeassistant_yellow", data={ "firmware": "ezsp", @@ -62,8 +63,9 @@ async def test_yellow_update_entity(hass: HomeAssistant) -> None: await hass.async_block_till_done() state_ezsp = hass.states.get(UPDATE_ENTITY_ID) + assert state_ezsp is not None assert state_ezsp.state == "unknown" - assert state_ezsp.attributes["title"] == "EmberZNet" + assert state_ezsp.attributes["title"] == "EmberZNet Zigbee" assert state_ezsp.attributes["installed_version"] == "7.3.1.0" assert state_ezsp.attributes["latest_version"] is None @@ -83,6 +85,7 @@ async def test_yellow_update_entity(hass: HomeAssistant) -> None: # After the firmware update, the entity has the new version and the correct state state_spinel = hass.states.get(UPDATE_ENTITY_ID) + assert state_spinel is not None assert state_spinel.state == "unknown" assert state_spinel.attributes["title"] == "OpenThread RCP" assert state_spinel.attributes["installed_version"] == "2.4.4.0"