Home IO Control
ESPHome add-on for IO-Homecontrol devices
Loading...
Searching...
No Matches
status_poll_policy.h File Reference

Per-device poll scheduling, failure backoff, and follow-up-poll state machine. More...

#include "proto_timing.h"
#include <algorithm>
#include <cstdint>
#include <map>
#include <optional>
#include <set>
#include <string>
#include <vector>
Include dependency graph for status_poll_policy.h:
This graph shows which files directly or indirectly include this file:

Go to the source code of this file.

Classes

struct  esphome::home_io_control::PollTracking
 Per-device poll scheduling state. More...
struct  esphome::home_io_control::ForeignTraffic
 Per-device bookkeeping of overheard foreign traffic. More...
class  esphome::home_io_control::StatusPollPolicy
 Per-hub poll scheduling and failure-backoff policy. More...

Namespaces

namespace  esphome
namespace  esphome::home_io_control

Enumerations

enum class  esphome::home_io_control::ForeignFrameKind : uint8_t { esphome::home_io_control::REQUEST_TO_DEVICE , esphome::home_io_control::DEVICE_CHALLENGE_TO_OTHER }
 What an overheard frame of another controller is. More...
enum class  esphome::home_io_control::ForeignAction : uint8_t { esphome::home_io_control::IGNORE , esphome::home_io_control::ARM , esphome::home_io_control::PUSH_PENDING }
 What the hub does about an overheard foreign frame. More...
enum class  esphome::home_io_control::MovingReportAction : uint8_t { esphome::home_io_control::FOLLOW , esphome::home_io_control::OPEN , esphome::home_io_control::END , esphome::home_io_control::NONE }
 What the hub does with a status report that reads as moving. More...

Functions

uint32_t esphome::home_io_control::settle_delay_ms (uint32_t interval_ms, uint32_t remaining_delay_ms, bool cap_for_stop)
 Resolve the follow-up settle-poll delay while a device may still be moving.

Variables

static constexpr uint8_t esphome::home_io_control::MOVING_POLL_MISSES_SKIPPED = 2
 Consecutive failed polls of a moving device that are skipped rather than backed off.
static constexpr uint32_t esphome::home_io_control::MAX_TRACKED_STATUS_POLL_WINDOW_MS = 240000
 Hard stop for bounded follow-up polling after a command, remote activity or the first moving report, measured from the moment the window opens (ADR 0061, amended by ADR 0079).
static constexpr uint32_t esphome::home_io_control::INITIAL_STATUS_REQUEST_DELAY_MS = 5000
 Delay before the first post-boot status request from an entity.
static constexpr uint32_t esphome::home_io_control::HEATING_POLL_DEFAULT_INTERVAL_MS = 300000
 Interval between regular polls of a heater (its status_poll_interval overrides it).
static constexpr uint32_t esphome::home_io_control::HEATING_POST_WRITE_POLL_DELAY_MS = 45000
 Delay of the extra heater poll after a register write the heater acknowledged.
static constexpr uint32_t esphome::home_io_control::FOREIGN_READ_RESYNC_MS = 600000
 How long a device's authenticated status or this hub's own read-triggered poll keeps a further overheard read from triggering another poll (0 = never polled, so the first read polls).
static constexpr uint32_t esphome::home_io_control::FOREIGN_CHALLENGE_AFTER_REQUEST_MS = 100
 A device challenges a request 2.5-3.3 ms after it; 100 ms is far above that and far below the next request.
static constexpr uint32_t esphome::home_io_control::ONEWAY_QUIET_PERIOD_MS = 700
 Hold queued background polls back for this long after any 1W frame, so a poll the hub itself scheduled does not occupy the (half-duplex) radio while the remote is still transmitting.
static constexpr uint32_t esphome::home_io_control::ONEWAY_POLL_DEFER_CAP_MS = 5000
 Hard cap on how long sustained 1W traffic may hold a background poll back in total, measured from the start of the burst rather than the most recent frame.
static constexpr uint32_t esphome::home_io_control::DEFAULT_SETTLE_POLL_DELAY_MS = 3000
 Default follow-up settle-poll delay used while a device may still be moving, when no explicit poll interval is configured (interval_ms == 0).
static constexpr uint32_t esphome::home_io_control::STOP_SETTLE_POLL_CAP_MS = 1000
 Upper bound on the settle-poll delay after a STOP command.
Background-poll retry delays after silent failures (no reply received)
static constexpr uint32_t esphome::home_io_control::STATUS_RETRY_AFTER_FAIL_MS = 5000
 First retry after a silent failure.
