ESPHome 2026.10.2
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 this->control(initial_state.value());
20 }
21}
22void ModbusSwitch::dump_config() { LOG_SWITCH(TAG, "Modbus Controller Switch", this); }
23
25
26void ModbusSwitch::parse_and_publish(std::span<const uint8_t> data) {
27 bool value = false;
28 // For coils/discrete inputs this is the bit index; for registers it is the byte offset.
29 const size_t offset = this->offset;
30 switch (this->register_type) {
34 break;
35 default:
37 break;
38 }
39
40 // Is there a lambda registered
41 // call it with the pre converted value and the raw data array
42 if (this->publish_transform_func_) {
43 // the lambda can parse the response itself
44 auto val = (*this->publish_transform_func_)(this, value, data);
45 if (val.has_value()) {
46 ESP_LOGV(TAG, "Value overwritten by lambda");
47 value = val.value();
48 }
49 }
50
51 ESP_LOGV(TAG, "Publish '%s': new value = %s type = %d address = %X offset = %zx", this->get_name().c_str(),
52 ONOFF(value), (int) this->register_type, this->start_address, offset);
53 this->publish_state(value);
54}
55
57 this->clear_dispatched_();
58 // A new write supersedes this entity's own not-yet-sent writes: drop them (and detach any in-flight one)
59 // so a rapidly-changing value writes the latest, not every intermediate.
62 bool write_value = state;
63 if (this->write_transform_func_.has_value()) {
64 // The lambda may drive the write itself via item->write_*/queue_pdu(), override the written value (return a
65 // value), or (deprecated) fill `data` with a custom PDU.
66 auto val = (*this->write_transform_func_)(this, state, data);
67 if (this->dispatched()) {
68 this->publish_state(state);
69 return;
70 }
71 if (!data.empty()) {
72 this->warn_write_buffer_deprecated_(LOG_STR("switch"), this->start_address);
73#if ESPHOME_LOG_LEVEL >= ESPHOME_LOG_LEVEL_VERBOSE
74 char hex_buf[format_hex_pretty_size(MODBUS_SWITCH_MAX_LOG_BYTES)];
75#endif
76 ESP_LOGV(TAG, "Modbus Switch write raw: %s",
77 format_hex_pretty_to(hex_buf, sizeof(hex_buf), data.data(), data.size()));
78 // The lambda filled a legacy raw frame (device address + function code + data).
79 if (!this->send_raw_frame_deprecated_(data)) {
80 ESP_LOGW(TAG, "Modbus write for '%s' was refused by the hub; state not published", this->get_name().c_str());
81 return;
82 }
83 this->publish_state(state);
84 return;
85 }
86 if (!val.has_value()) {
87 ESP_LOGV(TAG, "Communication handled by lambda - exiting control");
88 return;
89 }
90 // The returned bool is the wire value only; the entity still reports the requested state. A polled
91 // entity needs the read lambda inverted to match, or the next poll flips the display back.
92 ESP_LOGV(TAG, "Value overwritten by lambda");
93 write_value = val.value();
94 }
95 ESP_LOGV(TAG, "write_state '%s': new value = %s (wire = %s) type = %d address = %X offset = %x",
96 this->get_name().c_str(), ONOFF(state), ONOFF(write_value), (int) this->register_type, this->start_address,
97 this->offset);
98 bool queued;
99 if (this->register_type == EntityType::COIL) {
100 // offset for coil and discrete inputs is the coil/register number not bytes
101 if (this->use_write_multiple_) {
102 std::array<bool, 1> states{write_value};
103 queued = this->write_multiple_coils(this->write_address(), states);
104 } else {
105 queued = this->write_single_coil(this->write_address(), write_value);
106 }
107 } else {
108 if (this->use_write_multiple_) {
109 std::array<uint16_t, 1> states{static_cast<uint16_t>(write_value ? (0xFFFF & this->bitmask) : 0)};
110 queued = this->write_multiple_registers(this->write_address(), states);
111 } else {
112 queued = this->write_single_register(this->write_address(), write_value ? 0xFFFF & this->bitmask : 0u);
113 }
114 }
115 if (!queued) {
116 ESP_LOGW(TAG, "Modbus write for '%s' was refused by the hub; state not published", this->get_name().c_str());
117 return;
118 }
119 this->publish_state(state);
120}
121// ModbusSwitch end
122} // 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:291
void parse_and_publish(std::span< const uint8_t > data) override
optional< write_transform_func_t > write_transform_func_
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)
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:56
void control(bool target_state)
Control this switch using a boolean state value.
Definition switch.cpp:12
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:433
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:1532