File: test_util.py

package info (click to toggle)
python-bellows 0.40.5-1
  • links: PTS, VCS
  • area: main
  • in suites: sid
  • size: 992 kB
  • sloc: python: 13,630; sh: 7; makefile: 4
file content (176 lines) | stat: -rw-r--r-- 5,827 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
import importlib
import logging

import pytest
import zigpy.state
from zigpy.types import PanId
import zigpy.zdo.types as zdo_t

import bellows.types as t
import bellows.zigbee.util as util

from tests.test_application import ieee


@pytest.fixture
def node_info():
    return zigpy.state.NodeInfo(
        nwk=t.NWK(0x0000),
        ieee=t.EUI64.convert("00:12:4b:00:1c:a1:b8:46"),
        logical_type=zdo_t.LogicalType.Coordinator,
        model="Mock board",
        manufacturer="Mock Manufacturer",
        version="Mock version",
    )


@pytest.fixture
def network_info(node_info):
    return zigpy.state.NetworkInfo(
        extended_pan_id=t.ExtendedPanId.convert("bd:27:0b:38:37:95:dc:87"),
        pan_id=PanId(0x9BB0),
        nwk_update_id=18,
        nwk_manager_id=t.NWK(0x0000),
        channel=t.uint8_t(15),
        channel_mask=t.Channels.ALL_CHANNELS,
        security_level=t.uint8_t(5),
        network_key=zigpy.state.Key(
            key=t.KeyData.convert("2ccade06b3090c310315b3d574d3c85a"),
            seq=108,
            tx_counter=118785,
        ),
        tc_link_key=zigpy.state.Key(
            key=t.KeyData(b"ZigBeeAlliance09"),
            partner_ieee=node_info.ieee,
            tx_counter=8712428,
        ),
        key_table=[
            zigpy.state.Key(
                key=t.KeyData.convert(
                    "85:7C:05:00:3E:76:1A:F9:68:9A:49:41:6A:60:5C:76"
                ),
                tx_counter=3792973670,
                rx_counter=1083290572,
                seq=147,
                partner_ieee=t.EUI64.convert("CC:CC:CC:FF:FE:E6:8E:CA"),
            ),
            zigpy.state.Key(
                key=t.KeyData.convert(
                    "CA:02:E8:BB:75:7C:94:F8:93:39:D3:9C:B3:CD:A7:BE"
                ),
                tx_counter=2597245184,
                rx_counter=824424412,
                seq=19,
                partner_ieee=t.EUI64.convert("EC:1B:BD:FF:FE:2F:41:A4"),
            ),
        ],
        children=[
            t.EUI64.convert("00:0B:57:FF:FE:2B:D4:57"),
        ],
        # If exposed by the stack, NWK addresses of other connected devices on the network
        nwk_addresses={
            # Two routers
            t.EUI64.convert("CC:CC:CC:FF:FE:E6:8E:CA"): t.NWK(0x44CB),
            t.EUI64.convert("EC:1B:BD:FF:FE:2F:41:A4"): t.NWK(0x0702),
            # Child device
            t.EUI64.convert("00:0b:57:ff:fe:2b:d4:57"): t.NWK(0xC06B),
        },
        stack_specific={"ezsp": {"hashed_tclk": "abcdabcdabcdabcdabcdabcdabcdabcd"}},
        source=f"bellows@{importlib.metadata.version('bellows')}",
    )


@pytest.fixture
def zigpy_key(network_info, node_info):
    return network_info.network_key.replace(
        rx_counter=1234567,
        partner_ieee=node_info.ieee,
    )


@pytest.fixture
def ezsp_key(network_info, node_info, zigpy_key):
    return t.EmberKeyStruct(
        bitmask=(
            t.EmberKeyStructBitmask.KEY_HAS_SEQUENCE_NUMBER
            | t.EmberKeyStructBitmask.KEY_HAS_OUTGOING_FRAME_COUNTER
            | t.EmberKeyStructBitmask.KEY_HAS_INCOMING_FRAME_COUNTER
            | t.EmberKeyStructBitmask.KEY_HAS_PARTNER_EUI64
        ),
        key=t.KeyData(network_info.network_key.key),
        sequenceNumber=zigpy_key.seq,
        outgoingFrameCounter=zigpy_key.tx_counter,
        incomingFrameCounter=zigpy_key.rx_counter,
        partnerEUI64=t.EUI64(node_info.ieee),
    )


def test_zha_security_normal(network_info, node_info):
    security = util.zha_security(network_info=network_info, use_hashed_tclk=True)

    assert security.preconfiguredTrustCenterEui64 == node_info.ieee
    assert t.EmberInitialSecurityBitmask.HAVE_TRUST_CENTER_EUI64 in security.bitmask

    assert (
        security.preconfiguredKey.serialize().hex()
        == network_info.stack_specific["ezsp"]["hashed_tclk"]
    )
    assert (
        t.EmberInitialSecurityBitmask.TRUST_CENTER_USES_HASHED_LINK_KEY
        in security.bitmask
    )


def test_zha_security_router_unknown_tclk_partner_ieee(network_info):
    security = util.zha_security(
        network_info=network_info.replace(
            tc_link_key=network_info.tc_link_key.replace(partner_ieee=t.EUI64.UNKNOWN)
        ),
        use_hashed_tclk=False,
    )

    # Not set, since we don't know it
    assert security.preconfiguredTrustCenterEui64 == t.EUI64([0x00] * 8)
    assert t.EmberInitialSecurityBitmask.HAVE_TRUST_CENTER_EUI64 not in security.bitmask


def test_zha_security_hashed_nonstandard_tclk_warning(network_info, caplog):
    # Nothing should be logged normally
    with caplog.at_level(logging.WARNING):
        util.zha_security(
            network_info=network_info,
            use_hashed_tclk=True,
        )

    assert "Only the well-known TC Link Key is supported" not in caplog.text

    # But it will be when a non-standard TCLK is used along with TCLK hashing
    with caplog.at_level(logging.WARNING):
        util.zha_security(
            network_info=network_info.replace(
                tc_link_key=network_info.tc_link_key.replace(
                    key=t.KeyData(b"ANonstandardTCLK")
                )
            ),
            use_hashed_tclk=True,
        )

    assert "Only the well-known TC Link Key is supported" in caplog.text


def test_ezsp_key_to_zigpy_key(zigpy_key, ezsp_key):
    assert util.ezsp_key_to_zigpy_key(ezsp_key) == zigpy_key


def test_zigpy_key_to_ezsp_key(zigpy_key, ezsp_key):
    assert util.zigpy_key_to_ezsp_key(zigpy_key) == ezsp_key


def test_map_rssi_to_energy():
    assert 0 <= util.map_rssi_to_energy(-200) <= 0.01
    assert 254 <= util.map_rssi_to_energy(100) <= 255

    # Make sure the two functions are inverses
    for rssi in range(-100, 100):
        energy = util.map_rssi_to_energy(rssi)
        assert abs(util.map_energy_to_rssi(energy) - rssi) < 0.1