static constexpr uint32_t esphome::home_io_control::STATUS_RETRY_AFTER_FAIL_STEP2_MS = 15000
 Second retry after a silent failure.
static constexpr uint32_t esphome::home_io_control::STATUS_RETRY_AFTER_FAIL_STEP3_MS = 30000
 Third retry after a silent failure.
static constexpr uint32_t esphome::home_io_control::STATUS_RETRY_AFTER_FAIL_STEP4_MS = 60000
 Fourth retry after a silent failure.
static constexpr uint32_t esphome::home_io_control::STATUS_RETRY_AFTER_FAIL_MAX_MS = 300000
 Steady-state backoff after many silent failures.
Background-poll retry delays after auth-shaped failures (0x3C challenge seen)

A device that challenges the poll is awake and heard it.

A single such miss is usually a closing reply lost while the device switched its load — on a cover, the motor stopping at the end of its travel (issue #178) — so the first retry waits no longer than after a silent miss. Only a repeated one, which is what a wrong key looks like, backs off far: there every further try is a full authenticated exchange that cannot succeed.

static constexpr uint32_t esphome::home_io_control::STATUS_AUTH_RETRY_AFTER_FAIL_MS
 First retry after a challenge-seen failure.
static constexpr uint32_t esphome::home_io_control::STATUS_AUTH_RETRY_AFTER_FAIL_STEP2_MS = 30000
 Second retry.
static constexpr uint32_t esphome::home_io_control::STATUS_AUTH_RETRY_AFTER_FAIL_STEP3_MS = 120000
 Third retry.
static constexpr uint32_t esphome::home_io_control::STATUS_AUTH_RETRY_AFTER_FAIL_MAX_MS = 300000
 Steady-state after repeated auth failures.
Predicted-end read

A moving cover that reports how long it has left is read once more, with the settling plan, a second after that time has run out: the position it reports then is the final one.

That holds for every count, a short one or 0 included: the read at the end is the one most likely to meet the second in which the motor switches its load and a reply is lost. The ordinary progress reads keep running in between and stay single tries. The aim, the dropped progress read and the final-position read at count + 1 s are confirmed on a Somfy Sunea io awning (tests/corpus/captures/statuspoll/somfy_awning_statuspoll_close_with_remaining_time_predicted_end_read_sx1262.yaml).

Todo

Experimental: the stale-count rule has not met a frozen count on hardware, and a battery-powered cover's read at the end of its count has not run on one (ADR 0076).

The predicted-end read is aimed this long after the remaining time has run out. The motor takes up to about a second after its count reaches zero to stop and switch its load.

static constexpr uint32_t esphome::home_io_control::PREDICTED_END_MARGIN_MS = 1000
static constexpr uint32_t esphome::home_io_control::PREDICTED_END_DROPS_PROGRESS_WITHIN_MS = 1000
 A progress read due within this long before the predicted-end read is dropped: the settling read follows at once and a read of its own in front of it only fills the air.
static constexpr uint8_t esphome::home_io_control::PREDICTED_END_STALE_REPORTS = 3
 Reports in a row whose remaining time did not fall below the previous one before the count is taken as frozen and the predicted-end read is given up (progress reads only).
Frozen-count back-off

A count that has stopped falling is a cover that is waiting or stuck, not one that is nearly there, so the progress reads that follow back off instead of returning to the 3 s cadence (ADR 0079, amending ADR 0076).

The steps grow with each further report whose count did not fall and stop at the last; a falling count starts over. The back-off only begins after PREDICTED_END_STALE_REPORTS reports in a row that did not fall, and the slowest falling counts seen drop at every poll (3 s between two polls on a VELUX SML, 1 s per second on a Somfy awning), so a slow but alive count is not taken as frozen. 10 s, 15 s and 20 s keep a cover that starts after waiting within 20 s of being seen, while a stuck one costs a read per 20 s instead of one per 3 s.

Todo
Experimental: no frozen count has been captured on hardware yet.
static constexpr uint32_t esphome::home_io_control::FROZEN_COUNT_BACKOFF_FIRST_MS = 10000
 Delay after the first frozen report.
static constexpr uint32_t esphome::home_io_control::FROZEN_COUNT_BACKOFF_SECOND_MS = 15000
 Delay after the second.
static constexpr uint32_t esphome::home_io_control::FROZEN_COUNT_BACKOFF_MAX_MS = 20000
 Delay after the third and every later one.

Detailed Description

Per-device poll scheduling, failure backoff, and follow-up-poll state machine.

Owns all per-device poll bookkeeping (intervals, deadlines, failure counters). Pure logic with injected timestamps — fully host-testable without a clock mock.

Definition in file status_poll_policy.h.