mirror of
https://github.com/home-assistant/core.git
synced 2025-04-25 01:38:02 +00:00
Use node descriptor from Zigpy for ZHA (#24316)
* use zigpy node descriptor * cleanup
This commit is contained in:
parent
9fb1f2fa17
commit
ae1bcd5fef
@ -22,10 +22,6 @@ from ..const import (
|
||||
)
|
||||
from ..registries import CLUSTER_REPORT_CONFIGS
|
||||
|
||||
NODE_DESCRIPTOR_REQUEST = 0x0002
|
||||
MAINS_POWERED = 1
|
||||
BATTERY_OR_UNKNOWN = 0
|
||||
|
||||
ZIGBEE_CHANNEL_REGISTRY = {}
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
@ -268,11 +264,6 @@ class AttributeListeningChannel(ZigbeeChannel):
|
||||
class ZDOChannel:
|
||||
"""Channel for ZDO events."""
|
||||
|
||||
POWER_SOURCES = {
|
||||
MAINS_POWERED: 'Mains',
|
||||
BATTERY_OR_UNKNOWN: 'Battery or Unknown'
|
||||
}
|
||||
|
||||
def __init__(self, cluster, device):
|
||||
"""Initialize ZDOChannel."""
|
||||
self.name = ZDO_CHANNEL
|
||||
@ -281,8 +272,6 @@ class ZDOChannel:
|
||||
self._status = ChannelStatus.CREATED
|
||||
self._unique_id = "{}_ZDO".format(device.name)
|
||||
self._cluster.add_listener(self)
|
||||
self.power_source = None
|
||||
self.manufacturer_code = None
|
||||
|
||||
@property
|
||||
def unique_id(self):
|
||||
@ -314,49 +303,10 @@ class ZDOChannel:
|
||||
entry = self._zha_device.gateway.zha_storage.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 = self._zha_device.gateway.zha_storage.async_update(
|
||||
self._zha_device)
|
||||
_LOGGER.debug("entry after getting node desc in init: %s", entry)
|
||||
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):
|
||||
"""Configure channel."""
|
||||
await self.async_get_node_descriptor(False)
|
||||
self._status = ChannelStatus.CONFIGURED
|
||||
|
||||
|
||||
|
@ -8,7 +8,7 @@ import logging
|
||||
from homeassistant.core import callback
|
||||
from homeassistant.helpers.dispatcher import async_dispatcher_send
|
||||
from homeassistant.helpers.event import async_call_later
|
||||
from . import ZigbeeChannel, parse_and_log_command, MAINS_POWERED
|
||||
from . import ZigbeeChannel, parse_and_log_command
|
||||
from ..helpers import get_attr_id_by_name
|
||||
from ..const import (
|
||||
SIGNAL_ATTR_UPDATED, SIGNAL_MOVE_LEVEL, SIGNAL_SET_LEVEL,
|
||||
@ -87,7 +87,7 @@ class OnOffChannel(ZigbeeChannel):
|
||||
|
||||
async def async_update(self):
|
||||
"""Initialize channel."""
|
||||
from_cache = not self.device.power_source == MAINS_POWERED
|
||||
from_cache = not self.device.is_mains_powered
|
||||
_LOGGER.debug(
|
||||
"%s is attempting to update onoff state - from cache: %s",
|
||||
self._unique_id,
|
||||
|
@ -104,6 +104,8 @@ QUIRK_APPLIED = 'quirk_applied'
|
||||
QUIRK_CLASS = 'quirk_class'
|
||||
MANUFACTURER_CODE = 'manufacturer_code'
|
||||
POWER_SOURCE = 'power_source'
|
||||
MAINS_POWERED = 'Mains'
|
||||
BATTERY_OR_UNKNOWN = 'Battery or Unknown'
|
||||
|
||||
BELLOWS = 'bellows'
|
||||
ZHA = 'homeassistant.components.zha'
|
||||
|
@ -17,9 +17,10 @@ from .const import (
|
||||
ATTR_CLUSTER_ID, ATTR_ATTRIBUTE, ATTR_VALUE, ATTR_COMMAND, SERVER,
|
||||
ATTR_COMMAND_TYPE, ATTR_ARGS, CLIENT_COMMANDS, SERVER_COMMANDS,
|
||||
ATTR_ENDPOINT_ID, IEEE, MODEL, NAME, UNKNOWN, QUIRK_APPLIED,
|
||||
QUIRK_CLASS, ZDO_CHANNEL, MANUFACTURER_CODE, POWER_SOURCE
|
||||
QUIRK_CLASS, ZDO_CHANNEL, MANUFACTURER_CODE, POWER_SOURCE, MAINS_POWERED,
|
||||
BATTERY_OR_UNKNOWN
|
||||
)
|
||||
from .channels import EventRelayChannel, ZDOChannel
|
||||
from .channels import EventRelayChannel
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
@ -68,7 +69,6 @@ class ZHADevice:
|
||||
self._zigpy_device.__class__.__module__,
|
||||
self._zigpy_device.__class__.__name__
|
||||
)
|
||||
self._power_source = None
|
||||
self.status = DeviceStatus.CREATED
|
||||
|
||||
@property
|
||||
@ -91,6 +91,13 @@ class ZHADevice:
|
||||
"""Return model for device."""
|
||||
return self._model
|
||||
|
||||
@property
|
||||
def manufacturer_code(self):
|
||||
"""Return the manufacturer code for the device."""
|
||||
if self._zigpy_device.node_desc.is_valid:
|
||||
return self._zigpy_device.node_desc.manufacturer_code
|
||||
return None
|
||||
|
||||
@property
|
||||
def nwk(self):
|
||||
"""Return nwk for device."""
|
||||
@ -112,20 +119,29 @@ class ZHADevice:
|
||||
return self._zigpy_device.last_seen
|
||||
|
||||
@property
|
||||
def manufacturer_code(self):
|
||||
"""Return manufacturer code for device."""
|
||||
if ZDO_CHANNEL in self.cluster_channels:
|
||||
return self.cluster_channels.get(ZDO_CHANNEL).manufacturer_code
|
||||
return None
|
||||
def is_mains_powered(self):
|
||||
"""Return true if device is mains powered."""
|
||||
return self._zigpy_device.node_desc.is_mains_powered
|
||||
|
||||
@property
|
||||
def power_source(self):
|
||||
"""Return the power source for the device."""
|
||||
if self._power_source is not None:
|
||||
return self._power_source
|
||||
if ZDO_CHANNEL in self.cluster_channels:
|
||||
return self.cluster_channels.get(ZDO_CHANNEL).power_source
|
||||
return None
|
||||
return MAINS_POWERED if self.is_mains_powered else BATTERY_OR_UNKNOWN
|
||||
|
||||
@property
|
||||
def is_router(self):
|
||||
"""Return true if this is a routing capable device."""
|
||||
return self._zigpy_device.node_desc.is_router
|
||||
|
||||
@property
|
||||
def is_coordinator(self):
|
||||
"""Return true if this device represents the coordinator."""
|
||||
return self._zigpy_device.node_desc.is_coordinator
|
||||
|
||||
@property
|
||||
def is_end_device(self):
|
||||
"""Return true if this device is an end device."""
|
||||
return self._zigpy_device.node_desc.is_end_device
|
||||
|
||||
@property
|
||||
def gateway(self):
|
||||
@ -151,10 +167,6 @@ class ZHADevice:
|
||||
"""Set availability from restore and prevent signals."""
|
||||
self._available = available
|
||||
|
||||
def set_power_source(self, power_source):
|
||||
"""Set the power source."""
|
||||
self._power_source = power_source
|
||||
|
||||
def update_available(self, available):
|
||||
"""Set sensor availability."""
|
||||
if self._available != available and available:
|
||||
@ -183,7 +195,7 @@ class ZHADevice:
|
||||
QUIRK_APPLIED: self.quirk_applied,
|
||||
QUIRK_CLASS: self.quirk_class,
|
||||
MANUFACTURER_CODE: self.manufacturer_code,
|
||||
POWER_SOURCE: ZDOChannel.POWER_SOURCES.get(self.power_source)
|
||||
POWER_SOURCE: self.power_source
|
||||
}
|
||||
|
||||
def add_cluster_channel(self, cluster_channel):
|
||||
@ -256,7 +268,7 @@ class ZHADevice:
|
||||
_LOGGER.debug(
|
||||
'%s: power source: %s',
|
||||
self.name,
|
||||
ZDOChannel.POWER_SOURCES.get(self.power_source)
|
||||
self.power_source
|
||||
)
|
||||
self.status = DeviceStatus.INITIALIZED
|
||||
_LOGGER.debug('%s: completed initialization', self.name)
|
||||
|
@ -18,7 +18,6 @@ from homeassistant.helpers.dispatcher import async_dispatcher_send
|
||||
from homeassistant.helpers.entity_component import EntityComponent
|
||||
|
||||
from ..api import async_get_device_info
|
||||
from .channels import MAINS_POWERED, ZDOChannel
|
||||
from .const import (
|
||||
ADD_DEVICE_RELAY_LOGGERS, ATTR_MANUFACTURER, BELLOWS, CONF_BAUDRATE,
|
||||
CONF_DATABASE, CONF_RADIO_TYPE, CONF_USB_PATH, CONTROLLER, CURRENT,
|
||||
@ -234,7 +233,6 @@ class ZHAGateway:
|
||||
if not is_new_join:
|
||||
entry = self.zha_storage.async_get_or_create(zha_device)
|
||||
zha_device.async_update_last_seen(entry.last_seen)
|
||||
zha_device.set_power_source(entry.power_source)
|
||||
return zha_device
|
||||
|
||||
@callback
|
||||
@ -290,16 +288,13 @@ class ZHAGateway:
|
||||
# configure the device
|
||||
await zha_device.async_configure()
|
||||
zha_device.update_available(True)
|
||||
elif zha_device.power_source is not None\
|
||||
and zha_device.power_source == MAINS_POWERED:
|
||||
elif zha_device.is_mains_powered:
|
||||
# the device isn't a battery powered device so we should be able
|
||||
# to update it now
|
||||
_LOGGER.debug(
|
||||
"attempting to request fresh state for %s %s",
|
||||
zha_device.name,
|
||||
"with power source: {}".format(
|
||||
ZDOChannel.POWER_SOURCES.get(zha_device.power_source)
|
||||
)
|
||||
"with power source: {}".format(zha_device.power_source)
|
||||
)
|
||||
await zha_device.async_initialize(from_cache=False)
|
||||
else:
|
||||
|
@ -26,8 +26,6 @@ class ZhaDeviceEntry:
|
||||
|
||||
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)
|
||||
last_seen = attr.ib(type=float, default=None)
|
||||
|
||||
|
||||
@ -46,8 +44,6 @@ class ZhaDeviceStorage:
|
||||
device_entry = ZhaDeviceEntry(
|
||||
name=device.name,
|
||||
ieee=str(device.ieee),
|
||||
power_source=device.power_source,
|
||||
manufacturer_code=device.manufacturer_code,
|
||||
last_seen=device.last_seen
|
||||
|
||||
)
|
||||
@ -85,13 +81,6 @@ class ZhaDeviceStorage:
|
||||
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
|
||||
|
||||
changes['last_seen'] = device.last_seen
|
||||
|
||||
new = self.devices[ieee_str] = attr.evolve(old, **changes)
|
||||
@ -109,8 +98,6 @@ class ZhaDeviceStorage:
|
||||
devices[device['ieee']] = ZhaDeviceEntry(
|
||||
name=device['name'],
|
||||
ieee=device['ieee'],
|
||||
power_source=device['power_source'],
|
||||
manufacturer_code=device['manufacturer_code'],
|
||||
last_seen=device['last_seen'] if 'last_seen' in device
|
||||
else None
|
||||
)
|
||||
@ -135,8 +122,6 @@ class ZhaDeviceStorage:
|
||||
{
|
||||
'name': entry.name,
|
||||
'ieee': entry.ieee,
|
||||
'power_source': entry.power_source,
|
||||
'manufacturer_code': entry.manufacturer_code,
|
||||
'last_seen': entry.last_seen
|
||||
} for entry in self.devices.values()
|
||||
]
|
||||
|
@ -14,7 +14,6 @@ from .core.const import (
|
||||
DOMAIN, ATTR_MANUFACTURER, DATA_ZHA, DATA_ZHA_BRIDGE_ID, MODEL, NAME,
|
||||
SIGNAL_REMOVE
|
||||
)
|
||||
from .core.channels import MAINS_POWERED
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
@ -157,7 +156,7 @@ class ZhaEntity(RestoreEntity, entity.Entity):
|
||||
time.time() - self._zha_device.last_seen <
|
||||
RESTART_GRACE_PERIOD):
|
||||
self.async_set_available(True)
|
||||
if self.zha_device.power_source != MAINS_POWERED:
|
||||
if not self.zha_device.is_mains_powered:
|
||||
# mains powered devices will get real time state
|
||||
self.async_restore_last_state(last_state)
|
||||
self._zha_device.set_available(True)
|
||||
|
@ -82,6 +82,8 @@ class FakeDevice:
|
||||
self.initializing = False
|
||||
self.manufacturer = manufacturer
|
||||
self.model = model
|
||||
from zigpy.zdo.types import NodeDescriptor
|
||||
self.node_desc = NodeDescriptor()
|
||||
|
||||
|
||||
def make_device(in_cluster_ids, out_cluster_ids, device_type, ieee,
|
||||
|
Loading…
x
Reference in New Issue
Block a user