File: test_manufacturer_data.py

package info (click to toggle)
python-aioshelly 13.17.0-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 732 kB
  • sloc: python: 6,867; makefile: 7; sh: 3
file content (165 lines) | stat: -rw-r--r-- 5,903 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
"""Tests for the BLE manufacturer data parser."""

from __future__ import annotations

from aioshelly.ble.manufacturer_data import (
    ALLTERCO_MFID,
    BLOCK_TYPE_FLAGS,
    BLOCK_TYPE_MAC,
    BLOCK_TYPE_MODEL,
    has_rpc_over_ble,
    parse_shelly_manufacturer_data,
)


def test_parse_empty_manufacturer_data() -> None:
    """Test parsing empty manufacturer data."""
    assert parse_shelly_manufacturer_data({}) is None


def test_parse_wrong_manufacturer_id() -> None:
    """Test parsing manufacturer data with wrong ID."""
    assert parse_shelly_manufacturer_data({0x1234: b"\x01\x02\x03"}) is None


def test_parse_too_short_data() -> None:
    """Test parsing manufacturer data that is too short."""
    assert parse_shelly_manufacturer_data({ALLTERCO_MFID: b""}) is None


def test_parse_flags_only() -> None:
    """Test parsing manufacturer data with flags only."""
    # Block type 0x01 (flags) + 2 bytes flags (0x0004 = RPC over BLE enabled)
    data = bytes([BLOCK_TYPE_FLAGS, 0x04, 0x00])
    result = parse_shelly_manufacturer_data({ALLTERCO_MFID: data})
    assert result == {"flags": 0x0004}


def test_parse_mac_only() -> None:
    """Test parsing manufacturer data with MAC address only."""
    # Block type 0x0A (MAC) + 6 bytes MAC address
    data = bytes([BLOCK_TYPE_MAC, 0xC0, 0x49, 0xEF, 0x88, 0x73, 0xE8])
    result = parse_shelly_manufacturer_data({ALLTERCO_MFID: data})
    assert result == {"mac": "C0:49:EF:88:73:E8"}


def test_parse_model_only() -> None:
    """Test parsing manufacturer data with model ID only."""
    # Block type 0x0B (model) + 2 bytes model ID (0x1234)
    data = bytes([BLOCK_TYPE_MODEL, 0x34, 0x12])
    result = parse_shelly_manufacturer_data({ALLTERCO_MFID: data})
    assert result == {"model_id": 0x1234}


def test_parse_all_blocks() -> None:
    """Test parsing manufacturer data with all block types."""
    # Flags + MAC + Model
    data = bytes(
        [
            BLOCK_TYPE_FLAGS,
            0x07,
            0x00,  # flags = 0x0007 (discoverable, auth, RPC over BLE)
            BLOCK_TYPE_MAC,
            0xC0,
            0x49,
            0xEF,
            0x88,
            0x73,
            0xE8,  # MAC
            BLOCK_TYPE_MODEL,
            0x34,
            0x12,  # model = 0x1234
        ]
    )
    result = parse_shelly_manufacturer_data({ALLTERCO_MFID: data})
    assert result == {
        "flags": 0x0007,
        "mac": "C0:49:EF:88:73:E8",
        "model_id": 0x1234,
    }


def test_parse_truncated_flags() -> None:
    """Test parsing manufacturer data with truncated flags block."""
    # Block type 0x01 (flags) but only 1 byte of data (needs 2)
    data = bytes([BLOCK_TYPE_FLAGS, 0x04])
    result = parse_shelly_manufacturer_data({ALLTERCO_MFID: data})
    # Should stop parsing and return empty result
    assert result is None


def test_parse_truncated_mac() -> None:
    """Test parsing manufacturer data with truncated MAC block."""
    # Block type 0x0A (MAC) but only 3 bytes (needs 6)
    data = bytes([BLOCK_TYPE_MAC, 0xC0, 0x49, 0xEF])
    result = parse_shelly_manufacturer_data({ALLTERCO_MFID: data})
    # Should stop parsing and return empty result
    assert result is None


def test_parse_truncated_model() -> None:
    """Test parsing manufacturer data with truncated model block."""
    # Block type 0x0B (model) but only 1 byte (needs 2)
    data = bytes([BLOCK_TYPE_MODEL, 0x34])
    result = parse_shelly_manufacturer_data({ALLTERCO_MFID: data})
    # Should stop parsing and return empty result
    assert result is None


def test_parse_partial_blocks() -> None:
    """Test parsing with valid block followed by incomplete block."""
    # Valid flags block followed by incomplete MAC block
    data = bytes([BLOCK_TYPE_FLAGS, 0x04, 0x00, BLOCK_TYPE_MAC, 0xC0, 0x49])
    result = parse_shelly_manufacturer_data({ALLTERCO_MFID: data})
    # Should parse flags but stop at incomplete MAC
    assert result == {"flags": 0x0004}


def test_parse_lone_block_type() -> None:
    """Test parsing with lone block type byte at end (no data following)."""
    # Valid flags block followed by MODEL block type with no data
    data = bytes([BLOCK_TYPE_FLAGS, 0x04, 0x00, BLOCK_TYPE_MODEL])
    result = parse_shelly_manufacturer_data({ALLTERCO_MFID: data})
    # Should parse flags but stop at MODEL block that has no data
    assert result == {"flags": 0x0004}


def test_parse_unknown_block_type() -> None:
    """Test parsing manufacturer data with unknown block type."""
    # Flags + unknown block type 0xFF
    data = bytes([BLOCK_TYPE_FLAGS, 0x04, 0x00, 0xFF, 0x01, 0x02])
    result = parse_shelly_manufacturer_data({ALLTERCO_MFID: data})
    # Should parse flags and stop at unknown block
    assert result == {"flags": 0x0004}


def test_has_rpc_over_ble_enabled() -> None:
    """Test checking if RPC over BLE is enabled."""
    # RPC over BLE enabled (bit 2 = 0x04)
    data = bytes([BLOCK_TYPE_FLAGS, 0x04, 0x00])
    assert has_rpc_over_ble({ALLTERCO_MFID: data}) is True


def test_has_rpc_over_ble_disabled() -> None:
    """Test checking if RPC over BLE is disabled."""
    # RPC over BLE disabled (only bits 0 and 1 set)
    data = bytes([BLOCK_TYPE_FLAGS, 0x03, 0x00])
    assert has_rpc_over_ble({ALLTERCO_MFID: data}) is False


def test_has_rpc_over_ble_no_flags() -> None:
    """Test checking RPC over BLE with no flags block."""
    # Only MAC, no flags
    data = bytes([BLOCK_TYPE_MAC, 0xC0, 0x49, 0xEF, 0x88, 0x73, 0xE8])
    assert has_rpc_over_ble({ALLTERCO_MFID: data}) is False


def test_has_rpc_over_ble_empty_data() -> None:
    """Test checking RPC over BLE with empty data."""
    assert has_rpc_over_ble({}) is False


def test_has_rpc_over_ble_wrong_manufacturer() -> None:
    """Test checking RPC over BLE with wrong manufacturer ID."""
    data = bytes([BLOCK_TYPE_FLAGS, 0x04, 0x00])
    assert has_rpc_over_ble({0x1234: data}) is False