Home IO Control
ESPHome add-on for IO-Homecontrol devices
Loading...
Searching...
No Matches
platform_cover_controls.cpp
Go to the documentation of this file.
1/// @file platform_cover_controls.cpp
2/// @brief Generated per-cover auxiliary entities: command button, silent-operation toggle, travel speed select,
3/// rain and battery sensors.
4/// @ingroup hioc_platforms
5
7
8#include "esphome/core/log.h"
9
10#include <cmath>
11
12namespace esphome {
13namespace home_io_control {
14
15// One tag for the merged command button. The former per-button tags (home_io_control.favorite_button
16// / home_io_control.vent_button) both retire with the class merge; a user greps for the entity by
17// name, not by log tag, and this reads correctly for either command.
18static const char *const TAG_COMMAND_BUTTON = "home_io_control.cover_command_button";
19static const char *const TAG_SILENT = "home_io_control.cover.silent";
20static const char *const TAG_TRAVEL_SPEED = "home_io_control.cover.travel_speed";
21static const char *const TAG_RAIN = "home_io_control.cover.rain_sensor";
22static const char *const TAG_BATTERY = "home_io_control.cover.battery";
23static const char *const TAG_SUN_LEVEL = "home_io_control.cover.sun_level";
24static const char *const TAG_BATTERY_LEVEL = "home_io_control.cover.battery_level";
25
27 if (this->parent_ == nullptr) {
28 ESP_LOGW(TAG_COMMAND_BUTTON, "Ignoring %s press because the parent hub is not configured",
30 return;
31 }
32
33 if (this->parent_->get_device(this->device_id_) == nullptr) {
34 ESP_LOGW(TAG_COMMAND_BUTTON, "Ignoring %s press for %s because the device is not registered yet",
35 cover_command_name(this->command_), this->device_id_.c_str());
36 return;
37 }
38
40}
41
43 ESP_LOGCONFIG(TAG_COMMAND_BUTTON, "IO-Homecontrol %s Position Button", cover_command_name(this->command_));
44 ESP_LOGCONFIG(TAG_COMMAND_BUTTON, " Device ID: %s", this->device_id_.c_str());
45}
46
48 // The YAML value was already applied to the device at registration; mirror it outward so Home
49 // Assistant does not start out disagreeing with what the hub will actually send.
50 if (this->parent_ == nullptr)
51 return;
52 if (const IoDevice *dev = this->parent_->get_device(this->device_id_); dev != nullptr)
53 this->publish_state(dev->travel_speed == TravelSpeed::SILENT);
54}
55
57 if (this->parent_ == nullptr)
58 return;
60 this->publish_state(state);
61 ESP_LOGI(TAG_SILENT, "Device %s: silent operation %s", this->device_id_.c_str(), state ? "on" : "off");
62}
63
65 ESP_LOGCONFIG(TAG_SILENT, "IO-Homecontrol Silent Operation:");
66 ESP_LOGCONFIG(TAG_SILENT, " Device ID: %s", this->device_id_.c_str());
67}
68
70 // The YAML value was already applied to the device at registration; mirror it outward so Home
71 // Assistant does not start out disagreeing with what the hub will actually send.
72 if (this->parent_ == nullptr)
73 return;
74 if (const IoDevice *dev = this->parent_->get_device(this->device_id_); dev != nullptr)
75 this->publish_state(travel_speed_name(dev->travel_speed));
76}
77
78void IOHomeCoverTravelSpeedSelect::control(const std::string &value) {
79 if (this->parent_ == nullptr)
80 return;
82 if (!parse_travel_speed(value, speed)) {
83 ESP_LOGW(TAG_TRAVEL_SPEED, "Device %s: ignoring unknown travel speed '%s'", this->device_id_.c_str(),
84 value.c_str());
85 return;
86 }
87 this->parent_->set_device_travel_speed(this->device_id_, speed);
88 this->publish_state(value);
89 ESP_LOGI(TAG_TRAVEL_SPEED, "Device %s: travel speed %s", this->device_id_.c_str(), travel_speed_name(speed));
90}
91
93 ESP_LOGCONFIG(TAG_TRAVEL_SPEED, "IO-Homecontrol Travel Speed:");
94 ESP_LOGCONFIG(TAG_TRAVEL_SPEED, " Device ID: %s", this->device_id_.c_str());
95}
96
100
102 switch (state) {
104 this->publish_state(true);
105 break;
107 this->publish_state(false);
108 break;
110 // Nothing was read, or the last reads went unanswered; "dry" must mean a reply said so.
111 this->invalidate_state();
112 break;
113 }
114}
115
117 ESP_LOGCONFIG(TAG_RAIN, "IO-Homecontrol Rain Sensor:");
118 ESP_LOGCONFIG(TAG_RAIN, " Device ID: %s", this->device_id_.c_str());
119}
120
122 this->register_companion_binding_([this](const IoDevice &dev) {
123 // Every device update reaches here (link health, status, ...); publish only a change.
124 if (this->published_once_ && dev.sun_level == this->published_)
125 return;
126 this->published_once_ = true;
127 this->published_ = dev.sun_level;
128 // NAN is how an ESPHome sensor says "unknown": nothing read yet, or the last polls went unanswered.
129 this->publish_state(dev.sun_level.has_value() ? static_cast<float>(*dev.sun_level) : NAN);
130 });
131}
132
134 const char *const TAG = TAG_SUN_LEVEL; // LOG_SENSOR() logs under a local named TAG
135 LOG_SENSOR("", "IO-Homecontrol Sun Level", this);
136 ESP_LOGCONFIG(TAG, " Device ID: %s", this->device_id_.c_str());
137}
138
140 this->register_companion_binding_([this](const IoDevice &dev) {
141 if (this->published_once_ && dev.battery_level == this->published_)
142 return;
143 this->published_once_ = true;
144 this->published_ = dev.battery_level;
145 this->publish_state(dev.battery_level.has_value() ? static_cast<float>(*dev.battery_level) : NAN);
146 });
147}
148
150 const char *const TAG = TAG_BATTERY_LEVEL; // LOG_SENSOR() logs under a local named TAG
151 LOG_SENSOR("", "IO-Homecontrol Battery Level", this);
152 ESP_LOGCONFIG(TAG, " Device ID: %s", this->device_id_.c_str());
153}
154
156 this->register_companion_binding_([this](const IoDevice &dev) { this->publish_battery_(dev); });
157}
158
160 switch (dev.battery) {
162 this->publish_state(true);
163 return;
166 this->publish_state(false);
167 return;
169 break;
170 }
171 // "off" must mean the device said its battery is fine, so no level means no state.
172 this->invalidate_state();
173 // last_status != 0: a status was decoded and carried no battery, rather than none read yet.
174 if (dev.last_status != 0 && !this->logged_not_reported_) {
175 this->logged_not_reported_ = true;
176 ESP_LOGI(TAG_BATTERY, "Device %s reports no battery state; it is probably mains powered", this->device_id_.c_str());
177 }
178}
179
181 ESP_LOGCONFIG(TAG_BATTERY, "IO-Homecontrol Battery Low:");
182 ESP_LOGCONFIG(TAG_BATTERY, " Device ID: %s", this->device_id_.c_str());
183}
184
186 this->register_companion_binding_([this](const IoDevice &dev) {
188 this->publish_state(battery_state_name(dev.battery));
189 });
190}
191
193 ESP_LOGCONFIG(TAG_BATTERY, "IO-Homecontrol Battery:");
194 ESP_LOGCONFIG(TAG_BATTERY, " Device ID: %s", this->device_id_.c_str());
195}
196
197} // namespace home_io_control
198} // namespace esphome
void register_companion_binding_(const std::function< void(const IoDevice &)> &publish)
Shared setup() body: subscribe to this device's updates and publish the initial state.
void setup() override
Subscribe to the device's updates and publish what is already known.
std::optional< uint16_t > published_
Last value handed to publish_state(); avoids re-publishing on every update.
bool published_once_
True once anything (a value or "unknown") was published.
void dump_config() override
Dump sensor configuration to the log.
void setup() override
Subscribe to the device's updates and publish what is already known.
void dump_config() override
Dump sensor configuration to the log.
void publish_battery_(const IoDevice &dev)
Publish from a device record: ON for LOW_CHARGE, OFF for NORMAL/FULL, no state otherwise.
bool logged_not_reported_
The "reports no battery" INFO line was written once already.
void setup() override
Subscribe to the device's updates and publish what is already known.
void dump_config() override
Dump sensor configuration to the log.
virtual IoDevice * get_device(const std::string &device_id)
Retrieve a device by ID; returns nullptr if not found.
Definition hub_core.cpp:402
virtual void set_device_travel_speed(const std::string &device_id, TravelSpeed speed)
Select a device's travel speed at runtime (see IOHomeCoverTravelSpeedSelect and IOHomeCoverSilentSwit...
Definition hub_core.cpp:404
virtual bool queue_device_command(const std::string &device_id, CoverCommand cmd)
Queue an async named command (STOP, FAVORITE, VENT, PARTIAL, PEDESTRIAN, FORCE_OPEN); returns immedia...
void press_action() override
Handle a Home Assistant button press by queueing this button's cover command, which is then sent to t...
void dump_config() override
Dump button configuration to the log.
void write_state(bool state) override
Apply the requested travel profile to the bound device.
void setup() override
Publish the boot state declared in YAML so Home Assistant starts in sync.
void dump_config() override
Dump switch configuration to the log.
void dump_config() override
Dump select configuration to the log.
void control(const std::string &value) override
Apply the selected travel speed to the bound device.
void setup() override
Publish the boot state declared in YAML so Home Assistant starts in sync.
void dump_config() override
Dump sensor configuration to the log.
void publish_rain_state_(RainSensorState state)
Publish a rain state: true for RAIN, false for DRY, no state for UNKNOWN.
void setup() override
Subscribe to the device's updates and publish what is already known.
void dump_config() override
Dump sensor configuration to the log.
std::optional< uint16_t > published_
Last value handed to publish_state(); avoids re-publishing on every update.
void setup() override
Subscribe to the device's updates and publish what is already known.
bool published_once_
True once anything (a value or "unknown") was published.
static const char *const TAG_BATTERY_LEVEL
static const char *const TAG_COMMAND_BUTTON
const char * travel_speed_name(TravelSpeed speed)
Get the name a travel speed is configured and published under.
RainSensorState
What the hub's latest minimum-limitation read says about a rain-protection limit.
@ UNKNOWN
No usable reading: never read, or the last reads went unanswered.
@ DRY
The device answered and reports no rain-sized opening limit.
@ RAIN
The device answered and reports an opening limit of rain size.
static const char *const TAG_SUN_LEVEL
static const char *const TAG_SILENT
bool parse_travel_speed(const std::string &name, TravelSpeed &speed)
Look up a travel speed by the name travel_speed_name() gives it.
@ NORMAL_CHARGE
Battery neither low nor full.
@ NOT_REPORTED
The device does not report a battery (mains powered), or no status was read yet.
const char * cover_command_name(CoverCommand cmd)
Get a human-readable name for a CoverCommand.
static const char *const TAG_TRAVEL_SPEED
static const char *const TAG_BATTERY
static const char *const TAG_RAIN
const char * battery_state_name(BatteryState state)
Get the name a battery level is published under.
TravelSpeed
The speed a cover's position moves ask the motor for (ADR 0065).
@ SILENT
Profile 0x05, the manufacturer app's silent operation: slower than the motor's own.
@ FAST
Profile 0x06, what the hub sends unless a cover says otherwise.
Generated per-cover auxiliary entities: the command button (favorite / ventilation),...
Runtime state of a paired IO‑Homecontrol device.
uint32_t last_status
millis() timestamp of last received status.
std::optional< uint16_t > sun_level
Raw sun value from the opt-in EVENT_LOCAL_SENSOR poll.
std::optional< uint16_t > battery_level
Battery level in percent (0-100) from the opt-in EVENT_BATTERY_LEVEL_CHANGE poll (decode_battery_leve...
RainSensorState rain_sensor
Rain state from the opt-in minimum-limitation poll.
BatteryState battery
Coarse battery level from byte 1 of the device's own status: a reply to a status poll or an authentic...