Compare commits

...
Author SHA1 Message Date
J. Nick Koston 5b39cae870 Merge remote-tracking branch 'origin/dev' into HEAD 2026-11-14 08:38:34 -06:00
J. Nick Koston 54a80ea10d [components] Name the mk2pvrouter listener hook dump_child_config and drop unused includes 2026-10-30 09:05:25 -10:00
J. Nick Koston ce292960aa Note the dump buffering trade-off in the PR description 2026-10-30 08:22:25 -10:00
J. Nick Koston d9b887e22d Merge dev into leaf-entities-drop-component 2026-10-30 08:19:56 -10:00
J. Nick Koston 7ff60b88e1 [bedjet][emontx][mk2pvrouter][modbus_controller][uponor_smatrix] Print each child's own config dump from the hub
Restores the dev dump output for the entities that are no longer
components, and drops the hub's tag and address lines.
2026-10-30 08:09:39 -10:00
J. Nick Koston b3149594bb [output] Keep the Component base on the output lock
It has no hub to print its config dump, so it stays as on dev.
2026-10-30 08:09:16 -10:00
J. Nick Koston 7224a67208 [components] Drop the component.h include the converted leaf headers no longer use 2026-10-30 07:06:06 -10:00
J. Nick Koston 5ee8d902a5 [mk2pvrouter][uponor_smatrix] Cover every child type in the hub dump tests 2026-10-29 20:58:57 -10:00
J. Nick Koston 4c9f79cd8b [mk2pvrouter][uponor_smatrix] Stop repeating the hub's tag and address lines
The hubs now print each listener's tag and each device's address, so the
binary and text sensors drop their dump_config() and with it the
Component base, and the climate drops its address line.
2026-10-29 20:56:13 -10:00
J. Nick Koston 964950fb07 [mk2pvrouter][uponor_smatrix] Print the children's settings in the hub's config dump
The Mk2PVRouter hub prints each listener's tag and the Uponor Smatrix hub
each registered device's address, which the leaf sensors' own dumps used
to show.
2026-10-29 20:49:14 -10:00
J. Nick Koston 7f64026638 [bthome_mithermometer] Keep the Component base
It has no hub of its own to print its MAC address and bindkey, so it
keeps its dump_config().
2026-10-29 20:46:28 -10:00
J. Nick Koston 2400e29f51 [components] Drop the dump_config-only Component base from leaf entities
bedjet sensor, bthome_mithermometer, emontx sensor, mk2pvrouter sensor,
uponor_smatrix sensor, modbus_controller number and output lock only used
Component to print a config dump. Drop the base, the dump_config() and
get_setup_priority() overrides, register_component and COMPONENT_SCHEMA.
2026-10-29 20:29:29 -10:00
36 changed files with 241 additions and 58 deletions

No files matched your search

