Home IO Control
ESPHome add-on for IO-Homecontrol devices
Loading...
Searching...
No Matches
operation_queue.cpp
Go to the documentation of this file.
1/// @file operation_queue.cpp
2/// @brief OperationQueue implementation.
3/// @ingroup hioc_hub
4
5#include "operation_queue.h"
6
7#include <algorithm>
8
9namespace esphome {
10namespace home_io_control {
11
16
17void OperationQueue::push_control_(PendingOperation op) {
18 // The stale-poll drop assumes `device_id` names a device whose reply will supersede the poll.
19 // For the ONEWAY_* types it names a controller identity instead, and a 1W command produces no
20 // reply to supersede anything — so a handle that happened to match a device ID would silently
21 // cancel that device's status poll. Exclude them rather than rely on the two namespaces never
22 // colliding.
23 const bool addresses_a_device =
26 if (addresses_a_device) {
27 queue_.erase(std::remove_if(queue_.begin(), queue_.end(),
28 [&op](const PendingOperation &existing) {
29 return existing.type == PendingOperationType::REQUEST_STATUS &&
30 existing.device_id == op.device_id;
31 }),
32 queue_.end());
33 }
34 auto it =
35 std::find_if(queue_.begin(), queue_.end(), [](const PendingOperation &o) { return is_background_op(o.type); });
36 queue_.insert(it, std::move(op));
37}
38
39bool OperationQueue::enqueue_set_position(const std::string &device_id, uint8_t position) {
40 for (auto &op : queue_) {
41 if (op.type == PendingOperationType::SET_TILT && op.device_id == device_id) {
42 // SET_TILT stores tilt in op.position (the only field available on standalone tilt ops).
43 uint8_t const pending_tilt = op.position;
45 op.position = position;
46 op.tilt = pending_tilt;
47 return true;
48 }
49 }
50 push_control_({PendingOperationType::SET_POSITION, device_id, position});
51 return false;
52}
53
54bool OperationQueue::enqueue_set_tilt(const std::string &device_id, uint8_t tilt_percent) {
55 for (auto &op : queue_) {
56 if (op.type == PendingOperationType::SET_POSITION && op.device_id == device_id) {
58 op.tilt = tilt_percent;
59 // op.position already holds the correct position from the original SET_POSITION enqueue.
60 return true;
61 }
62 }
63 push_control_({PendingOperationType::SET_TILT, device_id, tilt_percent});
64 return false;
65}
66
67void OperationQueue::enqueue_set_position_and_tilt(const std::string &device_id, uint8_t position,
68 uint8_t tilt_percent) {
69 push_control_({PendingOperationType::SET_POSITION_AND_TILT, device_id, position, tilt_percent});
70}
71
72void OperationQueue::enqueue_device_command(const std::string &device_id, CoverCommand cmd) {
75 op.device_id = device_id;
76 op.command = cmd;
77 push_control_(std::move(op));
78}
79
80void OperationQueue::enqueue_oneway_command(const std::string &controller_id, CoverCommand cmd) {
83 op.device_id = controller_id;
84 op.command = cmd;
85 push_control_(std::move(op));
86}
87
88void OperationQueue::enqueue_oneway_position(const std::string &controller_id, uint8_t position) {
89 push_control_({PendingOperationType::ONEWAY_POSITION, controller_id, position});
90}
91
92void OperationQueue::enqueue_oneway_enroll(const std::string &controller_id) {
93 push_control_({PendingOperationType::ONEWAY_ENROLL, controller_id, 0});
94}
95
96void OperationQueue::enqueue_oneway_unenroll(const std::string &controller_id) {
97 push_control_({PendingOperationType::ONEWAY_UNENROLL, controller_id, 0});
98}
99
100void OperationQueue::enqueue_set_light_position(const std::string &device_id, uint8_t position) {
101 push_control_({PendingOperationType::SET_LIGHT_STATE, device_id, position});
102}
103
104void OperationQueue::enqueue_set_fan_position(const std::string &device_id, uint8_t position) {
105 push_control_({PendingOperationType::SET_FAN_POSITION, device_id, position});
106}
107
108void OperationQueue::enqueue_set_light_state(const std::string &device_id, bool on) {
109 this->enqueue_set_light_position(device_id, on ? BINARY_ENTITY_ON_POSITION : BINARY_ENTITY_OFF_POSITION);
110}
111
112void OperationQueue::enqueue_set_lock_state(const std::string &device_id, bool locked) {
113 push_control_({PendingOperationType::SET_LOCK_STATE, device_id,
114 locked ? BINARY_ENTITY_OFF_POSITION : BINARY_ENTITY_ON_POSITION});
115}
116
117void OperationQueue::enqueue_set_switch_state(const std::string &device_id, bool on) {
118 push_control_(
119 {PendingOperationType::SET_SWITCH_STATE, device_id, on ? BINARY_ENTITY_ON_POSITION : BINARY_ENTITY_OFF_POSITION});
120}
121
122bool OperationQueue::enqueue_background_unique_(PendingOperationType type, const std::string &device_id) {
123 for (const auto &op : queue_) {
124 if (op.type == type && op.device_id == device_id)
125 return false;
126 }
127 queue_.push_back({type, device_id, 0});
128 return true;
129}
130
131bool OperationQueue::enqueue_request_status(const std::string &device_id) {
132 return enqueue_background_unique_(PendingOperationType::REQUEST_STATUS, device_id);
133}
134
135bool OperationQueue::enqueue_request_name(const std::string &device_id) {
136 return enqueue_background_unique_(PendingOperationType::REQUEST_NAME, device_id);
137}
138
139bool OperationQueue::enqueue_request_limitation(const std::string &device_id) {
140 return enqueue_background_unique_(PendingOperationType::REQUEST_LIMITATION, device_id);
141}
142
144 for (const auto &op : queue_) {
146 return false;
147 }
148 queue_.erase(std::remove_if(queue_.begin(), queue_.end(),
149 [](const PendingOperation &op) { return is_background_op(op.type); }),
150 queue_.end());
151 queue_.push_front({PendingOperationType::DISCOVER_AND_PAIR, {}, 0});
152 return true;
153}
154
155std::optional<PendingOperation> OperationQueue::pop() {
156 if (queue_.empty())
157 return std::nullopt;
158 PendingOperation op = std::move(queue_.front());
159 queue_.pop_front();
160 return op;
161}
162
163bool OperationQueue::empty() const { return queue_.empty(); }
164
165std::size_t OperationQueue::size() const { return queue_.size(); }
166
167const PendingOperation &OperationQueue::front() const { return queue_.front(); }
168
169const PendingOperation &OperationQueue::back() const { return queue_.back(); }
170
171const PendingOperation &OperationQueue::operator[](std::size_t index) const { return queue_[index]; }
172
173std::deque<PendingOperation>::const_iterator OperationQueue::begin() const { return queue_.cbegin(); }
174
175std::deque<PendingOperation>::const_iterator OperationQueue::end() const { return queue_.cend(); }
176
177} // namespace home_io_control
178} // namespace esphome
std::deque< PendingOperation >::const_iterator begin() const
const PendingOperation & front() const
void enqueue_oneway_unenroll(const std::string &controller_id)
Enqueue a 1W un-enrollment (remove-controller) for a controller identity.
void enqueue_set_lock_state(const std::string &device_id, bool locked)
void enqueue_set_position_and_tilt(const std::string &device_id, uint8_t position, uint8_t tilt_percent)
Enqueue SET_POSITION_AND_TILT directly (no coalescing needed).
void enqueue_oneway_command(const std::string &controller_id, CoverCommand cmd)
Enqueue a 1W named command for a controller identity.
std::optional< PendingOperation > pop()
void enqueue_device_command(const std::string &device_id, CoverCommand cmd)
bool enqueue_request_status(const std::string &device_id)
Enqueue REQUEST_STATUS, suppressing duplicates.
bool enqueue_request_name(const std::string &device_id)
Enqueue REQUEST_NAME, suppressing duplicates.
bool enqueue_request_limitation(const std::string &device_id)
Enqueue REQUEST_LIMITATION, suppressing duplicates.
void enqueue_set_fan_position(const std::string &device_id, uint8_t position)
Enqueue SET_FAN_POSITION with an IO position (0-100; 0 = full heat, 100 = off).
void enqueue_set_switch_state(const std::string &device_id, bool on)
void enqueue_oneway_position(const std::string &controller_id, uint8_t position)
Enqueue a 1W numeric position for a controller identity.
bool enqueue_discover_and_pair()
Enqueue DISCOVER_AND_PAIR with elevated priority.
static bool is_background_op(PendingOperationType t)
True for background poll types (REQUEST_STATUS, REQUEST_NAME, REQUEST_LIMITATION) that yield to contr...
std::deque< PendingOperation >::const_iterator end() const
bool enqueue_set_position(const std::string &device_id, uint8_t position)
Enqueue SET_POSITION, or coalesce with a pending SET_TILT into SET_POSITION_AND_TILT.
const PendingOperation & back() const
void enqueue_oneway_enroll(const std::string &controller_id)
Enqueue a 1W enrollment (add-controller) for a controller identity.
void enqueue_set_light_state(const std::string &device_id, bool on)
void enqueue_set_light_position(const std::string &device_id, uint8_t position)
Enqueue SET_LIGHT_STATE with an arbitrary IO position (0-100), for dimmable lights.
const PendingOperation & operator[](std::size_t index) const
bool enqueue_set_tilt(const std::string &device_id, uint8_t tilt_percent)
Enqueue SET_TILT, or coalesce with a pending SET_POSITION into SET_POSITION_AND_TILT.
PendingOperationType
Discriminator for entries in the pending-operation deque.
@ SET_LOCK_STATE
set_lock_state call (locked/unlocked).
@ SET_TILT
set_device_tilt call (tilt percentage 0–100).
@ SET_POSITION_AND_TILT
Combined set_device_position_and_tilt call.
@ SET_LIGHT_STATE
set_light_state call (binary on/off).
@ SET_FAN_POSITION
set_fan_position call (heat level of an exterior heater as an IO position).
@ SET_SWITCH_STATE
set_switch_state call (binary on/off).
@ REQUEST_NAME
request_device_name call (poll for stored device name).
@ ONEWAY_COMMAND
1W named command sent as a controller identity.
@ DISCOVER_AND_PAIR
discover_and_pair call (starts 3-phase pairing flow).
@ REQUEST_STATUS
request_device_status call (poll for current position).
@ ONEWAY_UNENROLL
1W remove-controller (0x39) un-registering an identity.
@ DEVICE_COMMAND
Named device command (STOP, FAVORITE, VENT).
@ SET_POSITION
set_device_position call (position 0–100 or special values).
@ REQUEST_LIMITATION
request_device_limitation call (read the device's minimum limitation).
@ ONEWAY_ENROLL
1W add-controller (0x30) registering an identity.
@ ONEWAY_POSITION
1W numeric position sent as a controller identity.
CoverCommand
Named device commands for cover-type actuators.
Pending-operation queue with per-type coalescing and deduplication.
A single queued operation to be dispatched from loop().
CoverCommand command
Named command for DEVICE_COMMAND operations.
std::string device_id
Target device ID (hex string, e.g., "123ABC") — except for the ONEWAY_* types, where it carries the c...
uint8_t tilt
Tilt value for SET_POSITION_AND_TILT (0–100).
uint8_t position
Position/tilt value (0–100) or binary state (ON=0, OFF=100).
PendingOperationType type
Operation type (determines which handler to invoke).