Home IO Control
ESPHome add-on for IO-Homecontrol devices
Loading...
Searching...
No Matches
esphome::home_io_control::ManagementActions Class Reference

Encapsulates hub-level management operations exposed as Home Assistant actions. More...

#include <management_actions.h>

Collaboration diagram for esphome::home_io_control::ManagementActions:

Public Member Functions

 ManagementActions (const uint8_t *node_id, const uint8_t *system_key, const TuningConfig *tuning, ExchangeEngine &engine, DeviceRegistry &registry, const bool *initialized, IOHomeControlComponent *hub)
 Construct with all required collaborators.
 ManagementActions (const ManagementActions &)=delete
 Non-copyable — stores references and pointers into hub member addresses.
ManagementActionsoperator= (const ManagementActions &)=delete
void register_actions ()
 Register all management actions (rename, identify, force-open, ...) with ESPHome's native API server.
void api_rename_device (const std::string &device_id, const std::string &new_name)
 Native API callback: rename a device and publish the result as a HA event.
ManagementActionResult rename_device (const std::string &device_id, const std::string &new_name)
 Rename a registered device and verify the result by reading the name back.
void api_identify_device (const std::string &device_id)
 Native API callback: trigger a device's physical identify and publish the result as a HA event.
ManagementActionResult identify_device (const std::string &device_id)
 Trigger a registered device's physical identify (brief jog/flash).
void api_force_open_device (const std::string &device_id)
 Native API callback: force-open a device and publish the result as a HA event.
ManagementActionResult force_open_device (const std::string &device_id)
 Move a registered cover device to fully open at elevated priority, intended to bypass wind/rain soft locks.
void api_scan_paired_devices ()
 Native API callback: run a roll-call scan and publish the result as a HA event.
ManagementActionResult scan_paired_devices ()
 Broadcast a roll-call and report every device that answers.
void publish_result (const ManagementActionResult &result)
 Publish a management result as one or more structured log lines (one call per line of result.message, see log_multiline_result() in the .cpp) and a Home Assistant event.

Detailed Description

Encapsulates hub-level management operations exposed as Home Assistant actions.

ManagementActions is constructed once by IOHomeControlComponent. It stores pointers and references into hub members; it is non-copyable for the same reason as ExchangeEngine.

Definition at line 52 of file management_actions.h.

Constructor & Destructor Documentation

◆ ManagementActions() [1/2]

esphome::home_io_control::ManagementActions::ManagementActions ( const uint8_t * node_id,
const uint8_t * system_key,
const TuningConfig * tuning,
ExchangeEngine & engine,
DeviceRegistry & registry,
const bool * initialized,
IOHomeControlComponent * hub )

Construct with all required collaborators.

Parameters
node_idController 3-byte node ID, owned by the hub.
system_keyController 16-byte AES system key, owned by the hub. Only scan_paired_devices() uses this — it builds a self-authenticating CMD_DISCOVER_SPE_REQ itself, unlike rename/identify/force-open, which authenticate through ExchangeEngine's challenge-response.
tuningRuntime tuning config, owned by the hub. Only scan_paired_devices() uses this — it reuses pairing_discovery_wait_ms as the listen window for each of its own per-channel attempts.
engineShared exchange engine for radio transactions.
registryDevice registry for device lookups.
initializedPointer to the hub's initialized flag.
hubHub pointer for ESPHome API calls and public hub methods.

Definition at line 226 of file management_actions.cpp.

◆ ManagementActions() [2/2]

esphome::home_io_control::ManagementActions::ManagementActions ( const ManagementActions & )
delete

Non-copyable — stores references and pointers into hub member addresses.

Here is the call graph for this function:

Member Function Documentation

◆ api_force_open_device()

void esphome::home_io_control::ManagementActions::api_force_open_device ( const std::string & device_id)

Native API callback: force-open a device and publish the result as a HA event.

Definition at line 433 of file management_actions.cpp.

Here is the call graph for this function:

◆ api_identify_device()

void esphome::home_io_control::ManagementActions::api_identify_device ( const std::string & device_id)

Native API callback: trigger a device's physical identify and publish the result as a HA event.

Definition at line 391 of file management_actions.cpp.

Here is the call graph for this function:

◆ api_rename_device()

void esphome::home_io_control::ManagementActions::api_rename_device ( const std::string & device_id,
const std::string & new_name )

Native API callback: rename a device and publish the result as a HA event.

Definition at line 294 of file management_actions.cpp.

Here is the call graph for this function:

◆ api_scan_paired_devices()

void esphome::home_io_control::ManagementActions::api_scan_paired_devices ( )

Native API callback: run a roll-call scan and publish the result as a HA event.

Definition at line 457 of file management_actions.cpp.

Here is the call graph for this function:

◆ force_open_device()

ManagementActionResult esphome::home_io_control::ManagementActions::force_open_device ( const std::string & device_id)

Move a registered cover device to fully open at elevated priority, intended to bypass wind/rain soft locks.

Safety-sensitive: this deliberately requires an explicit device_id (a hub-level action, not a permanent per-device button) because it can override legitimate environmental protection. Queues the command onto the hub's normal cover-command dispatch path (IOHomeControlComponent::queue_device_command()), which already owns capability gating, poll tracking, and backoff — this method does not talk to the radio directly. The result therefore only confirms that the command was queued; the actual movement outcome arrives later via the device's normal cover-state/polling pipeline, so verified is always false.

Note
The elevated-priority mechanism (see create_force_open() in proto_commands.cpp) is confirmed on real hardware to correctly move a device to fully open, but bypassing an active environmental lock is still experimental — only that the device accepts the frame and opens when nothing is locking it has been observed so far.
Parameters
device_idTarget device ID (hex string, case-insensitive).
Returns
Structured result describing whether the command was queued.

