ESPHome 2026.10.0-dev
Loading...
Searching...
No Matches
modbus_select.cpp
Go to the documentation of this file.
1#include "modbus_select.h"
2#include "esphome/core/log.h"
3
5
6static const char *const TAG = "modbus_controller.select";
7
8void ModbusSelect::dump_config() { LOG_SELECT(TAG, "Modbus Controller Select", this); }
9
10void ModbusSelect::parse_and_publish(std::span<const uint8_t> data) {
11 int64_t value =
13
14 ESP_LOGD(TAG, "New select value %lld from payload", value);
15
16 optional<std::string> new_state;
17
18 if (this->transform_func_.has_value()) {
19 auto val = (*this->transform_func_)(this, value, data);
20 if (val.has_value()) {
21 new_state = *val;
22 ESP_LOGV(TAG, "lambda returned option %s", new_state->c_str());
23 }
24 }
25
26 if (!new_state.has_value()) {
27 auto map_it = std::find(this->mapping_.cbegin(), this->mapping_.cend(), value);
28
29 if (map_it != this->mapping_.cend()) {
30 size_t idx = std::distance(this->mapping_.cbegin(), map_it);
31 ESP_LOGV(TAG, "Found option %s for value %lld", this->option_at(idx), value);
32 this->publish_state(idx);
33 return;
34 } else {
35 ESP_LOGE(TAG, "No option found for mapping %lld", value);
36 }
37 }
38
39 if (new_state.has_value()) {
40 this->publish_state(new_state.value());
41 }
42}
43
44void ModbusSelect::control(size_t index) {
45 optional<int64_t> mapval = this->mapping_[index];
46 const char *option = this->option_at(index);
47 ESP_LOGD(TAG, "Found value %lld for option '%s'", *mapval, option);
48
49 this->clear_dispatched_();
50 // A new write supersedes this entity's own not-yet-sent writes: drop them (and detach any in-flight one)
51 // so a rapidly-changing value writes the latest, not every intermediate.
54
55 if (this->write_transform_func_.has_value()) {
56 // The lambda may drive the write itself via item->write_*(), override the mapping value (return a value),
57 // or (deprecated) fill `data` with the register words to write. Transform func requires string parameter
58 // for backward compatibility.
59 auto val = (*this->write_transform_func_)(this, std::string(option), *mapval, data);
60 if (this->dispatched()) {
61 if (this->optimistic_)
62 this->publish_state(index);
63 return;
64 }
65 if (!data.empty()) {
66 // Deprecated buffer path (frozen): the lambda supplied the register words for the shared write below.
67 this->warn_write_buffer_deprecated_(LOG_STR("select"), this->start_address);
68 } else if (!val.has_value()) {
69 ESP_LOGD(TAG, "Communication handled by write_lambda - exiting control");
70 return;
71 } else {
72 mapval = val;
73 ESP_LOGV(TAG, "write_lambda returned mapping value %lld", *mapval);
74 }
75 }
76
77 if (data.empty()) {
79 // number_to_payload() appends nothing for RAW.
80 if (data.empty()) {
81 ESP_LOGW(TAG, "No payload was created for updating select");
82 return;
83 }
84 }
85
86 // A write covers exactly the registers the value occupies: the quantity comes from the payload. A
87 // payload wider than the value type's register width means the config and the lambda disagree - drop it.
88 if (data.size() > this->entity_count()) {
89 ESP_LOGE(TAG, "Payload has %zu registers but the value type only spans %u; dropping write", data.size(),
90 this->entity_count());
91 return;
92 }
93
94 const uint16_t write_address = this->write_address();
95 bool queued;
96 if ((this->entity_count() == 1) && (!this->use_write_multiple_)) {
97 queued = this->write_single_register(write_address, data[0]);
98 } else {
99 queued = this->write_multiple_registers(write_address, data);
100 }
101
102 if (!queued) {
103 ESP_LOGW(TAG, "Modbus write for '%s' was refused by the hub; state not published", this->get_name().c_str());
104 return;
105 }
106 if (this->optimistic_)
107 this->publish_state(index);
108}
109
110} // 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
bool empty() const
Definition helpers.h:294
optional< write_transform_func_t > write_transform_func_
void control(size_t index) override
void parse_and_publish(std::span< const uint8_t > data) override
optional< transform_func_t > transform_func_
virtual uint16_t entity_count() const
Entities this item spans: one bit for bit-addressed types, ceil(bytes / 2) registers for RAW with a r...
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_register(uint16_t address, uint16_t value)
bool write_multiple_registers(uint16_t address, std::span< const uint16_t > values)
const char * option_at(size_t index) const
Return the option value at the provided index offset (as const char* from flash).
Definition select.cpp:60
void publish_state(const std::string &state)
Definition select.h:36
mopeka_std_values val[3]
void number_to_payload(Container &data, int64_t value, SensorValueType value_type)
Append the Modbus register words for value to data.
std::optional< int64_t > payload_to_number(const uint8_t *data, size_t size, SensorValueType sensor_value_type, uint8_t offset, uint32_t bitmask)
Convert a raw response payload to a number.
const std::vector< uint8_t > & data