+2
View File
@@ -12,6 +12,8 @@ class BedJetClient : public Parented<BedJetHub> {
public:
virtual void on_status(const BedjetStatusPacket *data) = 0;
virtual void on_bedjet_state(bool is_ready) = 0;
// Called from the hub's dump_config(); not dump_config() so Component children don't print twice
virtual void dump_child_config() {}
protected:
friend BedJetHub;
+2
View File
@@ -504,6 +504,8 @@ void BedJetHub::dump_config() {
for (auto *child : this->children_) {
ESP_LOGCONFIG(TAG, " - %s", child->describe().c_str());
}
for (auto *child : this->children_)
child->dump_child_config();
}
void BedJetHub::dispatch_state_(bool is_ready) {
+1 -2
View File
@@ -20,7 +20,7 @@ DEPENDENCIES = ["bedjet"]
CONF_OUTLET_TEMPERATURE = "outlet_temperature"
CONF_AMBIENT_TEMPERATURE = "ambient_temperature"
BedjetSensor = bedjet_ns.class_("BedjetSensor", cg.Component)
BedjetSensor = bedjet_ns.class_("BedjetSensor")
CONFIG_SCHEMA = cv.Schema(
{
@@ -41,7 +41,6 @@ CONFIG_SCHEMA = cv.Schema(
async def to_code(config: ConfigType) -> None:
var = cg.new_Pvariable(config[CONF_ID])
await cg.register_component(var, config)
await register_bedjet_child(var, config)
sensors = sensor.sub_sensors(config)
@@ -5,7 +5,7 @@ namespace esphome::bedjet {
std::string BedjetSensor::describe() { return "BedJet Sensor"; }
void BedjetSensor::dump_config() {
void BedjetSensor::dump_child_config() {
ESP_LOGCONFIG(TAG, "BedJet Sensor:");
LOG_SENSOR(" ", "Outlet Temperature", this->outlet_temperature_sensor_);
LOG_SENSOR(" ", "Ambient Temperature", this->ambient_temperature_sensor_);
@@ -1,18 +1,17 @@
#pragma once
#include "esphome/core/component.h"
#include "esphome/components/sensor/sensor.h"
#include "esphome/components/bedjet/bedjet_child.h"
#include "esphome/components/bedjet/bedjet_codec.h"
namespace esphome::bedjet {
class BedjetSensor final : public BedJetClient, public Component {
class BedjetSensor final : public BedJetClient {
SUB_SENSOR(outlet_temperature)
SUB_SENSOR(ambient_temperature)
public:
void dump_config() override;
void dump_child_config() override;
void on_status(const BedjetStatusPacket *data) override;
void on_bedjet_state(bool is_ready) override;
+1
View File
@@ -258,6 +258,7 @@ void EmonTx::dump_config() {
ESP_LOGCONFIG(TAG, " Registered sensors: %zu", this->sensors_.size());
for (const auto &sensor_pair : this->sensors_) {
ESP_LOGCONFIG(TAG, " Sensor: %s", sensor_pair.first);
LOG_SENSOR(" ", "EmonTx Sensor", sensor_pair.second);
}
#else
ESP_LOGCONFIG(TAG, " Sensor support: DISABLED");
+1 -2
View File
@@ -32,7 +32,7 @@ from esphome.types import ConfigType
from .. import CONF_EMONTX_ID, CONF_TAG_NAME, EmonTx, emontx_ns
EmonTxSensor = emontx_ns.class_("EmonTxSensor", sensor.Sensor, cg.Component)
EmonTxSensor = emontx_ns.class_("EmonTxSensor", sensor.Sensor)
# Known emonTx/avrdb JSON tag conventions, gathered from real firmware
# (see https://github.com/openenergymonitor/avrdb_firmware), used to decide
@@ -207,7 +207,6 @@ CONFIG_SCHEMA = cv.All(BASE_SCHEMA, apply_tag_defaults)
async def to_code(config: ConfigType) -> None:
var = cg.new_Pvariable(config[CONF_ID])
await cg.register_component(var, config)
await sensor.register_sensor(var, config)
hub = await cg.get_variable(config[CONF_EMONTX_ID])
cg.add(hub.register_sensor(config[CONF_TAG_NAME], var))
@@ -1,10 +0,0 @@
#include "emontx_sensor.h"
#include "esphome/core/log.h"
namespace esphome::emontx {
ESPHOME_LOG_TAG(TAG, "emontx_sensor");
void EmonTxSensor::dump_config() { LOG_SENSOR(" ", "EmonTx Sensor", this); }
} // namespace esphome::emontx
@@ -1,13 +1,9 @@
#pragma once
#include "esphome/components/sensor/sensor.h"
#include "esphome/core/component.h"
namespace esphome::emontx {
class EmonTxSensor final : public sensor::Sensor, public Component {
public:
void dump_config() override;
};
class EmonTxSensor final : public sensor::Sensor {};
} // namespace esphome::emontx
@@ -11,7 +11,7 @@ from .. import (
)
Mk2PVRouterBinarySensor = mk2pvrouter_ns.class_(
"Mk2PVRouterBinarySensor", binary_sensor.BinarySensor, cg.Component
"Mk2PVRouterBinarySensor", binary_sensor.BinarySensor
)
CONFIG_SCHEMA = binary_sensor.binary_sensor_schema(Mk2PVRouterBinarySensor).extend(
@@ -21,7 +21,6 @@ CONFIG_SCHEMA = binary_sensor.binary_sensor_schema(Mk2PVRouterBinarySensor).exte
async def to_code(config: ConfigType) -> None:
var = cg.new_Pvariable(config[CONF_ID], config[CONF_TAG])
await cg.register_component(var, config)
await binary_sensor.register_binary_sensor(var, config)
mk2pvrouter = await cg.get_variable(config[CONF_MK2PVROUTER_ID])
await register_mk2pvrouter_listener(mk2pvrouter, var)
@@ -13,7 +13,7 @@ void Mk2PVRouterBinarySensor::publish_val(const char *val) {
this->publish_state(state);
}
void Mk2PVRouterBinarySensor::dump_config() {
void Mk2PVRouterBinarySensor::dump_child_config() {
LOG_BINARY_SENSOR(" ", "Mk2PVRouter Binary Sensor", this);
ESP_LOGCONFIG(TAG, " Tag: %s", this->get_tag());
}
@@ -5,11 +5,11 @@
namespace esphome::mk2pvrouter {
class Mk2PVRouterBinarySensor final : public Mk2PVRouterListener, public binary_sensor::BinarySensor, public Component {
class Mk2PVRouterBinarySensor final : public Mk2PVRouterListener, public binary_sensor::BinarySensor {
public:
explicit Mk2PVRouterBinarySensor(const char *tag);
void publish_val(const char *val) override;
void dump_config() override;
void dump_child_config() override;
};
} // namespace esphome::mk2pvrouter
@@ -157,7 +157,13 @@ void Mk2PVRouter::publish_value_(const char *tag, const char *val) {
#endif
}
void Mk2PVRouter::dump_config() { ESP_LOGCONFIG(TAG, "Mk2PVRouter:"); }
void Mk2PVRouter::dump_config() {
ESP_LOGCONFIG(TAG, "Mk2PVRouter:");
#ifdef MK2PVROUTER_LISTENER_COUNT
for (auto *listener : this->mk2pvrouter_listeners_)
listener->dump_child_config();
#endif
}
#ifdef MK2PVROUTER_LISTENER_COUNT
void Mk2PVRouter::register_mk2pvrouter_listener(Mk2PVRouterListener *listener) {
@@ -27,6 +27,8 @@ class Mk2PVRouterListener {
virtual ~Mk2PVRouterListener() = default;
const char *get_tag() const { return this->tag_; }
virtual void publish_val(const char *val) = 0;
// Called from the hub's dump_config(); not dump_config() so Component children don't print twice
virtual void dump_child_config() {}
protected:
const char *tag_;
@@ -35,9 +35,7 @@ from .. import (
register_mk2pvrouter_listener,
)
Mk2PVRouterSensor = mk2pvrouter_ns.class_(
"Mk2PVRouterSensor", sensor.Sensor, cg.Component
)
Mk2PVRouterSensor = mk2pvrouter_ns.class_("Mk2PVRouterSensor", sensor.Sensor)
@dataclass(frozen=True)
@@ -122,7 +120,6 @@ async def to_code(config: ConfigType) -> None:
tag = config[CONF_TAG]
kind = tag_kind(tag)
var = cg.new_Pvariable(config[CONF_ID], tag, kind is not None and kind.scale_centi)
await cg.register_component(var, config)
await sensor.register_sensor(var, config)
mk2pvrouter = await cg.get_variable(config[CONF_MK2PVROUTER_ID])
await register_mk2pvrouter_listener(mk2pvrouter, var)
@@ -21,7 +21,7 @@ void Mk2PVRouterSensor::publish_val(const char *val) {
this->publish_state(value);
}
void Mk2PVRouterSensor::dump_config() {
void Mk2PVRouterSensor::dump_child_config() {
LOG_SENSOR(" ", "Mk2PVRouter Sensor", this);
ESP_LOGCONFIG(TAG, " Tag: %s", this->get_tag());
}
@@ -5,12 +5,11 @@
namespace esphome::mk2pvrouter {
// Component before Sensor so the flag lands in Sensor's tail padding
class Mk2PVRouterSensor final : public Mk2PVRouterListener, public Component, public sensor::Sensor {
class Mk2PVRouterSensor final : public Mk2PVRouterListener, public sensor::Sensor {
public:
Mk2PVRouterSensor(const char *tag, bool scale_centi);
void publish_val(const char *val) override;
void dump_config() override;
void dump_child_config() override;
protected:
bool scale_centi_;
@@ -11,7 +11,7 @@ from .. import (
)
Mk2PVRouterTextSensor = mk2pvrouter_ns.class_(
"Mk2PVRouterTextSensor", text_sensor.TextSensor, cg.Component
"Mk2PVRouterTextSensor", text_sensor.TextSensor
)
CONFIG_SCHEMA = text_sensor.text_sensor_schema(Mk2PVRouterTextSensor).extend(
@@ -21,7 +21,6 @@ CONFIG_SCHEMA = text_sensor.text_sensor_schema(Mk2PVRouterTextSensor).extend(
async def to_code(config: ConfigType) -> None:
var = cg.new_Pvariable(config[CONF_ID], config[CONF_TAG])
await cg.register_component(var, config)
await text_sensor.register_text_sensor(var, config)
mk2pvrouter = await cg.get_variable(config[CONF_MK2PVROUTER_ID])
await register_mk2pvrouter_listener(mk2pvrouter, var)
@@ -9,7 +9,7 @@ Mk2PVRouterTextSensor::Mk2PVRouterTextSensor(const char *tag) : Mk2PVRouterListe
void Mk2PVRouterTextSensor::publish_val(const char *val) { this->publish_state(val); }
void Mk2PVRouterTextSensor::dump_config() {
void Mk2PVRouterTextSensor::dump_child_config() {
LOG_TEXT_SENSOR(" ", "Mk2PVRouter Text Sensor", this);
ESP_LOGCONFIG(TAG, " Tag: %s", this->get_tag());
}
@@ -5,11 +5,11 @@
namespace esphome::mk2pvrouter {
class Mk2PVRouterTextSensor final : public Mk2PVRouterListener, public text_sensor::TextSensor, public Component {
class Mk2PVRouterTextSensor final : public Mk2PVRouterListener, public text_sensor::TextSensor {
public:
explicit Mk2PVRouterTextSensor(const char *tag);
void publish_val(const char *val) override;
void dump_config() override;
void dump_child_config() override;
};
} // namespace esphome::mk2pvrouter
@@ -538,6 +538,8 @@ void ModbusController::dump_config() {
" Max Command Retries: %d\n"
" Offline Skip Updates: %d\n",
this->address_, this->max_cmd_retries_, this->offline_skip_updates_);
for (auto *item : this->sensorset_)
item->dump_child_config();
#if ESPHOME_LOG_LEVEL >= ESPHOME_LOG_LEVEL_VERBOSE
ESP_LOGCONFIG(TAG, "sensormap");
@@ -72,6 +72,8 @@ class SensorItem {
/// response buffer and is only valid for the duration of the call. Read the sensor's data from
/// `offset` within it.
virtual void parse_and_publish(std::span<const uint8_t> data) = 0;
/// Called from the hub's dump_config(); not dump_config() so Component items don't print twice.
virtual void dump_child_config() {}
/// Coils and discrete inputs address individual bits; every other type addresses 16-bit registers.
bool addresses_bits() const { return modbus::helpers::is_entity_type_binary(this->register_type); }
@@ -42,9 +42,7 @@ DEPENDENCIES = ["modbus_controller"]
CODEOWNERS = ["@martgras"]
ModbusNumber = modbus_controller_ns.class_(
"ModbusNumber", cg.Component, number.Number, SensorItem
)
ModbusNumber = modbus_controller_ns.class_("ModbusNumber", number.Number, SensorItem)
def validate_min_max(config: ConfigType) -> ConfigType:
@@ -107,7 +105,6 @@ async def to_code(config: ConfigType) -> None:
RANGE_REUSE[config[CONF_REUSE_PREVIOUS_RANGE]],
)
await cg.register_component(var, config)
await number.register_number(
var,
config,
@@ -98,6 +98,6 @@ void ModbusNumber::control(float value) {
}
this->publish_state(value);
}
void ModbusNumber::dump_config() { LOG_NUMBER(TAG, "Modbus Number", this); }
void ModbusNumber::dump_child_config() { LOG_NUMBER(TAG, "Modbus Number", this); }
} // namespace esphome::modbus_controller
@@ -2,7 +2,6 @@
#include "esphome/components/number/number.h"
#include "esphome/components/modbus_controller/modbus_controller.h"
#include "esphome/core/component.h"
#include <vector>
@@ -10,7 +9,7 @@ namespace esphome::modbus_controller {
using value_to_data_t = std::function<float>(float);
class ModbusNumber final : public number::Number, public Component, public SensorItem, public WriterEntity {
class ModbusNumber final : public number::Number, public SensorItem, public WriterEntity {
public:
ModbusNumber(modbus::EntityType register_type, uint16_t start_address, uint8_t offset, uint32_t bitmask,
SensorValueType value_type, RangeReuse reuse_previous_range) {
@@ -22,9 +21,8 @@ class ModbusNumber final : public number::Number, public Component, public Senso
this->reuse_previous_range = reuse_previous_range;
};
void dump_config() override;
void dump_child_config() override;
void parse_and_publish(std::span<const uint8_t> data) override;
float get_setup_priority() const override { return setup_priority::HARDWARE; }
void set_parent(ModbusController *parent) { this->set_controller_(parent); }
void set_write_multiply(float factor) { this->multiply_by_ = factor; }
@@ -27,11 +27,10 @@ DEPENDENCIES = ["uponor_smatrix"]
UponorSmatrixSensor = uponor_smatrix_ns.class_(
"UponorSmatrixSensor",
sensor.Sensor,
cg.Component,
UponorSmatrixDevice,
)
CONFIG_SCHEMA = cv.COMPONENT_SCHEMA.extend(
CONFIG_SCHEMA = cv.Schema(
{
cv.GenerateID(): cv.declare_id(UponorSmatrixSensor),
cv.Optional(CONF_TEMPERATURE): sensor.sensor_schema(
@@ -64,7 +63,6 @@ CONFIG_SCHEMA = cv.COMPONENT_SCHEMA.extend(
async def to_code(config: ConfigType) -> None:
var = cg.new_Pvariable(config[CONF_ID])
await cg.register_component(var, config)
await register_uponor_smatrix_device(var, config)
sensors = sensor.sub_sensors(config)
@@ -7,7 +7,7 @@ namespace esphome::uponor_smatrix {
ESPHOME_LOG_TAG(TAG, "uponor_smatrix.sensor");
void UponorSmatrixSensor::dump_config() {
void UponorSmatrixSensor::dump_child_config() {
ESP_LOGCONFIG(TAG,
"Uponor Smatrix Sensor\n"
" Device address: 0x%08" PRIX32,
@@ -2,18 +2,17 @@
#include "esphome/components/sensor/sensor.h"
#include "esphome/components/uponor_smatrix/uponor_smatrix.h"
#include "esphome/core/component.h"
namespace esphome::uponor_smatrix {
class UponorSmatrixSensor final : public sensor::Sensor, public Component, public UponorSmatrixDevice {
class UponorSmatrixSensor final : public sensor::Sensor, public UponorSmatrixDevice {
SUB_SENSOR(temperature)
SUB_SENSOR(external_temperature)
SUB_SENSOR(humidity)
SUB_SENSOR(target_temperature)
public:
void dump_config() override;
void dump_child_config() override;
protected:
void on_device_data(const UponorSmatrixData *data, size_t data_len) override;
@@ -29,6 +29,9 @@ void UponorSmatrixComponent::dump_config() {
}
#endif
for (auto *device : this->devices_)
device->dump_child_config();
if (!this->unknown_devices_.empty()) {
ESP_LOGCONFIG(TAG, " Detected unknown device addresses:");
for (auto device_address : this->unknown_devices_) {
@@ -104,6 +104,8 @@ class UponorSmatrixDevice : public Parented<UponorSmatrixComponent> {
void set_address(uint32_t address) { this->address_ = address; }
virtual void on_device_data(const UponorSmatrixData *data, size_t data_len) = 0;
// Called from the hub's dump_config(); not dump_config() so Component devices don't print twice
virtual void dump_child_config() {}
bool send(const UponorSmatrixData *data, size_t data_len) {
return this->parent_->send(this->address_, data, data_len);
}
@@ -0,0 +1,39 @@
esphome:
name: hub-dump-children
host:
api:
logger:
level: DEBUG
uart:
- id: emontx_uart
baud_rate: 115200
port: /dev/null
rx_buffer_size: 2048
- id: modbus_uart
baud_rate: 9600
port: /dev/null
emontx:
uart_id: emontx_uart
modbus:
uart_id: modbus_uart
modbus_controller:
- id: controller
address: 0x2
update_interval: 1h
sensor:
- platform: emontx
name: Grid Power
tag_name: P1
number:
- platform: modbus_controller
modbus_controller_id: controller
name: Setpoint
address: 0x9001
value_type: U_WORD
@@ -0,0 +1,33 @@
esphome:
name: mk2pvrouter-dump-listeners
host:
api:
logger:
level: DEBUG
uart:
baud_rate: 9600
port: /dev/null
data_bits: 7
parity: EVEN
mk2pvrouter:
sensor:
- platform: mk2pvrouter
name: Power
tag: P
- platform: mk2pvrouter
name: Voltage Phase 1
tag: V1
binary_sensor:
- platform: mk2pvrouter
name: Relay 2
tag: R2
text_sensor:
- platform: mk2pvrouter
name: S per MC
tag: S_MC
@@ -0,0 +1,28 @@
esphome:
name: uponor-smatrix-dump-devices
host:
api:
logger:
level: DEBUG
uart:
baud_rate: 19200
port: /dev/null
uponor_smatrix:
sensor:
- platform: uponor_smatrix
address: 0x110BDE13
temperature:
name: Temperature Living Room
- platform: uponor_smatrix
address: 0x110BDE14
humidity:
name: Humidity Kitchen
climate:
- platform: uponor_smatrix
address: 0x110BDE15
name: Thermostat Bedroom
@@ -0,0 +1,27 @@
"""The emonTx and Modbus controller hubs print their non-component children's config dumps."""
from __future__ import annotations
import pytest
from .log_utils import LineWaiter
from .types import APIClientConnectedFactory, RunCompiledFunction
@pytest.mark.asyncio
async def test_hub_dump_children(
yaml_config: str,
run_compiled: RunCompiledFunction,
api_client_connected: APIClientConnectedFactory,
) -> None:
"""Each child prints the lines it printed when it was a component."""
waiter = LineWaiter()
async with (
run_compiled(yaml_config, line_callback=waiter.callback),
api_client_connected() as client,
):
await client.device_info()
await waiter.wait_for_each(
"EmonTx Sensor 'Grid Power'",
"Modbus Number 'Setpoint'",
)
@@ -0,0 +1,33 @@
"""The Mk2PVRouter hub prints each listener's config dump."""
from __future__ import annotations
import pytest
from .log_utils import LineWaiter
from .types import APIClientConnectedFactory, RunCompiledFunction
@pytest.mark.asyncio
async def test_mk2pvrouter_dump_listeners(
yaml_config: str,
run_compiled: RunCompiledFunction,
api_client_connected: APIClientConnectedFactory,
) -> None:
"""Each listener prints its entity and tag, as when it was a component."""
waiter = LineWaiter()
async with (
run_compiled(yaml_config, line_callback=waiter.callback),
api_client_connected() as client,
):
await client.device_info()
await waiter.wait_for_each(
"Mk2PVRouter Sensor 'Power'",
"Tag: P",
"Mk2PVRouter Sensor 'Voltage Phase 1'",
"Tag: V1",
"Mk2PVRouter Binary Sensor 'Relay 2'",
"Tag: R2",
"Mk2PVRouter Text Sensor 'S per MC'",
"Tag: S_MC",
)
@@ -0,0 +1,32 @@
"""The Uponor Smatrix hub prints each sensor device's config dump."""
from __future__ import annotations
import pytest
from .log_utils import LineWaiter
from .types import APIClientConnectedFactory, RunCompiledFunction
@pytest.mark.asyncio
async def test_uponor_smatrix_dump_devices(
yaml_config: str,
run_compiled: RunCompiledFunction,
api_client_connected: APIClientConnectedFactory,
) -> None:
"""Sensors print through the hub; the climate still prints its own dump."""
waiter = LineWaiter()
async with (
run_compiled(yaml_config, line_callback=waiter.callback),
api_client_connected() as client,
):
await client.device_info()
await waiter.wait_for_each(
"Uponor Smatrix Sensor",
"Device address: 0x110BDE13",
"Temperature 'Temperature Living Room'",
"Device address: 0x110BDE14",
"Humidity 'Humidity Kitchen'",
"Uponor Smatrix Climate 'Thermostat Bedroom'",
"Device address: 0x110BDE15",
)