Add update platform to ZHA (bumps zigpy to 0.61.0) (#107612)

* stub out zha update entity

* update matcher

* updates based on assumptions / conversation

* hook into current installed version

* post rebase cleanup

* incorporate zigpy changes

* fix async_setup_entry

* fix sw_version

* make ota work with config diagnostic match

* fix version format

* sync up with latest Zigpy changes

* fix name attribute

* disable ota providers for tests

* update device list

* review comment

* add current_file_version to Ota ZCL_INIT_ATTRS

* updates to update and start tests

* get installed version from restore data

* better version handling

* remove done todo notes

* reorganize test

* move image notify to cluster handler

* add test for manual update check

* firmware update success test

* coverage

* use zigpy defs

* clean up after rebase

* bump Zigpy

* cleanup from review comments

* fix busted F string

* fix empty error

* move inside check

* guard zigbee network from bulk check for updates
This commit is contained in:
David F. Mulcahey 2024-01-30 22:34:48 -05:00 committed by GitHub
parent 2f9f0bae46
commit 3115af1041
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
13 changed files with 1291 additions and 19 deletions

View file

@ -56,6 +56,7 @@ from ..const import (
SIGNAL_MOVE_LEVEL,
SIGNAL_SET_LEVEL,
SIGNAL_UPDATE_DEVICE,
UNKNOWN as ZHA_UNKNOWN,
)
from . import (
AttrReportConfig,
@ -523,12 +524,47 @@ class OnOffConfigurationClusterHandler(ClusterHandler):
@registries.ZIGBEE_CLUSTER_HANDLER_REGISTRY.register(Ota.cluster_id)
@registries.CLIENT_CLUSTER_HANDLER_REGISTRY.register(Ota.cluster_id)
class OtaClientClusterHandler(ClientClusterHandler):
class OtaClusterHandler(ClusterHandler):
"""OTA cluster handler."""
BIND: bool = False
# Some devices have this cluster in the wrong collection (e.g. Third Reality)
ZCL_INIT_ATTRS = {
Ota.AttributeDefs.current_file_version.name: True,
}
@property
def current_file_version(self) -> str:
"""Return cached value of current_file_version attribute."""
current_file_version = self.cluster.get(
Ota.AttributeDefs.current_file_version.name
)
if current_file_version is not None:
return f"0x{int(current_file_version):08x}"
return ZHA_UNKNOWN
@registries.CLIENT_CLUSTER_HANDLER_REGISTRY.register(Ota.cluster_id)
class OtaClientClusterHandler(ClientClusterHandler):
"""OTA client cluster handler."""
BIND: bool = False
ZCL_INIT_ATTRS = {
Ota.AttributeDefs.current_file_version.name: True,
}
@property
def current_file_version(self) -> str:
"""Return cached value of current_file_version attribute."""
current_file_version = self.cluster.get(
Ota.AttributeDefs.current_file_version.name
)
if current_file_version is not None:
return f"0x{int(current_file_version):08x}"
return ZHA_UNKNOWN
@callback
def cluster_command(
self, tsn: int, command_id: int, args: list[Any] | None
@ -540,10 +576,17 @@ class OtaClientClusterHandler(ClientClusterHandler):
cmd_name = command_id
signal_id = self._endpoint.unique_id.split("-")[0]
if cmd_name == "query_next_image":
if cmd_name == Ota.ServerCommandDefs.query_next_image.name:
assert args
self.async_send_signal(SIGNAL_UPDATE_DEVICE.format(signal_id), args[3])
async def async_check_for_update(self):
"""Check for firmware availability by issuing an image notify command."""
await self.cluster.image_notify(
payload_type=(self.cluster.ImageNotifyCommand.PayloadType.QueryJitter),
query_jitter=100,
)
@registries.ZIGBEE_CLUSTER_HANDLER_REGISTRY.register(Partition.cluster_id)
class PartitionClusterHandler(ClusterHandler):

View file

@ -89,6 +89,7 @@ CLUSTER_HANDLER_LEVEL = ATTR_LEVEL
CLUSTER_HANDLER_MULTISTATE_INPUT = "multistate_input"
CLUSTER_HANDLER_OCCUPANCY = "occupancy"
CLUSTER_HANDLER_ON_OFF = "on_off"
CLUSTER_HANDLER_OTA = "ota"
CLUSTER_HANDLER_POWER_CONFIGURATION = "power"
CLUSTER_HANDLER_PRESSURE = "pressure"
CLUSTER_HANDLER_SHADE = "shade"
@ -120,6 +121,7 @@ PLATFORMS = (
Platform.SENSOR,
Platform.SIREN,
Platform.SWITCH,
Platform.UPDATE,
)
CONF_ALARM_MASTER_CODE = "alarm_master_code"

View file

@ -26,6 +26,7 @@ from homeassistant.const import ATTR_COMMAND, ATTR_DEVICE_ID, ATTR_NAME
from homeassistant.core import HomeAssistant, callback
from homeassistant.exceptions import HomeAssistantError
from homeassistant.helpers import device_registry as dr
from homeassistant.helpers.device_registry import DeviceEntry
from homeassistant.helpers.dispatcher import (
async_dispatcher_connect,
async_dispatcher_send,
@ -406,6 +407,15 @@ class ZHADevice(LogMixin):
ATTR_MODEL: self.model,
}
@property
def sw_version(self) -> str | None:
"""Return the software version for this device."""
device_registry = dr.async_get(self.hass)
reg_device: DeviceEntry | None = device_registry.async_get(self.device_id)
if reg_device is None:
return None
return reg_device.sw_version
@classmethod
def new(
cls,

View file

@ -6,6 +6,8 @@ from collections.abc import Callable
import logging
from typing import TYPE_CHECKING, cast
from zigpy.zcl.clusters.general import Ota
from homeassistant.const import CONF_TYPE, Platform
from homeassistant.core import HomeAssistant, callback
from homeassistant.helpers import entity_registry as er
@ -32,6 +34,7 @@ from .. import ( # noqa: F401
sensor,
siren,
switch,
update,
)
from . import const as zha_const, registries as zha_regs
@ -233,10 +236,16 @@ class ProbeEndpoint:
cmpt_by_dev_type = zha_regs.DEVICE_CLASS[ep_profile_id].get(ep_device_type)
if config_diagnostic_entities:
cluster_handlers = list(endpoint.all_cluster_handlers.values())
ota_handler_id = f"{endpoint.id}:0x{Ota.cluster_id:04x}"
if ota_handler_id in endpoint.client_cluster_handlers:
cluster_handlers.append(
endpoint.client_cluster_handlers[ota_handler_id]
)
matches, claimed = zha_regs.ZHA_ENTITIES.get_config_diagnostic_entity(
endpoint.device.manufacturer,
endpoint.device.model,
list(endpoint.all_cluster_handlers.values()),
cluster_handlers,
endpoint.device.quirk_id,
)
else:

View file

@ -26,7 +26,7 @@
"pyserial-asyncio==0.6",
"zha-quirks==0.0.110",
"zigpy-deconz==0.22.4",
"zigpy==0.60.7",
"zigpy==0.61.0",
"zigpy-xbee==0.20.1",
"zigpy-zigate==0.12.0",
"zigpy-znp==0.12.1",

View file

@ -0,0 +1,235 @@
"""Representation of ZHA updates."""
from __future__ import annotations
from dataclasses import dataclass
import functools
from typing import TYPE_CHECKING, Any
from zigpy.ota.image import BaseOTAImage
from zigpy.types import uint16_t
from zigpy.zcl.foundation import Status
from homeassistant.components.update import (
ATTR_INSTALLED_VERSION,
ATTR_LATEST_VERSION,
UpdateDeviceClass,
UpdateEntity,
UpdateEntityFeature,
)
from homeassistant.config_entries import ConfigEntry
from homeassistant.const import EntityCategory, Platform
from homeassistant.core import HomeAssistant, callback
from homeassistant.exceptions import HomeAssistantError
from homeassistant.helpers.dispatcher import async_dispatcher_connect
from homeassistant.helpers.entity_platform import AddEntitiesCallback
from homeassistant.helpers.restore_state import ExtraStoredData
from .core import discovery
from .core.const import CLUSTER_HANDLER_OTA, SIGNAL_ADD_ENTITIES, UNKNOWN
from .core.helpers import get_zha_data
from .core.registries import ZHA_ENTITIES
from .entity import ZhaEntity
if TYPE_CHECKING:
from .core.cluster_handlers import ClusterHandler
from .core.device import ZHADevice
CONFIG_DIAGNOSTIC_MATCH = functools.partial(
ZHA_ENTITIES.config_diagnostic_match, Platform.UPDATE
)
# don't let homeassistant check for updates button hammer the zigbee network
PARALLEL_UPDATES = 1
@dataclass
class ZHAFirmwareUpdateExtraStoredData(ExtraStoredData):
"""Extra stored data for ZHA firmware update entity."""
image_type: uint16_t | None
def as_dict(self) -> dict[str, Any]:
"""Return a dict representation of the extra data."""
return {"image_type": self.image_type}
async def async_setup_entry(
hass: HomeAssistant,
config_entry: ConfigEntry,
async_add_entities: AddEntitiesCallback,
) -> None:
"""Set up the Zigbee Home Automation update from config entry."""
zha_data = get_zha_data(hass)
entities_to_create = zha_data.platforms[Platform.UPDATE]
unsub = async_dispatcher_connect(
hass,
SIGNAL_ADD_ENTITIES,
functools.partial(
discovery.async_add_entities, async_add_entities, entities_to_create
),
)
config_entry.async_on_unload(unsub)
@CONFIG_DIAGNOSTIC_MATCH(cluster_handler_names=CLUSTER_HANDLER_OTA)
class ZHAFirmwareUpdateEntity(ZhaEntity, UpdateEntity):
"""Representation of a ZHA firmware update entity."""
_unique_id_suffix = "firmware_update"
_attr_entity_category = EntityCategory.CONFIG
_attr_device_class = UpdateDeviceClass.FIRMWARE
_attr_supported_features = (
UpdateEntityFeature.INSTALL
| UpdateEntityFeature.PROGRESS
| UpdateEntityFeature.SPECIFIC_VERSION
)
def __init__(
self,
unique_id: str,
zha_device: ZHADevice,
channels: list[ClusterHandler],
**kwargs: Any,
) -> None:
"""Initialize the ZHA update entity."""
super().__init__(unique_id, zha_device, channels, **kwargs)
self._ota_cluster_handler: ClusterHandler = self.cluster_handlers[
CLUSTER_HANDLER_OTA
]
self._attr_installed_version: str = self.determine_installed_version()
self._image_type: uint16_t | None = None
self._latest_version_firmware: BaseOTAImage | None = None
self._result = None
@callback
def determine_installed_version(self) -> str:
"""Determine the currently installed firmware version."""
currently_installed_version = self._ota_cluster_handler.current_file_version
version_from_dr = self.zha_device.sw_version
if currently_installed_version == UNKNOWN and version_from_dr:
currently_installed_version = version_from_dr
return currently_installed_version
@property
def extra_restore_state_data(self) -> ZHAFirmwareUpdateExtraStoredData:
"""Return ZHA firmware update specific state data to be restored."""
return ZHAFirmwareUpdateExtraStoredData(self._image_type)
@callback
def device_ota_update_available(self, image: BaseOTAImage) -> None:
"""Handle ota update available signal from Zigpy."""
self._latest_version_firmware = image
self._attr_latest_version = f"0x{image.header.file_version:08x}"
self._image_type = image.header.image_type
self.async_write_ha_state()
@callback
def _update_progress(self, current: int, total: int, progress: float) -> None:
"""Update install progress on event."""
assert self._latest_version_firmware
self._attr_in_progress = int(progress)
self.async_write_ha_state()
@callback
def _reset_progress(self, write_state: bool = True) -> None:
"""Reset update install progress."""
self._result = None
self._attr_in_progress = False
if write_state:
self.async_write_ha_state()
async def async_update(self) -> None:
"""Handle the update entity service call to manually check for available firmware updates."""
await super().async_update()
# check for updates in the HA settings menu can invoke this so we need to check if the device
# is mains powered so we don't get a ton of errors in the logs from sleepy devices.
if self.zha_device.available and self.zha_device.is_mains_powered:
await self._ota_cluster_handler.async_check_for_update()
async def async_install(
self, version: str | None, backup: bool, **kwargs: Any
) -> None:
"""Install an update."""
firmware = self._latest_version_firmware
assert firmware
self._reset_progress(False)
self._attr_in_progress = True
self.async_write_ha_state()
try:
self._result = await self.zha_device.device.update_firmware(
self._latest_version_firmware,
self._update_progress,
)
except Exception as ex:
self._reset_progress()
raise HomeAssistantError(ex) from ex
assert self._result is not None
# If the update was not successful, we should throw an error to let the user know
if self._result != Status.SUCCESS:
# save result since reset_progress will clear it
results = self._result
self._reset_progress()
raise HomeAssistantError(f"Update was not successful - result: {results}")
# If we get here, all files were installed successfully
self._attr_installed_version = (
self._attr_latest_version
) = f"0x{firmware.header.file_version:08x}"
self._latest_version_firmware = None
self._reset_progress()
async def async_added_to_hass(self) -> None:
"""Call when entity is added."""
await super().async_added_to_hass()
last_state = await self.async_get_last_state()
# If we have a complete previous state, use that to set the installed version
if (
last_state
and self._attr_installed_version == UNKNOWN
and (installed_version := last_state.attributes.get(ATTR_INSTALLED_VERSION))
):
self._attr_installed_version = installed_version
# If we have a complete previous state, use that to set the latest version
if (
last_state
and (latest_version := last_state.attributes.get(ATTR_LATEST_VERSION))
is not None
and latest_version != UNKNOWN
):
self._attr_latest_version = latest_version
# If we have no state or latest version to restore, or the latest version is
# the same as the installed version, we can set the latest
# version to installed so that the entity starts as off.
elif (
not last_state
or not latest_version
or latest_version == self._attr_installed_version
):
self._attr_latest_version = self._attr_installed_version
if self._attr_latest_version != self._attr_installed_version and (
extra_data := await self.async_get_last_extra_data()
):
self._image_type = extra_data.as_dict()["image_type"]
if self._image_type:
self._latest_version_firmware = (
await self.zha_device.device.application.ota.get_ota_image(
self.zha_device.manufacturer_code, self._image_type
)
)
# if we can't locate an image but we have a latest version that differs
# we should set the latest version to the installed version to avoid
# confusion and errors
if not self._latest_version_firmware:
self._attr_latest_version = self._attr_installed_version
self.zha_device.device.add_listener(self)
async def async_will_remove_from_hass(self) -> None:
"""Call when entity will be removed."""
await super().async_will_remove_from_hass()
self._reset_progress(False)

View file

@ -2930,7 +2930,7 @@ zigpy-zigate==0.12.0
zigpy-znp==0.12.1
# homeassistant.components.zha
zigpy==0.60.7
zigpy==0.61.0
# homeassistant.components.zoneminder
zm-py==0.5.4

View file

@ -2241,7 +2241,7 @@ zigpy-zigate==0.12.0
zigpy-znp==0.12.1
# homeassistant.components.zha
zigpy==0.60.7
zigpy==0.61.0
# homeassistant.components.zwave_js
zwave-js-server-python==0.55.3

View file

@ -54,7 +54,6 @@ def patch_cluster(cluster):
cluster.configure_reporting_multiple = AsyncMock(
return_value=zcl_f.ConfigureReportingResponse.deserialize(b"\x00")[0]
)
cluster.deserialize = Mock()
cluster.handle_cluster_request = Mock()
cluster.read_attributes = AsyncMock(wraps=cluster.read_attributes)
cluster.read_attributes_raw = AsyncMock(side_effect=_read_attribute_raw)

View file

@ -154,6 +154,14 @@ async def zigpy_app_controller():
zigpy.config.CONF_STARTUP_ENERGY_SCAN: False,
zigpy.config.CONF_NWK_BACKUP_ENABLED: False,
zigpy.config.CONF_TOPO_SCAN_ENABLED: False,
zigpy.config.CONF_OTA: {
zigpy.config.CONF_OTA_IKEA: False,
zigpy.config.CONF_OTA_INOVELLI: False,
zigpy.config.CONF_OTA_LEDVANCE: False,
zigpy.config.CONF_OTA_SALUS: False,
zigpy.config.CONF_OTA_SONOFF: False,
zigpy.config.CONF_OTA_THIRDREALITY: False,
},
}
)

View file

@ -990,15 +990,9 @@ async def test_cluster_handler_naming() -> None:
assert issubclass(client_cluster_handler, cluster_handlers.ClientClusterHandler)
assert client_cluster_handler.__name__.endswith("ClientClusterHandler")
server_cluster_handlers = []
for cluster_handler_dict in registries.ZIGBEE_CLUSTER_HANDLER_REGISTRY.values():
# remove this filter in the update platform PR
server_cluster_handlers += [
cluster_handler
for cluster_handler in cluster_handler_dict.values()
if cluster_handler.__name__ != "OtaClientClusterHandler"
]
for cluster_handler in server_cluster_handlers:
assert not issubclass(cluster_handler, cluster_handlers.ClientClusterHandler)
assert cluster_handler.__name__.endswith("ClusterHandler")
for cluster_handler in cluster_handler_dict.values():
assert not issubclass(
cluster_handler, cluster_handlers.ClientClusterHandler
)
assert cluster_handler.__name__.endswith("ClusterHandler")

View file

@ -0,0 +1,587 @@
"""Test ZHA firmware updates."""
from unittest.mock import AsyncMock, MagicMock, call, patch
import pytest
from zigpy.exceptions import DeliveryError
from zigpy.ota import CachedImage
import zigpy.ota.image as firmware
import zigpy.profiles.zha as zha
import zigpy.types as t
import zigpy.zcl.clusters.general as general
import zigpy.zcl.foundation as foundation
from homeassistant.components.homeassistant import (
DOMAIN as HA_DOMAIN,
SERVICE_UPDATE_ENTITY,
)
from homeassistant.components.update import (
ATTR_IN_PROGRESS,
ATTR_INSTALLED_VERSION,
ATTR_LATEST_VERSION,
DOMAIN as UPDATE_DOMAIN,
SERVICE_INSTALL,
)
from homeassistant.components.update.const import ATTR_SKIPPED_VERSION
from homeassistant.const import ATTR_ENTITY_ID, STATE_OFF, STATE_ON, Platform
from homeassistant.core import HomeAssistant, State
from homeassistant.exceptions import HomeAssistantError
from homeassistant.setup import async_setup_component
from .common import async_enable_traffic, find_entity_id, update_attribute_cache
from .conftest import SIG_EP_INPUT, SIG_EP_OUTPUT, SIG_EP_TYPE
from tests.common import mock_restore_cache_with_extra_data
@pytest.fixture(autouse=True)
def update_platform_only():
"""Only set up the update and required base platforms to speed up tests."""
with patch(
"homeassistant.components.zha.PLATFORMS",
(
Platform.UPDATE,
Platform.SENSOR,
Platform.SELECT,
Platform.SWITCH,
),
):
yield
@pytest.fixture
def zigpy_device(zigpy_device_mock):
"""Device tracker zigpy device."""
endpoints = {
1: {
SIG_EP_INPUT: [general.Basic.cluster_id, general.OnOff.cluster_id],
SIG_EP_OUTPUT: [general.Ota.cluster_id],
SIG_EP_TYPE: zha.DeviceType.ON_OFF_SWITCH,
}
}
return zigpy_device_mock(
endpoints, node_descriptor=b"\x02@\x84_\x11\x7fd\x00\x00,d\x00\x00"
)
async def setup_test_data(
zha_device_joined_restored,
zigpy_device,
skip_attribute_plugs=False,
file_not_found=False,
):
"""Set up test data for the tests."""
fw_version = 0x12345678
installed_fw_version = fw_version - 10
cluster = zigpy_device.endpoints[1].out_clusters[general.Ota.cluster_id]
if not skip_attribute_plugs:
cluster.PLUGGED_ATTR_READS = {
general.Ota.AttributeDefs.current_file_version.name: installed_fw_version
}
update_attribute_cache(cluster)
# set up firmware image
fw_image = firmware.OTAImage()
fw_image.subelements = [firmware.SubElement(tag_id=0x0000, data=b"fw_image")]
fw_header = firmware.OTAImageHeader(
file_version=fw_version,
image_type=0x90,
manufacturer_id=zigpy_device.manufacturer_id,
upgrade_file_id=firmware.OTAImageHeader.MAGIC_VALUE,
header_version=256,
header_length=56,
field_control=0,
stack_version=2,
header_string="This is a test header!",
image_size=56 + 2 + 4 + 8,
)
fw_image.header = fw_header
fw_image.should_update = MagicMock(return_value=True)
cached_image = CachedImage(fw_image)
cluster.endpoint.device.application.ota.get_ota_image = AsyncMock(
return_value=None if file_not_found else cached_image
)
zha_device = await zha_device_joined_restored(zigpy_device)
zha_device.async_update_sw_build_id(installed_fw_version)
return zha_device, cluster, fw_image, installed_fw_version
async def test_firmware_update_notification_from_zigpy(
hass: HomeAssistant, zha_device_joined_restored, zigpy_device
) -> None:
"""Test ZHA update platform - firmware update notification."""
zha_device, cluster, fw_image, installed_fw_version = await setup_test_data(
zha_device_joined_restored, zigpy_device
)
entity_id = find_entity_id(Platform.UPDATE, zha_device, hass)
assert entity_id is not None
# allow traffic to flow through the gateway and device
await async_enable_traffic(hass, [zha_device])
assert hass.states.get(entity_id).state == STATE_OFF
# simulate an image available notification
await cluster._handle_query_next_image(
fw_image.header.field_control,
zha_device.manufacturer_code,
fw_image.header.image_type,
installed_fw_version,
fw_image.header.header_version,
tsn=15,
)
await hass.async_block_till_done()
state = hass.states.get(entity_id)
assert state.state == STATE_ON
attrs = state.attributes
assert attrs[ATTR_INSTALLED_VERSION] == f"0x{installed_fw_version:08x}"
assert not attrs[ATTR_IN_PROGRESS]
assert attrs[ATTR_LATEST_VERSION] == f"0x{fw_image.header.file_version:08x}"
async def test_firmware_update_notification_from_service_call(
hass: HomeAssistant, zha_device_joined_restored, zigpy_device
) -> None:
"""Test ZHA update platform - firmware update manual check."""
zha_device, cluster, fw_image, installed_fw_version = await setup_test_data(
zha_device_joined_restored, zigpy_device
)
entity_id = find_entity_id(Platform.UPDATE, zha_device, hass)
assert entity_id is not None
# allow traffic to flow through the gateway and device
await async_enable_traffic(hass, [zha_device])
assert hass.states.get(entity_id).state == STATE_OFF
async def _async_image_notify_side_effect(*args, **kwargs):
await cluster._handle_query_next_image(
fw_image.header.field_control,
zha_device.manufacturer_code,
fw_image.header.image_type,
installed_fw_version,
fw_image.header.header_version,
tsn=15,
)
await async_setup_component(hass, HA_DOMAIN, {})
cluster.image_notify = AsyncMock(side_effect=_async_image_notify_side_effect)
await hass.services.async_call(
HA_DOMAIN,
SERVICE_UPDATE_ENTITY,
service_data={ATTR_ENTITY_ID: entity_id},
blocking=True,
)
assert cluster.image_notify.await_count == 1
assert cluster.image_notify.call_args_list[0] == call(
payload_type=cluster.ImageNotifyCommand.PayloadType.QueryJitter,
query_jitter=100,
)
await hass.async_block_till_done()
state = hass.states.get(entity_id)
assert state.state == STATE_ON
attrs = state.attributes
assert attrs[ATTR_INSTALLED_VERSION] == f"0x{installed_fw_version:08x}"
assert not attrs[ATTR_IN_PROGRESS]
assert attrs[ATTR_LATEST_VERSION] == f"0x{fw_image.header.file_version:08x}"
def make_packet(zigpy_device, cluster, cmd_name: str, **kwargs):
"""Make a zigpy packet."""
req_hdr, req_cmd = cluster._create_request(
general=False,
command_id=cluster.commands_by_name[cmd_name].id,
schema=cluster.commands_by_name[cmd_name].schema,
disable_default_response=False,
direction=foundation.Direction.Server_to_Client,
args=(),
kwargs=kwargs,
)
ota_packet = t.ZigbeePacket(
src=t.AddrModeAddress(addr_mode=t.AddrMode.NWK, address=zigpy_device.nwk),
src_ep=1,
dst=t.AddrModeAddress(addr_mode=t.AddrMode.NWK, address=0x0000),
dst_ep=1,
tsn=req_hdr.tsn,
profile_id=260,
cluster_id=cluster.cluster_id,
data=t.SerializableBytes(req_hdr.serialize() + req_cmd.serialize()),
lqi=255,
rssi=-30,
)
return ota_packet
async def test_firmware_update_success(
hass: HomeAssistant, zha_device_joined_restored, zigpy_device
) -> None:
"""Test ZHA update platform - firmware update success."""
zha_device, cluster, fw_image, installed_fw_version = await setup_test_data(
zha_device_joined_restored, zigpy_device
)
entity_id = find_entity_id(Platform.UPDATE, zha_device, hass)
assert entity_id is not None
# allow traffic to flow through the gateway and device
await async_enable_traffic(hass, [zha_device])
assert hass.states.get(entity_id).state == STATE_OFF
# simulate an image available notification
await cluster._handle_query_next_image(
fw_image.header.field_control,
zha_device.manufacturer_code,
fw_image.header.image_type,
installed_fw_version,
fw_image.header.header_version,
tsn=15,
)
await hass.async_block_till_done()
state = hass.states.get(entity_id)
assert state.state == STATE_ON
attrs = state.attributes
assert attrs[ATTR_INSTALLED_VERSION] == f"0x{installed_fw_version:08x}"
assert not attrs[ATTR_IN_PROGRESS]
assert attrs[ATTR_LATEST_VERSION] == f"0x{fw_image.header.file_version:08x}"
async def endpoint_reply(cluster_id, tsn, data, command_id):
if cluster_id == general.Ota.cluster_id:
hdr, cmd = cluster.deserialize(data)
if isinstance(cmd, general.Ota.ImageNotifyCommand):
zigpy_device.packet_received(
make_packet(
zigpy_device,
cluster,
general.Ota.ServerCommandDefs.query_next_image.name,
field_control=general.Ota.QueryNextImageCommand.FieldControl.HardwareVersion,
manufacturer_code=fw_image.header.manufacturer_id,
image_type=fw_image.header.image_type,
current_file_version=fw_image.header.file_version - 10,
hardware_version=1,
)
)
elif isinstance(
cmd, general.Ota.ClientCommandDefs.query_next_image_response.schema
):
assert cmd.status == foundation.Status.SUCCESS
assert cmd.manufacturer_code == fw_image.header.manufacturer_id
assert cmd.image_type == fw_image.header.image_type
assert cmd.file_version == fw_image.header.file_version
assert cmd.image_size == fw_image.header.image_size
zigpy_device.packet_received(
make_packet(
zigpy_device,
cluster,
general.Ota.ServerCommandDefs.image_block.name,
field_control=general.Ota.ImageBlockCommand.FieldControl.RequestNodeAddr,
manufacturer_code=fw_image.header.manufacturer_id,
image_type=fw_image.header.image_type,
file_version=fw_image.header.file_version,
file_offset=0,
maximum_data_size=40,
request_node_addr=zigpy_device.ieee,
)
)
elif isinstance(
cmd, general.Ota.ClientCommandDefs.image_block_response.schema
):
if cmd.file_offset == 0:
assert cmd.status == foundation.Status.SUCCESS
assert cmd.manufacturer_code == fw_image.header.manufacturer_id
assert cmd.image_type == fw_image.header.image_type
assert cmd.file_version == fw_image.header.file_version
assert cmd.file_offset == 0
assert cmd.image_data == fw_image.serialize()[0:40]
zigpy_device.packet_received(
make_packet(
zigpy_device,
cluster,
general.Ota.ServerCommandDefs.image_block.name,
field_control=general.Ota.ImageBlockCommand.FieldControl.RequestNodeAddr,
manufacturer_code=fw_image.header.manufacturer_id,
image_type=fw_image.header.image_type,
file_version=fw_image.header.file_version,
file_offset=40,
maximum_data_size=40,
request_node_addr=zigpy_device.ieee,
)
)
elif cmd.file_offset == 40:
assert cmd.status == foundation.Status.SUCCESS
assert cmd.manufacturer_code == fw_image.header.manufacturer_id
assert cmd.image_type == fw_image.header.image_type
assert cmd.file_version == fw_image.header.file_version
assert cmd.file_offset == 40
assert cmd.image_data == fw_image.serialize()[40:70]
# make sure the state machine gets progress reports
state = hass.states.get(entity_id)
assert state.state == STATE_ON
attrs = state.attributes
assert (
attrs[ATTR_INSTALLED_VERSION] == f"0x{installed_fw_version:08x}"
)
assert attrs[ATTR_IN_PROGRESS] == 57
assert (
attrs[ATTR_LATEST_VERSION]
== f"0x{fw_image.header.file_version:08x}"
)
zigpy_device.packet_received(
make_packet(
zigpy_device,
cluster,
general.Ota.ServerCommandDefs.upgrade_end.name,
status=foundation.Status.SUCCESS,
manufacturer_code=fw_image.header.manufacturer_id,
image_type=fw_image.header.image_type,
file_version=fw_image.header.file_version,
)
)
elif isinstance(
cmd, general.Ota.ClientCommandDefs.upgrade_end_response.schema
):
assert cmd.manufacturer_code == fw_image.header.manufacturer_id
assert cmd.image_type == fw_image.header.image_type
assert cmd.file_version == fw_image.header.file_version
assert cmd.current_time == 0
assert cmd.upgrade_time == 0
cluster.endpoint.reply = AsyncMock(side_effect=endpoint_reply)
await hass.services.async_call(
UPDATE_DOMAIN,
SERVICE_INSTALL,
{
ATTR_ENTITY_ID: entity_id,
},
blocking=True,
)
state = hass.states.get(entity_id)
assert state.state == STATE_OFF
attrs = state.attributes
assert attrs[ATTR_INSTALLED_VERSION] == f"0x{fw_image.header.file_version:08x}"
assert not attrs[ATTR_IN_PROGRESS]
assert attrs[ATTR_LATEST_VERSION] == attrs[ATTR_INSTALLED_VERSION]
async def test_firmware_update_raises(
hass: HomeAssistant, zha_device_joined_restored, zigpy_device
) -> None:
"""Test ZHA update platform - firmware update raises."""
zha_device, cluster, fw_image, installed_fw_version = await setup_test_data(
zha_device_joined_restored, zigpy_device
)
entity_id = find_entity_id(Platform.UPDATE, zha_device, hass)
assert entity_id is not None
# allow traffic to flow through the gateway and device
await async_enable_traffic(hass, [zha_device])
assert hass.states.get(entity_id).state == STATE_OFF
# simulate an image available notification
await cluster._handle_query_next_image(
fw_image.header.field_control,
zha_device.manufacturer_code,
fw_image.header.image_type,
installed_fw_version,
fw_image.header.header_version,
tsn=15,
)
await hass.async_block_till_done()
state = hass.states.get(entity_id)
assert state.state == STATE_ON
attrs = state.attributes
assert attrs[ATTR_INSTALLED_VERSION] == f"0x{installed_fw_version:08x}"
assert not attrs[ATTR_IN_PROGRESS]
assert attrs[ATTR_LATEST_VERSION] == f"0x{fw_image.header.file_version:08x}"
async def endpoint_reply(cluster_id, tsn, data, command_id):
if cluster_id == general.Ota.cluster_id:
hdr, cmd = cluster.deserialize(data)
if isinstance(cmd, general.Ota.ImageNotifyCommand):
zigpy_device.packet_received(
make_packet(
zigpy_device,
cluster,
general.Ota.ServerCommandDefs.query_next_image.name,
field_control=general.Ota.QueryNextImageCommand.FieldControl.HardwareVersion,
manufacturer_code=fw_image.header.manufacturer_id,
image_type=fw_image.header.image_type,
current_file_version=fw_image.header.file_version - 10,
hardware_version=1,
)
)
elif isinstance(
cmd, general.Ota.ClientCommandDefs.query_next_image_response.schema
):
assert cmd.status == foundation.Status.SUCCESS
assert cmd.manufacturer_code == fw_image.header.manufacturer_id
assert cmd.image_type == fw_image.header.image_type
assert cmd.file_version == fw_image.header.file_version
assert cmd.image_size == fw_image.header.image_size
raise DeliveryError("failed to deliver")
cluster.endpoint.reply = AsyncMock(side_effect=endpoint_reply)
with pytest.raises(HomeAssistantError):
await hass.services.async_call(
UPDATE_DOMAIN,
SERVICE_INSTALL,
{
ATTR_ENTITY_ID: entity_id,
},
blocking=True,
)
with patch(
"zigpy.device.Device.update_firmware",
AsyncMock(side_effect=DeliveryError("failed to deliver")),
), pytest.raises(HomeAssistantError):
await hass.services.async_call(
UPDATE_DOMAIN,
SERVICE_INSTALL,
{
ATTR_ENTITY_ID: entity_id,
},
blocking=True,
)
async def test_firmware_update_restore_data(
hass: HomeAssistant, zha_device_joined_restored, zigpy_device
) -> None:
"""Test ZHA update platform - restore data."""
fw_version = 0x12345678
installed_fw_version = fw_version - 10
mock_restore_cache_with_extra_data(
hass,
[
(
State(
"update.fakemanufacturer_fakemodel_firmware",
STATE_ON,
{
ATTR_INSTALLED_VERSION: f"0x{installed_fw_version:08x}",
ATTR_LATEST_VERSION: f"0x{fw_version:08x}",
ATTR_SKIPPED_VERSION: None,
},
),
{"image_type": 0x90},
)
],
)
zha_device, cluster, fw_image, installed_fw_version = await setup_test_data(
zha_device_joined_restored, zigpy_device
)
entity_id = find_entity_id(Platform.UPDATE, zha_device, hass)
assert entity_id is not None
# allow traffic to flow through the gateway and device
await async_enable_traffic(hass, [zha_device])
state = hass.states.get(entity_id)
assert state.state == STATE_ON
attrs = state.attributes
assert attrs[ATTR_INSTALLED_VERSION] == f"0x{installed_fw_version:08x}"
assert not attrs[ATTR_IN_PROGRESS]
assert attrs[ATTR_LATEST_VERSION] == f"0x{fw_image.header.file_version:08x}"
async def test_firmware_update_restore_file_not_found(
hass: HomeAssistant, zha_device_joined_restored, zigpy_device
) -> None:
"""Test ZHA update platform - restore data - file not found."""
fw_version = 0x12345678
installed_fw_version = fw_version - 10
mock_restore_cache_with_extra_data(
hass,
[
(
State(
"update.fakemanufacturer_fakemodel_firmware",
STATE_ON,
{
ATTR_INSTALLED_VERSION: f"0x{installed_fw_version:08x}",
ATTR_LATEST_VERSION: f"0x{fw_version:08x}",
ATTR_SKIPPED_VERSION: None,
},
),
{"image_type": 0x90},
)
],
)
zha_device, cluster, fw_image, installed_fw_version = await setup_test_data(
zha_device_joined_restored, zigpy_device, file_not_found=True
)
entity_id = find_entity_id(Platform.UPDATE, zha_device, hass)
assert entity_id is not None
# allow traffic to flow through the gateway and device
await async_enable_traffic(hass, [zha_device])
state = hass.states.get(entity_id)
assert state.state == STATE_OFF
attrs = state.attributes
assert attrs[ATTR_INSTALLED_VERSION] == f"0x{installed_fw_version:08x}"
assert not attrs[ATTR_IN_PROGRESS]
assert attrs[ATTR_LATEST_VERSION] == f"0x{installed_fw_version:08x}"
async def test_firmware_update_restore_version_from_state_machine(
hass: HomeAssistant, zha_device_joined_restored, zigpy_device
) -> None:
"""Test ZHA update platform - restore data - file not found."""
fw_version = 0x12345678
installed_fw_version = fw_version - 10
mock_restore_cache_with_extra_data(
hass,
[
(
State(
"update.fakemanufacturer_fakemodel_firmware",
STATE_ON,
{
ATTR_INSTALLED_VERSION: f"0x{installed_fw_version:08x}",
ATTR_LATEST_VERSION: f"0x{fw_version:08x}",
ATTR_SKIPPED_VERSION: None,
},
),
{"image_type": 0x90},
)
],
)
zha_device, cluster, fw_image, installed_fw_version = await setup_test_data(
zha_device_joined_restored,
zigpy_device,
skip_attribute_plugs=True,
file_not_found=True,
)
entity_id = find_entity_id(Platform.UPDATE, zha_device, hass)
assert entity_id is not None
# allow traffic to flow through the gateway and device
await async_enable_traffic(hass, [zha_device])
state = hass.states.get(entity_id)
assert state.state == STATE_OFF
attrs = state.attributes
assert attrs[ATTR_INSTALLED_VERSION] == f"0x{installed_fw_version:08x}"
assert not attrs[ATTR_IN_PROGRESS]
assert attrs[ATTR_LATEST_VERSION] == f"0x{installed_fw_version:08x}"

View file

@ -127,6 +127,11 @@ DEVICES = [
DEV_SIG_ENT_MAP_CLASS: "LQISensor",
DEV_SIG_ENT_MAP_ID: "sensor.bosch_isw_zpr1_wp13_lqi",
},
("update", "00:11:22:33:44:55:66:77-5-25-firmware_update"): {
DEV_SIG_CLUSTER_HANDLERS: ["ota"],
DEV_SIG_ENT_MAP_CLASS: "ZHAFirmwareUpdateEntity",
DEV_SIG_ENT_MAP_ID: "update.bosch_isw_zpr1_wp13_firmware",
},
},
},
{
@ -165,6 +170,11 @@ DEVICES = [
DEV_SIG_ENT_MAP_CLASS: "LQISensor",
DEV_SIG_ENT_MAP_ID: "sensor.centralite_3130_lqi",
},
("update", "00:11:22:33:44:55:66:77-1-25-firmware_update"): {
DEV_SIG_CLUSTER_HANDLERS: ["ota"],
DEV_SIG_ENT_MAP_CLASS: "ZHAFirmwareUpdateEntity",
DEV_SIG_ENT_MAP_ID: "update.centralite_3130_firmware",
},
},
},
{
@ -248,6 +258,11 @@ DEVICES = [
DEV_SIG_ENT_MAP_CLASS: "LQISensor",
DEV_SIG_ENT_MAP_ID: "sensor.centralite_3210_l_lqi",
},
("update", "00:11:22:33:44:55:66:77-1-25-firmware_update"): {
DEV_SIG_CLUSTER_HANDLERS: ["ota"],
DEV_SIG_ENT_MAP_CLASS: "ZHAFirmwareUpdateEntity",
DEV_SIG_ENT_MAP_ID: "update.centralite_3210_l_firmware",
},
},
},
{
@ -296,6 +311,11 @@ DEVICES = [
DEV_SIG_ENT_MAP_CLASS: "Humidity",
DEV_SIG_ENT_MAP_ID: "sensor.centralite_3310_s_humidity",
},
("update", "00:11:22:33:44:55:66:77-1-25-firmware_update"): {
DEV_SIG_CLUSTER_HANDLERS: ["ota"],
DEV_SIG_ENT_MAP_CLASS: "ZHAFirmwareUpdateEntity",
DEV_SIG_ENT_MAP_ID: "update.centralite_3310_s_firmware",
},
},
},
{
@ -351,6 +371,11 @@ DEVICES = [
DEV_SIG_ENT_MAP_CLASS: "LQISensor",
DEV_SIG_ENT_MAP_ID: "sensor.centralite_3315_s_lqi",
},
("update", "00:11:22:33:44:55:66:77-1-25-firmware_update"): {
DEV_SIG_CLUSTER_HANDLERS: ["ota"],
DEV_SIG_ENT_MAP_CLASS: "ZHAFirmwareUpdateEntity",
DEV_SIG_ENT_MAP_ID: "update.centralite_3315_s_firmware",
},
},
},
{
@ -406,6 +431,11 @@ DEVICES = [
DEV_SIG_ENT_MAP_CLASS: "LQISensor",
DEV_SIG_ENT_MAP_ID: "sensor.centralite_3320_l_lqi",
},
("update", "00:11:22:33:44:55:66:77-1-25-firmware_update"): {
DEV_SIG_CLUSTER_HANDLERS: ["ota"],
DEV_SIG_ENT_MAP_CLASS: "ZHAFirmwareUpdateEntity",
DEV_SIG_ENT_MAP_ID: "update.centralite_3320_l_firmware",
},
},
},
{
@ -461,6 +491,11 @@ DEVICES = [
DEV_SIG_ENT_MAP_CLASS: "LQISensor",
DEV_SIG_ENT_MAP_ID: "sensor.centralite_3326_l_lqi",
},
("update", "00:11:22:33:44:55:66:77-1-25-firmware_update"): {
DEV_SIG_CLUSTER_HANDLERS: ["ota"],
DEV_SIG_ENT_MAP_CLASS: "ZHAFirmwareUpdateEntity",
DEV_SIG_ENT_MAP_ID: "update.centralite_3326_l_firmware",
},
},
},
{
@ -523,6 +558,11 @@ DEVICES = [
"binary_sensor.centralite_motion_sensor_a_occupancy"
),
},
("update", "00:11:22:33:44:55:66:77-1-25-firmware_update"): {
DEV_SIG_CLUSTER_HANDLERS: ["ota"],
DEV_SIG_ENT_MAP_CLASS: "ZHAFirmwareUpdateEntity",
DEV_SIG_ENT_MAP_ID: "update.centralite_motion_sensor_a_firmware",
},
},
},
{
@ -593,6 +633,11 @@ DEVICES = [
DEV_SIG_ENT_MAP_CLASS: "LQISensor",
DEV_SIG_ENT_MAP_ID: "sensor.climaxtechnology_psmp5_00_00_02_02tc_lqi",
},
("update", "00:11:22:33:44:55:66:77-4-25-firmware_update"): {
DEV_SIG_CLUSTER_HANDLERS: ["ota"],
DEV_SIG_ENT_MAP_CLASS: "ZHAFirmwareUpdateEntity",
DEV_SIG_ENT_MAP_ID: "update.climaxtechnology_psmp5_00_00_02_02tc_firmware",
},
},
},
{
@ -798,6 +843,11 @@ DEVICES = [
DEV_SIG_ENT_MAP_CLASS: "LQISensor",
DEV_SIG_ENT_MAP_ID: "sensor.heiman_smokesensor_em_lqi",
},
("update", "00:11:22:33:44:55:66:77-1-25-firmware_update"): {
DEV_SIG_CLUSTER_HANDLERS: ["ota"],
DEV_SIG_ENT_MAP_CLASS: "ZHAFirmwareUpdateEntity",
DEV_SIG_ENT_MAP_ID: "update.heiman_smokesensor_em_firmware",
},
},
},
{
@ -836,6 +886,11 @@ DEVICES = [
DEV_SIG_ENT_MAP_CLASS: "LQISensor",
DEV_SIG_ENT_MAP_ID: "sensor.heiman_co_v16_lqi",
},
("update", "00:11:22:33:44:55:66:77-1-25-firmware_update"): {
DEV_SIG_CLUSTER_HANDLERS: ["ota"],
DEV_SIG_ENT_MAP_CLASS: "ZHAFirmwareUpdateEntity",
DEV_SIG_ENT_MAP_ID: "update.heiman_co_v16_firmware",
},
},
},
{
@ -899,6 +954,11 @@ DEVICES = [
DEV_SIG_ENT_MAP_CLASS: "LQISensor",
DEV_SIG_ENT_MAP_ID: "sensor.heiman_warningdevice_lqi",
},
("update", "00:11:22:33:44:55:66:77-1-25-firmware_update"): {
DEV_SIG_CLUSTER_HANDLERS: ["ota"],
DEV_SIG_ENT_MAP_CLASS: "ZHAFirmwareUpdateEntity",
DEV_SIG_ENT_MAP_ID: "update.heiman_warningdevice_firmware",
},
},
},
{
@ -952,6 +1012,11 @@ DEVICES = [
DEV_SIG_ENT_MAP_CLASS: "LQISensor",
DEV_SIG_ENT_MAP_ID: "sensor.hivehome_com_mot003_lqi",
},
("update", "00:11:22:33:44:55:66:77-6-25-firmware_update"): {
DEV_SIG_CLUSTER_HANDLERS: ["ota"],
DEV_SIG_ENT_MAP_CLASS: "ZHAFirmwareUpdateEntity",
DEV_SIG_ENT_MAP_ID: "update.hivehome_com_mot003_firmware",
},
},
},
{
@ -1005,6 +1070,11 @@ DEVICES = [
"sensor.ikea_of_sweden_tradfri_bulb_e12_ws_opal_600lm_lqi"
),
},
("update", "00:11:22:33:44:55:66:77-1-25-firmware_update"): {
DEV_SIG_CLUSTER_HANDLERS: ["ota"],
DEV_SIG_ENT_MAP_CLASS: "ZHAFirmwareUpdateEntity",
DEV_SIG_ENT_MAP_ID: "update.ikea_of_sweden_tradfri_bulb_e12_ws_opal_600lm_firmware",
},
},
},
{
@ -1051,6 +1121,11 @@ DEVICES = [
"sensor.ikea_of_sweden_tradfri_bulb_e26_cws_opal_600lm_lqi"
),
},
("update", "00:11:22:33:44:55:66:77-1-25-firmware_update"): {
DEV_SIG_CLUSTER_HANDLERS: ["ota"],
DEV_SIG_ENT_MAP_CLASS: "ZHAFirmwareUpdateEntity",
DEV_SIG_ENT_MAP_ID: "update.ikea_of_sweden_tradfri_bulb_e26_cws_opal_600lm_firmware",
},
},
},
{
@ -1097,6 +1172,11 @@ DEVICES = [
"sensor.ikea_of_sweden_tradfri_bulb_e26_w_opal_1000lm_lqi"
),
},
("update", "00:11:22:33:44:55:66:77-1-25-firmware_update"): {
DEV_SIG_CLUSTER_HANDLERS: ["ota"],
DEV_SIG_ENT_MAP_CLASS: "ZHAFirmwareUpdateEntity",
DEV_SIG_ENT_MAP_ID: "update.ikea_of_sweden_tradfri_bulb_e26_w_opal_1000lm_firmware",
},
},
},
{
@ -1143,6 +1223,11 @@ DEVICES = [
"sensor.ikea_of_sweden_tradfri_bulb_e26_ws_opal_980lm_lqi"
),
},
("update", "00:11:22:33:44:55:66:77-1-25-firmware_update"): {
DEV_SIG_CLUSTER_HANDLERS: ["ota"],
DEV_SIG_ENT_MAP_CLASS: "ZHAFirmwareUpdateEntity",
DEV_SIG_ENT_MAP_ID: "update.ikea_of_sweden_tradfri_bulb_e26_ws_opal_980lm_firmware",
},
},
},
{
@ -1189,6 +1274,11 @@ DEVICES = [
"sensor.ikea_of_sweden_tradfri_bulb_e26_opal_1000lm_lqi"
),
},
("update", "00:11:22:33:44:55:66:77-1-25-firmware_update"): {
DEV_SIG_CLUSTER_HANDLERS: ["ota"],
DEV_SIG_ENT_MAP_CLASS: "ZHAFirmwareUpdateEntity",
DEV_SIG_ENT_MAP_ID: "update.ikea_of_sweden_tradfri_bulb_e26_opal_1000lm_firmware",
},
},
},
{
@ -1231,6 +1321,11 @@ DEVICES = [
DEV_SIG_ENT_MAP_CLASS: "LQISensor",
DEV_SIG_ENT_MAP_ID: "sensor.ikea_of_sweden_tradfri_control_outlet_lqi",
},
("update", "00:11:22:33:44:55:66:77-1-25-firmware_update"): {
DEV_SIG_CLUSTER_HANDLERS: ["ota"],
DEV_SIG_ENT_MAP_CLASS: "ZHAFirmwareUpdateEntity",
DEV_SIG_ENT_MAP_ID: "update.ikea_of_sweden_tradfri_control_outlet_firmware",
},
},
},
{
@ -1280,6 +1375,11 @@ DEVICES = [
"binary_sensor.ikea_of_sweden_tradfri_motion_sensor_motion"
),
},
("update", "00:11:22:33:44:55:66:77-1-25-firmware_update"): {
DEV_SIG_CLUSTER_HANDLERS: ["ota"],
DEV_SIG_ENT_MAP_CLASS: "ZHAFirmwareUpdateEntity",
DEV_SIG_ENT_MAP_ID: "update.ikea_of_sweden_tradfri_motion_sensor_firmware",
},
},
},
{
@ -1322,6 +1422,11 @@ DEVICES = [
DEV_SIG_ENT_MAP_CLASS: "LQISensor",
DEV_SIG_ENT_MAP_ID: "sensor.ikea_of_sweden_tradfri_on_off_switch_lqi",
},
("update", "00:11:22:33:44:55:66:77-1-25-firmware_update"): {
DEV_SIG_CLUSTER_HANDLERS: ["ota"],
DEV_SIG_ENT_MAP_CLASS: "ZHAFirmwareUpdateEntity",
DEV_SIG_ENT_MAP_ID: "update.ikea_of_sweden_tradfri_on_off_switch_firmware",
},
},
},
{
@ -1364,6 +1469,11 @@ DEVICES = [
DEV_SIG_ENT_MAP_CLASS: "LQISensor",
DEV_SIG_ENT_MAP_ID: "sensor.ikea_of_sweden_tradfri_remote_control_lqi",
},
("update", "00:11:22:33:44:55:66:77-1-25-firmware_update"): {
DEV_SIG_CLUSTER_HANDLERS: ["ota"],
DEV_SIG_ENT_MAP_CLASS: "ZHAFirmwareUpdateEntity",
DEV_SIG_ENT_MAP_ID: "update.ikea_of_sweden_tradfri_remote_control_firmware",
},
},
},
{
@ -1408,6 +1518,11 @@ DEVICES = [
DEV_SIG_ENT_MAP_CLASS: "LQISensor",
DEV_SIG_ENT_MAP_ID: "sensor.ikea_of_sweden_tradfri_signal_repeater_lqi",
},
("update", "00:11:22:33:44:55:66:77-1-25-firmware_update"): {
DEV_SIG_CLUSTER_HANDLERS: ["ota"],
DEV_SIG_ENT_MAP_CLASS: "ZHAFirmwareUpdateEntity",
DEV_SIG_ENT_MAP_ID: "update.ikea_of_sweden_tradfri_signal_repeater_firmware",
},
},
},
{
@ -1452,6 +1567,11 @@ DEVICES = [
DEV_SIG_ENT_MAP_CLASS: "LQISensor",
DEV_SIG_ENT_MAP_ID: "sensor.ikea_of_sweden_tradfri_wireless_dimmer_lqi",
},
("update", "00:11:22:33:44:55:66:77-1-25-firmware_update"): {
DEV_SIG_CLUSTER_HANDLERS: ["ota"],
DEV_SIG_ENT_MAP_CLASS: "ZHAFirmwareUpdateEntity",
DEV_SIG_ENT_MAP_ID: "update.ikea_of_sweden_tradfri_wireless_dimmer_firmware",
},
},
},
{
@ -1512,6 +1632,11 @@ DEVICES = [
DEV_SIG_ENT_MAP_CLASS: "LQISensor",
DEV_SIG_ENT_MAP_ID: "sensor.jasco_products_45852_lqi",
},
("update", "00:11:22:33:44:55:66:77-1-25-firmware_update"): {
DEV_SIG_CLUSTER_HANDLERS: ["ota"],
DEV_SIG_ENT_MAP_CLASS: "ZHAFirmwareUpdateEntity",
DEV_SIG_ENT_MAP_ID: "update.jasco_products_45852_firmware",
},
},
},
{
@ -1572,6 +1697,11 @@ DEVICES = [
DEV_SIG_ENT_MAP_CLASS: "LQISensor",
DEV_SIG_ENT_MAP_ID: "sensor.jasco_products_45856_lqi",
},
("update", "00:11:22:33:44:55:66:77-1-25-firmware_update"): {
DEV_SIG_CLUSTER_HANDLERS: ["ota"],
DEV_SIG_ENT_MAP_CLASS: "ZHAFirmwareUpdateEntity",
DEV_SIG_ENT_MAP_ID: "update.jasco_products_45856_firmware",
},
},
},
{
@ -1632,6 +1762,11 @@ DEVICES = [
DEV_SIG_ENT_MAP_CLASS: "LQISensor",
DEV_SIG_ENT_MAP_ID: "sensor.jasco_products_45857_lqi",
},
("update", "00:11:22:33:44:55:66:77-1-25-firmware_update"): {
DEV_SIG_CLUSTER_HANDLERS: ["ota"],
DEV_SIG_ENT_MAP_CLASS: "ZHAFirmwareUpdateEntity",
DEV_SIG_ENT_MAP_ID: "update.jasco_products_45857_firmware",
},
},
},
{
@ -1685,6 +1820,11 @@ DEVICES = [
DEV_SIG_ENT_MAP_CLASS: "LQISensor",
DEV_SIG_ENT_MAP_ID: "sensor.keen_home_inc_sv02_610_mp_1_3_lqi",
},
("update", "00:11:22:33:44:55:66:77-1-25-firmware_update"): {
DEV_SIG_CLUSTER_HANDLERS: ["ota"],
DEV_SIG_ENT_MAP_CLASS: "ZHAFirmwareUpdateEntity",
DEV_SIG_ENT_MAP_ID: "update.keen_home_inc_sv02_610_mp_1_3_firmware",
},
},
},
{
@ -1738,6 +1878,11 @@ DEVICES = [
DEV_SIG_ENT_MAP_CLASS: "LQISensor",
DEV_SIG_ENT_MAP_ID: "sensor.keen_home_inc_sv02_612_mp_1_2_lqi",
},
("update", "00:11:22:33:44:55:66:77-1-25-firmware_update"): {
DEV_SIG_CLUSTER_HANDLERS: ["ota"],
DEV_SIG_ENT_MAP_CLASS: "ZHAFirmwareUpdateEntity",
DEV_SIG_ENT_MAP_ID: "update.keen_home_inc_sv02_612_mp_1_2_firmware",
},
},
},
{
@ -1791,6 +1936,11 @@ DEVICES = [
DEV_SIG_ENT_MAP_CLASS: "LQISensor",
DEV_SIG_ENT_MAP_ID: "sensor.keen_home_inc_sv02_612_mp_1_3_lqi",
},
("update", "00:11:22:33:44:55:66:77-1-25-firmware_update"): {
DEV_SIG_CLUSTER_HANDLERS: ["ota"],
DEV_SIG_ENT_MAP_CLASS: "ZHAFirmwareUpdateEntity",
DEV_SIG_ENT_MAP_ID: "update.keen_home_inc_sv02_612_mp_1_3_firmware",
},
},
},
{
@ -1836,6 +1986,11 @@ DEVICES = [
DEV_SIG_ENT_MAP_CLASS: "KofFan",
DEV_SIG_ENT_MAP_ID: "fan.king_of_fans_inc_hbuniversalcfremote_fan",
},
("update", "00:11:22:33:44:55:66:77-1-25-firmware_update"): {
DEV_SIG_CLUSTER_HANDLERS: ["ota"],
DEV_SIG_ENT_MAP_CLASS: "ZHAFirmwareUpdateEntity",
DEV_SIG_ENT_MAP_ID: "update.king_of_fans_inc_hbuniversalcfremote_firmware",
},
},
},
{
@ -1874,6 +2029,11 @@ DEVICES = [
DEV_SIG_ENT_MAP_CLASS: "LQISensor",
DEV_SIG_ENT_MAP_ID: "sensor.lds_zbt_cctswitch_d0001_lqi",
},
("update", "00:11:22:33:44:55:66:77-1-25-firmware_update"): {
DEV_SIG_CLUSTER_HANDLERS: ["ota"],
DEV_SIG_ENT_MAP_CLASS: "ZHAFirmwareUpdateEntity",
DEV_SIG_ENT_MAP_ID: "update.lds_zbt_cctswitch_d0001_firmware",
},
},
},
{
@ -1912,6 +2072,11 @@ DEVICES = [
DEV_SIG_ENT_MAP_CLASS: "LQISensor",
DEV_SIG_ENT_MAP_ID: "sensor.ledvance_a19_rgbw_lqi",
},
("update", "00:11:22:33:44:55:66:77-1-25-firmware_update"): {
DEV_SIG_CLUSTER_HANDLERS: ["ota"],
DEV_SIG_ENT_MAP_CLASS: "ZHAFirmwareUpdateEntity",
DEV_SIG_ENT_MAP_ID: "update.ledvance_a19_rgbw_firmware",
},
},
},
{
@ -1950,6 +2115,11 @@ DEVICES = [
DEV_SIG_ENT_MAP_CLASS: "LQISensor",
DEV_SIG_ENT_MAP_ID: "sensor.ledvance_flex_rgbw_lqi",
},
("update", "00:11:22:33:44:55:66:77-1-25-firmware_update"): {
DEV_SIG_CLUSTER_HANDLERS: ["ota"],
DEV_SIG_ENT_MAP_CLASS: "ZHAFirmwareUpdateEntity",
DEV_SIG_ENT_MAP_ID: "update.ledvance_flex_rgbw_firmware",
},
},
},
{
@ -1988,6 +2158,11 @@ DEVICES = [
DEV_SIG_ENT_MAP_CLASS: "LQISensor",
DEV_SIG_ENT_MAP_ID: "sensor.ledvance_plug_lqi",
},
("update", "00:11:22:33:44:55:66:77-1-25-firmware_update"): {
DEV_SIG_CLUSTER_HANDLERS: ["ota"],
DEV_SIG_ENT_MAP_CLASS: "ZHAFirmwareUpdateEntity",
DEV_SIG_ENT_MAP_ID: "update.ledvance_plug_firmware",
},
},
},
{
@ -2026,6 +2201,11 @@ DEVICES = [
DEV_SIG_ENT_MAP_CLASS: "LQISensor",
DEV_SIG_ENT_MAP_ID: "sensor.ledvance_rt_rgbw_lqi",
},
("update", "00:11:22:33:44:55:66:77-1-25-firmware_update"): {
DEV_SIG_CLUSTER_HANDLERS: ["ota"],
DEV_SIG_ENT_MAP_CLASS: "ZHAFirmwareUpdateEntity",
DEV_SIG_ENT_MAP_ID: "update.ledvance_rt_rgbw_firmware",
},
},
},
{
@ -2110,6 +2290,11 @@ DEVICES = [
DEV_SIG_ENT_MAP_CLASS: "SmartEnergySummation",
DEV_SIG_ENT_MAP_ID: "sensor.lumi_lumi_plug_maus01_summation_delivered",
},
("update", "00:11:22:33:44:55:66:77-1-25-firmware_update"): {
DEV_SIG_CLUSTER_HANDLERS: ["ota"],
DEV_SIG_ENT_MAP_CLASS: "ZHAFirmwareUpdateEntity",
DEV_SIG_ENT_MAP_ID: "update.lumi_lumi_plug_maus01_firmware",
},
},
},
{
@ -2210,6 +2395,11 @@ DEVICES = [
DEV_SIG_ENT_MAP_CLASS: "Light",
DEV_SIG_ENT_MAP_ID: "light.lumi_lumi_relay_c2acn01_light_2",
},
("update", "00:11:22:33:44:55:66:77-1-25-firmware_update"): {
DEV_SIG_CLUSTER_HANDLERS: ["ota"],
DEV_SIG_ENT_MAP_CLASS: "ZHAFirmwareUpdateEntity",
DEV_SIG_ENT_MAP_ID: "update.lumi_lumi_relay_c2acn01_firmware",
},
},
},
{
@ -2262,6 +2452,11 @@ DEVICES = [
DEV_SIG_ENT_MAP_CLASS: "LQISensor",
DEV_SIG_ENT_MAP_ID: "sensor.lumi_lumi_remote_b186acn01_lqi",
},
("update", "00:11:22:33:44:55:66:77-1-25-firmware_update"): {
DEV_SIG_CLUSTER_HANDLERS: ["ota"],
DEV_SIG_ENT_MAP_CLASS: "ZHAFirmwareUpdateEntity",
DEV_SIG_ENT_MAP_ID: "update.lumi_lumi_remote_b186acn01_firmware",
},
},
},
{
@ -2314,6 +2509,11 @@ DEVICES = [
DEV_SIG_ENT_MAP_CLASS: "LQISensor",
DEV_SIG_ENT_MAP_ID: "sensor.lumi_lumi_remote_b286acn01_lqi",
},
("update", "00:11:22:33:44:55:66:77-1-25-firmware_update"): {
DEV_SIG_CLUSTER_HANDLERS: ["ota"],
DEV_SIG_ENT_MAP_CLASS: "ZHAFirmwareUpdateEntity",
DEV_SIG_ENT_MAP_ID: "update.lumi_lumi_remote_b286acn01_firmware",
},
},
},
{
@ -2780,6 +2980,11 @@ DEVICES = [
DEV_SIG_ENT_MAP_CLASS: "LQISensor",
DEV_SIG_ENT_MAP_ID: "sensor.lumi_lumi_sensor_86sw1_lqi",
},
("update", "00:11:22:33:44:55:66:77-1-25-firmware_update"): {
DEV_SIG_CLUSTER_HANDLERS: ["ota"],
DEV_SIG_ENT_MAP_CLASS: "ZHAFirmwareUpdateEntity",
DEV_SIG_ENT_MAP_ID: "update.lumi_lumi_sensor_86sw1_firmware",
},
},
},
{
@ -2832,6 +3037,11 @@ DEVICES = [
DEV_SIG_ENT_MAP_CLASS: "LQISensor",
DEV_SIG_ENT_MAP_ID: "sensor.lumi_lumi_sensor_cube_aqgl01_lqi",
},
("update", "00:11:22:33:44:55:66:77-1-25-firmware_update"): {
DEV_SIG_CLUSTER_HANDLERS: ["ota"],
DEV_SIG_ENT_MAP_CLASS: "ZHAFirmwareUpdateEntity",
DEV_SIG_ENT_MAP_ID: "update.lumi_lumi_sensor_cube_aqgl01_firmware",
},
},
},
{
@ -2894,6 +3104,11 @@ DEVICES = [
DEV_SIG_ENT_MAP_CLASS: "Humidity",
DEV_SIG_ENT_MAP_ID: "sensor.lumi_lumi_sensor_ht_humidity",
},
("update", "00:11:22:33:44:55:66:77-1-25-firmware_update"): {
DEV_SIG_CLUSTER_HANDLERS: ["ota"],
DEV_SIG_ENT_MAP_CLASS: "ZHAFirmwareUpdateEntity",
DEV_SIG_ENT_MAP_ID: "update.lumi_lumi_sensor_ht_firmware",
},
},
},
{
@ -2937,6 +3152,11 @@ DEVICES = [
DEV_SIG_ENT_MAP_CLASS: "Opening",
DEV_SIG_ENT_MAP_ID: "binary_sensor.lumi_lumi_sensor_magnet_opening",
},
("update", "00:11:22:33:44:55:66:77-1-25-firmware_update"): {
DEV_SIG_CLUSTER_HANDLERS: ["ota"],
DEV_SIG_ENT_MAP_CLASS: "ZHAFirmwareUpdateEntity",
DEV_SIG_ENT_MAP_ID: "update.lumi_lumi_sensor_magnet_firmware",
},
},
},
{
@ -3042,6 +3262,11 @@ DEVICES = [
DEV_SIG_ENT_MAP_CLASS: "LQISensor",
DEV_SIG_ENT_MAP_ID: "sensor.lumi_lumi_sensor_motion_aq2_lqi",
},
("update", "00:11:22:33:44:55:66:77-1-25-firmware_update"): {
DEV_SIG_CLUSTER_HANDLERS: ["ota"],
DEV_SIG_ENT_MAP_CLASS: "ZHAFirmwareUpdateEntity",
DEV_SIG_ENT_MAP_ID: "update.lumi_lumi_sensor_motion_aq2_firmware",
},
},
},
{
@ -3092,6 +3317,11 @@ DEVICES = [
DEV_SIG_ENT_MAP_CLASS: "LQISensor",
DEV_SIG_ENT_MAP_ID: "sensor.lumi_lumi_sensor_smoke_lqi",
},
("update", "00:11:22:33:44:55:66:77-1-25-firmware_update"): {
DEV_SIG_CLUSTER_HANDLERS: ["ota"],
DEV_SIG_ENT_MAP_CLASS: "ZHAFirmwareUpdateEntity",
DEV_SIG_ENT_MAP_ID: "update.lumi_lumi_sensor_smoke_firmware",
},
},
},
{
@ -3130,6 +3360,11 @@ DEVICES = [
DEV_SIG_ENT_MAP_CLASS: "LQISensor",
DEV_SIG_ENT_MAP_ID: "sensor.lumi_lumi_sensor_switch_lqi",
},
("update", "00:11:22:33:44:55:66:77-1-25-firmware_update"): {
DEV_SIG_CLUSTER_HANDLERS: ["ota"],
DEV_SIG_ENT_MAP_CLASS: "ZHAFirmwareUpdateEntity",
DEV_SIG_ENT_MAP_ID: "update.lumi_lumi_sensor_switch_firmware",
},
},
},
{
@ -3246,6 +3481,11 @@ DEVICES = [
DEV_SIG_ENT_MAP_CLASS: "LQISensor",
DEV_SIG_ENT_MAP_ID: "sensor.lumi_lumi_sensor_wleak_aq1_lqi",
},
("update", "00:11:22:33:44:55:66:77-1-25-firmware_update"): {
DEV_SIG_CLUSTER_HANDLERS: ["ota"],
DEV_SIG_ENT_MAP_CLASS: "ZHAFirmwareUpdateEntity",
DEV_SIG_ENT_MAP_ID: "update.lumi_lumi_sensor_wleak_aq1_firmware",
},
},
},
{
@ -3316,6 +3556,11 @@ DEVICES = [
DEV_SIG_ENT_MAP_CLASS: "DeviceTemperature",
DEV_SIG_ENT_MAP_ID: "sensor.lumi_lumi_vibration_aq1_device_temperature",
},
("update", "00:11:22:33:44:55:66:77-1-25-firmware_update"): {
DEV_SIG_CLUSTER_HANDLERS: ["ota"],
DEV_SIG_ENT_MAP_CLASS: "ZHAFirmwareUpdateEntity",
DEV_SIG_ENT_MAP_ID: "update.lumi_lumi_vibration_aq1_firmware",
},
},
},
{
@ -3534,6 +3779,11 @@ DEVICES = [
DEV_SIG_ENT_MAP_CLASS: "LQISensor",
DEV_SIG_ENT_MAP_ID: "sensor.osram_lightify_a19_rgbw_lqi",
},
("update", "00:11:22:33:44:55:66:77-3-25-firmware_update"): {
DEV_SIG_CLUSTER_HANDLERS: ["ota"],
DEV_SIG_ENT_MAP_CLASS: "ZHAFirmwareUpdateEntity",
DEV_SIG_ENT_MAP_ID: "update.osram_lightify_a19_rgbw_firmware",
},
},
},
{
@ -3572,6 +3822,11 @@ DEVICES = [
DEV_SIG_ENT_MAP_CLASS: "LQISensor",
DEV_SIG_ENT_MAP_ID: "sensor.osram_lightify_dimming_switch_lqi",
},
("update", "00:11:22:33:44:55:66:77-1-25-firmware_update"): {
DEV_SIG_CLUSTER_HANDLERS: ["ota"],
DEV_SIG_ENT_MAP_CLASS: "ZHAFirmwareUpdateEntity",
DEV_SIG_ENT_MAP_ID: "update.osram_lightify_dimming_switch_firmware",
},
},
},
{
@ -3610,6 +3865,11 @@ DEVICES = [
DEV_SIG_ENT_MAP_CLASS: "LQISensor",
DEV_SIG_ENT_MAP_ID: "sensor.osram_lightify_flex_rgbw_lqi",
},
("update", "00:11:22:33:44:55:66:77-3-25-firmware_update"): {
DEV_SIG_CLUSTER_HANDLERS: ["ota"],
DEV_SIG_ENT_MAP_CLASS: "ZHAFirmwareUpdateEntity",
DEV_SIG_ENT_MAP_ID: "update.osram_lightify_flex_rgbw_firmware",
},
},
},
{
@ -3684,6 +3944,11 @@ DEVICES = [
DEV_SIG_ENT_MAP_CLASS: "LQISensor",
DEV_SIG_ENT_MAP_ID: "sensor.osram_lightify_rt_tunable_white_lqi",
},
("update", "00:11:22:33:44:55:66:77-3-25-firmware_update"): {
DEV_SIG_CLUSTER_HANDLERS: ["ota"],
DEV_SIG_ENT_MAP_CLASS: "ZHAFirmwareUpdateEntity",
DEV_SIG_ENT_MAP_ID: "update.osram_lightify_rt_tunable_white_firmware",
},
},
},
{
@ -3722,6 +3987,11 @@ DEVICES = [
DEV_SIG_ENT_MAP_CLASS: "LQISensor",
DEV_SIG_ENT_MAP_ID: "sensor.osram_plug_01_lqi",
},
("update", "00:11:22:33:44:55:66:77-3-25-firmware_update"): {
DEV_SIG_CLUSTER_HANDLERS: ["ota"],
DEV_SIG_ENT_MAP_CLASS: "ZHAFirmwareUpdateEntity",
DEV_SIG_ENT_MAP_ID: "update.osram_plug_01_firmware",
},
},
},
{
@ -3816,6 +4086,11 @@ DEVICES = [
DEV_SIG_ENT_MAP_CLASS: "LQISensor",
DEV_SIG_ENT_MAP_ID: "sensor.osram_switch_4x_lightify_lqi",
},
("update", "00:11:22:33:44:55:66:77-1-25-firmware_update"): {
DEV_SIG_CLUSTER_HANDLERS: ["ota"],
DEV_SIG_ENT_MAP_CLASS: "ZHAFirmwareUpdateEntity",
DEV_SIG_ENT_MAP_ID: "update.osram_switch_4x_lightify_firmware",
},
},
},
{
@ -3866,6 +4141,11 @@ DEVICES = [
DEV_SIG_ENT_MAP_CLASS: "Battery",
DEV_SIG_ENT_MAP_ID: "sensor.philips_rwl020_battery",
},
("update", "00:11:22:33:44:55:66:77-2-25-firmware_update"): {
DEV_SIG_CLUSTER_HANDLERS: ["ota"],
DEV_SIG_ENT_MAP_CLASS: "ZHAFirmwareUpdateEntity",
DEV_SIG_ENT_MAP_ID: "update.philips_rwl020_firmware",
},
},
},
{
@ -3914,6 +4194,11 @@ DEVICES = [
DEV_SIG_ENT_MAP_CLASS: "LQISensor",
DEV_SIG_ENT_MAP_ID: "sensor.samjin_button_lqi",
},
("update", "00:11:22:33:44:55:66:77-1-25-firmware_update"): {
DEV_SIG_CLUSTER_HANDLERS: ["ota"],
DEV_SIG_ENT_MAP_CLASS: "ZHAFirmwareUpdateEntity",
DEV_SIG_ENT_MAP_ID: "update.samjin_button_firmware",
},
},
},
{
@ -3967,6 +4252,11 @@ DEVICES = [
DEV_SIG_ENT_MAP_CLASS: "LQISensor",
DEV_SIG_ENT_MAP_ID: "sensor.samjin_multi_lqi",
},
("update", "00:11:22:33:44:55:66:77-1-25-firmware_update"): {
DEV_SIG_CLUSTER_HANDLERS: ["ota"],
DEV_SIG_ENT_MAP_CLASS: "ZHAFirmwareUpdateEntity",
DEV_SIG_ENT_MAP_ID: "update.samjin_multi_firmware",
},
},
},
{
@ -4015,6 +4305,11 @@ DEVICES = [
DEV_SIG_ENT_MAP_CLASS: "LQISensor",
DEV_SIG_ENT_MAP_ID: "sensor.samjin_water_lqi",
},
("update", "00:11:22:33:44:55:66:77-1-25-firmware_update"): {
DEV_SIG_CLUSTER_HANDLERS: ["ota"],
DEV_SIG_ENT_MAP_CLASS: "ZHAFirmwareUpdateEntity",
DEV_SIG_ENT_MAP_ID: "update.samjin_water_firmware",
},
},
},
{
@ -4083,6 +4378,11 @@ DEVICES = [
DEV_SIG_ENT_MAP_CLASS: "Switch",
DEV_SIG_ENT_MAP_ID: "switch.securifi_ltd_unk_model_switch",
},
("update", "00:11:22:33:44:55:66:77-1-25-firmware_update"): {
DEV_SIG_CLUSTER_HANDLERS: ["ota"],
DEV_SIG_ENT_MAP_CLASS: "ZHAFirmwareUpdateEntity",
DEV_SIG_ENT_MAP_ID: "update.securifi_ltd_unk_model_firmware",
},
},
},
{
@ -4131,6 +4431,11 @@ DEVICES = [
DEV_SIG_ENT_MAP_CLASS: "LQISensor",
DEV_SIG_ENT_MAP_ID: "sensor.sercomm_corp_sz_dws04n_sf_lqi",
},
("update", "00:11:22:33:44:55:66:77-1-25-firmware_update"): {
DEV_SIG_CLUSTER_HANDLERS: ["ota"],
DEV_SIG_ENT_MAP_CLASS: "ZHAFirmwareUpdateEntity",
DEV_SIG_ENT_MAP_ID: "update.sercomm_corp_sz_dws04n_sf_firmware",
},
},
},
{
@ -4221,6 +4526,11 @@ DEVICES = [
DEV_SIG_ENT_MAP_CLASS: "LQISensor",
DEV_SIG_ENT_MAP_ID: "sensor.sercomm_corp_sz_esw01_lqi",
},
("update", "00:11:22:33:44:55:66:77-1-25-firmware_update"): {
DEV_SIG_CLUSTER_HANDLERS: ["ota"],
DEV_SIG_ENT_MAP_CLASS: "ZHAFirmwareUpdateEntity",
DEV_SIG_ENT_MAP_ID: "update.sercomm_corp_sz_esw01_firmware",
},
},
},
{
@ -4274,6 +4584,11 @@ DEVICES = [
DEV_SIG_ENT_MAP_CLASS: "LQISensor",
DEV_SIG_ENT_MAP_ID: "sensor.sercomm_corp_sz_pir04_lqi",
},
("update", "00:11:22:33:44:55:66:77-1-25-firmware_update"): {
DEV_SIG_CLUSTER_HANDLERS: ["ota"],
DEV_SIG_ENT_MAP_CLASS: "ZHAFirmwareUpdateEntity",
DEV_SIG_ENT_MAP_ID: "update.sercomm_corp_sz_pir04_firmware",
},
},
},
{
@ -4344,6 +4659,11 @@ DEVICES = [
DEV_SIG_ENT_MAP_CLASS: "Switch",
DEV_SIG_ENT_MAP_ID: "switch.sinope_technologies_rm3250zb_switch",
},
("update", "00:11:22:33:44:55:66:77-1-25-firmware_update"): {
DEV_SIG_CLUSTER_HANDLERS: ["ota"],
DEV_SIG_ENT_MAP_CLASS: "ZHAFirmwareUpdateEntity",
DEV_SIG_ENT_MAP_ID: "update.sinope_technologies_rm3250zb_firmware",
},
},
},
{
@ -4444,6 +4764,11 @@ DEVICES = [
DEV_SIG_ENT_MAP_CLASS: "SetpointChangeSource",
DEV_SIG_ENT_MAP_ID: "sensor.sinope_technologies_th1123zb_setpoint_change_source",
},
("update", "00:11:22:33:44:55:66:77-1-25-firmware_update"): {
DEV_SIG_CLUSTER_HANDLERS: ["ota"],
DEV_SIG_ENT_MAP_CLASS: "ZHAFirmwareUpdateEntity",
DEV_SIG_ENT_MAP_ID: "update.sinope_technologies_th1123zb_firmware",
},
},
},
{
@ -4544,6 +4869,11 @@ DEVICES = [
DEV_SIG_ENT_MAP_CLASS: "SetpointChangeSource",
DEV_SIG_ENT_MAP_ID: "sensor.sinope_technologies_th1124zb_setpoint_change_source",
},
("update", "00:11:22:33:44:55:66:77-1-25-firmware_update"): {
DEV_SIG_CLUSTER_HANDLERS: ["ota"],
DEV_SIG_ENT_MAP_CLASS: "ZHAFirmwareUpdateEntity",
DEV_SIG_ENT_MAP_ID: "update.sinope_technologies_th1124zb_firmware",
},
},
},
{
@ -4617,6 +4947,11 @@ DEVICES = [
DEV_SIG_ENT_MAP_CLASS: "Switch",
DEV_SIG_ENT_MAP_ID: "switch.smartthings_outletv4_switch",
},
("update", "00:11:22:33:44:55:66:77-1-25-firmware_update"): {
DEV_SIG_CLUSTER_HANDLERS: ["ota"],
DEV_SIG_ENT_MAP_CLASS: "ZHAFirmwareUpdateEntity",
DEV_SIG_ENT_MAP_ID: "update.smartthings_outletv4_firmware",
},
},
},
{
@ -4660,6 +4995,11 @@ DEVICES = [
DEV_SIG_ENT_MAP_CLASS: "LQISensor",
DEV_SIG_ENT_MAP_ID: "sensor.smartthings_tagv4_lqi",
},
("update", "00:11:22:33:44:55:66:77-1-25-firmware_update"): {
DEV_SIG_CLUSTER_HANDLERS: ["ota"],
DEV_SIG_ENT_MAP_CLASS: "ZHAFirmwareUpdateEntity",
DEV_SIG_ENT_MAP_ID: "update.smartthings_tagv4_firmware",
},
},
},
{
@ -4698,6 +5038,11 @@ DEVICES = [
DEV_SIG_ENT_MAP_CLASS: "Switch",
DEV_SIG_ENT_MAP_ID: "switch.third_reality_inc_3rss007z_switch",
},
("update", "00:11:22:33:44:55:66:77-1-25-firmware_update"): {
DEV_SIG_CLUSTER_HANDLERS: ["ota"],
DEV_SIG_ENT_MAP_CLASS: "ZHAFirmwareUpdateEntity",
DEV_SIG_ENT_MAP_ID: "update.third_reality_inc_3rss007z_firmware",
},
},
},
{
@ -4741,6 +5086,11 @@ DEVICES = [
DEV_SIG_ENT_MAP_CLASS: "Switch",
DEV_SIG_ENT_MAP_ID: "switch.third_reality_inc_3rss008z_switch",
},
("update", "00:11:22:33:44:55:66:77-1-25-firmware_update"): {
DEV_SIG_CLUSTER_HANDLERS: ["ota"],
DEV_SIG_ENT_MAP_CLASS: "ZHAFirmwareUpdateEntity",
DEV_SIG_ENT_MAP_ID: "update.third_reality_inc_3rss008z_firmware",
},
},
},
{
@ -4789,6 +5139,11 @@ DEVICES = [
DEV_SIG_ENT_MAP_CLASS: "LQISensor",
DEV_SIG_ENT_MAP_ID: "sensor.visonic_mct_340_e_lqi",
},
("update", "00:11:22:33:44:55:66:77-1-25-firmware_update"): {
DEV_SIG_CLUSTER_HANDLERS: ["ota"],
DEV_SIG_ENT_MAP_CLASS: "ZHAFirmwareUpdateEntity",
DEV_SIG_ENT_MAP_ID: "update.visonic_mct_340_e_firmware",
},
},
},
{
@ -4847,6 +5202,11 @@ DEVICES = [
DEV_SIG_ENT_MAP_CLASS: "SetpointChangeSource",
DEV_SIG_ENT_MAP_ID: "sensor.zen_within_zen_01_setpoint_change_source",
},
("update", "00:11:22:33:44:55:66:77-1-25-firmware_update"): {
DEV_SIG_CLUSTER_HANDLERS: ["ota"],
DEV_SIG_ENT_MAP_CLASS: "ZHAFirmwareUpdateEntity",
DEV_SIG_ENT_MAP_ID: "update.zen_within_zen_01_firmware",
},
},
},
{
@ -4916,6 +5276,11 @@ DEVICES = [
DEV_SIG_ENT_MAP_CLASS: "Light",
DEV_SIG_ENT_MAP_ID: "light.tyzb01_ns1ndbww_ts0004_light_4",
},
("update", "00:11:22:33:44:55:66:77-1-25-firmware_update"): {
DEV_SIG_CLUSTER_HANDLERS: ["ota"],
DEV_SIG_ENT_MAP_CLASS: "ZHAFirmwareUpdateEntity",
DEV_SIG_ENT_MAP_ID: "update.tyzb01_ns1ndbww_ts0004_firmware",
},
},
},
{
@ -5012,6 +5377,11 @@ DEVICES = [
DEV_SIG_ENT_MAP_CLASS: "LQISensor",
DEV_SIG_ENT_MAP_ID: "sensor.sengled_e11_g13_lqi",
},
("update", "00:11:22:33:44:55:66:77-1-25-firmware_update"): {
DEV_SIG_CLUSTER_HANDLERS: ["ota"],
DEV_SIG_ENT_MAP_CLASS: "ZHAFirmwareUpdateEntity",
DEV_SIG_ENT_MAP_ID: "update.sengled_e11_g13_firmware",
},
},
},
{
@ -5065,6 +5435,11 @@ DEVICES = [
DEV_SIG_ENT_MAP_CLASS: "LQISensor",
DEV_SIG_ENT_MAP_ID: "sensor.sengled_e12_n14_lqi",
},
("update", "00:11:22:33:44:55:66:77-1-25-firmware_update"): {
DEV_SIG_CLUSTER_HANDLERS: ["ota"],
DEV_SIG_ENT_MAP_CLASS: "ZHAFirmwareUpdateEntity",
DEV_SIG_ENT_MAP_ID: "update.sengled_e12_n14_firmware",
},
},
},
{
@ -5118,6 +5493,11 @@ DEVICES = [
DEV_SIG_ENT_MAP_CLASS: "LQISensor",
DEV_SIG_ENT_MAP_ID: "sensor.sengled_z01_a19nae26_lqi",
},
("update", "00:11:22:33:44:55:66:77-1-25-firmware_update"): {
DEV_SIG_CLUSTER_HANDLERS: ["ota"],
DEV_SIG_ENT_MAP_CLASS: "ZHAFirmwareUpdateEntity",
DEV_SIG_ENT_MAP_ID: "update.sengled_z01_a19nae26_firmware",
},
},
},
{
@ -5584,6 +5964,11 @@ DEVICES = [
DEV_SIG_ENT_MAP_CLASS: "HueV1MotionSensitivity",
DEV_SIG_ENT_MAP_ID: "select.philips_sml001_motion_sensitivity",
},
("update", "00:11:22:33:44:55:66:77-2-25-firmware_update"): {
DEV_SIG_CLUSTER_HANDLERS: ["ota"],
DEV_SIG_ENT_MAP_CLASS: "ZHAFirmwareUpdateEntity",
DEV_SIG_ENT_MAP_ID: "update.philips_sml001_firmware",
},
},
},
]