Home IO Control
ESPHome add-on for IO-Homecontrol devices
Loading...
Searching...
No Matches
status_poll_policy.cpp
Go to the documentation of this file.
1/// @file status_poll_policy.cpp
2/// @brief StatusPollPolicy implementation.
3/// @ingroup hioc_hub
4
6
7namespace esphome {
8namespace home_io_control {
9
10void StatusPollPolicy::set_interval(const std::string &device_id, uint32_t interval_ms) {
11 tracking_[device_id].interval_ms = interval_ms;
12}
13
14uint32_t StatusPollPolicy::get_interval(const std::string &device_id) const {
15 auto it = tracking_.find(device_id);
16 return (it != tracking_.end()) ? it->second.interval_ms : 0;
17}
18
19void StatusPollPolicy::begin_tracking(const std::string &device_id, uint32_t initial_delay_ms, uint32_t now) {
20 PollTracking &t = tracking_[device_id];
21 t.poll_deadline = now + MAX_TRACKED_STATUS_POLL_WINDOW_MS;
22 t.next_update = (initial_delay_ms == 0) ? 0 : (now + initial_delay_ms);
25 t.stop_settle = false;
26}
27
28void StatusPollPolicy::clear(const std::string &device_id) {
29 auto it = tracking_.find(device_id);
30 if (it == tracking_.end())
31 return;
32 PollTracking &t = it->second;
33 t.next_update = 0;
34 t.poll_deadline = 0;
37 t.stop_settle = false;
38 // interval_ms is configuration — NOT cleared; remains set for future tracking cycles.
39}
40
41void StatusPollPolicy::set_next_update(const std::string &device_id, uint32_t abs_time) {
42 tracking_[device_id].next_update = abs_time;
43}
44
45uint32_t StatusPollPolicy::get_next_update(const std::string &device_id) const {
46 auto it = tracking_.find(device_id);
47 return (it != tracking_.end()) ? it->second.next_update : 0;
48}
49
50uint32_t StatusPollPolicy::get_poll_deadline(const std::string &device_id) const {
51 auto it = tracking_.find(device_id);
52 return (it != tracking_.end()) ? it->second.poll_deadline : 0;
53}
54
55bool StatusPollPolicy::is_tracking_active(const std::string &device_id, uint32_t now) const {
56 auto it = tracking_.find(device_id);
57 if (it == tracking_.end())
58 return false;
59 const PollTracking &t = it->second;
60 return t.poll_deadline != 0 && now <= t.poll_deadline;
61}
62
63uint32_t StatusPollPolicy::on_exchange_failed(const std::string &device_id, bool auth_like, uint32_t now) {
64 auto it = tracking_.find(device_id);
65 if (it == tracking_.end())
66 return 0;
67 PollTracking &t = it->second;
68
69 uint32_t backoff_ms;
70 if (auth_like) {
72 if (t.auth_poll_failures < UINT8_MAX)
74 backoff_ms = retry_delay_ms(t.auth_poll_failures, true);
75 } else {
77 if (t.status_poll_failures < UINT8_MAX)
79 backoff_ms = retry_delay_ms(t.status_poll_failures, false);
80 }
81
82 if (is_tracking_active(device_id, now) && now + backoff_ms <= t.poll_deadline) {
83 t.next_update = now + backoff_ms;
84 return backoff_ms;
85 }
86
87 clear(device_id);
88 return 0;
89}
90
91void StatusPollPolicy::clear_failure_streaks(const std::string &device_id) {
92 auto it = tracking_.find(device_id);
93 if (it == tracking_.end())
94 return;
95 it->second.status_poll_failures = 0;
96 it->second.auth_poll_failures = 0;
97}
98
99void StatusPollPolicy::mark_stop_settle(const std::string &device_id) {
100 auto it = tracking_.find(device_id);
101 if (it != tracking_.end())
102 it->second.stop_settle = true;
103}
104
105bool StatusPollPolicy::take_stop_settle(const std::string &device_id) {
106 auto it = tracking_.find(device_id);
107 if (it == tracking_.end())
108 return false;
109 const bool marked = it->second.stop_settle;
110 it->second.stop_settle = false;
111 return marked;
112}
113
114uint8_t StatusPollPolicy::get_status_poll_failures(const std::string &device_id) const {
115 auto it = tracking_.find(device_id);
116 return (it != tracking_.end()) ? it->second.status_poll_failures : 0;
117}
118
119uint8_t StatusPollPolicy::get_auth_poll_failures(const std::string &device_id) const {
120 auto it = tracking_.find(device_id);
121 return (it != tracking_.end()) ? it->second.auth_poll_failures : 0;
122}
123
124std::optional<std::string> StatusPollPolicy::pop_due_device(uint32_t now) {
125 for (auto &pair : tracking_) {
126 PollTracking &t = pair.second;
127 if (t.next_update == 0 || now <= t.next_update)
128 continue;
129 if (!is_tracking_active(pair.first, now)) {
130 clear(pair.first);
131 continue;
132 }
133 t.next_update = 0;
134 return pair.first;
135 }
136 return std::nullopt;
137}
138
139uint32_t StatusPollPolicy::retry_delay_ms(uint8_t consecutive_failures, bool auth_like) {
140 if (auth_like) {
141 if (consecutive_failures <= 1)
142 return STATUS_AUTH_RETRY_AFTER_FAIL_MS;
143 if (consecutive_failures == 2)
144 return STATUS_AUTH_RETRY_AFTER_FAIL_STEP2_MS;
145 return STATUS_AUTH_RETRY_AFTER_FAIL_MAX_MS;
146 }
147 if (consecutive_failures <= 1)
148 return STATUS_RETRY_AFTER_FAIL_MS;
149 if (consecutive_failures == 2)
150 return STATUS_RETRY_AFTER_FAIL_STEP2_MS;
151 if (consecutive_failures == 3)
152 return STATUS_RETRY_AFTER_FAIL_STEP3_MS;
153 if (consecutive_failures == 4)
154 return STATUS_RETRY_AFTER_FAIL_STEP4_MS;
155 return STATUS_RETRY_AFTER_FAIL_MAX_MS;
156}
157
158} // namespace home_io_control
159} // namespace esphome
void mark_stop_settle(const std::string &device_id)
Mark the device's next poll as the one confirming an accepted STOP, so it gets STOP_SETTLE_POLL_TRIES...
void set_interval(const std::string &device_id, uint32_t interval_ms)
Set the configured follow-up poll interval (ms; 0 = legacy one-shot settle only).
bool is_tracking_active(const std::string &device_id, uint32_t now) const
True when the device has an active bounded polling window (deadline set and not expired).
uint32_t get_next_update(const std::string &device_id) const
uint32_t get_interval(const std::string &device_id) const
Return the configured interval (0 when not set or unconfigured).
void clear_failure_streaks(const std::string &device_id)
Reset failure streaks after a successful exchange response.
void set_next_update(const std::string &device_id, uint32_t abs_time)
Schedule the next poll at an absolute millis() timestamp.
uint32_t on_exchange_failed(const std::string &device_id, bool auth_like, uint32_t now)
Record a failed background poll; apply backoff or clear tracking if the window expired.
void begin_tracking(const std::string &device_id, uint32_t initial_delay_ms, uint32_t now)
Begin bounded follow-up polling after a command or remote activity.
uint8_t get_status_poll_failures(const std::string &device_id) const
uint8_t get_auth_poll_failures(const std::string &device_id) const
uint32_t get_poll_deadline(const std::string &device_id) const
bool take_stop_settle(const std::string &device_id)
Consume the mark set by mark_stop_settle(): true once, for the first poll dispatched after it.
void clear(const std::string &device_id)
Stop all polling for a device and reset failure counts. Preserves interval_ms.
std::optional< std::string > pop_due_device(uint32_t now)
Return and consume the first device whose next_update is overdue.
Per-device poll scheduling, failure backoff, and follow-up-poll state machine.
Per-device poll scheduling state.
uint8_t auth_poll_failures
Consecutive background poll failures that saw a 0x3C challenge.
bool stop_settle
The next poll confirms an accepted STOP (see STOP_SETTLE_POLL_TRIES).
uint8_t status_poll_failures
Consecutive background poll failures (silent — no reply).
uint32_t poll_deadline
Hard stop for bounded follow-up polling.
uint32_t next_update
Absolute millis() timestamp for the next poll; 0 = idle.