Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 31 additions & 1 deletion tests/test_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@

import asyncio
import logging
from unittest.mock import AsyncMock
from unittest.mock import AsyncMock, patch

import pytest

from zigpy_deconz import utils

Expand All @@ -22,3 +24,31 @@ async def test_restart_forever(caplog):
assert caplog.text.count("failed, restarting...") >= 2
assert caplog.text.count("RuntimeError") == 2
assert len(mock.mock_calls) >= 4


@pytest.mark.parametrize(
("device_path", "realpath_result", "expected_is_usb"),
[
# Platform serial ports (Raspbee)
("/dev/conbee", "/dev/ttyS0", False),
("/dev/raspbee", "/dev/ttyAMA0", False),
("/dev/ttyS0", "/dev/ttyS0", False),
("/dev/ttyAMA0", "/dev/ttyAMA0", False),
("/dev/ttyS1", "/dev/ttyS1", False),
("/dev/ttyAMA1", "/dev/ttyAMA1", False),
# USB serial ports (Conbee)
("/dev/conbee", "/dev/ttyUSB0", True),
("/dev/ttyUSB0", "/dev/ttyUSB0", True),
("/dev/ttyACM0", "/dev/ttyACM0", True),
("/dev/ttyUSB1", "/dev/ttyUSB1", True),
("/dev/ttyACM1", "/dev/ttyACM1", True),
# Symlink to USB serial (Conbee)
("/dev/serial/by-id/usb-conbee", "/dev/ttyUSB0", True),
],
)
def test_is_usb_serial_port(device_path, realpath_result, expected_is_usb):
"""Test is_usb_serial_port with various device paths and realpath results."""
with patch("zigpy_deconz.utils.os.path.realpath", return_value=realpath_result):
result = utils.is_usb_serial_port(device_path)

assert result == expected_is_usb
14 changes: 14 additions & 0 deletions zigpy_deconz/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@
import asyncio
import functools
import logging
import os.path
import re

LOGGER = logging.getLogger(__name__)

Expand All @@ -23,3 +25,15 @@ async def replacement(*args, **kwargs):
await asyncio.sleep(restart_delay)

return replacement


def is_usb_serial_port(device_path: str) -> bool:
"""Check if a device path is a USB serial port."""
resolved_device = os.path.realpath(device_path)

# Platform serial ports (Raspbee)
if re.match(r"/dev/tty(S|AMA)\d+", resolved_device):
return False

# Everything else is assumed to be USB serial
return True
15 changes: 9 additions & 6 deletions zigpy_deconz/zigbee/application.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
import asyncio
import importlib.metadata
import logging
import re
import sys
from typing import Any

Expand Down Expand Up @@ -41,6 +40,7 @@
)
from zigpy_deconz.config import CONFIG_SCHEMA
import zigpy_deconz.exception
from zigpy_deconz.utils import is_usb_serial_port

LIB_VERSION = importlib.metadata.version("zigpy-deconz")
LOGGER = logging.getLogger(__name__)
Expand Down Expand Up @@ -343,13 +343,16 @@ async def load_network_info(self, *, load_devices=False):

node_info.manufacturer = "dresden elektronik"

if re.match(
r"/dev/tty(S|AMA|ACM)\d+",
is_usb = await asyncio.get_running_loop().run_in_executor(
None,
is_usb_serial_port,
self._config[zigpy.config.CONF_DEVICE][zigpy.config.CONF_DEVICE_PATH],
):
node_info.model = "Raspbee"
else:
)

if is_usb:
node_info.model = "Conbee"
else:
node_info.model = "Raspbee"

node_info.model += {
FirmwarePlatform.Conbee: "",
Expand Down
Loading