ESPHome 2026.10.0-dev
Loading...
Searching...
No Matches
modbus_switch.cpp
Go to the documentation of this file.
1
2#include "modbus_switch.h"
4#include "esphome/core/log.h"
5
6#include <array>
7
9
10static const char *const TAG = "modbus_controller.switch";
11
12// Maximum bytes to log in verbose hex output
13static constexpr size_t MODBUS_SWITCH_MAX_LOG_BYTES = 64;
14
16 optional<bool> initial_state = Switch::get_initial_state_with_restore_mode();
17 if (initial_state.has_value()) {
18 // if it has a value, restore_mode is not "DISABLED", therefore act on the switch:
19 if (initial_state.value()) {
20 this->turn_on();
21 } else {
22 this->turn_off();
23 }
24 }
25}
26void ModbusSwitch::dump_config() { LOG_SWITCH(TAG, "Modbus Controller Switch", this); }
27
29
31
32void ModbusSwitch::parse_and_publish(std::span<const uint8_t> data) {
33 bool value = false;
34 // For coils/discrete inputs this is the bit index; for registers it is the byte offset.
35 const size_t offset = this->offset;
36 switch (this->register_type) {
40 break;
41 default:
43 break;
44 }
45
46 // Is there a lambda registered
47 // call it with the pre converted value and the raw data array
48 if (this->publish_transform_func_) {
49 // the lambda can parse the response itself
50 auto val = (*this->publish_transform_func_)(this, value, data);
51 if (val.has_value()) {
52 ESP_LOGV(TAG, "Value overwritten by lambda");
53 value = val.value();
54 }
55 }
56
57 ESP_LOGV(TAG, "Publish '%s': new value = %s type = %d address = %X offset = %zx", this->get_name().c_str(),
58 ONOFF(value), (int) this->register_type, this->start_address, offset);
59 this->publish_state(value);
60}
61
63 this->clear_dispatched_();
64 // A new write supersedes this entity's own not-yet-sent writes: drop them (and detach any in-flight one)
65 // so a rapidly-changing value writes the latest, not every intermediate.
68 bool write_value = state;
69 if (this->write_transform_func_.has_value()) {
70 // The lambda may drive the write itself via item->write_*/queue_pdu(), override the written value (return a
71 // value), or (deprecated) fill `data` with a custom PDU.
72 auto val = (*this->write_transform_func_)(this, state, data);
73 if (this->dispatched()) {
74 this->publish_state(state);
75 return;
76 }
77 if (!data.empty()) {
78 this->warn_write_buffer_deprecated_(LOG_STR("switch"), this->start_address);
79#if ESPHOME_LOG_LEVEL >= ESPHOME_LOG_LEVEL_VERBOSE
80 char hex_buf[format_hex_pretty_size(MODBUS_SWITCH_MAX_LOG_BYTES)];
81#endif
82 ESP_LOGV(TAG, "Modbus Switch write raw: %s",
83 format_hex_pretty_to(hex_buf, sizeof(hex_buf), data.data(), data.size()));
84 // The lambda filled a legacy raw frame (device address + function code + data).
85 if (!this->send_raw_frame_deprecated_(data)) {
86 ESP_LOGW(TAG, "Modbus write for '%s' was refused by the hub; state not published", this->get_name().c_str());
87 return;
88 }
89 this->publish_state(state);
90 return;
91 }
92 if (!val.has_value()) {
93 ESP_LOGV(TAG, "Communication handled by lambda - exiting control");
94 return;
95 }
96 // The returned bool is the wire value only; the entity still reports the requested state. A polled
97 // entity needs the read lambda inverted to match, or the next poll flips the display back.
98 ESP_LOGV(TAG, "Value overwritten by lambda");
99 write_value = val.value();
100 }
101 ESP_LOGV(TAG, "write_state '%s': new value = %s (wire = %s) type = %d address = %X offset = %x",
102 this->get_name().c_str(), ONOFF(state), ONOFF(write_value), (int) this->register_type, this->start_address,
103 this->offset);
104 bool queued;
105 if (this->register_type == EntityType::COIL) {
106 // offset for coil and discrete inputs is the coil/register number not bytes
107 if (this->use_write_multiple_) {
108 std::array<bool, 1> states{write_value};
109 queued = this->write_multiple_coils(this->write_address(), states);
110 } else {
111 queued = this->write_single_coil(this->write_address(), write_value);
112 }
113 } else {
114 if (this->use_write_multiple_) {
115 std::array<uint16_t, 1> states{static_cast<uint16_t>(write_value ? (0xFFFF & this->bitmask) : 0)};
116 queued = this->write_multiple_registers(this->write_address(), states);
117 } else {
118 queued = this->write_single_register(this->write_address(), write_value ? 0xFFFF & this->bitmask : 0u);
119 }
120 }
121 if (!queued) {
122 ESP_LOGW(TAG, "Modbus write for '%s' was refused by the hub; state not published", this->get_name().c_str());
123 return;
124 }
125 this->publish_state(state);
126}
127// ModbusSwitch end
128} // namespace esphome::modbus_controller
const StringRef & get_name() const
Definition entity_base.h:71
Minimal static vector - saves memory by avoiding std::vector overhead.
Definition helpers.h:227
void parse_and_publish(std::span< const uint8_t > data) override
optional< write_transform_func_t > write_transform_func_
void set_assumed_state(bool assumed_state)
optional< transform_func_t > publish_transform_func_
uint8_t offset
Position of this sensor's data within its range's response - a byte offset for registers,...
uint16_t write_address() const
Address a write entity (switch/number/select) targets, derived from its resolved position within the ...
void warn_write_buffer_deprecated_(const LogString *platform, uint16_t address)
bool dispatched() const
Whether the lambda called a request helper since the last clear_dispatched_().
bool write_single_coil(uint16_t address, bool value)
bool write_single_register(uint16_t address, uint16_t value)
bool send_raw_frame_deprecated_(std::span< const uint8_t > frame)
bool write_multiple_coils(uint16_t address, std::span< const bool > values)
bool write_multiple_registers(uint16_t address, std::span< const uint16_t > values)
void turn_on()
Turn this switch on.
Definition switch.cpp:20
void turn_off()
Turn this switch off.
Definition switch.cpp:24
bool state
The current reported state of the binary sensor.
Definition switch.h:55
void publish_state(bool state)
Publish a state to the front-end from the back-end.
Definition switch.cpp:57
bool state
Definition fan.h:2
mopeka_std_values val[3]
T get_data(const uint8_t *data, size_t buffer_offset)
Extract data from modbus response buffer.
bool bit_from_packed(int bit, std::span< const uint8_t > data)
Extract coil data from modbus response buffer Responses for coil are packed into bytes .
const std::vector< uint8_t > & data
char * format_hex_pretty_to(char *buffer, size_t buffer_size, const uint8_t *data, size_t length, char separator)
Format byte array as uppercase hex to buffer (base implementation).
Definition helpers.cpp:425
constexpr size_t format_hex_pretty_size(size_t byte_count)
Calculate buffer size needed for format_hex_pretty_to with separator: "XX:XX:...:XX\0".
Definition helpers.h:1438