Skip to content

Commit d6db849

Browse files
committed
fix: modbus for esphome 2026.7.x
1 parent 8e31fc6 commit d6db849

7 files changed

Lines changed: 28 additions & 53 deletions

File tree

‎.gitignore‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
/.esphome/
2+
/.venv/
23
/secrets.yaml
34
/tests/secrets.yaml
45

‎README.md‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,7 @@ Each microinverter (**MI**) only needs radio reach to **OpenDTU**. The bridge su
8080
- [OpenDTU](https://github.com/tbnobody/OpenDTU) running and reachable on your network (WebSocket `/livedata`, dashboard password), with **single-phase Hoymiles** microinverters
8181
- A **second ESP32** for this bridge (separate from the OpenDTU ESP32 in the tested setup)
8282
- RS485-to-TTL converter (no DE/RE pin required in the tested setup)
83-
- ESPHome **≥ 2025.6.0**
83+
- ESPHome **≥ 2026.7.0**
8484
- **Modbus master** (e.g. Deye hybrid inverter with Grid Tie Meter 2 / Eastron type) reading **Eastron SDM630** over Modbus RTU (**9600 8N1**)
8585
- **Home Assistant is not required** - ESPHome alone is enough to build, flash, and run this component
8686

‎components/opendtu_sdm630/__init__.py‎

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44

55
import esphome.codegen as cg
66
import esphome.config_validation as cv
7-
from esphome.components import binary_sensor, button, esp32, modbus, sensor, text_sensor
7+
from esphome.components import binary_sensor, button, esp32, modbus, sensor, text_sensor, wifi
88
from esphome.components.modbus import CONF_MODBUS_ID
99
from esphome.const import (
1010
CONF_ID,
@@ -192,7 +192,7 @@ def _ensure_default_entities(config):
192192
cv.Optional(CONF_PATH, default="/livedata"): cv.string,
193193
cv.Optional(CONF_USERNAME, default="admin"): cv.string,
194194
cv.Required(CONF_PASSWORD): cv.string,
195-
cv.Required(CONF_MODBUS_ID): cv.use_id(modbus.Modbus),
195+
cv.Required(CONF_MODBUS_ID): cv.use_id(modbus.ModbusServer),
196196
cv.Optional(CONF_SLAVE_ADDRESS, default=0x02): cv.hex_uint8_t,
197197
cv.Optional(CONF_DATA_TIMEOUT, default="15s"): cv.positive_time_period_milliseconds,
198198
cv.Optional(CONF_DEFAULT_VOLTAGE, default=230.0): cv.float_,
@@ -281,4 +281,5 @@ async def to_code(config):
281281
version_ts = await text_sensor.new_text_sensor(config[CONF_COMPONENT_VERSION])
282282
cg.add(var.set_component_version_text_sensor(version_ts))
283283

284+
wifi.request_wifi_connect_state_listener()
284285
_register_idf_components()

‎components/opendtu_sdm630/opendtu_sdm630.cpp‎

Lines changed: 11 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,6 @@ static constexpr uint16_t REG_POWER_L2 = 0x000E;
2828
static constexpr uint16_t REG_POWER_L3 = 0x0010;
2929
static constexpr uint16_t REG_POWER_TOTAL = 0x0034;
3030
static constexpr uint16_t REG_FREQUENCY = 0x0046;
31-
static constexpr uint8_t SDM630_FACTORY_SLAVE_ADDRESS = 0x01;
3231

3332
static constexpr float VOLTAGE_MIN_V = 100.0f;
3433
static constexpr float VOLTAGE_MAX_V = 300.0f;
@@ -528,37 +527,24 @@ float OpenDtuSdm630::get_frequency() {
528527

529528
bool OpenDtuSdm630::is_data_valid() { return !this->data_stale_ && this->ws_connected_; }
530529

531-
void OpenDtuSdm630ModbusServer::on_modbus_read_registers(uint8_t function_code, uint16_t start_address,
532-
uint16_t number_of_registers) {
533-
if (function_code != 0x03 && function_code != 0x04) {
534-
return;
535-
}
530+
modbus::ResponseStatus OpenDtuSdm630ModbusServer::on_read_registers(uint16_t start_address,
531+
uint16_t number_of_registers,
532+
modbus::RegisterValues &registers) {
536533
if (this->bridge_ == nullptr) {
537-
return;
534+
return modbus::ModbusExceptionCode::SERVICE_DEVICE_FAILURE;
538535
}
539536

540-
std::vector<uint8_t> response;
541-
response.reserve(number_of_registers * 2);
542-
543537
for (uint16_t offset = 0; offset < number_of_registers; offset++) {
544538
uint32_t address = (uint32_t) start_address + offset;
545539
if (address >= OpenDtuSdm630::MODBUS_REG_COUNT) {
546-
std::vector<uint8_t> error_response;
547-
error_response.push_back(this->address_);
548-
error_response.push_back(function_code | 0x80);
549-
error_response.push_back(0x02);
550-
this->send_raw(error_response);
551-
return;
540+
return modbus::ModbusExceptionCode::ILLEGAL_DATA_ADDRESS;
552541
}
553-
uint16_t value = this->bridge_->get_modbus_register((uint16_t) address);
554-
response.push_back((uint8_t) (value >> 8));
555-
response.push_back((uint8_t) (value & 0xFFu));
542+
registers.push_back(this->bridge_->get_modbus_register((uint16_t) address));
556543
}
557-
558-
this->send(function_code, start_address, number_of_registers, response.size(), response.data());
544+
return {};
559545
}
560546

561-
void OpenDtuSdm630::set_modbus_server(modbus::Modbus *parent, uint8_t slave_address) {
547+
void OpenDtuSdm630::set_modbus_server(modbus::ModbusServerHub *parent, uint8_t slave_address) {
562548
this->modbus_parent_ = parent;
563549
this->modbus_slave_address_ = slave_address;
564550
}
@@ -568,17 +554,10 @@ void OpenDtuSdm630::setup() {
568554
this->sync_modbus_registers_();
569555
if (this->modbus_parent_ != nullptr && this->modbus_slave_address_ != 0) {
570556
this->modbus_server_device_.set_bridge(this);
571-
this->modbus_server_device_.set_parent(this->modbus_parent_);
572557
this->modbus_server_device_.set_address(this->modbus_slave_address_);
573558
this->modbus_parent_->register_device(&this->modbus_server_device_);
574-
575-
if (this->modbus_slave_address_ != SDM630_FACTORY_SLAVE_ADDRESS) {
576-
this->modbus_silence_device_.set_parent(this->modbus_parent_);
577-
this->modbus_silence_device_.set_address(SDM630_FACTORY_SLAVE_ADDRESS);
578-
this->modbus_parent_->register_device(&this->modbus_silence_device_);
579-
}
580559
}
581-
#ifdef USE_WIFI_LISTENERS
560+
#ifdef USE_WIFI_CONNECT_STATE_LISTENERS
582561
wifi::global_wifi_component->add_connect_state_listener(this);
583562
#endif
584563
this->publish_state_();
@@ -589,8 +568,8 @@ void OpenDtuSdm630::setup() {
589568
#endif
590569
}
591570

592-
#ifdef USE_WIFI_LISTENERS
593-
void OpenDtuSdm630::on_wifi_connect_state(const std::string &ssid, const wifi::bssid_t &bssid) {
571+
#ifdef USE_WIFI_CONNECT_STATE_LISTENERS
572+
void OpenDtuSdm630::on_wifi_connect_state(StringRef ssid, std::span<const uint8_t, 6> bssid) {
594573
if (ssid.empty()) {
595574
ESP_LOGW(TAG, "WiFi disconnected, stopping WebSocket client");
596575
this->stop_websocket_();

‎components/opendtu_sdm630/opendtu_sdm630.h‎

Lines changed: 10 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
#include "esphome/core/defines.h"
66
#include "esphome/core/component.h"
77
#include "esphome/core/helpers.h"
8+
#include "esphome/core/string_ref.h"
89
#include "esphome/components/modbus/modbus.h"
910
#ifdef USE_BINARY_SENSOR
1011
#include "esphome/components/binary_sensor/binary_sensor.h"
@@ -28,6 +29,7 @@
2829
#include <freertos/semphr.h>
2930

3031
#include <cstdint>
32+
#include <span>
3133
#include <string>
3234
#include <vector>
3335

@@ -48,25 +50,18 @@ struct PhaseData {
4850

4951
class OpenDtuSdm630;
5052

51-
class OpenDtuSdm630ModbusServer : public modbus::ModbusDevice {
53+
class OpenDtuSdm630ModbusServer : public modbus::ModbusServerDevice {
5254
public:
5355
void set_bridge(OpenDtuSdm630 *bridge) { this->bridge_ = bridge; }
54-
void on_modbus_data(const std::vector<uint8_t> &data) override {}
55-
void on_modbus_read_registers(uint8_t function_code, uint16_t start_address,
56-
uint16_t number_of_registers) override;
56+
modbus::ResponseStatus on_read_registers(uint16_t start_address, uint16_t number_of_registers,
57+
modbus::RegisterValues &registers) override;
5758

5859
protected:
5960
OpenDtuSdm630 *bridge_{nullptr};
6061
};
6162

62-
// Swallows frames at factory address 0x01 to suppress unknown-address logs.
63-
class ModbusSilenceDevice : public modbus::ModbusDevice {
64-
public:
65-
void on_modbus_data(const std::vector<uint8_t> &data) override {}
66-
};
67-
6863
class OpenDtuSdm630 : public Component
69-
#ifdef USE_WIFI_LISTENERS
64+
#ifdef USE_WIFI_CONNECT_STATE_LISTENERS
7065
,
7166
public wifi::WiFiConnectStateListener
7267
#endif
@@ -81,7 +76,7 @@ class OpenDtuSdm630 : public Component
8176
void set_default_voltage(float value) { this->default_voltage_ = value; }
8277
void set_default_frequency(float value) { this->default_frequency_ = value; }
8378
void set_component_version(const std::string &version) { this->component_version_ = version; }
84-
void set_modbus_server(modbus::Modbus *parent, uint8_t slave_address);
79+
void set_modbus_server(modbus::ModbusServerHub *parent, uint8_t slave_address);
8580
void add_microinverter_map_by_serial(const std::string &inverter_serial, uint8_t grid_phase);
8681
void add_microinverter_map_by_name(const std::string &inverter_name, uint8_t grid_phase);
8782

@@ -128,8 +123,8 @@ class OpenDtuSdm630 : public Component
128123
void dump_config() override;
129124
float get_setup_priority() const override { return setup_priority::AFTER_WIFI; }
130125

131-
#ifdef USE_WIFI_LISTENERS
132-
void on_wifi_connect_state(const std::string &ssid, const wifi::bssid_t &bssid) override;
126+
#ifdef USE_WIFI_CONNECT_STATE_LISTENERS
127+
void on_wifi_connect_state(StringRef ssid, std::span<const uint8_t, 6> bssid) override;
133128
#endif
134129

135130
protected:
@@ -178,10 +173,9 @@ class OpenDtuSdm630 : public Component
178173
volatile int64_t last_data_us_{0};
179174
volatile bool data_stale_{true};
180175

181-
modbus::Modbus *modbus_parent_{nullptr};
176+
modbus::ModbusServerHub *modbus_parent_{nullptr};
182177
uint8_t modbus_slave_address_{0};
183178
OpenDtuSdm630ModbusServer modbus_server_device_{};
184-
ModbusSilenceDevice modbus_silence_device_{};
185179

186180
#ifdef USE_SENSOR
187181
sensor::Sensor *voltage_l1_sensor_{nullptr};

‎opendtu_sdm630.yaml‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@ substitutions:
6666
esphome:
6767
name: ${device_name}
6868
friendly_name: OpenDTU to SDM630
69-
min_version: "2025.6.0"
69+
min_version: "2026.7.0"
7070
name_add_mac_suffix: true
7171

7272
esp32:

‎tests/opendtu_sdm630_ci.yaml‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ opendtu_sdm630:
1818

1919
esphome:
2020
name: ${device_name}
21-
min_version: "2025.6.0"
21+
min_version: "2026.7.0"
2222

2323
esp32:
2424
variant: esp32

0 commit comments

Comments
 (0)