ESPHome 2026.11.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
10ESPHOME_LOG_TAG(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
15void ModbusSwitch::setup() { this->restore(); }
16void ModbusSwitch::dump_config() { LOG_SWITCH(TAG, "Modbus Controller Switch", this); }
17
19
20void ModbusSwitch::parse_and_publish(std::span<const uint8_t> data) {
21 bool value = false;
22 // For coils/discrete inputs this is the bit index; for registers it is the byte offset.
23 const size_t offset = this->offset;
24 switch (this->register_type) {
28 break;
29 default:
31 break;
32 }
33
34 // Is there a lambda registered
35 // call it with the pre converted value and the raw data array
36 if (this->publish_transform_func_) {
37 // the lambda can parse the response itself
38 auto val = (*this->publish_transform_func_)(this, value, data);
39 if (val.has_value()) {
40 ESP_LOGV(TAG, "Value overwritten by lambda");
41 value = val.value();
42 }
43 }
44
45 ESP_LOGV(TAG, "Publish '%s': new value = %s type = %d address = %X offset = %zx", LOG_STR_ARG(this->get_log_name()),
46 ONOFF(value), (int) this->register_type, this->start_address, offset);
47 this->publish_state(value);
48}
49
51 this->clear_dispatched_();
52 // A new write supersedes this entity's own not-yet-sent writes: drop them (and detach any in-flight one)
53 // so a rapidly-changing value writes the latest, not every intermediate.
56 bool write_value = state;
57 if (this->write_transform_func_.has_value()) {
58 // The lambda may drive the write itself via item->write_*/queue_pdu(), override the written value (return a
59 // value), or (deprecated) fill `data` with a custom PDU.
60 auto val = (*this->write_transform_func_)(this, state, data);
61 if (this->dispatched()) {
62 this->publish_state(state);
63 return;
64 }
65 if (!data.empty()) {
66 this->warn_write_buffer_deprecated_(LOG_STR("switch"), this->start_address);
67 ESP_LOGV_HEX(TAG, data.data(), data.size(), MODBUS_SWITCH_MAX_LOG_BYTES, "Modbus Switch write raw: %s");
68 // The lambda filled a legacy raw frame (device address + function code + data).
69 if (!this->send_raw_frame_deprecated_(data)) {
70 ESP_LOGW(TAG, "Modbus write for '%s' was refused by the hub; state not published",
71 LOG_STR_ARG(this->get_log_name()));
72 return;
73 }
74 this->publish_state(state);
75 return;
76 }
77 if (!val.has_value()) {
78 ESP_LOGV(TAG, "Communication handled by lambda - exiting control");
79 return;
80 }
81 // The returned bool is the wire value only; the entity still reports the requested state. A polled
82 // entity needs the read lambda inverted to match, or the next poll flips the display back.
83 ESP_LOGV(TAG, "Value overwritten by lambda");
84 write_value = val.value();
85 }
86 ESP_LOGV(TAG, "write_state '%s': new value = %s (wire = %s) type = %d address = %X offset = %x",
87 LOG_STR_ARG(this->get_log_name()), ONOFF(state), ONOFF(write_value), (int) this->register_type,
88 this->start_address, this->offset);
89 bool queued;
90 if (this->register_type == EntityType::COIL) {
91 // offset for coil and discrete inputs is the coil/register number not bytes
92 if (this->use_write_multiple_) {
93 std::array<bool, 1> states{write_value};
94 queued = this->write_multiple_coils(this->write_address(), states);
95 } else {
96 queued = this->write_single_coil(this->write_address(), write_value);
97 }
98 } else {
99 if (this->use_write_multiple_) {
100 std::array<uint16_t, 1> states{static_cast<uint16_t>(write_value ? (0xFFFF & this->bitmask) : 0)};
101 queued = this->write_multiple_registers(this->write_address(), states);
102 } else {
103 queued = this->write_single_register(this->write_address(), write_value ? 0xFFFF & this->bitmask : 0u);
104 }
105 }
106 if (!queued) {
107 ESP_LOGW(TAG, "Modbus write for '%s' was refused by the hub; state not published",
108 LOG_STR_ARG(this->get_log_name()));
109 return;
110 }
111 this->publish_state(state);
112}
113// ModbusSwitch end
114} // namespace esphome::modbus_controller
const LogString * get_log_name() const
Get the name for a "%s" log argument (LOG_STR_ARG).
Minimal static vector - saves memory by avoiding std::vector overhead.
Definition helpers.h:286
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:60
void restore()
Drive the switch to the state its restore mode yields, through control(), so inversion and write_stat...
Definition switch.cpp:63
void publish_state(bool state)
Publish a state to the front-end from the back-end.
Definition switch.cpp:70
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
ESPHOME_LOG_TAG(TAG, "modbus_controller.binary_sensor")