|
Home IO Control
ESPHome add-on for IO-Homecontrol devices
|
Pure transition helpers for hub-owned exchange and pairing frame decisions. More...
#include "proto_codecs.h"#include "proto_constants.h"#include "proto_device_model.h"#include "proto_frame.h"#include "proto_timing.h"#include "tuning_config.h"#include <algorithm>#include <array>#include <cstdint>#include <cstring>#include <optional>#include <string>Go to the source code of this file.
Classes | |
| struct | esphome::home_io_control::decisions::OneWayDedupState |
| Key fields of the last processed 1W frame, used to collapse a remote's repeat burst. More... | |
| struct | esphome::home_io_control::decisions::AttemptPlan |
| How one exchange spends its attempts. Handed to ExchangeEngine::send_and_receive(). More... | |
| struct | esphome::home_io_control::decisions::AttemptRequest |
| What a caller asks of one exchange's attempts: a plain cap on tries (today's shape), or a read kind whose plan the engine builds for the request (ExchangeEngine::plan_read()). More... | |
| struct | esphome::home_io_control::decisions::TargetEvidence |
| What the hub knows about the device an exchange is addressed to, as the exchange engine sees it. More... | |
| struct | esphome::home_io_control::decisions::RainLimitationInput |
| What one decoded status reply said, plus what the hub had predicted. More... | |
| struct | esphome::home_io_control::decisions::StoppedReportInput |
| One decoded status report, plus what the hub remembers and whether it will poll again. More... | |
Namespaces | |
| namespace | esphome |
| namespace | esphome::home_io_control |
| namespace | esphome::home_io_control::decisions |
Functions | |
| bool | esphome::home_io_control::decisions::is_exchange_internal_command (uint8_t cmd) |
| Returns true for commands that are internal to an exchange handshake and carry no useful information for a passive observer (challenge request/response). | |
| bool | esphome::home_io_control::decisions::is_device_event_report (const IoFrame &frame) |
| Returns true for a frame that reports a device event rather than a status: a CMD_SET_CONFIG1_RESP, or a well-formed CMD_STATUS_UPDATE whose event is not EVENT_STATUS_CHANGE (a subscribed sensor report, see decode_status_update_event()). | |
| bool | esphome::home_io_control::decisions::is_subscription_answer (const IoFrame &request, const IoFrame &response) |
Returns true when response is the answer to the subscription request: the request is a CMD_SUBSCRIBE_EVENT with the subscribe action, and the response a well-formed CMD_STATUS_UPDATE carrying the same event, from the device the request went to. | |
| bool | esphome::home_io_control::decisions::overheard_command_writes (uint8_t cmd) |
| Returns true for the commands that write to a device: an execute, a mode activation or a private register write. | |
| bool | esphome::home_io_control::decisions::frame_matches_nodes (const IoFrame &frame, const uint8_t expected_src[NODE_ID_SIZE], const uint8_t expected_dst[NODE_ID_SIZE]) |
| Check if two frames have identical src/dst node IDs. | |
| bool | esphome::home_io_control::decisions::frame_matches_exchange_endpoints (const IoFrame &request, const IoFrame &candidate) |
| Check if candidate frame endpoints are the reverse of the request (dst==request.src, src==request.dst). | |
| ExchangeFirstResponseDisposition | esphome::home_io_control::decisions::classify_exchange_first_response (const IoFrame &request, const IoFrame &candidate) |
| Decide how to handle the first response packet in an authenticated exchange. | |
| ExchangeFinalResponseDisposition | esphome::home_io_control::decisions::classify_exchange_final_response (const IoFrame &request, const IoFrame &candidate) |
| Decide if a candidate frame is an acceptable final response after authentication. | |
| bool | esphome::home_io_control::decisions::is_repeatable_execute (const IoFrame &request) |
| True for a CMD_EXECUTE that has the same effect sent twice as sent once. | |
| bool | esphome::home_io_control::decisions::retry_after_unconfirmed_accept_is_safe (const IoFrame &request, bool target_confirms_execute, uint8_t unconfirmed_tries) |
| Whether an authenticated-but-unanswered request may be sent again. | |
| PairingDiscoveryDisposition | esphome::home_io_control::decisions::classify_pairing_discovery_response (const IoFrame &candidate, const uint8_t controller_id[NODE_ID_SIZE]) |
| Decide if a frame is a valid discovery response (0x29) during pairing. | |
| PairingKeyChallengeDisposition | esphome::home_io_control::decisions::classify_pairing_key_challenge (const IoFrame &candidate, const uint8_t device_id[NODE_ID_SIZE], const uint8_t controller_id[NODE_ID_SIZE]) |
| Decide if a frame is a valid key-challenge (0x3C) during pairing key exchange. | |
| PairingDiscoverConfirmDisposition | esphome::home_io_control::decisions::classify_pairing_discover_confirm_reply (const IoFrame &candidate, const uint8_t device_id[NODE_ID_SIZE], const uint8_t controller_id[NODE_ID_SIZE]) |
| Decide how to handle a candidate reply to a discovery-confirm request (0x2C) during pairing. | |
| PairingKeyConfirmDisposition | esphome::home_io_control::decisions::classify_pairing_key_confirm_reply (const IoFrame &request, const IoFrame &candidate) |
| Decide how to handle a candidate reply to the key-transfer (0x32) confirm wait (wait_for_key_confirm_(), slow-turnaround chips only). | |
| bool | esphome::home_io_control::decisions::discover_confirm_try_rotates (uint8_t try_index) |
| Whether discover-confirm try try_index (1-based) should rotate channels rather than hold the request channel. | |
| bool | esphome::home_io_control::decisions::is_duplicate_1w_frame (const OneWayDedupState &last, const OneWayDedupState &incoming, uint32_t window_ms) |
| Decide whether an incoming 1W frame repeats the previous one inside the burst window. | |
| bool | esphome::home_io_control::decisions::defer_background_poll_for_1w_activity (bool next_op_is_background, uint32_t first_1w_activity_ms, uint32_t last_1w_activity_ms, uint32_t now, uint32_t quiet_ms, uint32_t max_defer_ms) |
| Decide whether to hold back a queued background poll because a 1W remote is still transmitting. | |
| bool | esphome::home_io_control::decisions::scheduled_poll_in_retry_band (uint8_t status_poll_failures, uint8_t auth_poll_failures) |
| True when a scheduler-owned status poll sits in the middle band of the backoff ladder. | |
| uint8_t | esphome::home_io_control::decisions::scheduled_poll_max_tries (uint8_t status_poll_failures, uint8_t auth_poll_failures) |
| Transmit-attempt budget for a scheduler-owned status poll of a heater, by ladder position. | |
| uint32_t | esphome::home_io_control::decisions::short_lead_cost_ms (uint32_t short_tx_ms) |
| What the short lead attempt costs the exchange: its transmission, its brief listen and the attempt overhead (the same terms attempt_fits() counts for any attempt). | |
| bool | esphome::home_io_control::decisions::attempt_fits (uint32_t elapsed_ms, uint32_t gap_ms, uint32_t tx_ms, uint32_t window_ms, uint32_t auth_tail_ms, uint32_t budget_ms) |
The attempt budget rule: an attempt starts only if the whole attempt, up to the end of the last wait it can make, ends inside budget_ms. | |
| uint8_t | esphome::home_io_control::decisions::attempts_that_fit (uint32_t budget_ms, uint32_t attempt_tx_ms, uint32_t early_window_ms, uint32_t last_window_ms, uint8_t cap) |
How many attempts to a silent target fit the budget when none waits a gap, capped at cap. | |
| AttemptPlan | esphome::home_io_control::decisions::attempt_plan_for_tries (uint8_t max_tries, uint16_t window_ms) |
The plan for a caller that names only a number of attempts: that many (at least one, at most EXCHANGE_RETRY_COUNT), the default gap, and window_ms. | |
| AttemptPlan | esphome::home_io_control::decisions::attempt_plan (ReadKind kind, bool low_power, const TuningConfig &tuning, uint32_t attempt_tx_ms, uint16_t last_window_ms=0, std::optional< uint32_t > short_lead_tx_ms=std::nullopt) |
| The attempt plan for a request, from what it is for and who it asks. | |
| bool | esphome::home_io_control::decisions::is_repeat_safe_command (const IoFrame &request) |
| Whether a request may be sent again to a device that said nothing, without a second action. | |
| ReadKind | esphome::home_io_control::decisions::scheduled_poll_read_kind (uint8_t status_poll_failures, uint8_t auth_poll_failures, bool settles_a_position) |
| What a cover's status poll is for, from where it sits and what it settles. | |
| TargetEvidence | esphome::home_io_control::decisions::target_evidence (const IoDevice &dev) |
| Build the exchange engine's view of a device record. | |
| bool | esphome::home_io_control::decisions::moving_until_holds (const TargetEvidence &evidence, uint32_t now) |
| True while the remaining time the receiver last reported has not run out: by its own account it is still travelling, so a silent exchange is not evidence that it has come to rest. | |
| bool | esphome::home_io_control::decisions::is_stop_request (const IoFrame &request) |
| True for a CMD_EXECUTE whose main byte is POS_STOP. | |
| WakeBelief | esphome::home_io_control::decisions::wake_belief (const TargetEvidence &evidence, uint32_t now, bool is_stop, bool is_status_read=false, bool state_unknown=false) |
| Judge whether a low-power receiver is travelling. | |
| uint16_t | esphome::home_io_control::decisions::low_power_try_preamble (WakeBelief belief, uint8_t try_index, uint16_t short_preamble, uint16_t wake_preamble) |
| Start-preamble length for one try of a directed exchange to a low-power receiver. | |
| const char * | esphome::home_io_control::decisions::wake_belief_name (WakeBelief belief) |
| Lowercase name of a belief for log lines. | |
| bool | esphome::home_io_control::decisions::is_one_way_pairing_gesture (bool oneway, const uint8_t dst[NODE_ID_SIZE], uint8_t cmd) |
| True if a frame's shape matches a 1W remote's pairing gesture (issue #27/#65): CTRL0 1W bit set, addressed to the 1W broadcast address (0x00003F), with one of the three command bytes observed in the field capture — 0x20 (WRITE_PRIVATE), 0x39 (1W remove), or 0x2E (alternate discovery, 1W-flagged). | |
| std::array< uint32_t, 3 > | esphome::home_io_control::decisions::status_update_ack_channel_order (uint32_t receive_freq_hz) |
| The order in which a 0x72 status-update acknowledgement is sent on the three channels: the channel the 0x71 exchange ran on first, then the other two in their usual CH1, CH2, CH3 order. | |
| bool | esphome::home_io_control::decisions::frame_issue_is_warning (bool dst_registered, bool dst_is_us) |
| Whether a frame issue (a frame the hub did not act on) is logged at WARN rather than DEBUG. | |
| bool | esphome::home_io_control::decisions::oneway_burst_started_fresh (uint32_t last_1w_activity_ms, uint32_t now, uint32_t quiet_ms) |
| Whether a 1W frame arriving at now starts a new burst rather than extending the current one — true if no frame has been seen yet, or the gap since the last one reached quiet_ms (the previous burst already released any deferred poll). | |
| uint32_t | esphome::home_io_control::decisions::remote_poll_timer_id (const uint8_t node_id[NODE_ID_SIZE]) |
| Numeric set_timeout() id for a device's remote-activity poll timer (hub_status.cpp's schedule_status_poll_()). | |
| uint32_t | esphome::home_io_control::decisions::heating_poll_timer_id (const uint8_t node_id[NODE_ID_SIZE]) |
| Numeric set_timeout() id for a heater's regular-cadence poll timer (IOHomeControlComponent::schedule_heating_poll_()). | |
| HeaterModelHint | esphome::home_io_control::decisions::classify_heater_model_hint (DeviceModel declared, bool towel_mode_has_value) |
| Compare a heater's declared model with whether it answered the towel-dryer mode register (0x0136) with a value. | |
| std::string | esphome::home_io_control::decisions::format_heater_model_name (const uint8_t *bytes, size_t len) |
| Render a heater's model-name register (0x0119, ASCII) for a log line. | |
| bool | esphome::home_io_control::decisions::pairing_node_has_heater_at_next_address (DeviceType type, uint8_t manufacturer) |
| Does a pairing node that answered discovery this way control a heater at the next address? | |
| bool | esphome::home_io_control::decisions::next_node_id (const uint8_t node[NODE_ID_SIZE], uint8_t next[NODE_ID_SIZE]) |
| The node address one above node, as a 24-bit number (B6BDFB -> B6BDFC). | |
| RainLimitationRule | esphome::home_io_control::decisions::rain_limitation_rule (const RainLimitationInput &in) |
| Decide which rain-limitation evidence, if any, a status reply provides. | |
| bool | esphome::home_io_control::decisions::is_rain_limited (const RainLimitationInput &in) |
| True when rain_limitation_rule() finds evidence of a rain limitation. | |
| const char * | esphome::home_io_control::decisions::rain_limitation_rule_name (RainLimitationRule rule) |
| Name of a rain-limitation rule for log lines. | |
| RainSensorState | esphome::home_io_control::decisions::rain_state_from_minimum_limit (uint16_t value_raw) |
| Rain state from the raw value of a minimum-limitation reply. | |
| RainSensorState | esphome::home_io_control::decisions::rain_state_from_limitation_reply (const LimitationStatus &status) |
| Rain state from a decoded limitation reply: the one place that knows which reply fields decide. | |
| bool | esphome::home_io_control::decisions::is_executing (ExecutionState state) |
| True when a device in this state is moving or about to: waiting for its turn (2), waiting for power (3) or executing (4). | |
| bool | esphome::home_io_control::decisions::reads_stopped_by_status_byte (uint8_t status_byte) |
| Does byte 0 of a status reply say the device is at rest? | |
| StoppedReport | esphome::home_io_control::decisions::classify_stopped_report (const StoppedReportInput &in) |
| Decide whether a "stopped" report means at rest. | |
| bool | esphome::home_io_control::decisions::stopped_report_at_rest (StoppedReport report) |
| True when a verdict means the device is at rest. | |
| bool | esphome::home_io_control::decisions::stopped_report_keeps_candidate (StoppedReport report) |
| True when a verdict leaves an off-target report to remember for the next one. | |
Variables | |
| constexpr uint32_t | esphome::home_io_control::decisions::REMOTE_POLL_TIMER_ID_TAG = 0x01000000 |
| Namespace tag for remote_poll_timer_id() below: the node address occupies the low 24 bits, and this is OR-ed in above them. | |
| constexpr uint32_t | esphome::home_io_control::decisions::HEATING_POLL_TIMER_ID_TAG = 0x02000000 |
| Namespace tag for heating_poll_timer_id(): the same node-address scheme under its own bit, so a heater's regular-cadence timer and a remote-activity poll timer for the same device never replace one another. | |
| constexpr uint8_t | esphome::home_io_control::decisions::HEATER_MODEL_CHECK_MAX_RUNS = 3 |
| Runs of the one-off heater model check before the hub gives up on it for this boot. | |
| constexpr uint8_t | esphome::home_io_control::decisions::MODEL_NAME_PRINTABLE_FIRST = 0x20 |
| First and last byte format_heater_model_name() prints as itself: printable ASCII, space to '~'. | |
| constexpr uint8_t | esphome::home_io_control::decisions::MODEL_NAME_PRINTABLE_LAST = 0x7E |
| See MODEL_NAME_PRINTABLE_FIRST. | |
| static constexpr uint8_t | esphome::home_io_control::decisions::RAIN_MINIMUM_LIMIT_PERCENT = 89 |
| A window with a wired rain sensor limits its own opening while it rains. | |
| static constexpr uint16_t | esphome::home_io_control::decisions::POSITION_FULL_PERCENT = 100 |
| Percent value of a fully limited position, the divisor of the raw position coding. | |
| static constexpr uint16_t | esphome::home_io_control::decisions::POSITION_RAW_PER_PERCENT = STATUS_POS_MAX / POSITION_FULL_PERCENT |
| Raw position units per percent (512). | |
| static constexpr uint16_t | esphome::home_io_control::decisions::RAIN_MINIMUM_LIMIT_RAW = POSITION_RAW_PER_PERCENT * RAIN_MINIMUM_LIMIT_PERCENT |
| RAIN_MINIMUM_LIMIT_PERCENT in the raw position coding (0xB200). | |
Pure transition helpers for hub-owned exchange and pairing frame decisions.
This header contains inline, testable decision logic: frame classification for exchange and pairing flows, plus shared timing utilities. No state, no side effects — suitable for unit testing without radio hardware.
Definition in file hub_decisions.h.