[modbus] Update client components to use ModbusClientDevice (#11987)

This commit is contained in:
Bonne Eggleston
2026-06-30 13:26:51 -05:00
committed by GitHub
parent 3035355c0a
commit afb5922f37
17 changed files with 94 additions and 25 deletions
@@ -65,7 +65,7 @@ constexpr size_t RTU2_TODAY_PRODUCTION = 53; // length = 2
constexpr size_t RTU2_TOTAL_ENERGY_PRODUCTION = 55; // length = 2
constexpr size_t RTU2_INVERTER_MODULE_TEMP = 93; // length = 1
class GrowattSolar final : public PollingComponent, public modbus::ModbusDevice {
class GrowattSolar final : public PollingComponent, public modbus::ModbusClientDevice {
public:
void loop() override;
void update() override;
+10 -2
View File
@@ -25,6 +25,7 @@ from esphome.const import (
UNIT_VOLT,
UNIT_WATT,
)
from esphome.types import ConfigType
CONF_ENERGY_PRODUCTION_DAY = "energy_production_day"
CONF_TOTAL_ENERGY_PRODUCTION = "total_energy_production"
@@ -47,7 +48,7 @@ CODEOWNERS = ["@leeuwte"]
growatt_solar_ns = cg.esphome_ns.namespace("growatt_solar")
GrowattSolar = growatt_solar_ns.class_(
"GrowattSolar", cg.PollingComponent, modbus.ModbusDevice
"GrowattSolar", cg.PollingComponent, modbus.ModbusClientDevice
)
PHASE_SENSORS = {
@@ -162,10 +163,17 @@ CONFIG_SCHEMA = (
)
def _final_validate(config: ConfigType) -> ConfigType:
return modbus.final_validate_modbus_device("growatt_solar", role="client")(config)
FINAL_VALIDATE_SCHEMA = _final_validate
async def to_code(config):
var = cg.new_Pvariable(config[CONF_ID])
await cg.register_component(var, config)
await modbus.register_modbus_device(var, config)
await modbus.register_modbus_client_device(var, config)
cg.add(var.set_protocol_version(config[CONF_PROTOCOL_VERSION]))
@@ -8,7 +8,7 @@
namespace esphome::havells_solar {
class HavellsSolar final : public PollingComponent, public modbus::ModbusDevice {
class HavellsSolar final : public PollingComponent, public modbus::ModbusClientDevice {
public:
void set_voltage_sensor(uint8_t phase, sensor::Sensor *voltage_sensor) {
this->phases_[phase].setup = true;
+10 -2
View File
@@ -28,6 +28,7 @@ from esphome.const import (
UNIT_VOLT_AMPS_REACTIVE,
UNIT_WATT,
)
from esphome.types import ConfigType
CONF_ENERGY_PRODUCTION_DAY = "energy_production_day"
CONF_TOTAL_ENERGY_PRODUCTION = "total_energy_production"
@@ -58,7 +59,7 @@ CODEOWNERS = ["@sourabhjaiswal"]
havells_solar_ns = cg.esphome_ns.namespace("havells_solar")
HavellsSolar = havells_solar_ns.class_(
"HavellsSolar", cg.PollingComponent, modbus.ModbusDevice
"HavellsSolar", cg.PollingComponent, modbus.ModbusClientDevice
)
PHASE_SENSORS = {
@@ -216,10 +217,17 @@ CONFIG_SCHEMA = (
)
def _final_validate(config: ConfigType) -> ConfigType:
return modbus.final_validate_modbus_device("havells_solar", role="client")(config)
FINAL_VALIDATE_SCHEMA = _final_validate
async def to_code(config):
var = cg.new_Pvariable(config[CONF_ID])
await cg.register_component(var, config)
await modbus.register_modbus_device(var, config)
await modbus.register_modbus_client_device(var, config)
if CONF_FREQUENCY in config:
sens = await sensor.new_sensor(config[CONF_FREQUENCY])
+1 -1
View File
@@ -6,7 +6,7 @@
namespace esphome::kuntze {
class Kuntze final : public PollingComponent, public modbus::ModbusDevice {
class Kuntze final : public PollingComponent, public modbus::ModbusClientDevice {
public:
void set_ph_sensor(sensor::Sensor *ph_sensor) { ph_sensor_ = ph_sensor; }
void set_temperature_sensor(sensor::Sensor *temperature_sensor) { temperature_sensor_ = temperature_sensor; }
+10 -2
View File
@@ -15,13 +15,14 @@ from esphome.const import (
UNIT_EMPTY,
UNIT_PH,
)
from esphome.types import ConfigType
CODEOWNERS = ["@ssieb"]
AUTO_LOAD = ["modbus"]
kuntze_ns = cg.esphome_ns.namespace("kuntze")
Kuntze = kuntze_ns.class_("Kuntze", cg.PollingComponent, modbus.ModbusDevice)
Kuntze = kuntze_ns.class_("Kuntze", cg.PollingComponent, modbus.ModbusClientDevice)
CONF_DIS1 = "dis1"
CONF_DIS2 = "dis2"
@@ -88,10 +89,17 @@ CONFIG_SCHEMA = (
)
def _final_validate(config: ConfigType) -> ConfigType:
return modbus.final_validate_modbus_device("kuntze", role="client")(config)
FINAL_VALIDATE_SCHEMA = _final_validate
async def to_code(config):
var = cg.new_Pvariable(config[CONF_ID])
await cg.register_component(var, config)
await modbus.register_modbus_device(var, config)
await modbus.register_modbus_client_device(var, config)
if CONF_PH in config:
conf = config[CONF_PH]
+8
View File
@@ -1,5 +1,6 @@
from __future__ import annotations
import logging
from typing import Literal
from esphome import pins
@@ -10,6 +11,8 @@ from esphome.const import CONF_ADDRESS, CONF_DISABLE_CRC, CONF_FLOW_CONTROL_PIN,
from esphome.cpp_helpers import gpio_pin_expression
import esphome.final_validate as fv
_LOGGER = logging.getLogger(__name__)
DEPENDENCIES = ["uart"]
modbus_ns = cg.esphome_ns.namespace("modbus")
@@ -129,4 +132,9 @@ async def register_modbus_server_device(var, config):
async def register_modbus_device(var, config):
# Remove before 2026.12.0
_LOGGER.warning(
"'register_modbus_device' is deprecated, use 'register_modbus_client_device' "
"instead. Will be removed in 2026.12.0"
)
return await register_modbus_client_device(var, config)
+3 -1
View File
@@ -197,7 +197,9 @@ class ModbusClientDevice {
};
// This is for compatibility with external components using the former class name
using ModbusDevice = ModbusClientDevice;
// Remove before 2026.12.0
using ModbusDevice ESPDEPRECATED("Use ModbusClientDevice instead. Removed in 2026.12.0",
"2026.6.0") = ModbusClientDevice;
// Result of a server register handler: std::nullopt means success, otherwise the Modbus exception code to return.
using ServerResponseStatus = std::optional<ModbusExceptionCode>;
@@ -11,6 +11,7 @@ from esphome.components.modbus.helpers import (
import esphome.config_validation as cv
from esphome.const import CONF_ADDRESS, CONF_ID, CONF_LAMBDA, CONF_NAME, CONF_OFFSET
from esphome.cpp_helpers import logging
from esphome.types import ConfigType
from .const import (
CONF_ALLOW_DUPLICATE_COMMANDS,
@@ -42,7 +43,7 @@ MULTI_CONF = True
modbus_controller_ns = cg.esphome_ns.namespace("modbus_controller")
ModbusController = modbus_controller_ns.class_(
"ModbusController", cg.PollingComponent, modbus.ModbusDevice
"ModbusController", cg.PollingComponent, modbus.ModbusClientDevice
)
SensorItem = modbus_controller_ns.struct("SensorItem")
@@ -117,7 +118,7 @@ def validate_modbus_register(config):
return config
def _final_validate(config):
def _final_validate(config: ConfigType) -> ConfigType:
return modbus.final_validate_modbus_device("modbus_controller", role="client")(
config
)
@@ -211,7 +212,7 @@ async def to_code(config):
async def register_modbus_device(var, config):
cg.add(var.set_address(config[CONF_ADDRESS]))
await cg.register_component(var, config)
return await modbus.register_modbus_device(var, config)
return await modbus.register_modbus_client_device(var, config)
def function_code_to_register(function_code):
+1 -1
View File
@@ -11,7 +11,7 @@ namespace esphome::pzemac {
template<typename... Ts> class ResetEnergyAction;
class PZEMAC final : public PollingComponent, public modbus::ModbusDevice {
class PZEMAC final : public PollingComponent, public modbus::ModbusClientDevice {
public:
void set_voltage_sensor(sensor::Sensor *voltage_sensor) { voltage_sensor_ = voltage_sensor; }
void set_current_sensor(sensor::Sensor *current_sensor) { current_sensor_ = current_sensor; }
+10 -2
View File
@@ -26,11 +26,12 @@ from esphome.const import (
UNIT_WATT,
UNIT_WATT_HOURS,
)
from esphome.types import ConfigType
AUTO_LOAD = ["modbus"]
pzemac_ns = cg.esphome_ns.namespace("pzemac")
PZEMAC = pzemac_ns.class_("PZEMAC", cg.PollingComponent, modbus.ModbusDevice)
PZEMAC = pzemac_ns.class_("PZEMAC", cg.PollingComponent, modbus.ModbusClientDevice)
# Actions
ResetEnergyAction = pzemac_ns.class_("ResetEnergyAction", automation.Action)
@@ -97,10 +98,17 @@ async def reset_energy_to_code(config, action_id, template_arg, args):
return cg.new_Pvariable(action_id, template_arg, paren)
def _final_validate(config: ConfigType) -> ConfigType:
return modbus.final_validate_modbus_device("pzemac", role="client")(config)
FINAL_VALIDATE_SCHEMA = _final_validate
async def to_code(config):
var = cg.new_Pvariable(config[CONF_ID])
await cg.register_component(var, config)
await modbus.register_modbus_device(var, config)
await modbus.register_modbus_client_device(var, config)
if CONF_VOLTAGE in config:
conf = config[CONF_VOLTAGE]
+1 -1
View File
@@ -9,7 +9,7 @@
namespace esphome::pzemdc {
class PZEMDC final : public PollingComponent, public modbus::ModbusDevice {
class PZEMDC final : public PollingComponent, public modbus::ModbusClientDevice {
public:
void set_voltage_sensor(sensor::Sensor *voltage_sensor) { voltage_sensor_ = voltage_sensor; }
void set_current_sensor(sensor::Sensor *current_sensor) { current_sensor_ = current_sensor; }
+10 -2
View File
@@ -20,11 +20,12 @@ from esphome.const import (
UNIT_VOLT,
UNIT_WATT,
)
from esphome.types import ConfigType
AUTO_LOAD = ["modbus"]
pzemdc_ns = cg.esphome_ns.namespace("pzemdc")
PZEMDC = pzemdc_ns.class_("PZEMDC", cg.PollingComponent, modbus.ModbusDevice)
PZEMDC = pzemdc_ns.class_("PZEMDC", cg.PollingComponent, modbus.ModbusClientDevice)
# Actions
ResetEnergyAction = pzemdc_ns.class_("ResetEnergyAction", automation.Action)
@@ -79,10 +80,17 @@ async def reset_energy_to_code(config, action_id, template_arg, args):
return cg.new_Pvariable(action_id, template_arg, paren)
def _final_validate(config: ConfigType) -> ConfigType:
return modbus.final_validate_modbus_device("pzemdc", role="client")(config)
FINAL_VALIDATE_SCHEMA = _final_validate
async def to_code(config):
var = cg.new_Pvariable(config[CONF_ID])
await cg.register_component(var, config)
await modbus.register_modbus_device(var, config)
await modbus.register_modbus_client_device(var, config)
if CONF_VOLTAGE in config:
conf = config[CONF_VOLTAGE]
+1 -1
View File
@@ -8,7 +8,7 @@
namespace esphome::sdm_meter {
class SDMMeter final : public PollingComponent, public modbus::ModbusDevice {
class SDMMeter final : public PollingComponent, public modbus::ModbusClientDevice {
public:
void set_voltage_sensor(uint8_t phase, sensor::Sensor *voltage_sensor) {
this->phases_[phase].setup = true;
+12 -2
View File
@@ -41,12 +41,15 @@ from esphome.const import (
UNIT_VOLT_AMPS_REACTIVE,
UNIT_WATT,
)
from esphome.types import ConfigType
AUTO_LOAD = ["modbus"]
CODEOWNERS = ["@polyfaces", "@jesserockz"]
sdm_meter_ns = cg.esphome_ns.namespace("sdm_meter")
SDMMeter = sdm_meter_ns.class_("SDMMeter", cg.PollingComponent, modbus.ModbusDevice)
SDMMeter = sdm_meter_ns.class_(
"SDMMeter", cg.PollingComponent, modbus.ModbusClientDevice
)
PHASE_SENSORS = {
CONF_VOLTAGE: sensor.sensor_schema(
@@ -145,10 +148,17 @@ CONFIG_SCHEMA = (
)
def _final_validate(config: ConfigType) -> ConfigType:
return modbus.final_validate_modbus_device("sdm_meter", role="client")(config)
FINAL_VALIDATE_SCHEMA = _final_validate
async def to_code(config):
var = cg.new_Pvariable(config[CONF_ID])
await cg.register_component(var, config)
await modbus.register_modbus_device(var, config)
await modbus.register_modbus_client_device(var, config)
if CONF_TOTAL_POWER in config:
sens = await sensor.new_sensor(config[CONF_TOTAL_POWER])
+1 -1
View File
@@ -15,7 +15,7 @@ namespace esphome::selec_meter {
public: \
void set_##name##_sensor(sensor::Sensor *(name)) { this->name##_sensor_ = name; }
class SelecMeter final : public PollingComponent, public modbus::ModbusDevice {
class SelecMeter final : public PollingComponent, public modbus::ModbusClientDevice {
public:
SELEC_METER_SENSOR(total_active_energy)
SELEC_METER_SENSOR(import_active_energy)
+10 -2
View File
@@ -32,6 +32,7 @@ from esphome.const import (
UNIT_VOLT_AMPS_REACTIVE,
UNIT_WATT,
)
from esphome.types import ConfigType
AUTO_LOAD = ["modbus"]
CODEOWNERS = ["@sourabhjaiswal"]
@@ -49,7 +50,7 @@ UNIT_KILOVOLT_AMPS_REACTIVE_HOURS = "kVARh"
selec_meter_ns = cg.esphome_ns.namespace("selec_meter")
SelecMeter = selec_meter_ns.class_(
"SelecMeter", cg.PollingComponent, modbus.ModbusDevice
"SelecMeter", cg.PollingComponent, modbus.ModbusClientDevice
)
SENSORS = {
@@ -163,10 +164,17 @@ CONFIG_SCHEMA = (
)
def _final_validate(config: ConfigType) -> ConfigType:
return modbus.final_validate_modbus_device("selec_meter", role="client")(config)
FINAL_VALIDATE_SCHEMA = _final_validate
async def to_code(config):
var = cg.new_Pvariable(config[CONF_ID])
await cg.register_component(var, config)
await modbus.register_modbus_device(var, config)
await modbus.register_modbus_client_device(var, config)
for name in SENSORS:
if name in config:
sens = await sensor.new_sensor(config[name])