Definition at line 437 of file management_actions.cpp.

◆ identify_device()

ManagementActionResult esphome::home_io_control::ManagementActions::identify_device ( const std::string & device_id)

Trigger a registered device's physical identify (brief jog/flash).

Deliberately performs no device-type gating beyond "is it registered on this hub" — identify exists precisely to let a user work out what an unknown registry entry physically is, so restricting it to specific device types would defeat the purpose. A CMD_ERROR_RESP reply is treated as a successful trigger (not a failure): per the protocol, devices may answer an identify request that way and still perform the jog.

Parameters
device_idTarget device ID (hex string, case-insensitive).
Returns
Structured result describing success and any validation failure. verified is always false — there is no readback that confirms a physical jog happened.

Definition at line 395 of file management_actions.cpp.

Here is the call graph for this function:

◆ operator=()

ManagementActions & esphome::home_io_control::ManagementActions::operator= ( const ManagementActions & )
delete
Here is the call graph for this function:

◆ publish_result()

void esphome::home_io_control::ManagementActions::publish_result ( const ManagementActionResult & result)

Publish a management result as one or more structured log lines (one call per line of result.message, see log_multiline_result() in the .cpp) and a Home Assistant event.

Definition at line 298 of file management_actions.cpp.

Here is the call graph for this function:

◆ register_actions()

void esphome::home_io_control::ManagementActions::register_actions ( )

Register all management actions (rename, identify, force-open, ...) with ESPHome's native API server.

Definition at line 237 of file management_actions.cpp.

Here is the call graph for this function:

◆ rename_device()

ManagementActionResult esphome::home_io_control::ManagementActions::rename_device ( const std::string & device_id,
const std::string & new_name )

Rename a registered device and verify the result by reading the name back.

Parameters
device_idTarget device ID (hex string, case-insensitive).
new_nameRequested UTF-8 device name.
Returns
Structured result describing success, verification, and any error.

Definition at line 329 of file management_actions.cpp.

Here is the call graph for this function:

◆ scan_paired_devices()

ManagementActionResult esphome::home_io_control::ManagementActions::scan_paired_devices ( )

Broadcast a roll-call and report every device that answers.

Not a discovery mechanism for new devices: only devices that already hold this hub's system key answer a CMD_DISCOVER_SPE_REQ (the 0x2A payload is self-authenticating — 6 random bytes plus a 6-byte HMAC over the command byte, computed with system_key_), so a device that has never paired with this hub stays silent. See tests/corpus/captures/somfy_awning/discover_spe_paired_rollcall.yaml for a captured exchange. Never writes DeviceRegistry — every responder is only looked up, never registered; an unknown responder usually means a device paired earlier whose YAML config was never saved, not an intruder. result.device_id stays empty (there is no single target) and verified stays false (nothing here is read back). success is true whenever the broadcast went out, including with zero replies — "nothing answered" is a valid result, not a failure. result.message carries the full multi-line report: a header line (devices detected, how many known vs. unknown), then a Known: section followed by an Unknown: section (either omitted if empty) — known responders get a summary line each, unknown responders additionally get a lead-in sentence and a ready-to-paste YAML block. Devices are grouped by known/unknown rather than left in arrival order, since the two groups need different follow-up and interleaving them made an unknown responder easy to miss between known ones. At most SCAN_MAX_REPLIES distinct responders are reported; if more answer, the report says so explicitly rather than quietly listing a subset.

Transmits the request once per channel (CH2, then CH1, then CH3), each with its own full pairing_discovery_wait_ms listen window, merging distinct responders across attempts — a paired device only hears the broadcast if it happens to be awake on the channel the hub transmits on at that instant, and real hardware testing found that single-channel duty-cycling paired devices are not reliably caught by a one-shot broadcast. A responder that answers more than one attempt still appears exactly once. Each attempt is a single transmit followed by its own window, never back-to-back transmits, so it does not reintroduce the different failure mode a 3-channel-burst transmit caused elsewhere: firing three long-preamble transmits back-to-back with no listening in between blew through the tight per-try response wait windows and broke exchanges in both directions (see IOHomeControlComponent::broadcast_key_extraction_reply_()'s doc comment).

This still blocks the caller for the full three-window duration (roughly 3 × pairing_discovery_wait_ms, ~6 s at the 2000 ms default) and therefore trips ESPHome's "operation took a long time" warning on every invocation. That warning uses a per-component ratchet (Component::should_warn_of_blocking()): it starts at 50 ms and, each time it fires, raises its own threshold to the observed duration plus a margin — but the threshold is stored in centiseconds in a uint8_t, so it saturates at 2550 ms. Anything that blocks longer than that can never ratchet out of warning range.

Accepting that is a deliberate tradeoff (confirmed on real hardware 2026-08-10). A shorter fixed window (500 ms/attempt, ~2.4 s total) was tried: it would have gone quiet after one warning, since 2.4 s sits under the 2550 ms cap — but it also caused real, correctly-decoded replies from registered devices to arrive after the window had already closed, where the passive path drops them (hub_status.cpp's unhandled_cmd catch-all). Losing devices from the report is worse than a recurring log line, so the long window won. Getting both would require restructuring this action to run across multiple scheduled loop() ticks so no single blocking unit approaches 2550 ms — not done here.

Returns
Structured result whose message is the full report.

Definition at line 541 of file management_actions.cpp.

Here is the call graph for this function:

The documentation for this class was generated from the following files: