Skip to content

Commit d7d73ba

Browse files
authored
Ignore /dev/ttyACM when detecting Raspbee (#274)
* Ignore `/dev/ttyACM` when detecting Raspbee * Add a test
1 parent 96b2572 commit d7d73ba

File tree

3 files changed

+54
-7
lines changed

3 files changed

+54
-7
lines changed

tests/test_utils.py

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,9 @@
22

33
import asyncio
44
import logging
5-
from unittest.mock import AsyncMock
5+
from unittest.mock import AsyncMock, patch
6+
7+
import pytest
68

79
from zigpy_deconz import utils
810

@@ -22,3 +24,31 @@ async def test_restart_forever(caplog):
2224
assert caplog.text.count("failed, restarting...") >= 2
2325
assert caplog.text.count("RuntimeError") == 2
2426
assert len(mock.mock_calls) >= 4
27+
28+
29+
@pytest.mark.parametrize(
30+
("device_path", "realpath_result", "expected_is_usb"),
31+
[
32+
# Platform serial ports (Raspbee)
33+
("/dev/conbee", "/dev/ttyS0", False),
34+
("/dev/raspbee", "/dev/ttyAMA0", False),
35+
("/dev/ttyS0", "/dev/ttyS0", False),
36+
("/dev/ttyAMA0", "/dev/ttyAMA0", False),
37+
("/dev/ttyS1", "/dev/ttyS1", False),
38+
("/dev/ttyAMA1", "/dev/ttyAMA1", False),
39+
# USB serial ports (Conbee)
40+
("/dev/conbee", "/dev/ttyUSB0", True),
41+
("/dev/ttyUSB0", "/dev/ttyUSB0", True),
42+
("/dev/ttyACM0", "/dev/ttyACM0", True),
43+
("/dev/ttyUSB1", "/dev/ttyUSB1", True),
44+
("/dev/ttyACM1", "/dev/ttyACM1", True),
45+
# Symlink to USB serial (Conbee)
46+
("/dev/serial/by-id/usb-conbee", "/dev/ttyUSB0", True),
47+
],
48+
)
49+
def test_is_usb_serial_port(device_path, realpath_result, expected_is_usb):
50+
"""Test is_usb_serial_port with various device paths and realpath results."""
51+
with patch("zigpy_deconz.utils.os.path.realpath", return_value=realpath_result):
52+
result = utils.is_usb_serial_port(device_path)
53+
54+
assert result == expected_is_usb

zigpy_deconz/utils.py

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@
55
import asyncio
66
import functools
77
import logging
8+
import os.path
9+
import re
810

911
LOGGER = logging.getLogger(__name__)
1012

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

2527
return replacement
28+
29+
30+
def is_usb_serial_port(device_path: str) -> bool:
31+
"""Check if a device path is a USB serial port."""
32+
resolved_device = os.path.realpath(device_path)
33+
34+
# Platform serial ports (Raspbee)
35+
if re.match(r"/dev/tty(S|AMA)\d+", resolved_device):
36+
return False
37+
38+
# Everything else is assumed to be USB serial
39+
return True

zigpy_deconz/zigbee/application.py

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@
55
import asyncio
66
import importlib.metadata
77
import logging
8-
import re
98
import sys
109
from typing import Any
1110

@@ -41,6 +40,7 @@
4140
)
4241
from zigpy_deconz.config import CONFIG_SCHEMA
4342
import zigpy_deconz.exception
43+
from zigpy_deconz.utils import is_usb_serial_port
4444

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

344344
node_info.manufacturer = "dresden elektronik"
345345

346-
if re.match(
347-
r"/dev/tty(S|AMA|ACM)\d+",
346+
is_usb = await asyncio.get_running_loop().run_in_executor(
347+
None,
348+
is_usb_serial_port,
348349
self._config[zigpy.config.CONF_DEVICE][zigpy.config.CONF_DEVICE_PATH],
349-
):
350-
node_info.model = "Raspbee"
351-
else:
350+
)
351+
352+
if is_usb:
352353
node_info.model = "Conbee"
354+
else:
355+
node_info.model = "Raspbee"
353356

354357
node_info.model += {
355358
FirmwarePlatform.Conbee: "",

0 commit comments

Comments
 (0)