21constexpr size_t DEVICE_TYPE_HEX_STRING_BUFFER_SIZE = 8;
24std::string format_device_type_hex(
DeviceType type) {
25 char buf[DEVICE_TYPE_HEX_STRING_BUFFER_SIZE];
26 snprintf(buf,
sizeof(buf),
"0x%02X",
static_cast<unsigned>(type));
27 return std::string(buf);
53 return "non_executing";
57 return "waiting_for_executing";
59 return "waiting_for_power";
73 return "not_reported";
81 return "not_reported";
119 return named !=
nullptr ? named->
name :
"UNKNOWN_COVER_CMD";
125 return static_cast<DeviceType>((type_msb << DEVICE_TYPE_LOW_BITS_SHIFT) |
126 (type_subtype >> DEVICE_TYPE_HIGH_BITS_SHIFT));
134 const auto raw =
static_cast<uint16_t
>(
static_cast<uint16_t
>(type) & DEVICE_TYPE_WIRE_MASK);
135 type_msb =
static_cast<uint8_t
>(raw >> DEVICE_TYPE_LOW_BITS_SHIFT);
136 type_subtype =
static_cast<uint8_t
>((raw << DEVICE_TYPE_HIGH_BITS_SHIFT) | (subtype & DEVICE_SUBTYPE_MASK));
141 bool const target_valid = target_raw <= STATUS_POS_MAX;
142 bool const current_valid = current_raw <= STATUS_POS_MAX;
143 float const decoded_current = current_valid ? current_raw * 100.0F / STATUS_POS_MAX : UNKNOWN_POSITION;
146 target = target_raw * 100.0F / STATUS_POS_MAX;
147 }
else if (is_stopped && current_valid) {
151 target = decoded_current;
153 target = UNKNOWN_POSITION;
157 position = decoded_current;
158 }
else if (is_stopped && target_valid) {
161 position = UNKNOWN_POSITION;
166 if (target == UNKNOWN_POSITION || position == UNKNOWN_POSITION)
168 float const tolerance = STATUS_POS_TOLERANCE_RAW * 100.0F / STATUS_POS_MAX;
169 return std::fabs(target - position) <= tolerance;
173 if (tilt_raw > STATUS_POS_MAX)
174 return UNKNOWN_POSITION;
175 return 100.0F - (tilt_raw * 100.0F / STATUS_POS_MAX);
185 return row !=
nullptr ? row->
name :
"unknown";
190 return row !=
nullptr && (row->
flags & device_type_flags::YAML_SELECTABLE) != 0 ? row->
name :
nullptr;
212 return "heater_level";
214 return "water_heater";
264 if (!
has_feature(features, device_feature::LEVELS))
265 return "cover_discrete";
266 return has_feature(features, device_feature::TILT) ?
"cover_position_tilt" :
"cover_position";
269 return "binary_on_off";
277 return "water_heater";
302 if (dev.
subtype == SUBTYPE_MAIN_PARAMETER_ONLY)
312 features |= device_feature::LEVELS;
325 std::string raw = format_device_type_hex(type);
326 if (name !=
nullptr && strcmp(name,
"unknown") != 0) {
327 return std::string(name) +
" (" + raw +
")";
334 if (name !=
nullptr) {
335 return std::string(
"\"") + name +
"\"";
337 return format_device_type_hex(type);
341 bool metadata_complete,
bool low_power,
const std::string &system_key_hex) {
345 const std::string low_power_line = low_power ?
" low_power: true\n" :
"";
346 const std::string system_key_line = system_key_hex.empty() ?
"" :
" system_key: \"" + system_key_hex +
"\"\n";
348 if (!metadata_complete) {
349 return " <cover|light|switch|lock>:\n"
350 " - platform: home_io_control\n"
351 " name: \"My Device\"\n"
352 " io_device_id: \"" +
355 " # io_device_type: left unset — this device didn't report its type during\n"
356 " # discovery, so the controller learns it automatically from the next status\n"
357 " # reply. Add it explicitly once you see it logged, to skip re-learning on\n"
358 " # every future boot.\n" +
359 low_power_line + system_key_line;
363 if (platform ==
nullptr)
366 std::string extra_lines;
369 if (
has_feature(features, device_feature::INVERTED))
370 extra_lines +=
" invert_position: true\n";
373 extra_lines +=
" dimmable: true\n";
374 extra_lines += low_power_line;
375 extra_lines += system_key_line;
377 const std::string subtype_line =
" io_subtype: " + std::to_string(subtype) +
"\n";
379 return " " + std::string(platform) +
381 " - platform: home_io_control\n"
382 " name: \"My Device\"\n"
383 " io_device_id: \"" +
Capability bits carried by a DeviceTypeInfo row and resolved onto a device record.
void encode_packed_device_type(DeviceType type, uint8_t subtype, uint8_t &type_msb, uint8_t &type_subtype)
Encode a DeviceType/subtype pair into the two-byte packed metadata format used by discovery responses...
DeviceFeatures device_features(DeviceType type, uint8_t subtype, DeviceModel model)
Capability bits of a (type, subtype, model).
ExecutionState execution_state_of(uint8_t status_byte)
Read the execution state out of byte 0 of a status reply.
const char * travel_speed_name(TravelSpeed speed)
Get the name a travel speed is configured and published under.
std::string format_device_type_for_yaml(DeviceType type)
Build the YAML value for a device's io_device_type key.
RainSensorState
What the hub's latest minimum-limitation read says about a rain-protection limit.
@ UNKNOWN
No usable reading: never read, or the last reads went unanswered.
@ DRY
The device answered and reports no rain-sized opening limit.
@ RAIN
The device answered and reports an opening limit of rain size.
DeviceFeatures undeclared_kind_features(DeviceCapabilityClass entity_class)
Features of a device kind that has no table row, or that is not declared at all.
const HeatingProfile * find_heating_profile(DeviceType type, uint8_t subtype, DeviceModel model)
Look up the heating profile for a (type, subtype, model).
const char * rain_sensor_state_name(RainSensorState state)
Get a human-readable name for a RainSensorState.
DeviceCapabilityClass device_capability_class(DeviceType type)
Map a raw IO‑Homecontrol type to the closest ESPHome/Home Assistant entity family.
CoverCommand
Named device commands for cover-type actuators.
@ STOP
Stop movement immediately.
@ FORCE_OPEN
Move to fully open at elevated priority; intended to bypass soft locks and environmental limits (conf...
bool device_supports_position_control(DeviceType type)
Does this device type support precise position control (0–100)?
@ NONE
No model: the (type, subtype) row.
void resolve_device_features(IoDevice &dev)
Resolve a device's capability bits and inversion from its kind and its YAML overrides.
const char * execution_state_name(ExecutionState state)
Get a human-readable name for an ExecutionState.
const char * device_type_name(DeviceType type)
Convert a DeviceType to a lowercase string identifier.
const NamedPosition * find_named_position(CoverCommand command)
The named position a command sends.
DeviceType
IO-Homecontrol node types.
@ UNKNOWN
Unknown/unspecified device.
bool device_supports_binary_control(DeviceType type)
Does this device type support binary on/off control?
@ PRIVATE_REGISTERS
CMD_WRITE_PRIVATE register reads and writes (proto_heating.h).
static constexpr uint8_t STATUS_BATTERY_MASK
Byte 1 bits 5-6: the device's coarse battery level (BatteryState).
float decode_tilt_report(uint16_t tilt_raw)
Decode tilt angle from raw 16‑bit value.
const char * device_operation_profile_name(DeviceType type, uint8_t subtype)
Human‑readable operation profile name for a device kind.
constexpr bool has_feature(DeviceFeatures set, DeviceFeatures bit)
True when every bit of bit is set in set.
bool device_supports_lock_control(DeviceType type)
Does this device type support binary lock/unlock control via execute commands?
const DeviceTypeInfo * find_exact_device_type_info(DeviceType type, uint8_t subtype)
Look up the table row with exactly this (type, subtype), without falling back.
bool parse_travel_speed(const std::string &name, TravelSpeed &speed)
Look up a travel speed by the name travel_speed_name() gives it.
bool device_supports_climate_control(DeviceType type, uint8_t subtype)
Can a climate entity drive this device?
DeviceType decode_packed_device_type(uint8_t type_msb, uint8_t type_subtype)
Decode the 10-bit node type from two packed metadata bytes.
BatteryState
A device's coarse battery level, as it reports it in byte 1 of its status payload.
@ FULL_CHARGE
Battery full.
@ NORMAL_CHARGE
Battery neither low nor full.
@ NOT_REPORTED
The device does not report a battery (mains powered), or no status was read yet.
const char * cover_command_name(CoverCommand cmd)
Get a human-readable name for a CoverCommand.
constexpr DeviceFeatures main_parameter_only_variant(DeviceFeatures type_features)
The features of a subtype-58 variant of a kind.
const char * device_platform_name(DeviceType type, uint8_t subtype)
Name of the ESPHome platform that drives this (type, subtype).
bool device_supports_heat_level_control(DeviceType type)
Does this device take a heat level as its position (exterior heating)?
std::string build_device_yaml_snippet(DeviceType type, uint8_t subtype, const std::string &device_id, bool metadata_complete, bool low_power, const std::string &system_key_hex)
Build the ready-to-paste YAML block describing a device, for both a fully-decoded device and one whos...
const char * device_capability_class_name(DeviceType type)
Get a human‑readable name for a capability class.
ExecutionState
What a device says it is doing, from bits [2:0] of byte 0 of a status reply (CMD_PRIVATE_RESP,...
@ WAITING_FOR_POWER
Accepted, waiting for power (queued behind another device on a shared supply).
@ UNDEFINED
Bits [2:0] hold 6 or 7, which no source defines.
@ DONE
Not executing, no error, ready.
@ NON_EXECUTING
Unable to execute under the given conditions (example: temperature too high).
@ WAITING_FOR_EXECUTING
Accepted, waiting for its turn to run.
@ EXECUTION_ERROR
An error occurred during the previous execution; cleared when the next starts.
bool is_known_device_type(uint16_t raw)
True when a raw 10-bit device-type value is a named DeviceType enumerator.
uint16_t DeviceFeatures
Capability bits of a device kind; see device_feature.
uint8_t decode_packed_device_subtype(uint8_t type_subtype)
Decode a protocol-packed device subtype from the second metadata byte.
constexpr uint8_t error_resp_state(uint8_t result)
Execution state (bits [2:0]) of a CMD_ERROR_RESP byte.
bool device_supports_status_requests(DeviceType type)
Does this device type support status request commands (0x03)?
static constexpr uint8_t STATUS_BATTERY_SHIFT
Right shift that brings STATUS_BATTERY_MASK down to 0-3.
bool device_has_no_motion(DeviceType type, uint8_t subtype)
Does this device never move, so a status reply must not start the movement settle loop?
bool has_reached_target_position(float target, float position)
Has the device reached its target within tolerance?
const char * yaml_device_type_name(DeviceType type)
Return the YAML-friendly device-type name for types exposed in the Python schema.
DeviceCapabilityClass
High‑level capability class derived from DeviceType.
@ CLIMATE
Climate device (heating/cooling).
@ SWITCH
Binary on/off switch.
@ UNKNOWN
Unknown capability.
@ HEATER_LEVEL
Heater with a heat level in the position field (exterior heating).
@ COVER
Position‑controlled cover (shutter/blind/awning).
@ WATER_HEATER
Domestic hot water heater (device_feature::WATER_HEATER).
@ LIGHT
Binary on/off light.
bool device_takes_execute_position(DeviceType type)
Does this device type take position EXECUTEs (a percent, on / off, or stop in the main parameter)?
BatteryState decode_battery_state(uint8_t status_byte)
Decode the coarse battery level from byte 1 of a status payload (CMD_PRIVATE_RESP to a status poll,...
const char * battery_state_name(BatteryState state)
Get the name a battery level is published under.
TravelSpeed
The speed a cover's position moves ask the motor for (ADR 0065).
@ SILENT
Profile 0x05, the manufacturer app's silent operation: slower than the motor's own.
@ MOTOR_DEFAULT
The 6-byte frame with no profile: the motor's own speed, as its wall pad moves it.
@ FAST
Profile 0x06, what the hub sends unless a cover says otherwise.
std::string format_device_type_diagnostic(DeviceType type)
Human-readable device type string for diagnostics, including the raw numeric value.
void decode_position_report(uint16_t target_raw, uint16_t current_raw, bool is_stopped, float &target, float &position)
Decode target/current position values from a status frame.
IO-Homecontrol command IDs, result codes and protocol enumerations.
IO-Homecontrol device-type model, capabilities and runtime device state.
The IO node-type and subtype table: names, entity class, platform and behaviour flags.
Heating profiles: what a climate card offers for each kind of heater, and how.
One row of the node-type / subtype table.
uint8_t flags
device_type_flags bits.
const char * name
Type row: lowercase identifier ("roller_shutter").
HeatingControlPath path
How the heater is commanded and read.
Runtime state of a paired IO‑Homecontrol device.
DeviceCapabilityClass entity_class
Class of the entity that declared the device; UNKNOWN for a device no entity declared (the type's cla...
DeviceFeatures features
What this device can do, resolved by resolve_device_features().
std::optional< bool > invert_override
YAML invert_position: when declared; wins over the kind's default.
bool inverted
True if open/close positions are swapped (e.g., horizontal awning).
uint8_t subtype
Device subtype (manufacturer‑specific).
DeviceModel model
YAML-declared product model below the subtype (io_model:); never on the wire.
bool dimmable
YAML-declared dimmable: true of a LIGHT entity.
DeviceType type
Device type (shutter, awning, etc.).
One named position: its feature bit, its main-parameter value and the command that sends it.
const char * name
cover_command_name() of the command ("FAVORITE", "VENT", "PEDESTRIAN", "PARTIAL").