Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Some small bugfixes and stability improvements #324

Merged
merged 12 commits into from
Jun 19, 2023
4 changes: 3 additions & 1 deletion .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,14 @@ repos:
- --quiet
files: ^(matter_server|scripts)/.+\.py$
- repo: https://github.com/codespell-project/codespell
rev: v2.2.2
rev: v2.2.4
hooks:
- id: codespell
args: []
exclude_types: [csv, json]
exclude: ^tests/fixtures/
additional_dependencies:
- tomli
- repo: https://github.com/PyCQA/flake8
rev: 6.0.0
hooks:
Expand Down
2 changes: 1 addition & 1 deletion matter_server/client/models/device_types.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ def __init_subclass__(cls, *, device_type: int, **kwargs: typing.Any) -> None:

def __hash__(self) -> int:
"""Return unique hash for this object."""
return hash(self.device_type)
return self.device_type


class OrphanClusters(DeviceType, device_type=0xF001):
Expand Down
2 changes: 1 addition & 1 deletion matter_server/client/models/node.py
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ def __init__(
self.node = node
self.endpoint_id = endpoint_id
self.clusters: dict[int, Clusters.Cluster] = {}
self.device_types: set[DeviceType] = set()
self.device_types: set[type[DeviceType]] = set()
self.update(attributes_data)

@property
Expand Down
42 changes: 34 additions & 8 deletions matter_server/server/device_controller.py
Original file line number Diff line number Diff line change
Expand Up @@ -285,7 +285,7 @@ async def interview_node(self, node_id: int) -> None:

LOGGER.debug("Interviewing node: %s", node_id)
try:
await self._call_sdk(self.chip_controller.ResolveNode, nodeid=node_id)
await self._resolve_node(node_id=node_id)
marcelveldt marked this conversation as resolved.
Show resolved Hide resolved
read_response: Attribute.AsyncReadTransaction.ReadResponse = (
await self.chip_controller.Read(
nodeid=node_id, attributes="*", events="*", fabricFiltered=False
Expand Down Expand Up @@ -406,12 +406,9 @@ async def subscribe_node(self, node_id: int) -> None:
node_logger.debug("Setting up subscriptions...")

node = cast(MatterNodeData, self._nodes[node_id])

try:
await self._call_sdk(self.chip_controller.ResolveNode, nodeid=node_id)
except ChipStackError as err:
node.available = False
raise NodeNotResolving(f"Failed to resolve node {node_id}") from err
node.available = False
await self._resolve_node(node_id=node_id)
node.available = True

# we follow the pattern of apple and google here and
# just do a wildcard subscription for all clusters and properties
Expand All @@ -421,7 +418,7 @@ async def subscribe_node(self, node_id: int) -> None:
sub: Attribute.SubscriptionTransaction = await self.chip_controller.Read(
nodeid=node_id,
attributes="*",
events=[("*", 0)],
events=[("*", 1)],
MartinHjelmare marked this conversation as resolved.
Show resolved Hide resolved
reportInterval=(0, 120),
fabricFiltered=False,
)
Expand Down Expand Up @@ -632,3 +629,32 @@ def _parse_attributes_from_read_result(
)
result[attribute_path] = attr_value
return result

async def _resolve_node(
self, node_id: int, retries: int = 3, allow_pase: bool = False
) -> None:
"""Resolve a Node on the network."""
if self.chip_controller is None:
raise RuntimeError("Device Controller not initialized.")
try:
if allow_pase:
# last attempt allows PASE connection (last resort)
LOGGER.debug(
"Attempting to resolve node %s (with PASE connection)", node_id
)
await self._call_sdk(
self.chip_controller.GetConnectedDeviceSync,
nodeid=node_id,
allowPASE=True,
)
return
LOGGER.debug("Resolving node %s", node_id)
await self._call_sdk(self.chip_controller.ResolveNode, nodeid=node_id)
except (ChipStackError, TimeoutError) as err:
if not retries:
# when we're out of retries, raise NodeNotResolving
raise NodeNotResolving(f"Unable to resolve Node {node_id}") from err
await self._resolve_node(
node_id=node_id, retries=retries - 1, allow_pase=retries == 0
marcelveldt marked this conversation as resolved.
Show resolved Hide resolved
)
await asyncio.sleep(2)
5 changes: 5 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,9 @@ test = [
[project.scripts]
matter-server = "matter_server.server.__main__:main"

[tool.codespell]
ignore-words-list = "pase,hass"
marcelveldt marked this conversation as resolved.
Show resolved Hide resolved

[tool.mypy]
python_version = "3.10"
check_untyped_defs = true
Expand Down Expand Up @@ -134,3 +137,5 @@ forced_separate = [
"tests",
]
combine_as_imports = true


2 changes: 1 addition & 1 deletion scripts/beautify_diagnostics.py
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ def process_node(node):
continue

for device_type in device_types:
device_type_id = device_type["type"]
device_type_id = device_type["deviceType"]
if device_type_id in ALL_TYPES:
device_type_name = ALL_TYPES[device_type_id].__name__
else:
Expand Down
2 changes: 1 addition & 1 deletion scripts/generate_devices.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
CHIP_ROOT = REPO_ROOT / "../../project-chip/connectedhomeip"
DEVICE_XML = CHIP_ROOT / "src/app/zap-templates/zcl/data-model/chip/matter-devices.xml"

OUTPUT_PYTHON = REPO_ROOT / "matter_server/common/models/device_types.py"
OUTPUT_PYTHON = REPO_ROOT / "matter_server/client/models/device_types.py"


def gen_cls_name(name: str):
Expand Down