Home IO Control
ESPHome add-on for IO-Homecontrol devices
Loading...
Searching...
No Matches
proto_constants.h
Go to the documentation of this file.
1#pragma once
2
3/// @file proto_constants.h
4/// @brief IO-Homecontrol command IDs, result codes and protocol enumerations.
5/// @ingroup hioc_protocol
6///
7/// Command bytes, CMD_ERROR_RESP result codes, wire position/status flags,
8/// cryptographic constants, and the manufacturer/originator/ACEI/discovery
9/// lookups. These describe *what* travels on the wire, independent of the
10/// frame container (proto_frame.h) and the device model (proto_device_model.h).
11
12#include "proto_sizes.h"
13
14#include <cstdint>
15
16namespace esphome {
17namespace home_io_control {
18
19// ============================================================================
20// Command IDs
21// ============================================================================
22
23// Normal operation commands
24static constexpr uint8_t CMD_EXECUTE = 0x00; ///< Set position/open/close/stop — requires authentication
25static constexpr uint8_t CMD_ACTIVATE_MODE = 0x01; ///< Activate device mode (scene, ventilation) — requires auth
26static constexpr uint8_t CMD_PRIVATE = 0x03; ///< Get device status — no authentication needed
27static constexpr uint8_t CMD_PRIVATE_RESP = 0x04; ///< Response to 0x00 and 0x03 (contains position data)
28static constexpr uint8_t CMD_PRIVATE2 =
29 0x0C; ///< Content otherwise undecoded by the wire parser. Its request payload matches
30 ///< CMD_EXECUTE's POS_FAVORITE/POS_VENT_MODIFIER stored-position selector with the
31 ///< execution prefix stripped, so it reads like a stored-position readback rather
32 ///< than a live telemetry poll — see the somfy_rs100_* / somfy_oximo40_* captures and
33 ///< multi_somfy_probe_private2_{long_form,short_form}.yaml for real request/response
34 ///< pairs. Not handled by any dispatch path in this codebase.
35static constexpr uint8_t CMD_PRIVATE2_RESP = 0x0D; ///< Response to CMD_PRIVATE2. See CMD_PRIVATE2's comment.
36
37// Sensor and private register commands
38static constexpr uint8_t CMD_SET_SENSOR =
39 0x19; ///< Inject sensor value into a device. First real-world capture (issue #27, community,
40 ///< a real Somfy TaHoma Switch writing directly to a real Sunea io screen it already
41 ///< owned) is tests/corpus/captures/exchange/somfy_awning_exchange_set_sensor_sx1276.yaml
42 ///< — a single-byte payload, values 0x02 and then 0x04 seen ~212ms apart. Likely the
43 ///< mechanism a real hub uses to push a wind/rain/sun sensor reading into a device (the
44 ///< mirror image of reading a rain/limitation state back out, which issue #98 has been
45 ///< chasing separately) — not confirmed, and no CMD_SET_SENSOR_ACK was captured either,
46 ///< so the full round trip and payload semantics remain unknown. No builder or dispatch
47 ///< path exists anywhere in this codebase.
48static constexpr uint8_t CMD_SET_SENSOR_ACK = 0x1A; ///< Acknowledgment to CMD_SET_SENSOR. Never
49 ///< observed on the wire — see CMD_SET_SENSOR.
50
51// Device identification
52static constexpr uint8_t CMD_IDENTIFY = 0x1E; ///< Device physical identification / jog — requires authentication
53
54static constexpr uint8_t CMD_WRITE_PRIVATE = 0x20; ///< Write private register (climate/heating devices)
55static constexpr uint8_t CMD_WRITE_PRIVATE_ACK = 0x21; ///< Acknowledgment to CMD_WRITE_PRIVATE
56
57// Discovery and pairing commands
58static constexpr uint8_t CMD_DISCOVER_REQ = 0x28; ///< Broadcast discovery request
59static constexpr uint8_t CMD_DISCOVER_RESP = 0x29; ///< Device responds with its ID and type
60static constexpr uint8_t CMD_DISCOVER_SPE_REQ =
61 0x2A; ///< Broadcast roll-call answered by every device that already holds this controller's
62 ///< system key, regardless of device type. A device that holds no key yet — one in
63 ///< learning mode, mid-pairing — has nothing to authenticate the request against and
64 ///< stays silent, so this enumerates already-managed devices and cannot discover new
65 ///< ones. Do not offer it as a pairing-discovery command: it can only ever add replies
66 ///< from devices already paired, never help reach an unpaired one.
67 ///< The 12-byte payload authenticates itself in a single frame — 6 random challenge
68 ///< bytes followed by a 6-byte HMAC over the command byte alone — instead of the usual
69 ///< 0x3C/0x3D round trip, which is what lets it be broadcast;
70 ///< create_discovery_request() (proto_commands.cpp) builds it to match. Real captured
71 ///< bytes: tests/corpus/captures/pairing/velux_kux100_pairing_full.yaml (request shape, HMAC
72 ///< recomputed under that installation's key before the capture was re-keyed) and
73 ///< tests/corpus/captures/discovery/somfy_awning_discovery_spe_paired_rollcall.yaml plus
74 ///< tests/corpus/captures/discovery/somfy_izymo_dimmer_discovery_spe_paired_rollcall.yaml (two awnings
75 ///< and a dimmer answering one broadcast). No dispatch path consumes the reply yet;
76 ///< classify_pairing_discovery_response() accepts only 0x29 and must not be extended to
77 ///< accept 0x2B, because a roll-call reply from an already-paired device is not a
78 ///< newly-discovered one and must never enter the pairing flow. See
79 ///< CMD_DISCOVER_SPE_RESP.
80static constexpr uint8_t CMD_DISCOVER_SPE_RESP =
81 0x2B; ///< Roll-call reply to CMD_DISCOVER_SPE_REQ, sent only by devices that already hold the
82 ///< requesting controller's system key. The payload is the same DISCOVERY_RESP_FULL_SIZE
83 ///< layout as a CMD_DISCOVER_RESP (0x29), so every DISCOVERY_RESP_*_OFFSET constant
84 ///< below applies unchanged — packed type/subtype at data[0..1], backbone address at
85 ///< DISCOVERY_RESP_BACKBONE_OFFSET, manufacturer at DISCOVERY_RESP_MANUFACTURER_OFFSET,
86 ///< Multi Information Byte at DISCOVERY_RESP_FLAGS_OFFSET, timestamp at
87 ///< DISCOVERY_RESP_TIMESTAMP_OFFSET — and PairingEngine::parse_device_from_discovery()
88 ///< decodes a 0x2B correctly with no special-casing. Real captured replies (two awnings
89 ///< and a dimmer) are in tests/corpus/captures/discovery/somfy_awning_discovery_spe_paired_rollcall.yaml
90 ///< and tests/corpus/captures/discovery/somfy_izymo_dimmer_discovery_spe_paired_rollcall.yaml. The
91 ///< timestamp is the field that advances between
92 ///< successive replies from one device; it is not a response to the request's random
93 ///< challenge, since that HMAC covers only the constant command byte. Treat a reply as
94 ///< self-description, not proof of identity: nothing in it is bound to the request, so
95 ///< report it, never act on it. No dispatch path consumes a 0x2B yet.
96static constexpr uint8_t CMD_DISCOVER_CONFIRM = 0x2C; ///< Confirm discovery to device
97static constexpr uint8_t CMD_DISCOVER_CONFIRM_ACK = 0x2D; ///< Device acknowledges confirmation
98static constexpr uint8_t CMD_DISCOVER_ALT_REQ =
99 0x2E; ///< Alternate discovery. Broadcast (to 0x00003F) draws no response at all on every
100 ///< device this project has real evidence for — a Somfy Izymo dimmer
101 ///< (tests/corpus/captures/discovery/somfy_izymo_dimmer_discovery_alt_no_response.yaml) and a Velux
102 ///< KLR200/KUX100 pair
103 ///< (tests/corpus/captures/discovery/velux_kux100_discovery_alt_broadcast_no_response.yaml) both went
104 ///< unanswered; the older "response is 0x29" guess never had real evidence and appears to have been wrong.
105 ///< Directly *addressed* to a known device instead of broadcast, it does draw a
106 ///< response, but a 0x3C/0x3D challenge-response followed by CMD_DISCOVER_ALT_RESP
107 ///< (0x2F), not 0x29 — see
108 ///< tests/corpus/captures/discovery/velux_kux100_discovery_alt_addressed_challenge_response.yaml.
109static constexpr uint8_t CMD_DISCOVER_ALT_RESP =
110 0x2F; ///< Reply to an addressed (non-broadcast) CMD_DISCOVER_ALT_REQ, following a
111 ///< 0x3C/0x3D challenge-response. See CMD_DISCOVER_ALT_REQ's comment and
112 ///< tests/corpus/captures/discovery/velux_kux100_discovery_alt_addressed_challenge_response.yaml
113 ///< — the only capture this project has of it. Not otherwise used anywhere in this
114 ///< codebase (no dispatch logic added).
115static constexpr uint8_t CMD_ONEWAY_ADD_CONTROLLER =
116 0x30; ///< 1W "add controller" — a 1W device broadcasts this while its key-copy gesture is
117 ///< active, handing its network's wrapped system key to whichever controller is
118 ///< listening. Its 20-byte declared payload (enc_key[16] + man_id[1] + data[1] +
119 ///< sequence[2]) plus a genuine 6-byte MAC does not fit inside CTRL0's 5-bit length
120 ///< field together (29 + 6 = 35, unrepresentable in 5 bits), so the MAC rides after
121 ///< the declared length instead, still under the CRC — see IoFrame::has_mac and
122 ///< frame_carries_mac_trailer() (proto_frame.h). Reference:
123 ///< tests/corpus/captures/enrollment/reference_1w_enrollment_add_controller_kat.yaml.
124static constexpr uint8_t CMD_ONEWAY_REMOVE = 0x39; ///< 1W "remove controller" (un-pair a 1W remote from a device);
125 ///< same payload shape as 0x2E.
126
127// Key exchange commands (used during pairing)
128static constexpr uint8_t CMD_KEY_INIT = 0x31; ///< Initiate key transfer to device
129static constexpr uint8_t CMD_KEY_TRANSFER = 0x32; ///< Send encrypted system key to device
130static constexpr uint8_t CMD_KEY_CONFIRM = 0x33; ///< Device confirms key was received
131
132// Address and device-initiated key exchange
133static constexpr uint8_t CMD_ADDRESS_REQ =
134 0x36; ///< "Report your address" request. Not pairing-specific: it's captured both closing a
135 ///< Velux KLR200 pairing (tests/corpus/captures/pairing/velux_kux100_pairing_full.yaml) and, on
136 ///< a completely different hub, sent to an already-paired device with no pairing in
137 ///< progress at all (a Velux KIG300 probing a Somfy dimmer in
138 ///< tests/corpus/captures/probe/velux_kig300_probe_capability_burst.yaml). Answered by the
139 ///< key-extraction responder's create_address_resp_device_role()
140 ///< (handle_address_req_() in key_extraction_responder.cpp).
141static constexpr uint8_t CMD_ADDRESS_RESP =
142 0x37; ///< Address assignment response: the device returns its own 3-byte backbone address,
143 ///< byte-identical to the one it reported at data[2..4]
144 ///< (DISCOVERY_RESP_BACKBONE_OFFSET) of its CMD_DISCOVER_RESP earlier in the same
145 ///< session — an independent confirmation of that offset. The only capture of this
146 ///< command in the corpus is still
147 ///< tests/corpus/captures/pairing/velux_kux100_pairing_full.yaml, where a Velux KLR200 closes
148 ///< pairing with 0x36 and then challenges the 0x37 it gets back (see
149 ///< CMD_CHALLENGE_REQ) — sent by create_address_resp_device_role()
150 ///< (proto_commands.h/.cpp), the key-extraction responder's answer to CMD_ADDRESS_REQ.
151static constexpr uint8_t CMD_LAUNCH_KEY_TRANSFER =
152 0x38; ///< Device-initiated ("pull") key transfer request: documented elsewhere as a command
153 ///< ID plus a 6-byte challenge, nothing more — never observed in our corpus or in any
154 ///< field log, and not sent or handled anywhere in this codebase — the constant is
155 ///< used only to construct a hypothetical device-side IV in
156 ///< tests/proto_crypto_test.cpp, exercising the crypto primitive, not a dispatch path.
157
158// Authentication commands (challenge-response for secured commands)
159static constexpr uint8_t CMD_CHALLENGE_REQ =
160 0x3C; ///< 6-byte random challenge. Usually a device challenging a controller's command, but
161 ///< the protocol is symmetric and controllers challenge devices too: in
162 ///< tests/corpus/captures/pairing/velux_kux100_pairing_full.yaml a KLR200 issues 0x3C against
163 ///< the device's own CMD_ADDRESS_RESP. The key-extraction responder now answers exactly
164 ///< that inbound direction (KeyExtractionResponder::handle_address_challenge_() in
165 ///< key_extraction_responder.cpp), so both directions are implemented, not just the outbound
166 ///< one.
167static constexpr uint8_t CMD_CHALLENGE_RESP =
168 0x3D; ///< HMAC proof answering a 0x3C. Whoever is challenged authenticates *its own*
169 ///< preceding frame: the transcript is [cmd, data...] of the challenged party's last
170 ///< frame (create_challenge_resp()), never the challenger's. That holds in both
171 ///< directions — the device-side 0x3D in velux_kux100_pairing_full.yaml (over its own 0x37) was
172 ///< recomputed under that installation's recovered key and confirmed before the
173 ///< capture was re-keyed, so it is measured, not assumed by symmetry.
174
175// File/blob management block (0x48-0x4B)
176static constexpr uint8_t CMD_UNKNOWN4A_REQ =
177 0x4A; ///< Content undecoded. The leading published interpretation of this opcode is "Delete
178 ///< File" (a large-data-transfer / "ioblob" request), with "Rename File" as a
179 ///< conflicting second reading — both writes, and no project this codebase draws on
180 ///< has ever transmitted it. Never captured on the wire either. This constant exists
181 ///< solely so a received 0x4A frame renders by name in the log instead of as
182 ///< UNKNOWN_CMD; it must never be sent, and no builder for it exists anywhere in this
183 ///< codebase — see CMD_ONEWAY_ADD_CONTROLLER for the same "named but never sent"
184 ///< precedent, and docs/adr/ for the standing decision not to add one.
185static constexpr uint8_t CMD_UNKNOWN4A_RESP =
186 0x4B; ///< Observed on the wire (tests/corpus/captures/probe/velux_kig300_probe_capability_burst.yaml) answering an
187 ///< ON_OFF_SWITCH-type device's traffic, but its request opcode is unconfirmed — it is not established to be
188 ///< CMD_UNKNOWN4A_REQ's reply rather than CMD_GET_GENERAL_INFO3's. Content undecoded.
189 ///< Not sent or handled anywhere in this codebase.
190
191// Device info commands
192static constexpr uint8_t CMD_GET_NAME = 0x50; ///< Request device name
193static constexpr uint8_t CMD_GET_NAME_RESP = 0x51; ///< Device name response
194static constexpr uint8_t CMD_SET_NAME = 0x52; ///< Set device name (authenticated)
195static constexpr uint8_t CMD_SET_NAME_RESP = 0x53; ///< Device-name write response
196static constexpr uint8_t CMD_GET_INFO1 =
197 0x54; ///< Request device general info 1. Sent by the `get_info1` diagnostic probe
198 ///< (docs/radio_diagnostics.md, ADR 0024); field-observed on air from a real hub.
199static constexpr uint8_t CMD_GET_INFO1_RESP = 0x55; ///< Device general info 1 response. Still never captured on our
200 ///< wire or in any field log, and nothing decodes it — accepted by
201 ///< the soft-PHY only so a probe reply is not dropped as an
202 ///< unknown command.
203static constexpr uint8_t CMD_GET_INFO2 = 0x56; ///< Request device type/model info. The only thing that sends
204 ///< it is the `get_info2` diagnostic probe; the corpus has no
205 ///< fixture for the request itself -- every 0x57 capture on
206 ///< hand is a reply to someone else's request. Closing this
207 ///< needs a fresh device-add on owned hardware, ingested with
208 ///< `--rekey` (tests/corpus/README.md).
209static constexpr uint8_t CMD_GET_INFO2_RESP = 0x57; ///< Device type/model response
210static constexpr uint8_t CMD_GET_GENERAL_INFO3 =
211 0x58; ///< Observed on the wire (tests/corpus/captures/probe/velux_kig300_probe_capability_burst.yaml)
212 ///< with no payload. Content undecoded. Not sent or handled anywhere in this codebase.
213static constexpr uint8_t CMD_GET_GENERAL_INFO3_RESP =
214 0x59; ///< Never captured on our own wire. This constant exists so a received 0x59 frame
215 ///< renders by name instead of as UNKNOWN_CMD. Not sent or handled anywhere in this
216 ///< codebase.
217
218// Configuration and status update commands
219static constexpr uint8_t CMD_SET_CONFIG1 = 0x6F; ///< Configure device to auto-send status updates
220static constexpr uint8_t CMD_SET_CONFIG1_RESP =
221 0x70; ///< Config response, otherwise undocumented. Never observed in our corpus or in any
222 ///< field log, and not sent or handled anywhere in this codebase beyond the generic
223 ///< "is this a known command byte" check in radio_soft_phy.cpp.
224static constexpr uint8_t CMD_STATUS_UPDATE = 0x71; ///< Device-initiated status update (needs auth)
225static constexpr uint8_t CMD_STATUS_UPDATE_RESP = 0x72; ///< Acknowledge status update
226
227static constexpr uint8_t CMD_SEND_RAW_MESSAGE =
228 0xF0; ///< Named "Send Raw Message" / "Find Hardware" — two candidate names, neither settled.
229 ///< Never observed in our corpus or in any field log, and not sent or handled
230 ///< anywhere in this codebase.
231static constexpr uint8_t CMD_READ_GROUPS =
232 0xF1; ///< Named "Actuator: Read Groups" / "ActuatorAnyConfigIsLocal" (uncertain) / "Service
233 ///< ACK" — three candidate names, one itself flagged uncertain. Never observed in our
234 ///< corpus or in any field log, and not sent or handled anywhere in this codebase.
235static constexpr uint8_t CMD_REBOOT =
236 0xF2; ///< Named "Reboot" / "Service Status" — two candidate names, one of them
237 ///< destructive-sounding, on no field evidence at all. Never observed in our corpus or
238 ///< in any field log, and not sent or handled anywhere in this codebase; treat the
239 ///< "reboot" reading with particular caution — it is a guess.
240static constexpr uint8_t CMD_SERVICE_STATUS_ACK = 0xF3; ///< No description available at all for this opcode, not even
241 ///< a hedge. Never observed in our corpus or in any field log,
242 ///< and not sent or handled anywhere in this codebase.
243
244static constexpr uint8_t CMD_ERROR_RESP = 0xFE; ///< Error response to any command
245
246// Command-result / limitation codes carried in CMD_ERROR_RESP DATA[0].
247static constexpr uint8_t RESULT_UNKNOWN_STATUS_REPLY = 0x00; ///< Device returned an unknown status reply.
248static constexpr uint8_t RESULT_COMMAND_COMPLETED_OK = 0x01; ///< No errors detected.
249static constexpr uint8_t RESULT_NO_CONTACT = 0x02; ///< No communication to node.
250static constexpr uint8_t RESULT_MANUALLY_OPERATED = 0x03; ///< Manually operated by a user.
251static constexpr uint8_t RESULT_BLOCKED = 0x04; ///< Node blocked by an object.
252static constexpr uint8_t RESULT_WRONG_SYSTEMKEY = 0x05; ///< Node contains the wrong system key.
253static constexpr uint8_t RESULT_PRIORITY_LEVEL_LOCKED = 0x06; ///< Node is locked on this priority level.
254static constexpr uint8_t RESULT_REACHED_WRONG_POSITION = 0x07; ///< Node stopped in another position than expected.
255static constexpr uint8_t RESULT_ERROR_DURING_EXECUTION = 0x08; ///< Generic execution failure.
256static constexpr uint8_t RESULT_NO_EXECUTION = 0x09; ///< Node did not move.
257static constexpr uint8_t RESULT_CALIBRATING = 0x0A; ///< Node is calibrating.
258static constexpr uint8_t RESULT_POWER_CONSUMPTION_TOO_HIGH = 0x0B; ///< Node power consumption is too high.
259static constexpr uint8_t RESULT_POWER_CONSUMPTION_TOO_LOW = 0x0C; ///< Node power consumption is too low.
260static constexpr uint8_t RESULT_LOCK_POSITION_OPEN = 0x0D; ///< Lock command failed because the door is open.
261static constexpr uint8_t RESULT_MOTION_TIME_TOO_LONG = 0x0E; ///< Target was not reached in time.
262static constexpr uint8_t RESULT_THERMAL_PROTECTION = 0x0F; ///< Node entered thermal protection mode.
263static constexpr uint8_t RESULT_PRODUCT_NOT_OPERATIONAL = 0x10; ///< Node is not currently operational.
264static constexpr uint8_t RESULT_FILTER_MAINTENANCE_NEEDED = 0x11; ///< Filter needs maintenance.
265static constexpr uint8_t RESULT_BATTERY_LEVEL = 0x12; ///< Battery level is low.
266static constexpr uint8_t RESULT_TARGET_MODIFIED = 0x13; ///< Node modified the requested target value.
267static constexpr uint8_t RESULT_MODE_NOT_IMPLEMENTED = 0x14; ///< Mode is not supported by the node.
268static constexpr uint8_t RESULT_COMMAND_INCOMPATIBLE_TO_MOVEMENT = 0x15; ///< Command cannot move the node that way.
269static constexpr uint8_t RESULT_USER_ACTION = 0x16; ///< User action overrode the command.
270static constexpr uint8_t RESULT_DEAD_BOLT_ERROR = 0x17; ///< Dead bolt error.
271static constexpr uint8_t RESULT_AUTOMATIC_CYCLE_ENGAGED = 0x18; ///< Node entered automatic cycle mode.
272static constexpr uint8_t RESULT_WRONG_LOAD_CONNECTED = 0x19; ///< Wrong load connected to node.
273static constexpr uint8_t RESULT_COLOUR_NOT_REACHABLE = 0x1A; ///< Requested colour not reachable.
274static constexpr uint8_t RESULT_TARGET_NOT_REACHABLE = 0x1B; ///< Requested target not reachable.
275static constexpr uint8_t RESULT_BAD_INDEX_RECEIVED = 0x1C; ///< Invalid index received.
276static constexpr uint8_t RESULT_COMMAND_OVERRULED = 0x1D; ///< Command was overruled by a newer command.
277static constexpr uint8_t RESULT_NODE_WAITING_FOR_POWER = 0x1E; ///< Node is waiting for power.
278static constexpr uint8_t RESULT_NODE_LOCKED = 0x20; ///< Node is locked.
279static constexpr uint8_t RESULT_WRONG_POSITION = 0x21; ///< Node reports wrong position.
280static constexpr uint8_t RESULT_LIMITS_NOT_SET = 0x22; ///< Device limits are not set.
281static constexpr uint8_t RESULT_IP_NOT_SET = 0x23; ///< Intermediate position is not set.
282static constexpr uint8_t RESULT_OUT_OF_RANGE = 0x24; ///< Requested value is out of range.
283static constexpr uint8_t RESULT_PRIORITY_LOCKED_NON_EXEC =
284 0x38; ///< Priority locked, command not executed (ACEI priority too low).
285static constexpr uint8_t RESULT_INVALID_FUNCTION_INDEX =
286 0x58; ///< CMD_PRIVATE-family function ID / sub-index / selector-block outside the range the
287 ///< device implements. Seen cross-vendor (Somfy Sunea awning + dimmer, Velux window)
288 ///< when a diagnostic probe walks past the last supported index; not in any reference
289 ///< error table. Distinct from RESULT_BAD_INDEX_RECEIVED (0x1C).
290static constexpr uint8_t RESULT_INFORMATION_CODE = 0xDF; ///< Information-only code with unknown semantics.
291static constexpr uint8_t RESULT_PARAMETER_LIMITED = 0xE0; ///< Parameter limited by an unknown device.
292static constexpr uint8_t RESULT_LIMITATION_BY_LOCAL_USER = 0xE1; ///< Parameter limited by local button.
293static constexpr uint8_t RESULT_LIMITATION_BY_USER = 0xE2; ///< Parameter limited by a remote control.
294static constexpr uint8_t RESULT_LIMITATION_BY_RAIN = 0xE3; ///< Parameter limited by a rain sensor.
295static constexpr uint8_t RESULT_LIMITATION_BY_TIMER = 0xE4; ///< Parameter limited by a timer.
296static constexpr uint8_t RESULT_LIMITATION_BY_SCD = 0xE5; ///< Parameter limited by a security actuator.
297static constexpr uint8_t RESULT_LIMITATION_BY_UPS = 0xE6; ///< Parameter limited by a power supply.
298static constexpr uint8_t RESULT_LIMITATION_BY_UNKNOWN_DEVICE = 0xE7; ///< Parameter limited by an unknown device.
299static constexpr uint8_t RESULT_LIMITATION_BY_SAAC = 0xEA; ///< Parameter limited by a standalone automatic controller.
300static constexpr uint8_t RESULT_LIMITATION_BY_WIND = 0xEB; ///< Parameter limited by a wind sensor.
301static constexpr uint8_t RESULT_LIMITATION_BY_MYSELF = 0xEC; ///< Parameter limited by the node itself.
302static constexpr uint8_t RESULT_LIMITATION_BY_AUTOMATIC_CYCLE = 0xED; ///< Parameter limited by an automatic cycle.
303static constexpr uint8_t RESULT_LIMITATION_BY_EMERGENCY = 0xEE; ///< Parameter limited by an emergency.
304
305// ============================================================================
306// Position and Status Wire Constants
307// ============================================================================
308
309/// Position values in the IO protocol.
310/// Normal positions are 0-100 (0=fully open, 100=fully closed).
311/// Special values above 100 are control commands encoded as the "main" parameter
312/// byte in CMD_EXECUTE payloads. These are internal wire constants — callers should
313/// prefer CoverCommand for type-safe command dispatch.
314static constexpr uint8_t POS_STOP = 0xD2; ///< Wire value: stop movement.
315static constexpr uint8_t POS_UNKNOWN = 0xD4; ///< Wire value: position unknown / keep current.
316static constexpr uint8_t POS_FAVORITE = 0xD8; ///< Wire value: move to favorite/"My" position.
317
318/// @brief Wire value for the secured target position command.
319///
320/// Moves the actuator to its pre-programmed secured/safety position from the
321/// Execution Parameter Buffer. Typically sent by environmental sensors (wind, rain)
322/// to retract an awning or close a shutter to a wind-safe state.
323static constexpr uint8_t POS_SECURED_TARGET = 0xD1;
324
325/// @brief Wire value for the default position command.
326///
327/// Moves the actuator to its factory or user-configured default position.
328static constexpr uint8_t POS_DEFAULT = 0xD3;
329
330/// @brief Ambiguous wire value used only for passive 1W-traffic intent decoding
331/// (decode_1w_main_intent() / oneway_intent_to_target() in proto_codecs.cpp).
332///
333/// 0x64 (100) is simultaneously the ordinary doubled-position wire value for 50% and a value
334/// some physical 1W remotes send for their "force open" button — the protocol has no dedicated
335/// override code, so the two are indistinguishable on the wire. For passively decoding someone
336/// else's remote traffic, "FORCE_OPEN" is the more useful diagnostic label (physical remotes
337/// rarely send a numeric 50%). This is NOT used by any outbound builder in this codebase:
338/// real-hardware testing confirmed that sending main=0x64 as an outbound 2W
339/// CMD_EXECUTE command makes a real device move to 50% open, not bypass anything — see
340/// create_force_open() in proto_commands.cpp for the actual (ACEI-priority-based) force-open
341/// implementation.
342static constexpr uint8_t POS_FORCE_OPEN = 0x64;
343
344/// @brief Modifier byte for the ventilation command.
345///
346/// Both favorite and ventilation use POS_FAVORITE (0xD8) as the main parameter byte,
347/// but ventilation sets the secondary byte (main[1]) to 0x03 while favorite leaves it 0x00.
348static constexpr uint8_t POS_VENT_MODIFIER = 0x03;
349
350/// @brief Scale factor between a 0-100 percent position and its CMD_EXECUTE main-byte wire value.
351///
352/// Percent 0-100 maps to wire 0-200 (wire = percent * POSITION_WIRE_SCALE), leaving 201-255 free
353/// for the POS_* special codes above. Builders multiply by this to encode a position; decoders
354/// divide by it to recover one — both directions belong on this one constant so they cannot
355/// silently drift apart into two different bare "2"s.
356static constexpr uint8_t POSITION_WIRE_SCALE = 2;
357/// @brief Highest doubled-position wire value: 100% * POSITION_WIRE_SCALE. A main byte at or
358/// below this is an ordinary position; above it, one of the POS_* special codes.
359static constexpr uint8_t POSITION_WIRE_MAX = 200;
360
361/// Status byte flags in CMD_PRIVATE_RESP and CMD_STATUS_UPDATE.
362static constexpr uint8_t STATUS_STOPPED = 0x01; ///< Byte 0 bit 0: device is not moving
363static constexpr uint8_t STATUS_EXPECTED = 0x80; ///< Byte 1 bit 7: device will send auto status update
364static constexpr uint8_t STATUS_TILT_SELECTOR = 0x20; ///< Extended status payload marker for tilt-capable devices
365
366/// @brief CMD_PRIVATE (0x03) function ID for a position-status request — data[0] of the payload.
367///
368/// The only function ID this codebase has ever captured on its own wire: it is what
369/// create_get_status() freezes create_private_function() at, and the default `function_id` of
370/// create_get_status_extended(). Lives here rather than in proto_commands.cpp because that
371/// default argument is spelled in proto_commands.h.
372static constexpr uint8_t PRIVATE_GET_POSITION_STATUS = 0x03;
373
374// ============================================================================
375// Cryptographic Constants
376// ============================================================================
377
378/// The transfer key is a hardcoded key used ONLY during pairing to obfuscate
379/// the system key during over-the-air transfer. It is NOT the system key.
380/// This is the same across all IO-Homecontrol devices worldwide.
381static constexpr uint8_t TRANSFER_KEY[AES_KEY_SIZE] = {0x34, 0xC3, 0x46, 0x6E, 0xD8, 0x8F, 0x4E, 0x8E,
382 0x16, 0xAA, 0x47, 0x39, 0x49, 0x88, 0x43, 0x73};
383static constexpr uint16_t CRC_POLYNOMIAL_REVERSED = 0x8408; ///< Reversed CRC-CCITT polynomial used by IO-homecontrol
384static constexpr uint16_t CRC_LSB_MASK = 0x0001; ///< Least-significant-bit mask for reflected CRC update
385
386/// Broadcast address for device discovery (0x00003B).
387/// Used as destination in CMD_DISCOVER_REQ frames to trigger all pairable devices to respond.
388static constexpr uint8_t BROADCAST_DISCOVER[NODE_ID_SIZE] = {0x00, 0x00, 0x3B};
389
390/// Alternate discovery / 1W broadcast address (0x00003F).
391/// Used as destination for CMD_DISCOVER_ALT_REQ (0x2E) alternate discovery, and the address
392/// on which devices in 1W-triggered pairing mode listen. Distinct from the 2W discovery
393/// broadcast BROADCAST_DISCOVER (0x00003B).
394static constexpr uint8_t BROADCAST_DISCOVER_ALT[NODE_ID_SIZE] = {0x00, 0x00, 0x3F};
395
396// ============================================================================
397// Command Name Lookup
398// ============================================================================
399
400/// @brief Get a human-readable name for any IO-Homecontrol command ID.
401///
402/// Returns a short uppercase identifier suitable for log lines (e.g., "EXECUTE",
403/// "DISCOVER_REQ", "CHALLENGE_RESP"). Unknown commands return "UNKNOWN_CMD".
404/// @param cmd Command byte from the frame header.
405/// @return Null-terminated string.
406const char *command_name(uint8_t cmd);
407
408// ============================================================================
409// Manufacturer ID Lookup
410// ============================================================================
411
412/// @brief Maximum manufacturer ID with a known name in the lookup table.
413static constexpr uint8_t MANUFACTURER_ID_MAX = 12;
414
415/// @brief IO-Homecontrol manufacturer ID constants.
416///
417/// These 1-based identifiers are assigned by the IO-Homecontrol alliance and
418/// appear in the discovery response payload at DISCOVERY_RESP_MANUFACTURER_OFFSET.
419/// @{
420static constexpr uint8_t MANUFACTURER_VELUX = 1; ///< VELUX (roof windows, skylights).
421static constexpr uint8_t MANUFACTURER_SOMFY = 2; ///< Somfy (shutters, awnings, blinds).
422static constexpr uint8_t MANUFACTURER_HONEYWELL = 3; ///< Honeywell.
423static constexpr uint8_t MANUFACTURER_HORMANN = 4; ///< Hörmann (garage doors, gates).
424static constexpr uint8_t MANUFACTURER_ASSA_ABLOY = 5; ///< ASSA ABLOY (locks, access).
425static constexpr uint8_t MANUFACTURER_NIKO = 6; ///< Niko (switches, home automation).
426static constexpr uint8_t MANUFACTURER_WINDOW_MASTER = 7; ///< WINDOW MASTER (ventilation).
427static constexpr uint8_t MANUFACTURER_RENSON = 8; ///< Renson (ventilation, sun protection).
428static constexpr uint8_t MANUFACTURER_CIAT = 9; ///< CIAT (HVAC).
429static constexpr uint8_t MANUFACTURER_SECUYOU = 10; ///< Secuyou (security).
430static constexpr uint8_t MANUFACTURER_OVERKIZ = 11; ///< OVERKIZ (Somfy connectivity platform).
431static constexpr uint8_t MANUFACTURER_ATLANTIC_GROUP = 12; ///< Atlantic Group (heating, hot water).
432/// @}
433
434/// @brief Get a human-readable manufacturer name from the protocol manufacturer byte.
435///
436/// The manufacturer ID is a 1-based index assigned by the IO-Homecontrol alliance.
437/// IDs outside the known range return "unknown". When an unknown ID appears at runtime,
438/// the pairing flow logs a warning suggesting the user file a GitHub issue.
439/// @warning **Display-only — do not use this for YAML.** Four of the twelve names do not
440/// round-trip through `.strip().lower()` to their `manufacturer:` YAML token
441/// (`MANUFACTURER_OPTIONS`, `__init__.py`): `"Hörmann"` has an umlaut the YAML token
442/// (`hormann`) drops, and `"ASSA ABLOY"`/`"WINDOW MASTER"`/`"Atlantic Group"` use a space
443/// where the YAML token uses `_`. There is currently no YAML-token accessor for
444/// manufacturers — see `yaml_device_type_name()` (proto_device_model.h) for the pattern this
445/// would follow if one is ever added.
446/// @param id Manufacturer ID byte (1–12 for known manufacturers).
447/// @return Null-terminated lowercase string such as "unknown", or mixed-case name like "Somfy".
448const char *manufacturer_name(uint8_t id);
449
450// ============================================================================
451// Command Originator Codes
452// ============================================================================
453
454/// @brief Command originator codes indicating what or who triggered a command.
455///
456/// The originator byte is the first byte of the CMD_EXECUTE payload. It tells
457/// the actuator (and any eavesdropping controller) who initiated the movement.
458/// This is useful for understanding device-initiated status updates.
459///
460/// ORIGINATOR_WIND_SENSOR and ORIGINATOR_RAIN_SENSOR are both field-confirmed on real hardware —
461/// a single combined wind/rain protection station (issue #27, community capture) broadcasting
462/// both values minutes apart:
463/// tests/corpus/captures/oneway/wind_sensor_oneway_favorite_wind_originator_sx1276.yaml and
464/// tests/corpus/captures/oneway/wind_sensor_oneway_favorite_rain_originator_sx1276.yaml. The
465/// latter is this project's first real-hardware confirmation of ORIGINATOR_RAIN_SENSOR at all —
466/// every prior wind/rain-station capture used ORIGINATOR_WIND_SENSOR only.
467/// @{
468static constexpr uint8_t ORIGINATOR_LOCAL_USER = 0x00; ///< User pressed a button on the actuator.
469static constexpr uint8_t ORIGINATOR_USER_REMOTE = 0x01; ///< User sent command from a remote control.
470static constexpr uint8_t ORIGINATOR_RAIN_SENSOR = 0x02; ///< Rain sensor triggered the movement.
471static constexpr uint8_t ORIGINATOR_TIMER = 0x03; ///< Timer or schedule triggered the movement.
472static constexpr uint8_t ORIGINATOR_SECURITY = 0x04; ///< Security controlling device (SCD) action.
473static constexpr uint8_t ORIGINATOR_UPS = 0x05; ///< Uninterruptible power supply action.
474static constexpr uint8_t ORIGINATOR_SMART_CONTROLLER = 0x06; ///< Smart function controller.
475static constexpr uint8_t ORIGINATOR_LIFESTYLE = 0x07; ///< Lifestyle scenario controller.
476static constexpr uint8_t ORIGINATOR_SAAC = 0x08; ///< Stand-alone automatic controller (SAAC).
477static constexpr uint8_t ORIGINATOR_WIND_SENSOR = 0x09; ///< Wind sensor triggered the movement.
478static constexpr uint8_t ORIGINATOR_LOAD_SHEDDING = 0x0B; ///< Load-shedding manager.
479static constexpr uint8_t ORIGINATOR_LOCAL_LIGHT = 0x0C; ///< Local light sensor.
480static constexpr uint8_t ORIGINATOR_ENVIRONMENT = 0x0D; ///< Unspecified environment sensor.
481static constexpr uint8_t ORIGINATOR_MYSELF = 0x10; ///< Actuator decided to move by itself.
482static constexpr uint8_t ORIGINATOR_AUTOMATIC_CYCLE = 0xFE; ///< Automatic cycle / external access.
483static constexpr uint8_t ORIGINATOR_EMERGENCY = 0xFF; ///< Emergency command (never disabled).
484/// @}
485
486/// @brief Get a human-readable name for a command originator byte.
487///
488/// @param originator Originator code from the first data byte of CMD_EXECUTE.
489/// @return Null-terminated string such as "rain_sensor" or "user_remote".
490const char *originator_name(uint8_t originator);
491
492// ============================================================================
493// ACEI (Application Command Execution Interface)
494// ============================================================================
495
496/// @brief ACEI byte bit-field definitions.
497///
498/// The ACEI byte is the second byte of the CMD_EXECUTE payload. It encodes the
499/// priority level and service class of the command, controlling which commands
500/// can override others. Devices reject commands with lower priority than their
501/// current locked level (resulting in RESULT_PRIORITY_LEVEL_LOCKED).
502/// @{
503static constexpr uint8_t ACEI_VALID_BIT = 0x01; ///< Bit 0: command validity flag.
504static constexpr uint8_t ACEI_EXTENDED_MASK = 0x06; ///< Bits [2:1]: extended field.
505static constexpr uint8_t ACEI_EXTENDED_SHIFT = 1; ///< Shift for extended field extraction.
506static constexpr uint8_t ACEI_SERVICE_MASK = 0x18; ///< Bits [4:3]: service type.
507static constexpr uint8_t ACEI_SERVICE_SHIFT = 3; ///< Shift for service field extraction.
508static constexpr uint8_t ACEI_LEVEL_MASK = 0xE0; ///< Bits [7:5]: priority level (0–7).
509static constexpr uint8_t ACEI_LEVEL_SHIFT = 5; ///< Shift for priority level extraction.
510/// @}
511
512/// @brief ACEI priority level values (0–7).
513///
514/// These values are extracted from the ACEI byte via (acei & ACEI_LEVEL_MASK) >> ACEI_LEVEL_SHIFT.
515/// @{
516static constexpr uint8_t ACEI_LEVEL_PROTECTION_HUMAN = 0; ///< Personal safety (highest, overrides all).
517static constexpr uint8_t ACEI_LEVEL_PROTECTION_SENSOR = 1; ///< Goods/environment protection via sensors.
518static constexpr uint8_t ACEI_LEVEL_USER_HIGH = 2; ///< High-priority user controller.
519static constexpr uint8_t ACEI_LEVEL_USER_DEFAULT = 3; ///< Default remote controller priority.
520static constexpr uint8_t ACEI_LEVEL_COMFORT_1 = 4; ///< Comfort automation level 1.
521static constexpr uint8_t ACEI_LEVEL_COMFORT_2 = 5; ///< Comfort automation level 2.
522static constexpr uint8_t ACEI_LEVEL_AUTO_SAAC = 6; ///< Stand-alone automatic controller.
523static constexpr uint8_t ACEI_LEVEL_AUTO_DEFAULT = 7; ///< Default automatic level (lowest).
524/// @}
525
526/// @brief Get a human-readable name for an ACEI priority level (0–7).
527///
528/// Priority levels form a hierarchy: level 0 (human protection) is highest
529/// and overrides all others. Level 3 is the default for remote controllers.
530/// @param level Priority level value (0–7).
531/// @return Null-terminated string such as "user_default" or "protection_sensor".
532const char *acei_level_name(uint8_t level);
533
534/// @brief ACEI byte for a 1W CMD_EXECUTE frame — the Somfy-shaped default.
535///
536/// Level 2 (user_high), extended-info bit set. The 2W EXECUTE_ACEI (proto_commands.cpp) pins
537/// level 3 to a real captured 2W hub; this level-2 value is what the published 1W reference
538/// vector (tests/corpus/captures/oneway/reference_1w_oneway_execute_iv_vector.yaml) and every
539/// Somfy 1W remote frame in the corpus (7+ frames across 3 nodes: 9D6085, 485B37, 7B8240) carry.
540/// Lives here (not proto_commands.cpp) so oneway_controller.h's resolve_oneway_wire_profile() can
541/// name it without the protocol layer depending on the controller layer.
542/// Composition: (ACEI_LEVEL_USER_HIGH << 5) | (1 << 1) | 1 = 0x43.
543static constexpr uint8_t ONEWAY_EXECUTE_ACEI =
545
546/// @brief ACEI byte for a 1W CMD_EXECUTE frame from a VELUX KLI-class remote.
547///
548/// Level 3 (user_default), extended-info bits clear. This is also iown-homecontrol's generic
549/// `ACEI_DEFAULT`. **Confidence: n=1** — one corpus frame
550/// (tests/corpus/captures/oneway/velux_kli313_oneway_stop.yaml) plus samr037/iohc-flipper's
551/// README, which names cmd 0x00 payload[1] the "vendor byte" (`0x43` Somfy / `0x61` Velux).
552/// The `unidentified_1w_remote_*` frames also carry 0x61 but their manufacturer is unknown, so
553/// they can't corroborate the VELUX attribution. `execute_acei:` on the identity is the escape
554/// hatch. Composition: (ACEI_LEVEL_USER_DEFAULT << 5) | ACEI_VALID_BIT = 0x61.
556
557// ============================================================================
558// Discovery Response Extended Fields
559// ============================================================================
560
561/// @brief Byte offsets within CMD_DISCOVER_RESP (0x29) payload data.
562///
563/// The full discovery response carries up to 9 bytes of device metadata:
564/// bytes 0–1 hold the packed device type/subtype (already parsed by
565/// decode_packed_device_type()), and bytes 2–8 hold additional fields.
566/// @{
567static constexpr uint8_t DISCOVERY_RESP_BACKBONE_OFFSET = 2; ///< Backbone address starts at data[2] (3 bytes);
568 ///< cross-confirmed by CMD_ADDRESS_RESP (0x37),
569 ///< which returns the same 3 bytes for the same
570 ///< device (see that constant's comment).
571static constexpr uint8_t DISCOVERY_RESP_MANUFACTURER_OFFSET = 5; ///< Manufacturer ID at data[5].
572static constexpr uint8_t DISCOVERY_RESP_FLAGS_OFFSET = 6; ///< Flags byte at data[6].
573static constexpr uint8_t DISCOVERY_RESP_TIMESTAMP_OFFSET = 7; ///< Timestamp starts at data[7] (2 bytes).
574static constexpr uint8_t DISCOVERY_RESP_FULL_SIZE = 9; ///< Full discovery response payload size.
575/// @}
576
577// === Multi Information Byte (Discovery Response data[6]) ===
578// The flags byte in the discovery response encodes device capabilities and timing
579// characteristics that help a controller tune its interaction with the actuator.
580
581/// @brief Bit masks and shifts for the Multi Information Byte fields.
582/// @{
583static constexpr uint8_t DISCOVERY_FLAGS_ATT_MASK = 0xC0; ///< Bits [7:6]: actuator turnaround time class.
584static constexpr uint8_t DISCOVERY_FLAGS_ATT_SHIFT = 6; ///< Shift for ATT field extraction.
585static constexpr uint8_t DISCOVERY_FLAGS_SYNC_CTRL_GRP = 0x20; ///< Bit 5: supports sync control group.
586// TODO(hardware-verify): a set bit here would literally read as "no RF support in node," which is
587// an odd claim for a radio responder to make of itself (see KEY_EXTRACTION_DISCOVER_RESP_FLAGS in
588// proto_commands.cpp, which sets this bit because a real captured device did too). Left as an open
589// question rather than resolved here — not clear whether the polarity note is backwards, the field
590// means something narrower than "RF support" suggests, or the captured device is simply also
591// getting this bit "wrong" in some sense that doesn't matter to real hubs in practice.
592static constexpr uint8_t DISCOVERY_FLAGS_RF_SUPPORT = 0x08; ///< Bit 3: RF support in node (0=yes, 1=no).
593static constexpr uint8_t DISCOVERY_FLAGS_POWER_SAVE_MASK = 0x03; ///< Bits [1:0]: power save mode.
594/// @}
595
596/// @brief Actuator Turnaround Time (ATT) class values.
597///
598/// Indicates the maximum time window in which the actuator normally responds after
599/// receiving a command. Extracted from the Multi Information Byte via
600/// `(flags & DISCOVERY_FLAGS_ATT_MASK) >> DISCOVERY_FLAGS_ATT_SHIFT`.
601/// @{
602static constexpr uint8_t ATT_CLASS_5S = 0; ///< Response within 5 seconds.
603static constexpr uint8_t ATT_CLASS_10S = 1; ///< Response within 10 seconds.
604static constexpr uint8_t ATT_CLASS_20S = 2; ///< Response within 20 seconds.
605static constexpr uint8_t ATT_CLASS_40S = 3; ///< Response within 40 seconds.
606/// @}
607
608/// @brief Power save mode values from the Multi Information Byte.
609///
610/// Extracted from `flags & DISCOVERY_FLAGS_POWER_SAVE_MASK`.
611/// Devices in low-power mode require long preamble (1024 bytes) to wake their receiver.
612/// @{
613static constexpr uint8_t POWER_SAVE_ALWAYS_ALIVE = 0; ///< Device is always listening — short preamble works.
614static constexpr uint8_t POWER_SAVE_LOW_POWER = 1; ///< Device sleeps — needs long preamble to wake.
615/// @}
616
617/// @brief Extract the ATT class field from a discovery response's Multi Information Byte.
618/// @param flags Multi Information Byte (data[DISCOVERY_RESP_FLAGS_OFFSET]).
619/// @return ATT class value (0–3); pass to att_class_name() for a human-readable string.
620inline uint8_t discovery_att_class(uint8_t flags) {
622}
623
624/// @brief Extract the power save mode field from a discovery response's Multi Information Byte.
625/// @param flags Multi Information Byte (data[DISCOVERY_RESP_FLAGS_OFFSET]).
626/// @return Power save mode value (0–1); pass to power_save_mode_name() for a human-readable string.
627inline uint8_t discovery_power_save_mode(uint8_t flags) { return flags & DISCOVERY_FLAGS_POWER_SAVE_MASK; }
628
629/// @brief Get a human-readable turnaround time string for an ATT class value.
630/// @param att_class ATT class (0–3) extracted from the Multi Information Byte.
631/// @return Null-terminated string such as "5s", "10s", "20s", or "40s".
632const char *att_class_name(uint8_t att_class);
633
634/// @brief Get a human-readable power save mode name.
635/// @param mode Power save value (0–1) extracted from the Multi Information Byte.
636/// @return Null-terminated string such as "always_alive" or "low_power".
637const char *power_save_mode_name(uint8_t mode);
638
639// ============================================================================
640// Command Result Codes
641// ============================================================================
642
643/// @brief Return a stable symbolic name for a CMD_ERROR_RESP result code.
644/// @param result Result byte from CMD_ERROR_RESP data[0].
645/// @return Uppercase symbolic name, or "UNKNOWN_RESULT_CODE" when unmapped.
646const char *command_result_name(uint8_t result);
647/// @brief Return a human-readable explanation for a CMD_ERROR_RESP result code.
648/// @param result Result byte from CMD_ERROR_RESP data[0].
649/// @return Short description suitable for warn-level logs.
650const char *command_result_description(uint8_t result);
651/// @brief Check whether a result code represents an environmental or control limitation.
652/// @param result Result byte from CMD_ERROR_RESP data[0].
653/// @return true when the response reports a limitation rather than a generic execution error.
654bool is_limitation_result(uint8_t result);
655
656} // namespace home_io_control
657} // namespace esphome
static constexpr uint8_t ACEI_LEVEL_USER_HIGH
High-priority user controller.
const char * manufacturer_name(uint8_t id)
Get a human-readable manufacturer name from the protocol manufacturer byte.
static constexpr uint8_t RESULT_OUT_OF_RANGE
Requested value is out of range.
static constexpr uint8_t MANUFACTURER_HONEYWELL
Honeywell.
static constexpr uint8_t RESULT_CALIBRATING
Node is calibrating.
static constexpr uint8_t ACEI_LEVEL_COMFORT_1
Comfort automation level 1.
static constexpr uint8_t CMD_SET_SENSOR
Inject sensor value into a device.
uint8_t discovery_power_save_mode(uint8_t flags)
Extract the power save mode field from a discovery response's Multi Information Byte.
static constexpr uint8_t CMD_DISCOVER_REQ
Broadcast discovery request.
static constexpr uint8_t NODE_ID_SIZE
Device/node addresses are 3 bytes (e.g., "123ABC").
Definition proto_sizes.h:20
static constexpr uint8_t CMD_SET_CONFIG1
Configure device to auto-send status updates.
static constexpr uint8_t RESULT_COLOUR_NOT_REACHABLE
Requested colour not reachable.
static constexpr uint8_t RESULT_LIMITATION_BY_AUTOMATIC_CYCLE
Parameter limited by an automatic cycle.
static constexpr uint8_t ORIGINATOR_UPS
Uninterruptible power supply action.
static constexpr uint8_t ORIGINATOR_LOCAL_LIGHT
Local light sensor.
static constexpr uint8_t CMD_KEY_TRANSFER
Send encrypted system key to device.
static constexpr uint8_t RESULT_TARGET_MODIFIED
Node modified the requested target value.
static constexpr uint8_t RESULT_REACHED_WRONG_POSITION
Node stopped in another position than expected.
static constexpr uint8_t RESULT_LIMITATION_BY_MYSELF
Parameter limited by the node itself.
static constexpr uint8_t CMD_SERVICE_STATUS_ACK
No description available at all for this opcode, not even a hedge.
static constexpr uint8_t TRANSFER_KEY[AES_KEY_SIZE]
The transfer key is a hardcoded key used ONLY during pairing to obfuscate the system key during over-...
static constexpr uint8_t CMD_ERROR_RESP
Error response to any command.
static constexpr uint8_t CMD_GET_NAME_RESP
Device name response.
static constexpr uint8_t RESULT_AUTOMATIC_CYCLE_ENGAGED
Node entered automatic cycle mode.
static constexpr uint8_t CMD_DISCOVER_CONFIRM_ACK
Device acknowledges confirmation.
static constexpr uint8_t CMD_ACTIVATE_MODE
Activate device mode (scene, ventilation) — requires auth.
static constexpr uint8_t MANUFACTURER_CIAT
CIAT (HVAC).
static constexpr uint8_t RESULT_MODE_NOT_IMPLEMENTED
Mode is not supported by the node.
static constexpr uint8_t ACEI_LEVEL_USER_DEFAULT
Default remote controller priority.
static constexpr uint8_t RESULT_COMMAND_OVERRULED
Command was overruled by a newer command.
const char * att_class_name(uint8_t att_class)
Get a human-readable turnaround time string for an ATT class value.
static constexpr uint8_t RESULT_INVALID_FUNCTION_INDEX
CMD_PRIVATE-family function ID / sub-index / selector-block outside the range the device implements.
static constexpr uint8_t MANUFACTURER_VELUX
IO-Homecontrol manufacturer ID constants.
static constexpr uint8_t DISCOVERY_RESP_MANUFACTURER_OFFSET
Manufacturer ID at data[5].
static constexpr uint8_t POS_UNKNOWN
Wire value: position unknown / keep current.
static constexpr uint8_t CMD_ADDRESS_RESP
Address assignment response: the device returns its own 3-byte backbone address, byte-identical to th...
static constexpr uint8_t RESULT_BAD_INDEX_RECEIVED
Invalid index received.
static constexpr uint8_t RESULT_MANUALLY_OPERATED
Manually operated by a user.
static constexpr uint8_t POS_FORCE_OPEN
Ambiguous wire value used only for passive 1W-traffic intent decoding (decode_1w_main_intent() / onew...
static constexpr uint8_t CMD_STATUS_UPDATE
Device-initiated status update (needs auth).
static constexpr uint8_t ACEI_LEVEL_AUTO_SAAC
Stand-alone automatic controller.
const char * power_save_mode_name(uint8_t mode)
Get a human-readable power save mode name.
static constexpr uint8_t CMD_UNKNOWN4A_REQ
Content undecoded.
static constexpr uint8_t DISCOVERY_FLAGS_RF_SUPPORT
Bit 3: RF support in node (0=yes, 1=no).
static constexpr uint8_t ACEI_LEVEL_PROTECTION_SENSOR
Goods/environment protection via sensors.
static constexpr uint8_t RESULT_LIMITATION_BY_UNKNOWN_DEVICE
Parameter limited by an unknown device.
static constexpr uint8_t CMD_DISCOVER_ALT_REQ
Alternate discovery.
static constexpr uint8_t POSITION_WIRE_MAX
Highest doubled-position wire value: 100% * POSITION_WIRE_SCALE.
static constexpr uint8_t RESULT_LOCK_POSITION_OPEN
Lock command failed because the door is open.
static constexpr uint8_t CMD_PRIVATE2_RESP
Response to CMD_PRIVATE2. See CMD_PRIVATE2's comment.
static constexpr uint8_t RESULT_WRONG_SYSTEMKEY
Node contains the wrong system key.
static constexpr uint8_t POSITION_WIRE_SCALE
Scale factor between a 0-100 percent position and its CMD_EXECUTE main-byte wire value.
static constexpr uint8_t CMD_GET_NAME
Request device name.
static constexpr uint8_t MANUFACTURER_RENSON
Renson (ventilation, sun protection).
static constexpr uint8_t POWER_SAVE_ALWAYS_ALIVE
Power save mode values from the Multi Information Byte.
const char * command_name(uint8_t cmd)
Get a human-readable name for any IO-Homecontrol command ID.
static constexpr uint8_t ORIGINATOR_LOAD_SHEDDING
Load-shedding manager.
static constexpr uint8_t ORIGINATOR_LIFESTYLE
Lifestyle scenario controller.
static constexpr uint8_t MANUFACTURER_NIKO
Niko (switches, home automation).
static constexpr uint8_t CMD_DISCOVER_SPE_RESP
Roll-call reply to CMD_DISCOVER_SPE_REQ, sent only by devices that already hold the requesting contro...
static constexpr uint8_t ACEI_LEVEL_MASK
Bits [7:5]: priority level (0–7).
static constexpr uint8_t RESULT_LIMITATION_BY_EMERGENCY
Parameter limited by an emergency.
static constexpr uint8_t CMD_DISCOVER_ALT_RESP
Reply to an addressed (non-broadcast) CMD_DISCOVER_ALT_REQ, following a 0x3C/0x3D challenge-response.
static constexpr uint8_t RESULT_NODE_WAITING_FOR_POWER
Node is waiting for power.
static constexpr uint8_t ACEI_EXTENDED_SHIFT
Shift for extended field extraction.
static constexpr uint8_t ATT_CLASS_40S
Response within 40 seconds.
static constexpr uint8_t RESULT_BATTERY_LEVEL
Battery level is low.
static constexpr uint8_t CMD_WRITE_PRIVATE
Write private register (climate/heating devices).
static constexpr uint8_t RESULT_MOTION_TIME_TOO_LONG
Target was not reached in time.
static constexpr uint8_t RESULT_THERMAL_PROTECTION
Node entered thermal protection mode.
static constexpr uint8_t DISCOVERY_RESP_BACKBONE_OFFSET
Byte offsets within CMD_DISCOVER_RESP (0x29) payload data.
static constexpr uint8_t RESULT_POWER_CONSUMPTION_TOO_HIGH
Node power consumption is too high.
static constexpr uint8_t ORIGINATOR_MYSELF
Actuator decided to move by itself.
static constexpr uint8_t RESULT_ERROR_DURING_EXECUTION
Generic execution failure.
static constexpr uint8_t CMD_KEY_CONFIRM
Device confirms key was received.
static constexpr uint8_t CMD_WRITE_PRIVATE_ACK
Acknowledgment to CMD_WRITE_PRIVATE.
static constexpr uint8_t ACEI_LEVEL_PROTECTION_HUMAN
ACEI priority level values (0–7).
static constexpr uint8_t RESULT_PARAMETER_LIMITED
Parameter limited by an unknown device.
static constexpr uint8_t CMD_KEY_INIT
Initiate key transfer to device.
static constexpr uint8_t RESULT_LIMITATION_BY_RAIN
Parameter limited by a rain sensor.
static constexpr uint8_t RESULT_USER_ACTION
User action overrode the command.
static constexpr uint8_t CMD_GET_GENERAL_INFO3_RESP
Never captured on our own wire.
const char * command_result_description(uint8_t result)
Return a human-readable explanation for a CMD_ERROR_RESP result code.
static constexpr uint8_t POS_VENT_MODIFIER
Modifier byte for the ventilation command.
static constexpr uint8_t CMD_GET_INFO1_RESP
Device general info 1 response.
static constexpr uint8_t ACEI_LEVEL_AUTO_DEFAULT
Default automatic level (lowest).
static constexpr uint8_t POS_DEFAULT
Wire value for the default position command.
static constexpr uint8_t MANUFACTURER_OVERKIZ
OVERKIZ (Somfy connectivity platform).
static constexpr uint8_t CMD_ONEWAY_ADD_CONTROLLER
1W "add controller" — a 1W device broadcasts this while its key-copy gesture is active,...
const char * command_result_name(uint8_t result)
Return a stable symbolic name for a CMD_ERROR_RESP result code.
static constexpr uint8_t DISCOVERY_FLAGS_ATT_MASK
Bit masks and shifts for the Multi Information Byte fields.
static constexpr uint8_t CMD_SET_NAME_RESP
Device-name write response.
static constexpr uint8_t RESULT_DEAD_BOLT_ERROR
Dead bolt error.
static constexpr uint8_t RESULT_NO_EXECUTION
Node did not move.
static constexpr uint16_t CRC_LSB_MASK
Least-significant-bit mask for reflected CRC update.
static constexpr uint8_t ATT_CLASS_20S
Response within 20 seconds.
static constexpr uint8_t ACEI_LEVEL_COMFORT_2
Comfort automation level 2.
static constexpr uint8_t DISCOVERY_RESP_FLAGS_OFFSET
Flags byte at data[6].
static constexpr uint8_t ORIGINATOR_SECURITY
Security controlling device (SCD) action.
static constexpr uint8_t ORIGINATOR_EMERGENCY
Emergency command (never disabled).
static constexpr uint8_t ACEI_SERVICE_SHIFT
Shift for service field extraction.
static constexpr uint8_t MANUFACTURER_ID_MAX
Maximum manufacturer ID with a known name in the lookup table.
static constexpr uint8_t RESULT_LIMITATION_BY_WIND
Parameter limited by a wind sensor.
static constexpr uint8_t RESULT_LIMITATION_BY_UPS
Parameter limited by a power supply.
static constexpr uint8_t CMD_DISCOVER_SPE_REQ
Broadcast roll-call answered by every device that already holds this controller's system key,...
static constexpr uint8_t CMD_EXECUTE
Set position/open/close/stop — requires authentication.
static constexpr uint8_t CMD_PRIVATE_RESP
Response to 0x00 and 0x03 (contains position data).
static constexpr uint8_t RESULT_LIMITATION_BY_USER
Parameter limited by a remote control.
static constexpr uint8_t ONEWAY_EXECUTE_ACEI_VELUX
ACEI byte for a 1W CMD_EXECUTE frame from a VELUX KLI-class remote.
static constexpr uint8_t RESULT_COMMAND_COMPLETED_OK
No errors detected.
static constexpr uint8_t ORIGINATOR_WIND_SENSOR
Wind sensor triggered the movement.
static constexpr uint8_t MANUFACTURER_ATLANTIC_GROUP
Atlantic Group (heating, hot water).
static constexpr uint8_t CMD_CHALLENGE_REQ
6-byte random challenge.
static constexpr uint8_t CMD_STATUS_UPDATE_RESP
Acknowledge status update.
static constexpr uint8_t RESULT_WRONG_POSITION
Node reports wrong position.
static constexpr uint8_t RESULT_COMMAND_INCOMPATIBLE_TO_MOVEMENT
Command cannot move the node that way.
static constexpr uint8_t ACEI_SERVICE_MASK
Bits [4:3]: service type.
static constexpr uint8_t ACEI_EXTENDED_MASK
Bits [2:1]: extended field.
static constexpr uint8_t RESULT_PRODUCT_NOT_OPERATIONAL
Node is not currently operational.
static constexpr uint8_t ATT_CLASS_5S
Actuator Turnaround Time (ATT) class values.
static constexpr uint8_t ATT_CLASS_10S
Response within 10 seconds.
const char * acei_level_name(uint8_t level)
Get a human-readable name for an ACEI priority level (0–7).
static constexpr uint8_t CMD_SET_NAME
Set device name (authenticated).
static constexpr uint8_t CMD_SET_CONFIG1_RESP
Config response, otherwise undocumented.
static constexpr uint8_t CMD_LAUNCH_KEY_TRANSFER
Device-initiated ("pull") key transfer request: documented elsewhere as a command ID plus a 6-byte ch...
static constexpr uint8_t DISCOVERY_FLAGS_SYNC_CTRL_GRP
Bit 5: supports sync control group.
static constexpr uint8_t CMD_DISCOVER_CONFIRM
Confirm discovery to device.
static constexpr uint8_t ACEI_LEVEL_SHIFT
Shift for priority level extraction.
static constexpr uint8_t CMD_ONEWAY_REMOVE
1W "remove controller" (un-pair a 1W remote from a device); same payload shape as 0x2E.
static constexpr uint8_t POS_FAVORITE
Wire value: move to favorite/"My" position.
static constexpr uint8_t STATUS_EXPECTED
Byte 1 bit 7: device will send auto status update.
static constexpr uint8_t CMD_SEND_RAW_MESSAGE
Named "Send Raw Message" / "Find Hardware" — two candidate names, neither settled.
static constexpr uint8_t BROADCAST_DISCOVER[NODE_ID_SIZE]
Broadcast address for device discovery (0x00003B).
static constexpr uint8_t CMD_DISCOVER_RESP
Device responds with its ID and type.
static constexpr uint8_t RESULT_POWER_CONSUMPTION_TOO_LOW
Node power consumption is too low.
static constexpr uint8_t RESULT_FILTER_MAINTENANCE_NEEDED
Filter needs maintenance.
static constexpr uint8_t DISCOVERY_RESP_FULL_SIZE
Full discovery response payload size.
static constexpr uint8_t MANUFACTURER_ASSA_ABLOY
ASSA ABLOY (locks, access).
static constexpr uint8_t DISCOVERY_RESP_TIMESTAMP_OFFSET
Timestamp starts at data[7] (2 bytes).
static constexpr uint8_t RESULT_WRONG_LOAD_CONNECTED
Wrong load connected to node.
static constexpr uint8_t STATUS_TILT_SELECTOR
Extended status payload marker for tilt-capable devices.
static constexpr uint8_t MANUFACTURER_HORMANN
Hörmann (garage doors, gates).
static constexpr uint8_t CMD_READ_GROUPS
Named "Actuator: Read Groups" / "ActuatorAnyConfigIsLocal" (uncertain) / "Service ACK" — three...
static constexpr uint8_t BROADCAST_DISCOVER_ALT[NODE_ID_SIZE]
Alternate discovery / 1W broadcast address (0x00003F).
uint8_t discovery_att_class(uint8_t flags)
Extract the ATT class field from a discovery response's Multi Information Byte.
static constexpr uint8_t DISCOVERY_FLAGS_ATT_SHIFT
Shift for ATT field extraction.
static constexpr uint8_t POWER_SAVE_LOW_POWER
Device sleeps — needs long preamble to wake.
static constexpr uint8_t CMD_SET_SENSOR_ACK
Acknowledgment to CMD_SET_SENSOR.
static constexpr uint8_t DISCOVERY_FLAGS_POWER_SAVE_MASK
Bits [1:0]: power save mode.
static constexpr uint8_t CMD_GET_GENERAL_INFO3
Observed on the wire (tests/corpus/captures/probe/velux_kig300_probe_capability_burst....
static constexpr uint8_t PRIVATE_GET_POSITION_STATUS
CMD_PRIVATE (0x03) function ID for a position-status request — data[0] of the payload.
static constexpr uint8_t ORIGINATOR_RAIN_SENSOR
Rain sensor triggered the movement.
static constexpr uint8_t CMD_IDENTIFY
Device physical identification / jog — requires authentication.
static constexpr uint8_t RESULT_LIMITATION_BY_SAAC
Parameter limited by a standalone automatic controller.
static constexpr uint8_t CMD_PRIVATE
Get device status — no authentication needed.
static constexpr uint8_t MANUFACTURER_WINDOW_MASTER
WINDOW MASTER (ventilation).
static constexpr uint8_t RESULT_LIMITATION_BY_TIMER
Parameter limited by a timer.
static constexpr uint8_t CMD_PRIVATE2
Content otherwise undecoded by the wire parser.
static constexpr uint8_t AES_KEY_SIZE
AES-128 key size.
Definition proto_sizes.h:23
static constexpr uint8_t MANUFACTURER_SECUYOU
Secuyou (security).
static constexpr uint8_t CMD_GET_INFO2_RESP
Device type/model response.
static constexpr uint8_t RESULT_PRIORITY_LEVEL_LOCKED
Node is locked on this priority level.
static constexpr uint8_t ORIGINATOR_SAAC
Stand-alone automatic controller (SAAC).
static constexpr uint8_t POS_STOP
Position values in the IO protocol.
static constexpr uint8_t ORIGINATOR_USER_REMOTE
User sent command from a remote control.
static constexpr uint8_t ONEWAY_EXECUTE_ACEI
ACEI byte for a 1W CMD_EXECUTE frame — the Somfy-shaped default.
static constexpr uint8_t CMD_CHALLENGE_RESP
HMAC proof answering a 0x3C.
bool is_limitation_result(uint8_t result)
Check whether a result code represents an environmental or control limitation.
static constexpr uint8_t CMD_GET_INFO2
Request device type/model info.
const char * originator_name(uint8_t originator)
Get a human-readable name for a command originator byte.
static constexpr uint8_t RESULT_UNKNOWN_STATUS_REPLY
Device returned an unknown status reply.
static constexpr uint8_t CMD_GET_INFO1
Request device general info 1.
static constexpr uint8_t MANUFACTURER_SOMFY
Somfy (shutters, awnings, blinds).
static constexpr uint8_t ORIGINATOR_ENVIRONMENT
Unspecified environment sensor.
static constexpr uint8_t RESULT_LIMITS_NOT_SET
Device limits are not set.
static constexpr uint8_t CMD_REBOOT
Named "Reboot" / "Service Status" — two candidate names, one of them destructive-sounding,...
static constexpr uint8_t RESULT_NODE_LOCKED
Node is locked.
static constexpr uint8_t RESULT_PRIORITY_LOCKED_NON_EXEC
Priority locked, command not executed (ACEI priority too low).
static constexpr uint8_t ORIGINATOR_TIMER
Timer or schedule triggered the movement.
static constexpr uint16_t CRC_POLYNOMIAL_REVERSED
Reversed CRC-CCITT polynomial used by IO-homecontrol.
static constexpr uint8_t RESULT_NO_CONTACT
No communication to node.
static constexpr uint8_t ORIGINATOR_SMART_CONTROLLER
Smart function controller.
static constexpr uint8_t STATUS_STOPPED
Status byte flags in CMD_PRIVATE_RESP and CMD_STATUS_UPDATE.
static constexpr uint8_t RESULT_BLOCKED
Node blocked by an object.
static constexpr uint8_t CMD_ADDRESS_REQ
"Report your address" request.
static constexpr uint8_t RESULT_INFORMATION_CODE
Information-only code with unknown semantics.
static constexpr uint8_t ORIGINATOR_AUTOMATIC_CYCLE
Automatic cycle / external access.
static constexpr uint8_t RESULT_LIMITATION_BY_LOCAL_USER
Parameter limited by local button.
static constexpr uint8_t RESULT_TARGET_NOT_REACHABLE
Requested target not reachable.
static constexpr uint8_t ACEI_VALID_BIT
ACEI byte bit-field definitions.
static constexpr uint8_t RESULT_IP_NOT_SET
Intermediate position is not set.
static constexpr uint8_t POS_SECURED_TARGET
Wire value for the secured target position command.
static constexpr uint8_t CMD_UNKNOWN4A_RESP
Observed on the wire (tests/corpus/captures/probe/velux_kig300_probe_capability_burst....
static constexpr uint8_t ORIGINATOR_LOCAL_USER
Command originator codes indicating what or who triggered a command.
static constexpr uint8_t RESULT_LIMITATION_BY_SCD
Parameter limited by a security actuator.
Fundamental IO-Homecontrol frame and crypto size constants.