|
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