mirror of
https://github.com/home-assistant/core.git
synced 2025-07-10 14:57:09 +00:00
Add storage helper to ZHA and use it for the device node descriptor (#21500)
* node descriptor implementation add info to device info disable pylint rule check for success * review comments * send manufacturer code for get attr value for mfg clusters * ST report configs * do zdo task first * add guard * use faster reporting config * disable false positive pylint
This commit is contained in:
parent
ee6f09dd29
commit
fc07d3a159
@ -18,8 +18,13 @@ from ..helpers import (
|
|||||||
safe_read, get_attr_id_by_name)
|
safe_read, get_attr_id_by_name)
|
||||||
from ..const import (
|
from ..const import (
|
||||||
CLUSTER_REPORT_CONFIGS, REPORT_CONFIG_DEFAULT, SIGNAL_ATTR_UPDATED,
|
CLUSTER_REPORT_CONFIGS, REPORT_CONFIG_DEFAULT, SIGNAL_ATTR_UPDATED,
|
||||||
ATTRIBUTE_CHANNEL, EVENT_RELAY_CHANNEL
|
ATTRIBUTE_CHANNEL, EVENT_RELAY_CHANNEL, ZDO_CHANNEL
|
||||||
)
|
)
|
||||||
|
from ..store import async_get_registry
|
||||||
|
|
||||||
|
NODE_DESCRIPTOR_REQUEST = 0x0002
|
||||||
|
MAINS_POWERED = 1
|
||||||
|
BATTERY_OR_UNKNOWN = 0
|
||||||
|
|
||||||
ZIGBEE_CHANNEL_REGISTRY = {}
|
ZIGBEE_CHANNEL_REGISTRY = {}
|
||||||
_LOGGER = logging.getLogger(__name__)
|
_LOGGER = logging.getLogger(__name__)
|
||||||
@ -181,11 +186,16 @@ class ZigbeeChannel:
|
|||||||
|
|
||||||
async def get_attribute_value(self, attribute, from_cache=True):
|
async def get_attribute_value(self, attribute, from_cache=True):
|
||||||
"""Get the value for an attribute."""
|
"""Get the value for an attribute."""
|
||||||
|
manufacturer = None
|
||||||
|
manufacturer_code = self._zha_device.manufacturer_code
|
||||||
|
if self.cluster.cluster_id >= 0xfc00 and manufacturer_code:
|
||||||
|
manufacturer = manufacturer_code
|
||||||
result = await safe_read(
|
result = await safe_read(
|
||||||
self._cluster,
|
self._cluster,
|
||||||
[attribute],
|
[attribute],
|
||||||
allow_cache=from_cache,
|
allow_cache=from_cache,
|
||||||
only_cache=from_cache
|
only_cache=from_cache,
|
||||||
|
manufacturer=manufacturer
|
||||||
)
|
)
|
||||||
return result.get(attribute)
|
return result.get(attribute)
|
||||||
|
|
||||||
@ -235,14 +245,21 @@ class AttributeListeningChannel(ZigbeeChannel):
|
|||||||
class ZDOChannel:
|
class ZDOChannel:
|
||||||
"""Channel for ZDO events."""
|
"""Channel for ZDO events."""
|
||||||
|
|
||||||
|
POWER_SOURCES = {
|
||||||
|
MAINS_POWERED: 'Mains',
|
||||||
|
BATTERY_OR_UNKNOWN: 'Battery or Unknown'
|
||||||
|
}
|
||||||
|
|
||||||
def __init__(self, cluster, device):
|
def __init__(self, cluster, device):
|
||||||
"""Initialize ZDOChannel."""
|
"""Initialize ZDOChannel."""
|
||||||
self.name = 'zdo'
|
self.name = ZDO_CHANNEL
|
||||||
self._cluster = cluster
|
self._cluster = cluster
|
||||||
self._zha_device = device
|
self._zha_device = device
|
||||||
self._status = ChannelStatus.CREATED
|
self._status = ChannelStatus.CREATED
|
||||||
self._unique_id = "{}_ZDO".format(device.name)
|
self._unique_id = "{}_ZDO".format(device.name)
|
||||||
self._cluster.add_listener(self)
|
self._cluster.add_listener(self)
|
||||||
|
self.power_source = None
|
||||||
|
self.manufacturer_code = None
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def unique_id(self):
|
def unique_id(self):
|
||||||
@ -271,10 +288,52 @@ class ZDOChannel:
|
|||||||
|
|
||||||
async def async_initialize(self, from_cache):
|
async def async_initialize(self, from_cache):
|
||||||
"""Initialize channel."""
|
"""Initialize channel."""
|
||||||
|
entry = (await async_get_registry(
|
||||||
|
self._zha_device.hass)).async_get_or_create(self._zha_device)
|
||||||
|
_LOGGER.debug("entry loaded from storage: %s", entry)
|
||||||
|
if entry is not None:
|
||||||
|
self.power_source = entry.power_source
|
||||||
|
self.manufacturer_code = entry.manufacturer_code
|
||||||
|
|
||||||
|
if self.power_source is None:
|
||||||
|
self.power_source = BATTERY_OR_UNKNOWN
|
||||||
|
|
||||||
|
if self.manufacturer_code is None and not from_cache:
|
||||||
|
# this should always be set. This is from us not doing
|
||||||
|
# this previously so lets set it up so users don't have
|
||||||
|
# to reconfigure every device.
|
||||||
|
await self.async_get_node_descriptor(False)
|
||||||
|
entry = (await async_get_registry(
|
||||||
|
self._zha_device.hass)).async_update(self._zha_device)
|
||||||
|
_LOGGER.debug("entry after getting node desc in init: %s", entry)
|
||||||
self._status = ChannelStatus.INITIALIZED
|
self._status = ChannelStatus.INITIALIZED
|
||||||
|
|
||||||
|
async def async_get_node_descriptor(self, from_cache):
|
||||||
|
"""Request the node descriptor from the device."""
|
||||||
|
from zigpy.zdo.types import Status
|
||||||
|
|
||||||
|
if from_cache:
|
||||||
|
return
|
||||||
|
|
||||||
|
node_descriptor = await self._cluster.request(
|
||||||
|
NODE_DESCRIPTOR_REQUEST,
|
||||||
|
self._cluster.device.nwk, tries=3, delay=2)
|
||||||
|
|
||||||
|
def get_bit(byteval, idx):
|
||||||
|
return int(((byteval & (1 << idx)) != 0))
|
||||||
|
|
||||||
|
if node_descriptor is not None and\
|
||||||
|
node_descriptor[0] == Status.SUCCESS:
|
||||||
|
mac_capability_flags = node_descriptor[2].mac_capability_flags
|
||||||
|
|
||||||
|
self.power_source = get_bit(mac_capability_flags, 2)
|
||||||
|
self.manufacturer_code = node_descriptor[2].manufacturer_code
|
||||||
|
|
||||||
|
_LOGGER.debug("node descriptor: %s", node_descriptor)
|
||||||
|
|
||||||
async def async_configure(self):
|
async def async_configure(self):
|
||||||
"""Configure channel."""
|
"""Configure channel."""
|
||||||
|
await self.async_get_node_descriptor(False)
|
||||||
self._status = ChannelStatus.CONFIGURED
|
self._status = ChannelStatus.CONFIGURED
|
||||||
|
|
||||||
|
|
||||||
|
@ -71,6 +71,7 @@ OCCUPANCY = 'occupancy'
|
|||||||
|
|
||||||
ATTR_LEVEL = 'level'
|
ATTR_LEVEL = 'level'
|
||||||
|
|
||||||
|
ZDO_CHANNEL = 'zdo'
|
||||||
ON_OFF_CHANNEL = 'on_off'
|
ON_OFF_CHANNEL = 'on_off'
|
||||||
ATTRIBUTE_CHANNEL = 'attribute'
|
ATTRIBUTE_CHANNEL = 'attribute'
|
||||||
BASIC_CHANNEL = 'basic'
|
BASIC_CHANNEL = 'basic'
|
||||||
@ -91,6 +92,8 @@ SIGNAL_REMOVE = 'remove'
|
|||||||
|
|
||||||
QUIRK_APPLIED = 'quirk_applied'
|
QUIRK_APPLIED = 'quirk_applied'
|
||||||
QUIRK_CLASS = 'quirk_class'
|
QUIRK_CLASS = 'quirk_class'
|
||||||
|
MANUFACTURER_CODE = 'manufacturer_code'
|
||||||
|
POWER_SOURCE = 'power_source'
|
||||||
|
|
||||||
|
|
||||||
class RadioType(enum.Enum):
|
class RadioType(enum.Enum):
|
||||||
|
@ -17,10 +17,10 @@ from .const import (
|
|||||||
ATTR_CLUSTER_ID, ATTR_ATTRIBUTE, ATTR_VALUE, ATTR_COMMAND, SERVER,
|
ATTR_CLUSTER_ID, ATTR_ATTRIBUTE, ATTR_VALUE, ATTR_COMMAND, SERVER,
|
||||||
ATTR_COMMAND_TYPE, ATTR_ARGS, CLIENT_COMMANDS, SERVER_COMMANDS,
|
ATTR_COMMAND_TYPE, ATTR_ARGS, CLIENT_COMMANDS, SERVER_COMMANDS,
|
||||||
ATTR_ENDPOINT_ID, IEEE, MODEL, NAME, UNKNOWN, QUIRK_APPLIED,
|
ATTR_ENDPOINT_ID, IEEE, MODEL, NAME, UNKNOWN, QUIRK_APPLIED,
|
||||||
QUIRK_CLASS, BASIC_CHANNEL
|
QUIRK_CLASS, ZDO_CHANNEL, MANUFACTURER_CODE, POWER_SOURCE
|
||||||
)
|
)
|
||||||
from .channels import EventRelayChannel
|
from .channels import EventRelayChannel, ZDOChannel
|
||||||
from .channels.general import BasicChannel
|
from .store import async_get_registry
|
||||||
|
|
||||||
_LOGGER = logging.getLogger(__name__)
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
@ -69,7 +69,6 @@ class ZHADevice:
|
|||||||
self._zigpy_device.__class__.__module__,
|
self._zigpy_device.__class__.__module__,
|
||||||
self._zigpy_device.__class__.__name__
|
self._zigpy_device.__class__.__name__
|
||||||
)
|
)
|
||||||
self.power_source = None
|
|
||||||
self.status = DeviceStatus.CREATED
|
self.status = DeviceStatus.CREATED
|
||||||
|
|
||||||
@property
|
@property
|
||||||
@ -84,12 +83,12 @@ class ZHADevice:
|
|||||||
|
|
||||||
@property
|
@property
|
||||||
def manufacturer(self):
|
def manufacturer(self):
|
||||||
"""Return ieee address for device."""
|
"""Return manufacturer for device."""
|
||||||
return self._manufacturer
|
return self._manufacturer
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def model(self):
|
def model(self):
|
||||||
"""Return ieee address for device."""
|
"""Return model for device."""
|
||||||
return self._model
|
return self._model
|
||||||
|
|
||||||
@property
|
@property
|
||||||
@ -115,7 +114,15 @@ class ZHADevice:
|
|||||||
@property
|
@property
|
||||||
def manufacturer_code(self):
|
def manufacturer_code(self):
|
||||||
"""Return manufacturer code for device."""
|
"""Return manufacturer code for device."""
|
||||||
# will eventually get this directly from Zigpy
|
if ZDO_CHANNEL in self.cluster_channels:
|
||||||
|
return self.cluster_channels.get(ZDO_CHANNEL).manufacturer_code
|
||||||
|
return None
|
||||||
|
|
||||||
|
@property
|
||||||
|
def power_source(self):
|
||||||
|
"""Return True if sensor is available."""
|
||||||
|
if ZDO_CHANNEL in self.cluster_channels:
|
||||||
|
return self.cluster_channels.get(ZDO_CHANNEL).power_source
|
||||||
return None
|
return None
|
||||||
|
|
||||||
@property
|
@property
|
||||||
@ -164,7 +171,9 @@ class ZHADevice:
|
|||||||
MODEL: self.model,
|
MODEL: self.model,
|
||||||
NAME: self.name or ieee,
|
NAME: self.name or ieee,
|
||||||
QUIRK_APPLIED: self.quirk_applied,
|
QUIRK_APPLIED: self.quirk_applied,
|
||||||
QUIRK_CLASS: self.quirk_class
|
QUIRK_CLASS: self.quirk_class,
|
||||||
|
MANUFACTURER_CODE: self.manufacturer_code,
|
||||||
|
POWER_SOURCE: ZDOChannel.POWER_SOURCES.get(self.power_source)
|
||||||
}
|
}
|
||||||
|
|
||||||
def add_cluster_channel(self, cluster_channel):
|
def add_cluster_channel(self, cluster_channel):
|
||||||
@ -186,19 +195,19 @@ class ZHADevice:
|
|||||||
_LOGGER.debug('%s: started configuration', self.name)
|
_LOGGER.debug('%s: started configuration', self.name)
|
||||||
await self._execute_channel_tasks('async_configure')
|
await self._execute_channel_tasks('async_configure')
|
||||||
_LOGGER.debug('%s: completed configuration', self.name)
|
_LOGGER.debug('%s: completed configuration', self.name)
|
||||||
|
entry = (await async_get_registry(
|
||||||
|
self.hass)).async_create_or_update(self)
|
||||||
|
_LOGGER.debug('%s: stored in registry: %s', self.name, entry)
|
||||||
|
|
||||||
async def async_initialize(self, from_cache=False):
|
async def async_initialize(self, from_cache=False):
|
||||||
"""Initialize channels."""
|
"""Initialize channels."""
|
||||||
_LOGGER.debug('%s: started initialization', self.name)
|
_LOGGER.debug('%s: started initialization', self.name)
|
||||||
await self._execute_channel_tasks('async_initialize', from_cache)
|
await self._execute_channel_tasks('async_initialize', from_cache)
|
||||||
if BASIC_CHANNEL in self.cluster_channels:
|
_LOGGER.debug(
|
||||||
self.power_source = self.cluster_channels.get(
|
'%s: power source: %s',
|
||||||
BASIC_CHANNEL).get_power_source()
|
self.name,
|
||||||
_LOGGER.debug(
|
ZDOChannel.POWER_SOURCES.get(self.power_source)
|
||||||
'%s: power source: %s',
|
)
|
||||||
self.name,
|
|
||||||
BasicChannel.POWER_SOURCES.get(self.power_source)
|
|
||||||
)
|
|
||||||
self.status = DeviceStatus.INITIALIZED
|
self.status = DeviceStatus.INITIALIZED
|
||||||
_LOGGER.debug('%s: completed initialization', self.name)
|
_LOGGER.debug('%s: completed initialization', self.name)
|
||||||
|
|
||||||
@ -206,9 +215,18 @@ class ZHADevice:
|
|||||||
"""Gather and execute a set of CHANNEL tasks."""
|
"""Gather and execute a set of CHANNEL tasks."""
|
||||||
channel_tasks = []
|
channel_tasks = []
|
||||||
semaphore = asyncio.Semaphore(3)
|
semaphore = asyncio.Semaphore(3)
|
||||||
|
zdo_task = None
|
||||||
for channel in self.all_channels:
|
for channel in self.all_channels:
|
||||||
channel_tasks.append(
|
if channel.name == ZDO_CHANNEL:
|
||||||
self._async_create_task(semaphore, channel, task_name, *args))
|
# pylint: disable=E1111
|
||||||
|
zdo_task = self._async_create_task(
|
||||||
|
semaphore, channel, task_name, *args)
|
||||||
|
else:
|
||||||
|
channel_tasks.append(
|
||||||
|
self._async_create_task(
|
||||||
|
semaphore, channel, task_name, *args))
|
||||||
|
if zdo_task is not None:
|
||||||
|
await zdo_task
|
||||||
await asyncio.gather(*channel_tasks)
|
await asyncio.gather(*channel_tasks)
|
||||||
|
|
||||||
async def _async_create_task(self, semaphore, channel, func_name, *args):
|
async def _async_create_task(self, semaphore, channel, func_name, *args):
|
||||||
|
@ -27,9 +27,8 @@ from .const import (
|
|||||||
from .device import ZHADevice, DeviceStatus
|
from .device import ZHADevice, DeviceStatus
|
||||||
from ..device_entity import ZhaDeviceEntity
|
from ..device_entity import ZhaDeviceEntity
|
||||||
from .channels import (
|
from .channels import (
|
||||||
AttributeListeningChannel, EventRelayChannel, ZDOChannel
|
AttributeListeningChannel, EventRelayChannel, ZDOChannel, MAINS_POWERED
|
||||||
)
|
)
|
||||||
from .channels.general import BasicChannel
|
|
||||||
from .channels.registry import ZIGBEE_CHANNEL_REGISTRY
|
from .channels.registry import ZIGBEE_CHANNEL_REGISTRY
|
||||||
from .helpers import convert_ieee
|
from .helpers import convert_ieee
|
||||||
|
|
||||||
@ -38,6 +37,7 @@ _LOGGER = logging.getLogger(__name__)
|
|||||||
SENSOR_TYPES = {}
|
SENSOR_TYPES = {}
|
||||||
BINARY_SENSOR_TYPES = {}
|
BINARY_SENSOR_TYPES = {}
|
||||||
SMARTTHINGS_HUMIDITY_CLUSTER = 64581
|
SMARTTHINGS_HUMIDITY_CLUSTER = 64581
|
||||||
|
SMARTTHINGS_ACCELERATION_CLUSTER = 64514
|
||||||
EntityReference = collections.namedtuple(
|
EntityReference = collections.namedtuple(
|
||||||
'EntityReference', 'reference_id zha_device cluster_channels device_info')
|
'EntityReference', 'reference_id zha_device cluster_channels device_info')
|
||||||
|
|
||||||
@ -163,15 +163,14 @@ class ZHAGateway:
|
|||||||
# configure the device
|
# configure the device
|
||||||
await zha_device.async_configure()
|
await zha_device.async_configure()
|
||||||
elif not zha_device.available and zha_device.power_source is not None\
|
elif not zha_device.available and zha_device.power_source is not None\
|
||||||
and zha_device.power_source != BasicChannel.BATTERY\
|
and zha_device.power_source == MAINS_POWERED:
|
||||||
and zha_device.power_source != BasicChannel.UNKNOWN:
|
|
||||||
# the device is currently marked unavailable and it isn't a battery
|
# the device is currently marked unavailable and it isn't a battery
|
||||||
# powered device so we should be able to update it now
|
# powered device so we should be able to update it now
|
||||||
_LOGGER.debug(
|
_LOGGER.debug(
|
||||||
"attempting to request fresh state for %s %s",
|
"attempting to request fresh state for %s %s",
|
||||||
zha_device.name,
|
zha_device.name,
|
||||||
"with power source: {}".format(
|
"with power source: {}".format(
|
||||||
BasicChannel.POWER_SOURCES.get(zha_device.power_source)
|
ZDOChannel.POWER_SOURCES.get(zha_device.power_source)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
await zha_device.async_initialize(from_cache=False)
|
await zha_device.async_initialize(from_cache=False)
|
||||||
@ -453,6 +452,7 @@ def establish_device_mappings():
|
|||||||
NO_SENSOR_CLUSTERS.append(
|
NO_SENSOR_CLUSTERS.append(
|
||||||
zcl.clusters.general.PowerConfiguration.cluster_id)
|
zcl.clusters.general.PowerConfiguration.cluster_id)
|
||||||
NO_SENSOR_CLUSTERS.append(zcl.clusters.lightlink.LightLink.cluster_id)
|
NO_SENSOR_CLUSTERS.append(zcl.clusters.lightlink.LightLink.cluster_id)
|
||||||
|
NO_SENSOR_CLUSTERS.append(SMARTTHINGS_ACCELERATION_CLUSTER)
|
||||||
|
|
||||||
BINDABLE_CLUSTERS.append(zcl.clusters.general.LevelControl.cluster_id)
|
BINDABLE_CLUSTERS.append(zcl.clusters.general.LevelControl.cluster_id)
|
||||||
BINDABLE_CLUSTERS.append(zcl.clusters.general.OnOff.cluster_id)
|
BINDABLE_CLUSTERS.append(zcl.clusters.general.OnOff.cluster_id)
|
||||||
@ -575,6 +575,27 @@ def establish_device_mappings():
|
|||||||
50
|
50
|
||||||
)
|
)
|
||||||
}],
|
}],
|
||||||
|
SMARTTHINGS_ACCELERATION_CLUSTER: [{
|
||||||
|
'attr': 'acceleration',
|
||||||
|
'config': REPORT_CONFIG_ASAP
|
||||||
|
}, {
|
||||||
|
'attr': 'x_axis',
|
||||||
|
'config': REPORT_CONFIG_ASAP
|
||||||
|
}, {
|
||||||
|
'attr': 'y_axis',
|
||||||
|
'config': REPORT_CONFIG_ASAP
|
||||||
|
}, {
|
||||||
|
'attr': 'z_axis',
|
||||||
|
'config': REPORT_CONFIG_ASAP
|
||||||
|
}],
|
||||||
|
SMARTTHINGS_HUMIDITY_CLUSTER: [{
|
||||||
|
'attr': 'measured_value',
|
||||||
|
'config': (
|
||||||
|
REPORT_CONFIG_MIN_INT,
|
||||||
|
REPORT_CONFIG_MAX_INT,
|
||||||
|
50
|
||||||
|
)
|
||||||
|
}],
|
||||||
zcl.clusters.measurement.PressureMeasurement.cluster_id: [{
|
zcl.clusters.measurement.PressureMeasurement.cluster_id: [{
|
||||||
'attr': 'measured_value',
|
'attr': 'measured_value',
|
||||||
'config': REPORT_CONFIG_DEFAULT
|
'config': REPORT_CONFIG_DEFAULT
|
||||||
|
146
homeassistant/components/zha/core/store.py
Normal file
146
homeassistant/components/zha/core/store.py
Normal file
@ -0,0 +1,146 @@
|
|||||||
|
"""Data storage helper for ZHA."""
|
||||||
|
import logging
|
||||||
|
from collections import OrderedDict
|
||||||
|
# pylint: disable=W0611
|
||||||
|
from typing import MutableMapping # noqa: F401
|
||||||
|
from typing import cast
|
||||||
|
|
||||||
|
import attr
|
||||||
|
|
||||||
|
from homeassistant.core import callback
|
||||||
|
from homeassistant.loader import bind_hass
|
||||||
|
from homeassistant.helpers.typing import HomeAssistantType
|
||||||
|
|
||||||
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
DATA_REGISTRY = 'zha_storage'
|
||||||
|
|
||||||
|
STORAGE_KEY = 'zha.storage'
|
||||||
|
STORAGE_VERSION = 1
|
||||||
|
SAVE_DELAY = 10
|
||||||
|
|
||||||
|
|
||||||
|
@attr.s(slots=True, frozen=True)
|
||||||
|
class ZhaDeviceEntry:
|
||||||
|
"""Zha Device storage Entry."""
|
||||||
|
|
||||||
|
name = attr.ib(type=str, default=None)
|
||||||
|
ieee = attr.ib(type=str, default=None)
|
||||||
|
power_source = attr.ib(type=int, default=None)
|
||||||
|
manufacturer_code = attr.ib(type=int, default=None)
|
||||||
|
|
||||||
|
|
||||||
|
class ZhaDeviceStorage:
|
||||||
|
"""Class to hold a registry of zha devices."""
|
||||||
|
|
||||||
|
def __init__(self, hass: HomeAssistantType) -> None:
|
||||||
|
"""Initialize the zha device storage."""
|
||||||
|
self.hass = hass
|
||||||
|
self.devices = {} # type: MutableMapping[str, ZhaDeviceEntry]
|
||||||
|
self._store = hass.helpers.storage.Store(STORAGE_VERSION, STORAGE_KEY)
|
||||||
|
|
||||||
|
@callback
|
||||||
|
def async_create(self, device) -> ZhaDeviceEntry:
|
||||||
|
"""Create a new ZhaDeviceEntry."""
|
||||||
|
device_entry = ZhaDeviceEntry(
|
||||||
|
name=device.name,
|
||||||
|
ieee=str(device.ieee),
|
||||||
|
power_source=device.power_source,
|
||||||
|
manufacturer_code=device.manufacturer_code
|
||||||
|
|
||||||
|
)
|
||||||
|
self.devices[device_entry.ieee] = device_entry
|
||||||
|
|
||||||
|
return self.async_update(device)
|
||||||
|
|
||||||
|
@callback
|
||||||
|
def async_get_or_create(self, device) -> ZhaDeviceEntry:
|
||||||
|
"""Create a new ZhaDeviceEntry."""
|
||||||
|
ieee_str = str(device.ieee)
|
||||||
|
if ieee_str in self.devices:
|
||||||
|
return self.devices[ieee_str]
|
||||||
|
return self.async_create(device)
|
||||||
|
|
||||||
|
@callback
|
||||||
|
def async_create_or_update(self, device) -> ZhaDeviceEntry:
|
||||||
|
"""Create or update a ZhaDeviceEntry."""
|
||||||
|
if str(device.ieee) in self.devices:
|
||||||
|
return self.async_update(device)
|
||||||
|
return self.async_create(device)
|
||||||
|
|
||||||
|
async def async_delete(self, ieee: str) -> None:
|
||||||
|
"""Delete ZhaDeviceEntry."""
|
||||||
|
del self.devices[ieee]
|
||||||
|
self.async_schedule_save()
|
||||||
|
|
||||||
|
@callback
|
||||||
|
def async_update(self, device) -> ZhaDeviceEntry:
|
||||||
|
"""Update name of ZhaDeviceEntry."""
|
||||||
|
ieee_str = str(device.ieee)
|
||||||
|
old = self.devices[ieee_str]
|
||||||
|
|
||||||
|
changes = {}
|
||||||
|
|
||||||
|
if device.power_source != old.power_source:
|
||||||
|
changes['power_source'] = device.power_source
|
||||||
|
|
||||||
|
if device.manufacturer_code != old.manufacturer_code:
|
||||||
|
changes['manufacturer_code'] = device.manufacturer_code
|
||||||
|
|
||||||
|
new = self.devices[ieee_str] = attr.evolve(old, **changes)
|
||||||
|
self.async_schedule_save()
|
||||||
|
return new
|
||||||
|
|
||||||
|
async def async_load(self) -> None:
|
||||||
|
"""Load the registry of zha device entries."""
|
||||||
|
data = await self._store.async_load()
|
||||||
|
|
||||||
|
devices = OrderedDict() # type: OrderedDict[str, ZhaDeviceEntry]
|
||||||
|
|
||||||
|
if data is not None:
|
||||||
|
for device in data['devices']:
|
||||||
|
devices[device['ieee']] = ZhaDeviceEntry(
|
||||||
|
name=device['name'],
|
||||||
|
ieee=device['ieee'],
|
||||||
|
power_source=device['power_source'],
|
||||||
|
manufacturer_code=device['manufacturer_code']
|
||||||
|
)
|
||||||
|
|
||||||
|
self.devices = devices
|
||||||
|
|
||||||
|
@callback
|
||||||
|
def async_schedule_save(self) -> None:
|
||||||
|
"""Schedule saving the registry of zha devices."""
|
||||||
|
self._store.async_delay_save(self._data_to_save, SAVE_DELAY)
|
||||||
|
|
||||||
|
@callback
|
||||||
|
def _data_to_save(self) -> dict:
|
||||||
|
"""Return data for the registry of zha devices to store in a file."""
|
||||||
|
data = {}
|
||||||
|
|
||||||
|
data['devices'] = [
|
||||||
|
{
|
||||||
|
'name': entry.name,
|
||||||
|
'ieee': entry.ieee,
|
||||||
|
'power_source': entry.power_source,
|
||||||
|
'manufacturer_code': entry.manufacturer_code,
|
||||||
|
} for entry in self.devices.values()
|
||||||
|
]
|
||||||
|
|
||||||
|
return data
|
||||||
|
|
||||||
|
|
||||||
|
@bind_hass
|
||||||
|
async def async_get_registry(hass: HomeAssistantType) -> ZhaDeviceStorage:
|
||||||
|
"""Return zha device storage instance."""
|
||||||
|
task = hass.data.get(DATA_REGISTRY)
|
||||||
|
|
||||||
|
if task is None:
|
||||||
|
async def _load_reg() -> ZhaDeviceStorage:
|
||||||
|
registry = ZhaDeviceStorage(hass)
|
||||||
|
await registry.async_load()
|
||||||
|
return registry
|
||||||
|
|
||||||
|
task = hass.data[DATA_REGISTRY] = hass.async_create_task(_load_reg())
|
||||||
|
|
||||||
|
return cast(ZhaDeviceStorage, await task)
|
Loading…
x
Reference in New Issue
Block a user