sofapython 0.0.1rc1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- sofapython/__init__.py +136 -0
- sofapython/ack.py +79 -0
- sofapython/aio.py +552 -0
- sofapython/backup_export.py +507 -0
- sofapython/blob_decoders.py +806 -0
- sofapython/cli.py +447 -0
- sofapython/commands.py +1273 -0
- sofapython/deframer.py +73 -0
- sofapython/device_create.py +1174 -0
- sofapython/devices.py +534 -0
- sofapython/discovery.py +315 -0
- sofapython/frame_handlers.py +131 -0
- sofapython/hub_listener.py +242 -0
- sofapython/hub_logging.py +152 -0
- sofapython/hub_versions.py +112 -0
- sofapython/inputs.py +501 -0
- sofapython/macros.py +669 -0
- sofapython/notify_demuxer.py +434 -0
- sofapython/opcode_handlers.py +1655 -0
- sofapython/protocol_const.py +633 -0
- sofapython/proxy_ack_waiters.py +660 -0
- sofapython/proxy_activity_ops.py +943 -0
- sofapython/proxy_backup.py +504 -0
- sofapython/proxy_backup_export.py +486 -0
- sofapython/proxy_catalog.py +915 -0
- sofapython/proxy_frame_decode.py +227 -0
- sofapython/proxy_ir_blob.py +676 -0
- sofapython/proxy_restore.py +2004 -0
- sofapython/proxy_wifi_device.py +1101 -0
- sofapython/state_helpers.py +713 -0
- sofapython/transport_bridge.py +876 -0
- sofapython/version.py +4 -0
- sofapython/wire_schema.py +164 -0
- sofapython/x1_proxy.py +1833 -0
- sofapython-0.0.1rc1.dist-info/METADATA +162 -0
- sofapython-0.0.1rc1.dist-info/RECORD +39 -0
- sofapython-0.0.1rc1.dist-info/WHEEL +4 -0
- sofapython-0.0.1rc1.dist-info/entry_points.txt +2 -0
- sofapython-0.0.1rc1.dist-info/licenses/LICENSE +21 -0
|
@@ -0,0 +1,633 @@
|
|
|
1
|
+
"""Common protocol constants shared by the Sofabaton proxy helpers."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from typing import Any, Dict
|
|
6
|
+
|
|
7
|
+
# Frame markers used by the hub protocol
|
|
8
|
+
SYNC0, SYNC1 = 0xA5, 0x5A
|
|
9
|
+
|
|
10
|
+
# Frame invariant: opcode_hi == payload_length
|
|
11
|
+
# ---------------------------------------------
|
|
12
|
+
# The byte at offset [2] (which is also the high byte of the 16-bit BE opcode)
|
|
13
|
+
# equals the number of payload bytes between the opcode and the checksum.
|
|
14
|
+
# Total frame length is therefore always `5 + (opcode >> 8)`.
|
|
15
|
+
#
|
|
16
|
+
# Companion-client framing relies on this directly instead of
|
|
17
|
+
# sync-scanning. Every opcode in this file obeys the invariant; if you
|
|
18
|
+
# add a new opcode constant whose high byte does not match the payload size in
|
|
19
|
+
# the comment, suspect the comment first.
|
|
20
|
+
#
|
|
21
|
+
# Example: OP_REQ_BUTTONS = 0x023C carries a 2-byte payload [act_lo, 0xFF];
|
|
22
|
+
# the frame is 7 bytes total: A5 5A 02 3C XX XX SUM.
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
class ButtonName:
|
|
26
|
+
"""Enumeration of known Sofabaton button codes."""
|
|
27
|
+
|
|
28
|
+
# X2-only / extended keys (below 0xAE)
|
|
29
|
+
C = 0x97
|
|
30
|
+
B = 0x98
|
|
31
|
+
A = 0x99
|
|
32
|
+
EXIT = 0x9A
|
|
33
|
+
DVR = 0x9B
|
|
34
|
+
PLAY = 0x9C
|
|
35
|
+
GUIDE = 0x9D
|
|
36
|
+
|
|
37
|
+
# Shared X1/X1S/X2 keys (existing)
|
|
38
|
+
UP = 0xAE
|
|
39
|
+
DOWN = 0xB2
|
|
40
|
+
LEFT = 0xAF
|
|
41
|
+
RIGHT = 0xB1
|
|
42
|
+
OK = 0xB0
|
|
43
|
+
HOME = 0xB4
|
|
44
|
+
BACK = 0xB3
|
|
45
|
+
MENU = 0xB5
|
|
46
|
+
VOL_UP = 0xB6
|
|
47
|
+
VOL_DOWN = 0xB9
|
|
48
|
+
MUTE = 0xB8
|
|
49
|
+
CH_UP = 0xB7
|
|
50
|
+
CH_DOWN = 0xBA
|
|
51
|
+
REW = 0xBB
|
|
52
|
+
PAUSE = 0xBC
|
|
53
|
+
FWD = 0xBD
|
|
54
|
+
RED = 0xBE
|
|
55
|
+
GREEN = 0xBF
|
|
56
|
+
YELLOW = 0xC0
|
|
57
|
+
BLUE = 0xC1
|
|
58
|
+
POWER_ON = 0xC6
|
|
59
|
+
POWER_OFF = 0xC7
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
BUTTONNAME_BY_CODE = {
|
|
63
|
+
v: k
|
|
64
|
+
for k, v in ButtonName.__dict__.items()
|
|
65
|
+
if isinstance(v, int) and k.isupper() and not k.startswith("_")
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
# A→H requests (from client to hub)
|
|
70
|
+
OP_REQ_BANNER = 0x0001 # yields family-0x02 banner reply with model/batch/hub-fw
|
|
71
|
+
OP_REQ_DEVICES = 0x000A # yields CATALOG_ROW_DEVICE rows (0xD50B)
|
|
72
|
+
OP_REQ_ACTIVITIES = 0x003A # yields CATALOG_ROW_ACTIVITY rows (0xD53B)
|
|
73
|
+
OP_REQ_BUTTONS = 0x023C # payload: [act_lo, 0xFF]
|
|
74
|
+
OP_REQ_COMMANDS = 0x025C # payload: [dev_lo, cmd] (1-byte) or [dev_lo, 0xFF] for full list
|
|
75
|
+
OP_REQ_BLOB = 0x020C # payload: [dev_lo, item_lo] or [dev_lo, 0xFF] for full blob/object dump
|
|
76
|
+
OP_REQ_ACTIVATE = 0x023F # payload: [id_lo, key_code] (activity or device ID)
|
|
77
|
+
OP_REQ_ACTIVITY_MAP = 0x016C # payload: [act_lo] request activity favorites mapping (X1)
|
|
78
|
+
OP_DELETE_DEVICE = 0x0109 # payload: [dev_lo] delete an existing device (observed X1)
|
|
79
|
+
OP_SET_HUB_NAME = 0x0030 # payload: GB2312-encoded hub name bytes
|
|
80
|
+
FAMILY_HUB_NAME_REPLY = 0x31 # H→A variable-length hub-name reply family
|
|
81
|
+
OP_FIND_REMOTE = 0x0023 # payload: [0x01] to trigger remote buzzer
|
|
82
|
+
OP_ERASE_CONFIGURATION = 0x001D # payload: empty; wipes all devices/activities/macros/favorites/inputs.
|
|
83
|
+
# Identical across X1, X1S, X2. The hub commonly disconnects after the ack; clients
|
|
84
|
+
# must reconnect and re-fetch the catalogs from scratch. See docs/protocol/erase.md.
|
|
85
|
+
# NOTE: opcode_hi=0x00 contradicts the documented 1-byte payload (frame
|
|
86
|
+
# invariant says payload length == opcode_hi). Two possibilities:
|
|
87
|
+
# - The actual opcode is 0x0123 (1-byte payload) and was mis-recorded, or
|
|
88
|
+
# - The payload is empty and the "[0x01]" in this comment is wrong.
|
|
89
|
+
# The X2 variant OP_FIND_REMOTE_X2 = 0x0323 correctly has a 3-byte payload.
|
|
90
|
+
# Worth verifying against a captured X1/X1S buzzer frame.
|
|
91
|
+
OP_FIND_REMOTE_X2 = 0x0323 # payload: [0x00, 0x00, 0x08] observed on X2 hubs
|
|
92
|
+
OP_REMOTE_SYNC = 0x0064 # payload: empty; force remote<->hub sync on X1/X1S
|
|
93
|
+
OP_X2_REMOTE_LIST = 0x012E # payload: [0x00]; request connected remotes on X2
|
|
94
|
+
OP_X2_REMOTE_LIST_ROW = 0x332F # payload includes 3-byte remote id and remote metadata
|
|
95
|
+
OP_X2_REMOTE_SYNC = 0x0464 # payload: [remote_id:3][0x01]; force sync for selected remote
|
|
96
|
+
OP_CREATE_DEVICE_HEAD = 0x07D5 # payload includes UTF-16LE device name
|
|
97
|
+
OP_DEFINE_IP_CMD = 0x0ED3 # payload includes HTTP method/URL/headers
|
|
98
|
+
OP_DEFINE_IP_CMD_EXISTING = 0x0EAE # payload defines IP command for an existing device
|
|
99
|
+
OP_PREPARE_SAVE = 0x4102 # payload triggers save transaction start
|
|
100
|
+
OP_FINALIZE_DEVICE = 0x4677
|
|
101
|
+
OP_DEVICE_SAVE_HEAD = 0x8D5D # hub assigns device id
|
|
102
|
+
OP_SAVE_COMMIT = 0x6501
|
|
103
|
+
# ACK family — dispatch by opcode-lo only
|
|
104
|
+
# ----------------------------------------
|
|
105
|
+
# ACK dispatch reads
|
|
106
|
+
# opcode-lo and ignores opcode-hi entirely when classifying ACKs.
|
|
107
|
+
# Because of the opcode-hi == payload-length invariant, `0x0103`, `0x0203`,
|
|
108
|
+
# `0x0303`, etc. are all the **same** "status ack" family with progressively
|
|
109
|
+
# larger payloads — only the payload-0 byte is the verdict.
|
|
110
|
+
#
|
|
111
|
+
# Success/failure rule (binary):
|
|
112
|
+
# payload[0] == 0x00 → success
|
|
113
|
+
# payload[0] != 0x00 → failure
|
|
114
|
+
#
|
|
115
|
+
# Other ACK / event opcode-lo families observed in the dispatcher:
|
|
116
|
+
# 0x15 batch-resume cursor: `cursor = BE(payload[1..4]) - 1`, re-emit
|
|
117
|
+
# 0x3E bare progression ack (no status byte; just "advance")
|
|
118
|
+
# 0x42 device power-state / idle-behavior reply family (status byte at payload offset 1)
|
|
119
|
+
# 0x45 one-shot status return (status byte at payload offset 1)
|
|
120
|
+
# 0x57 one-shot status return (status byte at payload offset 2)
|
|
121
|
+
# 0x60 ACK_READY family — posts "activity_update" event globally
|
|
122
|
+
# 0x67 OTA-update push event from hub
|
|
123
|
+
#
|
|
124
|
+
# `ACK_SUCCESS = 0x0301` and `OP_STATUS_ACK = 0x0103` are both opcode-lo 0x03;
|
|
125
|
+
# they are the same family with different payload widths. The naming is
|
|
126
|
+
# retained for backward compatibility but the dispatcher logic should treat
|
|
127
|
+
# any opcode with low byte 0x03 as a status frame.
|
|
128
|
+
ACK_SUCCESS = 0x0301
|
|
129
|
+
OP_STATUS_ACK = 0x0103 # H→A generic status/ack frame; payload[0] carries the status byte
|
|
130
|
+
# Status-ack family identifier (low byte). Use `opcode & 0xFF == FAMILY_STATUS_ACK`.
|
|
131
|
+
FAMILY_STATUS_ACK = 0x03
|
|
132
|
+
|
|
133
|
+
# IP command synchronization (existing devices)
|
|
134
|
+
OP_REQ_IPCMD_SYNC = 0x0C02
|
|
135
|
+
OP_IPCMD_ROW_A = 0x0DD3
|
|
136
|
+
OP_IPCMD_ROW_B = 0x0DAC
|
|
137
|
+
OP_IPCMD_ROW_C = 0x0D9B
|
|
138
|
+
OP_IPCMD_ROW_D = 0x0DAE
|
|
139
|
+
|
|
140
|
+
# H→A responses (from hub to app/client)
|
|
141
|
+
OP_ACK_READY = 0x0160
|
|
142
|
+
# H→A push emitted when the hub begins a firmware OTA update. The hub goes
|
|
143
|
+
# silent for a few minutes while the update runs and does not actively
|
|
144
|
+
# manage the connection during that window, so receivers are expected to
|
|
145
|
+
# tear down the session and stay disconnected until the hub comes back.
|
|
146
|
+
OP_OTA_UPDATE = 0x0167
|
|
147
|
+
OP_MARKER = 0x0C3D # segment marker before continuation
|
|
148
|
+
|
|
149
|
+
OP_CATALOG_ROW_DEVICE = 0xD50B # Row from list of devices
|
|
150
|
+
OP_CATALOG_ROW_ACTIVITY = 0xD53B # Row from list of activities
|
|
151
|
+
OP_DEVBTN_HEADER = 0xD95D # H→A: REQ_COMMANDS header page on X1S/X2
|
|
152
|
+
OP_DEVBTN_PAGE = 0xD55D # H→A: REQ_COMMANDS body page on X1S/X2
|
|
153
|
+
OP_DEVBTN_SINGLE = 0x4D5D # H→A: single-command metadata page (response to targeted REQ_COMMANDS)
|
|
154
|
+
OP_DEVBTN_TAIL = 0x495D # H→A: REQ_COMMANDS final page variant on X1S/X2
|
|
155
|
+
OP_KEYMAP_EXTRA = 0x303D # H→A: small follow-up page sometimes present (REQ_BUTTONS family)
|
|
156
|
+
OP_DEVBTN_MORE = 0x8F5D # H→A: REQ_COMMANDS final page variant on X1S/X2
|
|
157
|
+
OP_DEVBTN_PAGE_ALT1 = 0xF75D # H→A: REQ_COMMANDS header page on X1
|
|
158
|
+
OP_DEVBTN_PAGE_ALT2 = 0xA35D # H→A: REQ_COMMANDS final page variant on X1
|
|
159
|
+
OP_DEVBTN_PAGE_ALT3 = 0x2F5D # H→A: REQ_COMMANDS page variant on X1
|
|
160
|
+
OP_DEVBTN_PAGE_ALT4 = 0xF35D # H→A: REQ_COMMANDS body page on X1
|
|
161
|
+
OP_DEVBTN_PAGE_ALT5 = 0x7B5D # H→A: REQ_COMMANDS final/page variant on X1
|
|
162
|
+
OP_DEVBTN_PAGE_ALT6 = 0xCB5D # H→A: REQ_COMMANDS final page variant on X1
|
|
163
|
+
OP_DEVBTN_PAGE_ALT7 = 0x535D # H→A: REQ_COMMANDS final page variant on X1
|
|
164
|
+
|
|
165
|
+
# X1 hub responses
|
|
166
|
+
OP_X1_DEVICE = 0x7B0B # Row from list of devices (X1 firmware)
|
|
167
|
+
OP_X1_ACTIVITY = 0x7B3B # Row from list of activities (X1 firmware)
|
|
168
|
+
|
|
169
|
+
OP_KEYMAP_TBL_A = 0xF13D
|
|
170
|
+
OP_KEYMAP_TBL_B = 0xFA3D
|
|
171
|
+
OP_KEYMAP_TBL_C = 0x3D3D # Returned when Hue buttons requested
|
|
172
|
+
OP_KEYMAP_TBL_D = 0x1E3D # Observed keymap table variant
|
|
173
|
+
OP_KEYMAP_TBL_F = 0x783D # Observed keymap table variant
|
|
174
|
+
OP_KEYMAP_TBL_E = 0xBB3D # Observed keymap table variant
|
|
175
|
+
OP_KEYMAP_TBL_G = 0xCD3D # Observed keymap table variant
|
|
176
|
+
OP_KEYMAP_CONT = 0x543D # Observed continuation page after MARKER
|
|
177
|
+
OP_KEYMAP_FINAL_X1S = 0x233D # H→A: short final REQ_BUTTONS page on X1S/X2
|
|
178
|
+
OP_KEYMAP_PAGE_X2_C03D = 0xC03D # H→A: REQ_BUTTONS continuation/data page on X2
|
|
179
|
+
OP_KEYMAP_PAGE_X1_663D = 0x663D # H→A: REQ_BUTTONS page variant on X1
|
|
180
|
+
OP_KEYMAP_OVERLAY_X1 = 0x733D # H→A: single-page REQ_BUTTONS overlay burst on X1
|
|
181
|
+
OP_KEYMAP_PAGE_X1_AE3D = 0xAE3D # H→A: REQ_BUTTONS page variant on X1
|
|
182
|
+
OP_KEYMAP_PAGE_X1_E43D = 0xE43D # H→A: REQ_BUTTONS page variant on X1
|
|
183
|
+
|
|
184
|
+
# UDP CALL_ME (same frame used both directions over UDP)
|
|
185
|
+
OP_CALL_ME = 0x0CC3
|
|
186
|
+
|
|
187
|
+
# X1 activity mapping pages (favorites)
|
|
188
|
+
OP_ACTIVITY_MAP_PAGE = 0x7B6D
|
|
189
|
+
|
|
190
|
+
# noise we're not using (kept for reference)
|
|
191
|
+
OP_REQ_VERSION = 0x0058 # yields WIFI_FW (0x0359) then INFO_BANNER (0x112F)
|
|
192
|
+
OP_REQ_IDLE_BEHAVIOR = 0x0140 # payload: [dev_lo]; query device idle/power behavior
|
|
193
|
+
OP_REQ_ACTIVITY_INPUTS = 0x0148 # payload: [0x01] request activity input candidates
|
|
194
|
+
OP_SET_IDLE_BEHAVIOR = 0x0241 # payload: [dev_lo, mode]; update device idle/power behavior
|
|
195
|
+
OP_IDLE_BEHAVIOR = 0x0242 # H→A payload: [dev_lo, mode] current device idle/power behavior
|
|
196
|
+
OP_ACTIVITY_DEVICE_CONFIRM = 0x024F # payload: [dev_lo, include_flag]
|
|
197
|
+
OP_REQ_MACRO_LABELS = 0x024D # payload: [act_lo, 0xFF]
|
|
198
|
+
OP_REQ_MACROS = OP_REQ_MACRO_LABELS # backward-compat alias
|
|
199
|
+
OP_MACROS_A1 = 0x6E13
|
|
200
|
+
OP_MACROS_B1 = 0x5A13
|
|
201
|
+
OP_MACROS_A2 = 0x8213
|
|
202
|
+
OP_MACROS_B2 = 0x6413
|
|
203
|
+
OP_ACTIVITY_INPUTS_PAGE_A = 0xFA47 # H→A activity-input candidates page (X1S/X2)
|
|
204
|
+
OP_ACTIVITY_INPUTS_PAGE_B = 0xC947 # H→A final activity-input candidates page (X1S/X2)
|
|
205
|
+
OP_ACTIVITY_ASSIGN_FINALIZE = 0xD538 # A→H post-macro activity assignment save (X1S/X2)
|
|
206
|
+
OP_ACTIVITY_ASSIGN_COMMIT = 0x0265 # A→H post-save commit marker (observed on X2)
|
|
207
|
+
OP_ACTIVITY_CONFIRM = 0x7B38 # A→H activity confirmation row write (observed X1)
|
|
208
|
+
OP_ACTIVITY_CREATE_ACK = 0x0137 # H→A assigned activity id after family-0x37 create write (X1)
|
|
209
|
+
OP_ACTIVITY_MAP_PAGE_X1S = 0xD56D # H→A activity mapping page variant (X1S/X2)
|
|
210
|
+
OP_BANNER = 0x1D02 # representative family-0x02 banner reply with hub ident/name/batch/fw
|
|
211
|
+
OP_WIFI_FW = 0x0359 # WiFi firmware ver (Vx.y.z)
|
|
212
|
+
OP_INFO_BANNER = 0x112F # vendor tag, batch date, remote fw byte, etc.
|
|
213
|
+
|
|
214
|
+
|
|
215
|
+
# Backward-compatible aliases retained for older call sites and docs.
|
|
216
|
+
OP_PING2 = OP_REQ_IDLE_BEHAVIOR
|
|
217
|
+
OP_PING2_ACK = OP_IDLE_BEHAVIOR
|
|
218
|
+
|
|
219
|
+
OPNAMES: Dict[int, str] = {
|
|
220
|
+
OP_CALL_ME: "CALL_ME",
|
|
221
|
+
OP_REQ_BANNER: "REQ_BANNER",
|
|
222
|
+
OP_REQ_ACTIVITIES: "REQ_ACTIVITIES",
|
|
223
|
+
OP_REQ_DEVICES: "REQ_DEVICES",
|
|
224
|
+
OP_REQ_BUTTONS: "REQ_BUTTONS",
|
|
225
|
+
OP_REQ_COMMANDS: "REQ_COMMANDS",
|
|
226
|
+
OP_REQ_BLOB: "REQ_BLOB",
|
|
227
|
+
OP_REQ_ACTIVATE: "REQ_ACTIVATE",
|
|
228
|
+
OP_REQ_ACTIVITY_MAP: "REQ_ACTIVITY_MAP",
|
|
229
|
+
OP_DELETE_DEVICE: "DELETE_DEVICE",
|
|
230
|
+
OP_SET_HUB_NAME: "SET_HUB_NAME",
|
|
231
|
+
OP_FIND_REMOTE: "FIND_REMOTE",
|
|
232
|
+
OP_ERASE_CONFIGURATION: "ERASE_CONFIGURATION",
|
|
233
|
+
OP_FIND_REMOTE_X2: "FIND_REMOTE_X2",
|
|
234
|
+
OP_REMOTE_SYNC: "REMOTE_SYNC",
|
|
235
|
+
OP_X2_REMOTE_LIST: "X2_REMOTE_LIST",
|
|
236
|
+
OP_X2_REMOTE_LIST_ROW: "X2_REMOTE_LIST_ROW",
|
|
237
|
+
OP_X2_REMOTE_SYNC: "X2_REMOTE_SYNC",
|
|
238
|
+
OP_CREATE_DEVICE_HEAD: "CREATE_DEVICE_HEAD",
|
|
239
|
+
OP_DEFINE_IP_CMD: "DEFINE_IP_CMD",
|
|
240
|
+
OP_DEFINE_IP_CMD_EXISTING: "DEFINE_IP_CMD_EXISTING",
|
|
241
|
+
OP_PREPARE_SAVE: "PREPARE_SAVE",
|
|
242
|
+
OP_FINALIZE_DEVICE: "FINALIZE_DEVICE",
|
|
243
|
+
OP_DEVICE_SAVE_HEAD: "DEVICE_SAVE_HEAD",
|
|
244
|
+
OP_SAVE_COMMIT: "SAVE_COMMIT",
|
|
245
|
+
OP_REQ_IPCMD_SYNC: "REQ_IPCMD_SYNC",
|
|
246
|
+
OP_IPCMD_ROW_A: "IPCMD_ROW_A",
|
|
247
|
+
OP_IPCMD_ROW_B: "IPCMD_ROW_B",
|
|
248
|
+
OP_IPCMD_ROW_C: "IPCMD_ROW_C",
|
|
249
|
+
OP_IPCMD_ROW_D: "IPCMD_ROW_D",
|
|
250
|
+
ACK_SUCCESS: "ACK_SUCCESS",
|
|
251
|
+
OP_STATUS_ACK: "STATUS_ACK",
|
|
252
|
+
OP_ACTIVITY_CREATE_ACK: "ACTIVITY_CREATE_ACK",
|
|
253
|
+
OP_ACK_READY: "ACK_READY",
|
|
254
|
+
OP_OTA_UPDATE: "OTA_UPDATE",
|
|
255
|
+
OP_MARKER: "REQ_BUTTONS_MARKER_X1S_X2",
|
|
256
|
+
OP_CATALOG_ROW_DEVICE: "CATALOG_ROW_DEVICE",
|
|
257
|
+
OP_CATALOG_ROW_ACTIVITY: "CATALOG_ROW_ACTIVITY",
|
|
258
|
+
OP_KEYMAP_TBL_A: "REQ_BUTTONS_PAGE_A",
|
|
259
|
+
OP_KEYMAP_TBL_B: "REQ_BUTTONS_HEADER_OR_PAGE",
|
|
260
|
+
OP_KEYMAP_TBL_C: "REQ_BUTTONS_PAGE_C",
|
|
261
|
+
OP_KEYMAP_TBL_D: "REQ_BUTTONS_PAGE_D",
|
|
262
|
+
OP_KEYMAP_TBL_F: "REQ_BUTTONS_PAGE_F",
|
|
263
|
+
OP_KEYMAP_TBL_E: "REQ_BUTTONS_PAGE_E",
|
|
264
|
+
OP_KEYMAP_TBL_G: "REQ_BUTTONS_PAGE_G",
|
|
265
|
+
OP_KEYMAP_CONT: "REQ_BUTTONS_PAGE_X1S_X2",
|
|
266
|
+
OP_KEYMAP_FINAL_X1S: "REQ_BUTTONS_FINAL_X1S_X2_233D",
|
|
267
|
+
OP_KEYMAP_PAGE_X2_C03D: "REQ_BUTTONS_PAGE_X1S_X2_C03D",
|
|
268
|
+
OP_KEYMAP_PAGE_X1_663D: "REQ_BUTTONS_PAGE_X1_663D",
|
|
269
|
+
OP_KEYMAP_OVERLAY_X1: "REQ_BUTTONS_OVERLAY_X1",
|
|
270
|
+
OP_KEYMAP_PAGE_X1_AE3D: "REQ_BUTTONS_PAGE_X1_AE3D",
|
|
271
|
+
OP_KEYMAP_PAGE_X1_E43D: "REQ_BUTTONS_PAGE_X1_E43D",
|
|
272
|
+
OP_ACTIVITY_MAP_PAGE: "ACTIVITY_MAP_PAGE",
|
|
273
|
+
OP_DEVBTN_HEADER: "REQ_COMMANDS_HEADER_X1S_X2",
|
|
274
|
+
OP_DEVBTN_PAGE: "REQ_COMMANDS_PAGE_X1S_X2",
|
|
275
|
+
OP_DEVBTN_SINGLE: "REQ_COMMANDS_SINGLE",
|
|
276
|
+
OP_DEVBTN_TAIL: "REQ_COMMANDS_FINAL_X1S_X2_495D",
|
|
277
|
+
OP_KEYMAP_EXTRA: "REQ_BUTTONS_PAGE_EXTRA",
|
|
278
|
+
OP_DEVBTN_MORE: "REQ_COMMANDS_FINAL_X1S_X2_8F5D",
|
|
279
|
+
OP_DEVBTN_PAGE_ALT1: "REQ_COMMANDS_HEADER_X1",
|
|
280
|
+
OP_DEVBTN_PAGE_ALT2: "REQ_COMMANDS_FINAL_X1_A35D",
|
|
281
|
+
OP_DEVBTN_PAGE_ALT3: "REQ_COMMANDS_PAGE_X1_2F5D",
|
|
282
|
+
OP_DEVBTN_PAGE_ALT4: "REQ_COMMANDS_PAGE_X1",
|
|
283
|
+
OP_DEVBTN_PAGE_ALT5: "REQ_COMMANDS_PAGE_OR_FINAL_X1_7B5D",
|
|
284
|
+
OP_DEVBTN_PAGE_ALT6: "REQ_COMMANDS_FINAL_X1_CB5D",
|
|
285
|
+
OP_DEVBTN_PAGE_ALT7: "REQ_COMMANDS_FINAL_X1_535D",
|
|
286
|
+
OP_X1_DEVICE: "X1_DEVICE",
|
|
287
|
+
OP_X1_ACTIVITY: "X1_ACTIVITY",
|
|
288
|
+
# The rest are unused but kept for completeness
|
|
289
|
+
OP_BANNER: "BANNER",
|
|
290
|
+
OP_WIFI_FW: "WIFI_FW",
|
|
291
|
+
OP_INFO_BANNER: "INFO_BANNER",
|
|
292
|
+
OP_MACROS_A1: "MACROS_A1",
|
|
293
|
+
OP_MACROS_B1: "MACROS_B1",
|
|
294
|
+
OP_MACROS_A2: "MACROS_A2",
|
|
295
|
+
OP_MACROS_B2: "MACROS_B2",
|
|
296
|
+
OP_ACTIVITY_DEVICE_CONFIRM: "ACTIVITY_DEVICE_CONFIRM",
|
|
297
|
+
OP_REQ_ACTIVITY_INPUTS: "REQ_ACTIVITY_INPUTS",
|
|
298
|
+
OP_IDLE_BEHAVIOR: "IDLE_BEHAVIOR",
|
|
299
|
+
OP_REQ_MACRO_LABELS: "REQ_MACRO_LABELS",
|
|
300
|
+
OP_ACTIVITY_INPUTS_PAGE_A: "ACTIVITY_INPUTS_PAGE_A",
|
|
301
|
+
OP_ACTIVITY_INPUTS_PAGE_B: "ACTIVITY_INPUTS_PAGE_B",
|
|
302
|
+
OP_ACTIVITY_ASSIGN_FINALIZE: "ACTIVITY_ASSIGN_FINALIZE",
|
|
303
|
+
OP_ACTIVITY_ASSIGN_COMMIT: "ACTIVITY_ASSIGN_COMMIT",
|
|
304
|
+
OP_ACTIVITY_CONFIRM: "ACTIVITY_CONFIRM",
|
|
305
|
+
OP_ACTIVITY_MAP_PAGE_X1S: "ACTIVITY_MAP_PAGE_X1S",
|
|
306
|
+
OP_REQ_VERSION: "REQ_VERSION",
|
|
307
|
+
OP_REQ_IDLE_BEHAVIOR: "REQ_IDLE_BEHAVIOR",
|
|
308
|
+
OP_SET_IDLE_BEHAVIOR: "SET_IDLE_BEHAVIOR",
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
|
|
312
|
+
def opcode_hi(opcode: int) -> int:
|
|
313
|
+
"""Return the high byte of an opcode."""
|
|
314
|
+
|
|
315
|
+
return (opcode >> 8) & 0xFF
|
|
316
|
+
|
|
317
|
+
|
|
318
|
+
def opcode_lo(opcode: int) -> int:
|
|
319
|
+
"""Return the low byte of an opcode."""
|
|
320
|
+
|
|
321
|
+
return opcode & 0xFF
|
|
322
|
+
|
|
323
|
+
|
|
324
|
+
def opcode_family(opcode: int) -> int:
|
|
325
|
+
"""Return the low-byte "family" for list/table opcodes."""
|
|
326
|
+
|
|
327
|
+
return opcode_lo(opcode)
|
|
328
|
+
|
|
329
|
+
|
|
330
|
+
# Known opcode families (low byte) grouped by semantic row/page type
|
|
331
|
+
FAMILY_STATUS_ACK = 0x03 # generic status / ack responses
|
|
332
|
+
FAMILY_DEV_ROW = 0x0B # device catalog rows (OP_CATALOG_ROW_DEVICE, OP_X1_DEVICE)
|
|
333
|
+
FAMILY_ACT_ROW = 0x3B # activity catalog rows (OP_CATALOG_ROW_ACTIVITY, OP_X1_ACTIVITY)
|
|
334
|
+
FAMILY_MACROS = 0x13 # macro pages (OP_MACROS_A1/B1/A2/B2)
|
|
335
|
+
FAMILY_KEYMAP = 0x3D # keymap / continuation / devbtn-extra pages
|
|
336
|
+
FAMILY_DEVBTNS = 0x5D # device button pages (header, body, tail, variants)
|
|
337
|
+
|
|
338
|
+
# Favorites reorder / delete families (observed in app traffic)
|
|
339
|
+
FAMILY_FAV_DELETE = 0x10 # A→H: delete a single favorite from an activity (opcode 0x0210)
|
|
340
|
+
FAMILY_FAV_ORDER_REQ = 0x62 # A→H: request current favorites ordering (opcode 0x0162)
|
|
341
|
+
FAMILY_FAV_ORDER_RESP = 0x63 # H→A: hub returns current favorites ordering (opcode variable)
|
|
342
|
+
|
|
343
|
+
FAMILY_KEY_SORT_REQ = 0x62
|
|
344
|
+
FAMILY_KEY_SORT_RESP = 0x63
|
|
345
|
+
|
|
346
|
+
# IR blob playback.
|
|
347
|
+
# Each frame carries a 247-byte slice of a single contiguous body buffer.
|
|
348
|
+
# The body buffer is laid out as:
|
|
349
|
+
# body[0] = 0x01
|
|
350
|
+
# body[1..2] = total_pages BE
|
|
351
|
+
# body[3..5] = 0x00 0x00 0x00
|
|
352
|
+
# body[6..11] = 0x00 * 6
|
|
353
|
+
# body[12..] = library_data
|
|
354
|
+
# body[-1] = sum8 over the preceding bytes
|
|
355
|
+
# Every wire frame prepends a 3-byte page header [0x01, 0x00, page_no_lo] to
|
|
356
|
+
# its slice. Frame opcode_hi is the resulting payload length (full chunks are
|
|
357
|
+
# OP_FA0F = 250B payload).
|
|
358
|
+
FAMILY_PLAY_BLOB = 0x0F
|
|
359
|
+
PLAY_BLOB_MAX_PAYLOAD = 0xFA # 250B — full-chunk payload size
|
|
360
|
+
PLAY_BLOB_PAGE_HEADER_LEN = 3 # [0x01, 0x00, page_no_lo] preface per frame
|
|
361
|
+
PLAY_BLOB_BODY_HEADER_LEN = 12 # body bytes preceding library_data
|
|
362
|
+
PLAY_BLOB_CHUNK_SIZE = PLAY_BLOB_MAX_PAYLOAD - PLAY_BLOB_PAGE_HEADER_LEN # 247B body slice per frame
|
|
363
|
+
|
|
364
|
+
FAMILY_BLOB_ROW = 0x0D # IP-command sync rows, input-config refresh, REQ_BLOB dump pages
|
|
365
|
+
|
|
366
|
+
FAMILY_NAMES: Dict[int, str] = {
|
|
367
|
+
FAMILY_STATUS_ACK: "STATUS_ACK",
|
|
368
|
+
FAMILY_HUB_NAME_REPLY: "HUB_NAME_REPLY",
|
|
369
|
+
FAMILY_DEV_ROW: "DEVICE_ROW",
|
|
370
|
+
FAMILY_BLOB_ROW: "BLOB_ROW",
|
|
371
|
+
FAMILY_PLAY_BLOB: "PLAY_BLOB",
|
|
372
|
+
FAMILY_FAV_DELETE: "FAV_DELETE",
|
|
373
|
+
FAMILY_MACROS: "MACROS",
|
|
374
|
+
FAMILY_KEYMAP: "KEYMAP",
|
|
375
|
+
FAMILY_ACT_ROW: "ACTIVITY_ROW",
|
|
376
|
+
FAMILY_DEVBTNS: "COMMANDS",
|
|
377
|
+
FAMILY_FAV_ORDER_REQ: "FAV_ORDER_REQ",
|
|
378
|
+
FAMILY_FAV_ORDER_RESP: "FAV_ORDER_RESP",
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
|
|
382
|
+
DEVICE_CLASS_IR = "ir"
|
|
383
|
+
DEVICE_CLASS_BLUETOOTH = "bluetooth"
|
|
384
|
+
DEVICE_CLASS_WIFI_HUE = "wifi_hue"
|
|
385
|
+
DEVICE_CLASS_WIFI_ROKU = "wifi_roku"
|
|
386
|
+
DEVICE_CLASS_WIFI_IP = "wifi_ip"
|
|
387
|
+
DEVICE_CLASS_WIFI_MQTT = "wifi_mqtt"
|
|
388
|
+
DEVICE_CLASS_WIFI_SONOS = "wifi_sonos"
|
|
389
|
+
DEVICE_CLASS_RF_315 = "rf_315mhz"
|
|
390
|
+
DEVICE_CLASS_RF_433 = "rf_433mhz"
|
|
391
|
+
|
|
392
|
+
DEVICE_CLASS_CODE_BLUETOOTH = 0x03
|
|
393
|
+
DEVICE_CLASS_CODE_WIFI_ROKU = 0x0A
|
|
394
|
+
DEVICE_CLASS_CODE_IR = 0x0D
|
|
395
|
+
DEVICE_CLASS_CODE_WIFI_HUE = 0x1A
|
|
396
|
+
DEVICE_CLASS_CODE_WIFI_SONOS = 0x1B
|
|
397
|
+
DEVICE_CLASS_CODE_WIFI_IP = 0x1C
|
|
398
|
+
DEVICE_CLASS_CODE_WIFI_MQTT = 0x20
|
|
399
|
+
|
|
400
|
+
DEVICE_CLASS_BY_CODE: Dict[int, str] = {
|
|
401
|
+
DEVICE_CLASS_CODE_BLUETOOTH: DEVICE_CLASS_BLUETOOTH,
|
|
402
|
+
DEVICE_CLASS_CODE_WIFI_ROKU: DEVICE_CLASS_WIFI_ROKU,
|
|
403
|
+
DEVICE_CLASS_CODE_IR: DEVICE_CLASS_IR,
|
|
404
|
+
DEVICE_CLASS_CODE_WIFI_HUE: DEVICE_CLASS_WIFI_HUE,
|
|
405
|
+
DEVICE_CLASS_CODE_WIFI_SONOS: DEVICE_CLASS_WIFI_SONOS,
|
|
406
|
+
DEVICE_CLASS_CODE_WIFI_IP: DEVICE_CLASS_WIFI_IP,
|
|
407
|
+
DEVICE_CLASS_CODE_WIFI_MQTT: DEVICE_CLASS_WIFI_MQTT,
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
DEVICE_CLASS_ALIASES: Dict[str, str] = {
|
|
411
|
+
"bt": DEVICE_CLASS_BLUETOOTH,
|
|
412
|
+
"bluetooth": DEVICE_CLASS_BLUETOOTH,
|
|
413
|
+
"device_type_bluetooth": DEVICE_CLASS_BLUETOOTH,
|
|
414
|
+
"hue": DEVICE_CLASS_WIFI_HUE,
|
|
415
|
+
"ip": DEVICE_CLASS_WIFI_IP,
|
|
416
|
+
"ir": DEVICE_CLASS_IR,
|
|
417
|
+
"rf": DEVICE_CLASS_RF_433,
|
|
418
|
+
"rf/315": DEVICE_CLASS_RF_315,
|
|
419
|
+
"rf/433": DEVICE_CLASS_RF_433,
|
|
420
|
+
"rf315": DEVICE_CLASS_RF_315,
|
|
421
|
+
"rf315mhz": DEVICE_CLASS_RF_315,
|
|
422
|
+
"rf_315": DEVICE_CLASS_RF_315,
|
|
423
|
+
"rf_315mhz": DEVICE_CLASS_RF_315,
|
|
424
|
+
"rf433": DEVICE_CLASS_RF_433,
|
|
425
|
+
"rf433mhz": DEVICE_CLASS_RF_433,
|
|
426
|
+
"rf_433": DEVICE_CLASS_RF_433,
|
|
427
|
+
"rf_433mhz": DEVICE_CLASS_RF_433,
|
|
428
|
+
"roku": DEVICE_CLASS_WIFI_ROKU,
|
|
429
|
+
"sonos": DEVICE_CLASS_WIFI_SONOS,
|
|
430
|
+
"mqtt": DEVICE_CLASS_WIFI_MQTT,
|
|
431
|
+
"315": DEVICE_CLASS_RF_315,
|
|
432
|
+
"315mhz": DEVICE_CLASS_RF_315,
|
|
433
|
+
"433": DEVICE_CLASS_RF_433,
|
|
434
|
+
"433mhz": DEVICE_CLASS_RF_433,
|
|
435
|
+
"virtual_http": DEVICE_CLASS_WIFI_IP,
|
|
436
|
+
"wifi/hue": DEVICE_CLASS_WIFI_HUE,
|
|
437
|
+
"wifi/ip": DEVICE_CLASS_WIFI_IP,
|
|
438
|
+
"wifi/mqtt": DEVICE_CLASS_WIFI_MQTT,
|
|
439
|
+
"wifi/roku": DEVICE_CLASS_WIFI_ROKU,
|
|
440
|
+
"wifi/sonos": DEVICE_CLASS_WIFI_SONOS,
|
|
441
|
+
"wifi_hue": DEVICE_CLASS_WIFI_HUE,
|
|
442
|
+
"wifi_ip": DEVICE_CLASS_WIFI_IP,
|
|
443
|
+
"wifi_mqtt": DEVICE_CLASS_WIFI_MQTT,
|
|
444
|
+
"wifi_roku": DEVICE_CLASS_WIFI_ROKU,
|
|
445
|
+
"wifi_sonos": DEVICE_CLASS_WIFI_SONOS,
|
|
446
|
+
}
|
|
447
|
+
|
|
448
|
+
PUBLIC_DEVICE_CLASSES: tuple[str, ...] = (
|
|
449
|
+
DEVICE_CLASS_IR,
|
|
450
|
+
DEVICE_CLASS_BLUETOOTH,
|
|
451
|
+
DEVICE_CLASS_WIFI_HUE,
|
|
452
|
+
DEVICE_CLASS_WIFI_ROKU,
|
|
453
|
+
DEVICE_CLASS_WIFI_IP,
|
|
454
|
+
DEVICE_CLASS_WIFI_MQTT,
|
|
455
|
+
DEVICE_CLASS_WIFI_SONOS,
|
|
456
|
+
DEVICE_CLASS_RF_315,
|
|
457
|
+
DEVICE_CLASS_RF_433,
|
|
458
|
+
)
|
|
459
|
+
|
|
460
|
+
|
|
461
|
+
def classify_device_class_code(device_class_code: Any) -> str | None:
|
|
462
|
+
"""Map an observed device-class code to a stable normalized string."""
|
|
463
|
+
|
|
464
|
+
try:
|
|
465
|
+
code = int(device_class_code) & 0xFF
|
|
466
|
+
except (TypeError, ValueError):
|
|
467
|
+
return None
|
|
468
|
+
return DEVICE_CLASS_BY_CODE.get(code)
|
|
469
|
+
|
|
470
|
+
|
|
471
|
+
def normalize_device_class(value: Any) -> str | None:
|
|
472
|
+
text = str(value or "").strip().lower()
|
|
473
|
+
if not text:
|
|
474
|
+
return None
|
|
475
|
+
return DEVICE_CLASS_ALIASES.get(text, text)
|
|
476
|
+
|
|
477
|
+
|
|
478
|
+
def known_public_device_classes() -> tuple[str, ...]:
|
|
479
|
+
"""Return the normalized public device classes we intentionally model."""
|
|
480
|
+
|
|
481
|
+
return PUBLIC_DEVICE_CLASSES
|
|
482
|
+
|
|
483
|
+
|
|
484
|
+
def opcode_family_name(opcode: int) -> str | None:
|
|
485
|
+
"""Return a human-friendly name for an opcode family, if known."""
|
|
486
|
+
|
|
487
|
+
return FAMILY_NAMES.get(opcode_family(opcode))
|
|
488
|
+
|
|
489
|
+
|
|
490
|
+
def group_known_opcodes_by_family() -> dict[int, list[str]]:
|
|
491
|
+
"""Return a mapping of low-byte opcode families to names defined here."""
|
|
492
|
+
|
|
493
|
+
family_map: dict[int, list[str]] = {}
|
|
494
|
+
for name, value in globals().items():
|
|
495
|
+
if not name.startswith("OP_"):
|
|
496
|
+
continue
|
|
497
|
+
if not isinstance(value, int):
|
|
498
|
+
continue
|
|
499
|
+
low = opcode_lo(value)
|
|
500
|
+
family_map.setdefault(low, []).append(name)
|
|
501
|
+
return family_map
|
|
502
|
+
|
|
503
|
+
|
|
504
|
+
__all__ = [
|
|
505
|
+
"SYNC0",
|
|
506
|
+
"SYNC1",
|
|
507
|
+
"ButtonName",
|
|
508
|
+
"BUTTONNAME_BY_CODE",
|
|
509
|
+
"DEVICE_CLASS_IR",
|
|
510
|
+
"DEVICE_CLASS_BLUETOOTH",
|
|
511
|
+
"DEVICE_CLASS_WIFI_HUE",
|
|
512
|
+
"DEVICE_CLASS_WIFI_ROKU",
|
|
513
|
+
"DEVICE_CLASS_WIFI_IP",
|
|
514
|
+
"DEVICE_CLASS_WIFI_MQTT",
|
|
515
|
+
"DEVICE_CLASS_WIFI_SONOS",
|
|
516
|
+
"DEVICE_CLASS_RF_315",
|
|
517
|
+
"DEVICE_CLASS_RF_433",
|
|
518
|
+
"DEVICE_CLASS_CODE_BLUETOOTH",
|
|
519
|
+
"DEVICE_CLASS_CODE_WIFI_ROKU",
|
|
520
|
+
"DEVICE_CLASS_CODE_IR",
|
|
521
|
+
"DEVICE_CLASS_CODE_WIFI_HUE",
|
|
522
|
+
"DEVICE_CLASS_CODE_WIFI_SONOS",
|
|
523
|
+
"DEVICE_CLASS_CODE_WIFI_IP",
|
|
524
|
+
"DEVICE_CLASS_CODE_WIFI_MQTT",
|
|
525
|
+
"DEVICE_CLASS_BY_CODE",
|
|
526
|
+
"DEVICE_CLASS_ALIASES",
|
|
527
|
+
"PUBLIC_DEVICE_CLASSES",
|
|
528
|
+
"classify_device_class_code",
|
|
529
|
+
"known_public_device_classes",
|
|
530
|
+
"normalize_device_class",
|
|
531
|
+
"OP_REQ_BANNER",
|
|
532
|
+
"OP_REQ_DEVICES",
|
|
533
|
+
"OP_REQ_ACTIVITIES",
|
|
534
|
+
"OP_REQ_BUTTONS",
|
|
535
|
+
"OP_REQ_COMMANDS",
|
|
536
|
+
"OP_REQ_BLOB",
|
|
537
|
+
"OP_REQ_ACTIVATE",
|
|
538
|
+
"OP_REQ_ACTIVITY_MAP",
|
|
539
|
+
"OP_DELETE_DEVICE",
|
|
540
|
+
"OP_SET_HUB_NAME",
|
|
541
|
+
"OP_FIND_REMOTE",
|
|
542
|
+
"OP_FIND_REMOTE_X2",
|
|
543
|
+
"OP_REMOTE_SYNC",
|
|
544
|
+
"OP_X2_REMOTE_LIST",
|
|
545
|
+
"OP_X2_REMOTE_LIST_ROW",
|
|
546
|
+
"OP_X2_REMOTE_SYNC",
|
|
547
|
+
"OP_CREATE_DEVICE_HEAD",
|
|
548
|
+
"OP_DEFINE_IP_CMD",
|
|
549
|
+
"OP_DEFINE_IP_CMD_EXISTING",
|
|
550
|
+
"OP_PREPARE_SAVE",
|
|
551
|
+
"OP_FINALIZE_DEVICE",
|
|
552
|
+
"OP_DEVICE_SAVE_HEAD",
|
|
553
|
+
"OP_SAVE_COMMIT",
|
|
554
|
+
"ACK_SUCCESS",
|
|
555
|
+
"OP_STATUS_ACK",
|
|
556
|
+
"OP_REQ_IPCMD_SYNC",
|
|
557
|
+
"OP_IPCMD_ROW_A",
|
|
558
|
+
"OP_IPCMD_ROW_B",
|
|
559
|
+
"OP_IPCMD_ROW_C",
|
|
560
|
+
"OP_IPCMD_ROW_D",
|
|
561
|
+
"OP_ACK_READY",
|
|
562
|
+
"OP_OTA_UPDATE",
|
|
563
|
+
"OP_MARKER",
|
|
564
|
+
"OP_CATALOG_ROW_DEVICE",
|
|
565
|
+
"OP_CATALOG_ROW_ACTIVITY",
|
|
566
|
+
"OP_DEVBTN_HEADER",
|
|
567
|
+
"OP_DEVBTN_PAGE",
|
|
568
|
+
"OP_DEVBTN_SINGLE",
|
|
569
|
+
"OP_DEVBTN_TAIL",
|
|
570
|
+
"OP_KEYMAP_EXTRA",
|
|
571
|
+
"OP_DEVBTN_MORE",
|
|
572
|
+
"OP_DEVBTN_PAGE_ALT1",
|
|
573
|
+
"OP_DEVBTN_PAGE_ALT2",
|
|
574
|
+
"OP_DEVBTN_PAGE_ALT3",
|
|
575
|
+
"OP_DEVBTN_PAGE_ALT4",
|
|
576
|
+
"OP_DEVBTN_PAGE_ALT5",
|
|
577
|
+
"OP_DEVBTN_PAGE_ALT6",
|
|
578
|
+
"OP_X1_DEVICE",
|
|
579
|
+
"OP_X1_ACTIVITY",
|
|
580
|
+
"OP_KEYMAP_TBL_A",
|
|
581
|
+
"OP_KEYMAP_TBL_B",
|
|
582
|
+
"OP_KEYMAP_TBL_C",
|
|
583
|
+
"OP_KEYMAP_TBL_D",
|
|
584
|
+
"OP_KEYMAP_TBL_F",
|
|
585
|
+
"OP_KEYMAP_TBL_E",
|
|
586
|
+
"OP_KEYMAP_TBL_G",
|
|
587
|
+
"OP_KEYMAP_CONT",
|
|
588
|
+
"OP_KEYMAP_FINAL_X1S",
|
|
589
|
+
"OP_KEYMAP_PAGE_X2_C03D",
|
|
590
|
+
"OP_KEYMAP_PAGE_X1_663D",
|
|
591
|
+
"OP_KEYMAP_OVERLAY_X1",
|
|
592
|
+
"OP_KEYMAP_PAGE_X1_AE3D",
|
|
593
|
+
"OP_KEYMAP_PAGE_X1_E43D",
|
|
594
|
+
"OP_CALL_ME",
|
|
595
|
+
"OP_ACTIVITY_MAP_PAGE",
|
|
596
|
+
"OP_REQ_VERSION",
|
|
597
|
+
"OP_REQ_IDLE_BEHAVIOR",
|
|
598
|
+
"OP_SET_IDLE_BEHAVIOR",
|
|
599
|
+
"OP_IDLE_BEHAVIOR",
|
|
600
|
+
"OP_PING2",
|
|
601
|
+
"OP_PING2_ACK",
|
|
602
|
+
"OP_ACTIVITY_DEVICE_CONFIRM",
|
|
603
|
+
"OP_REQ_ACTIVITY_INPUTS",
|
|
604
|
+
"OP_REQ_MACRO_LABELS",
|
|
605
|
+
"OP_MACROS_A1",
|
|
606
|
+
"OP_MACROS_B1",
|
|
607
|
+
"OP_MACROS_A2",
|
|
608
|
+
"OP_MACROS_B2",
|
|
609
|
+
"OP_ACTIVITY_INPUTS_PAGE_A",
|
|
610
|
+
"OP_ACTIVITY_INPUTS_PAGE_B",
|
|
611
|
+
"OP_ACTIVITY_ASSIGN_FINALIZE",
|
|
612
|
+
"OP_ACTIVITY_CONFIRM",
|
|
613
|
+
"OP_ACTIVITY_MAP_PAGE_X1S",
|
|
614
|
+
"OP_BANNER",
|
|
615
|
+
"OP_WIFI_FW",
|
|
616
|
+
"OP_INFO_BANNER",
|
|
617
|
+
"OPNAMES",
|
|
618
|
+
"opcode_hi",
|
|
619
|
+
"opcode_lo",
|
|
620
|
+
"opcode_family",
|
|
621
|
+
"opcode_family_name",
|
|
622
|
+
"FAMILY_NAMES",
|
|
623
|
+
"FAMILY_STATUS_ACK",
|
|
624
|
+
"FAMILY_HUB_NAME_REPLY",
|
|
625
|
+
"FAMILY_DEV_ROW",
|
|
626
|
+
"FAMILY_ACT_ROW",
|
|
627
|
+
"FAMILY_MACROS",
|
|
628
|
+
"FAMILY_KEYMAP",
|
|
629
|
+
"FAMILY_DEVBTNS",
|
|
630
|
+
"FAMILY_FAV_DELETE",
|
|
631
|
+
"FAMILY_FAV_ORDER_REQ",
|
|
632
|
+
"FAMILY_FAV_ORDER_RESP",
|
|
633
|
+
]
|