|
11 | 11 | from typing import TYPE_CHECKING, Any, Final, ParamSpec, TypedDict
|
12 | 12 |
|
13 | 13 | import zigpy.exceptions
|
| 14 | +import zigpy.types |
14 | 15 | import zigpy.util
|
15 | 16 | import zigpy.zcl
|
16 | 17 | from zigpy.zcl.foundation import (
|
17 | 18 | CommandSchema,
|
18 | 19 | ConfigureReportingResponseRecord,
|
| 20 | + DiscoverAttributesResponseRecord, |
| 21 | + GeneralCommand, |
19 | 22 | Status,
|
20 | 23 | ZCLAttributeDef,
|
21 | 24 | )
|
@@ -441,6 +444,10 @@ async def async_configure(self) -> None:
|
441 | 444 | if ch_specific_cfg:
|
442 | 445 | self.debug("Performing cluster handler specific configuration")
|
443 | 446 | await ch_specific_cfg()
|
| 447 | + |
| 448 | + self.debug("Discovering available attributes") |
| 449 | + await self.discover_unsupported_attributes() |
| 450 | + |
444 | 451 | self.debug("finished cluster handler configuration")
|
445 | 452 | else:
|
446 | 453 | self.debug("skipping cluster handler configuration")
|
@@ -624,6 +631,46 @@ async def write_attributes_safe(
|
624 | 631 | f"Failed to write attribute {name}={value}: {record.status}",
|
625 | 632 | )
|
626 | 633 |
|
| 634 | + async def _discover_attributes_all( |
| 635 | + self, |
| 636 | + ) -> list[DiscoverAttributesResponseRecord] | None: |
| 637 | + discovery_complete = zigpy.types.Bool.false |
| 638 | + start_attribute_id = 0 |
| 639 | + attribute_info = [] |
| 640 | + cluster = self.cluster |
| 641 | + while discovery_complete != zigpy.types.Bool.true: |
| 642 | + rsp = await cluster.discover_attributes( |
| 643 | + start_attribute_id=start_attribute_id, max_attribute_ids=0xFF |
| 644 | + ) |
| 645 | + assert rsp, "Must have a response to discover request" |
| 646 | + |
| 647 | + if rsp.command.id == GeneralCommand.Default_Response: |
| 648 | + self.debug( |
| 649 | + "Ignoring attribute discovery due to unexpected default response" |
| 650 | + ) |
| 651 | + return None |
| 652 | + |
| 653 | + attribute_info.extend(rsp.attribute_info) |
| 654 | + discovery_complete = rsp.discovery_complete |
| 655 | + start_attribute_id = ( |
| 656 | + max((info.attrid for info in rsp.attribute_info), default=0) + 1 |
| 657 | + ) |
| 658 | + return attribute_info |
| 659 | + |
| 660 | + async def discover_unsupported_attributes(self): |
| 661 | + """Discover the list of unsupported attributes from the device.""" |
| 662 | + attribute_info = await self._discover_attributes_all() |
| 663 | + if attribute_info is None: |
| 664 | + return |
| 665 | + attr_ids = {info.attrid for info in attribute_info} |
| 666 | + |
| 667 | + cluster = self.cluster |
| 668 | + for attr_id in cluster.attributes: |
| 669 | + if attr_id in attr_ids: |
| 670 | + cluster.remove_unsupported_attribute(attr_id) |
| 671 | + else: |
| 672 | + cluster.add_unsupported_attribute(attr_id) |
| 673 | + |
627 | 674 | def log(self, level, msg, *args, **kwargs) -> None:
|
628 | 675 | """Log a message."""
|
629 | 676 | msg = f"[%s:%s]: {msg}"
|
|
0 commit comments