mirror of
https://github.com/esphome/esphome.git
synced 2026-06-01 01:19:45 +08:00
[emontx] emonTx component (#9027)
Co-authored-by: Claude <noreply@anthropic.com> Co-authored-by: J. Nick Koston <nick@koston.org> Co-authored-by: J. Nick Koston <nick@home-assistant.io>
This commit is contained in:
@@ -148,6 +148,7 @@ esphome/components/ee895/* @Stock-M
|
|||||||
esphome/components/ektf2232/touchscreen/* @jesserockz
|
esphome/components/ektf2232/touchscreen/* @jesserockz
|
||||||
esphome/components/emc2101/* @ellull
|
esphome/components/emc2101/* @ellull
|
||||||
esphome/components/emmeti/* @E440QF
|
esphome/components/emmeti/* @E440QF
|
||||||
|
esphome/components/emontx/* @FredM67 @glynhudson @TrystanLea
|
||||||
esphome/components/ens160/* @latonita
|
esphome/components/ens160/* @latonita
|
||||||
esphome/components/ens160_base/* @latonita @vincentscode
|
esphome/components/ens160_base/* @latonita @vincentscode
|
||||||
esphome/components/ens160_i2c/* @latonita
|
esphome/components/ens160_i2c/* @latonita
|
||||||
|
|||||||
@@ -0,0 +1,152 @@
|
|||||||
|
from dataclasses import dataclass, field
|
||||||
|
|
||||||
|
from esphome import automation
|
||||||
|
import esphome.codegen as cg
|
||||||
|
from esphome.components import uart
|
||||||
|
import esphome.config_validation as cv
|
||||||
|
from esphome.const import (
|
||||||
|
CONF_COMMAND,
|
||||||
|
CONF_ID,
|
||||||
|
CONF_ON_DATA,
|
||||||
|
CONF_RX_BUFFER_SIZE,
|
||||||
|
CONF_UART_ID,
|
||||||
|
)
|
||||||
|
from esphome.core import CORE
|
||||||
|
import esphome.final_validate as fv
|
||||||
|
from esphome.types import ConfigType
|
||||||
|
|
||||||
|
AUTO_LOAD = ["json"]
|
||||||
|
CODEOWNERS = ["@FredM67", "@TrystanLea", "@glynhudson"]
|
||||||
|
DEPENDENCIES = ["uart"]
|
||||||
|
|
||||||
|
emontx_ns = cg.esphome_ns.namespace("emontx")
|
||||||
|
EmonTx = emontx_ns.class_("EmonTx", cg.Component, uart.UARTDevice)
|
||||||
|
|
||||||
|
# Action to send command to emonTx
|
||||||
|
EmonTxSendCommandAction = emontx_ns.class_("EmonTxSendCommandAction", automation.Action)
|
||||||
|
|
||||||
|
CONF_EMONTX_ID = "emontx_id"
|
||||||
|
CONF_TAG_NAME = "tag_name"
|
||||||
|
CONF_ON_JSON = "on_json"
|
||||||
|
|
||||||
|
DOMAIN = "emontx"
|
||||||
|
|
||||||
|
MINIMUM_RX_BUFFER_SIZE = 2048
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class EmonTxData:
|
||||||
|
sensor_counts: dict[str, int] = field(default_factory=dict)
|
||||||
|
|
||||||
|
|
||||||
|
def _get_data() -> EmonTxData:
|
||||||
|
if DOMAIN not in CORE.data:
|
||||||
|
CORE.data[DOMAIN] = EmonTxData()
|
||||||
|
return CORE.data[DOMAIN]
|
||||||
|
|
||||||
|
|
||||||
|
# Main configuration schema
|
||||||
|
CONFIG_SCHEMA = (
|
||||||
|
cv.Schema(
|
||||||
|
{
|
||||||
|
cv.GenerateID(): cv.declare_id(EmonTx),
|
||||||
|
cv.Optional(CONF_ON_JSON): automation.validate_automation({}),
|
||||||
|
cv.Optional(CONF_ON_DATA): automation.validate_automation({}),
|
||||||
|
}
|
||||||
|
)
|
||||||
|
.extend(cv.COMPONENT_SCHEMA)
|
||||||
|
.extend(uart.UART_DEVICE_SCHEMA)
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def final_validate(config: ConfigType) -> ConfigType:
|
||||||
|
full_config = fv.full_config.get()
|
||||||
|
|
||||||
|
# Count sensors registered to this hub (IDs are resolved at final_validate stage)
|
||||||
|
hub_id = str(config[CONF_ID])
|
||||||
|
sensor_count = sum(
|
||||||
|
1
|
||||||
|
for s in full_config.get("sensor", [])
|
||||||
|
if s.get("platform") == "emontx" and str(s.get(CONF_EMONTX_ID)) == hub_id
|
||||||
|
)
|
||||||
|
_get_data().sensor_counts[hub_id] = sensor_count
|
||||||
|
|
||||||
|
# Ensure UART RX buffer size is large enough to handle data bursts from firmware
|
||||||
|
for uart_conf in full_config["uart"]:
|
||||||
|
if uart_conf[CONF_ID] == config[CONF_UART_ID]:
|
||||||
|
current_buffer_size = uart_conf[CONF_RX_BUFFER_SIZE]
|
||||||
|
if current_buffer_size < MINIMUM_RX_BUFFER_SIZE:
|
||||||
|
raise cv.Invalid(
|
||||||
|
f"Component emontx requires UART '{config[CONF_UART_ID]}' to have "
|
||||||
|
f"rx_buffer_size of at least {MINIMUM_RX_BUFFER_SIZE} bytes "
|
||||||
|
f"(currently set to {current_buffer_size} bytes). "
|
||||||
|
f"Please add 'rx_buffer_size: {MINIMUM_RX_BUFFER_SIZE}' to your uart configuration.",
|
||||||
|
path=[CONF_UART_ID],
|
||||||
|
)
|
||||||
|
break
|
||||||
|
|
||||||
|
# Validate UART settings
|
||||||
|
schema = uart.final_validate_device_schema(
|
||||||
|
"emontx",
|
||||||
|
baud_rate=115200,
|
||||||
|
require_tx=False,
|
||||||
|
require_rx=True,
|
||||||
|
data_bits=8,
|
||||||
|
parity="NONE",
|
||||||
|
stop_bits=1,
|
||||||
|
)
|
||||||
|
return schema(config)
|
||||||
|
|
||||||
|
|
||||||
|
FINAL_VALIDATE_SCHEMA = final_validate
|
||||||
|
|
||||||
|
|
||||||
|
_CALLBACK_AUTOMATIONS = (
|
||||||
|
automation.CallbackAutomation(
|
||||||
|
CONF_ON_JSON,
|
||||||
|
"add_on_json_callback",
|
||||||
|
[(cg.JsonObject, "json"), (cg.std_string, "raw_json")],
|
||||||
|
),
|
||||||
|
automation.CallbackAutomation(
|
||||||
|
CONF_ON_DATA, "add_on_data_callback", [(cg.std_string, "data")]
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
async def to_code(config: ConfigType) -> None:
|
||||||
|
var = cg.new_Pvariable(config[CONF_ID])
|
||||||
|
await cg.register_component(var, config)
|
||||||
|
await uart.register_uart_device(var, config)
|
||||||
|
|
||||||
|
# Initialize sensor storage with count from final_validate
|
||||||
|
sensor_count = _get_data().sensor_counts.get(str(config[CONF_ID]), 0)
|
||||||
|
if sensor_count > 0:
|
||||||
|
cg.add(var.init_sensors(sensor_count))
|
||||||
|
|
||||||
|
await automation.build_callback_automations(var, config, _CALLBACK_AUTOMATIONS)
|
||||||
|
|
||||||
|
|
||||||
|
# Action: emontx.send_command
|
||||||
|
|
||||||
|
EMONTX_SEND_COMMAND_ACTION_SCHEMA = cv.Schema(
|
||||||
|
{
|
||||||
|
cv.GenerateID(): cv.use_id(EmonTx),
|
||||||
|
cv.Required(CONF_COMMAND): cv.templatable(cv.string),
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@automation.register_action(
|
||||||
|
"emontx.send_command",
|
||||||
|
EmonTxSendCommandAction,
|
||||||
|
EMONTX_SEND_COMMAND_ACTION_SCHEMA,
|
||||||
|
synchronous=True,
|
||||||
|
)
|
||||||
|
async def emontx_send_command_action_to_code(
|
||||||
|
config: ConfigType, action_id, template_arg, args
|
||||||
|
) -> None:
|
||||||
|
var = cg.new_Pvariable(action_id, template_arg)
|
||||||
|
await cg.register_parented(var, config[CONF_ID])
|
||||||
|
template_ = await cg.templatable(config[CONF_COMMAND], args, cg.std_string)
|
||||||
|
cg.add(var.set_command(template_))
|
||||||
|
return var
|
||||||
@@ -0,0 +1,116 @@
|
|||||||
|
#include "emontx.h"
|
||||||
|
#include "esphome/core/log.h"
|
||||||
|
#include "esphome/components/json/json_util.h"
|
||||||
|
|
||||||
|
namespace esphome::emontx {
|
||||||
|
|
||||||
|
static const char *const TAG = "emontx";
|
||||||
|
|
||||||
|
void EmonTx::setup() { this->buffer_pos_ = 0; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Implements the main loop for parsing data from the serial port.
|
||||||
|
*
|
||||||
|
* @details Continuously processes incoming UART data line-by-line:
|
||||||
|
* 1. Fire on_data callbacks for all received lines
|
||||||
|
* 2. If line starts with '{', parse as JSON and update sensors/callbacks
|
||||||
|
*/
|
||||||
|
void EmonTx::loop() {
|
||||||
|
// Read all available data to prevent UART buffer overflow
|
||||||
|
while (this->available() > 0) {
|
||||||
|
uint8_t received = this->read();
|
||||||
|
|
||||||
|
if (received == '\r') {
|
||||||
|
continue; // Ignore CR
|
||||||
|
} else if (received == '\n') {
|
||||||
|
// End of line - process the buffer
|
||||||
|
if (this->buffer_pos_ > 0) {
|
||||||
|
// Null-terminate for safe logging and c_str() use
|
||||||
|
size_t len = this->buffer_pos_;
|
||||||
|
this->buffer_[len] = '\0';
|
||||||
|
this->buffer_pos_ = 0;
|
||||||
|
|
||||||
|
StringRef line(this->buffer_.data(), len);
|
||||||
|
ESP_LOGD(TAG, "Received line: %s", line.c_str());
|
||||||
|
|
||||||
|
// Fire data callbacks for all received lines
|
||||||
|
this->data_callbacks_.call(line);
|
||||||
|
|
||||||
|
// Check if this line is JSON (starts with '{')
|
||||||
|
if (this->buffer_[0] == '{') {
|
||||||
|
ESP_LOGV(TAG, "Line is JSON, parsing...");
|
||||||
|
this->parse_json_(this->buffer_.data(), len);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (this->buffer_pos_ >= MAX_LINE_LENGTH) {
|
||||||
|
ESP_LOGW(TAG, "Buffer overflow (>%zu bytes), discarding buffer", MAX_LINE_LENGTH);
|
||||||
|
this->buffer_pos_ = 0;
|
||||||
|
} else {
|
||||||
|
this->buffer_[this->buffer_pos_++] = static_cast<char>(received);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void EmonTx::parse_json_(const char *data, size_t len) {
|
||||||
|
bool success = json::parse_json(reinterpret_cast<const uint8_t *>(data), len, [this, data, len](JsonObject root) {
|
||||||
|
#ifdef USE_SENSOR
|
||||||
|
for (auto &sensor_pair : this->sensors_) {
|
||||||
|
auto val = root[sensor_pair.first];
|
||||||
|
if (val.is<JsonVariant>()) {
|
||||||
|
float value = val;
|
||||||
|
ESP_LOGV(TAG, "Updating sensor '%s' with value: %.2f", sensor_pair.first, value);
|
||||||
|
sensor_pair.second->publish_state(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
this->json_callbacks_.call(root, StringRef(data, len));
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!success) {
|
||||||
|
ESP_LOGW(TAG, "Failed to parse JSON");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Logs the EmonTx component configuration details.
|
||||||
|
*/
|
||||||
|
void EmonTx::dump_config() {
|
||||||
|
ESP_LOGCONFIG(TAG, "EmonTx:");
|
||||||
|
|
||||||
|
#ifdef USE_SENSOR
|
||||||
|
ESP_LOGCONFIG(TAG, " Registered sensors: %zu", this->sensors_.size());
|
||||||
|
for (const auto &sensor_pair : this->sensors_) {
|
||||||
|
ESP_LOGCONFIG(TAG, " Sensor: %s", sensor_pair.first);
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
ESP_LOGCONFIG(TAG, " Sensor support: DISABLED");
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Sends a command string to the emonTx device via UART.
|
||||||
|
*
|
||||||
|
* @param command The command string to send (LF will be appended automatically).
|
||||||
|
*/
|
||||||
|
void EmonTx::send_command(const std::string &command) {
|
||||||
|
ESP_LOGD(TAG, "Sending command to emonTx: %s", command.c_str());
|
||||||
|
this->write_str(command.c_str());
|
||||||
|
this->write_byte('\n');
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef USE_SENSOR
|
||||||
|
/**
|
||||||
|
* @brief Registers a sensor to receive updates for a specific JSON tag.
|
||||||
|
*
|
||||||
|
* @param tag_name The JSON key to monitor for this sensor (must be a string literal).
|
||||||
|
* @param sensor Pointer to the sensor that will receive value updates.
|
||||||
|
*/
|
||||||
|
void EmonTx::register_sensor(const char *tag_name, sensor::Sensor *sensor) {
|
||||||
|
ESP_LOGCONFIG(TAG, "Registering sensor for tag: %s", tag_name);
|
||||||
|
this->sensors_.emplace_back(tag_name, sensor);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
} // namespace esphome::emontx
|
||||||
@@ -0,0 +1,69 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "esphome/core/component.h"
|
||||||
|
#include "esphome/core/defines.h"
|
||||||
|
#include "esphome/core/automation.h"
|
||||||
|
#include "esphome/core/helpers.h"
|
||||||
|
#include "esphome/core/string_ref.h"
|
||||||
|
#include "esphome/components/uart/uart.h"
|
||||||
|
#include "esphome/components/json/json_util.h"
|
||||||
|
|
||||||
|
#include <array>
|
||||||
|
|
||||||
|
#ifdef USE_SENSOR
|
||||||
|
#include "esphome/components/sensor/sensor.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
namespace esphome::emontx {
|
||||||
|
|
||||||
|
/// Maximum line length in bytes (plus one byte reserved for null terminator)
|
||||||
|
static constexpr size_t MAX_LINE_LENGTH = 1024;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @class EmonTx
|
||||||
|
* @brief Main class for the EmonTx component.
|
||||||
|
*
|
||||||
|
* The EmonTx processes incoming data frames via UART,
|
||||||
|
* extracts tags and values, and publishes them to registered sensors.
|
||||||
|
*/
|
||||||
|
class EmonTx : public Component, public uart::UARTDevice {
|
||||||
|
public:
|
||||||
|
EmonTx() = default;
|
||||||
|
|
||||||
|
void loop() override;
|
||||||
|
void setup() override;
|
||||||
|
void dump_config() override;
|
||||||
|
|
||||||
|
template<typename F> void add_on_json_callback(F &&callback) { this->json_callbacks_.add(std::forward<F>(callback)); }
|
||||||
|
|
||||||
|
template<typename F> void add_on_data_callback(F &&callback) { this->data_callbacks_.add(std::forward<F>(callback)); }
|
||||||
|
|
||||||
|
// Send command to emonTx via UART
|
||||||
|
void send_command(const std::string &command);
|
||||||
|
|
||||||
|
#ifdef USE_SENSOR
|
||||||
|
void init_sensors(size_t count) { this->sensors_.init(count); }
|
||||||
|
void register_sensor(const char *tag_name, sensor::Sensor *sensor);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
protected:
|
||||||
|
void parse_json_(const char *data, size_t len);
|
||||||
|
|
||||||
|
#ifdef USE_SENSOR
|
||||||
|
FixedVector<std::pair<const char *, sensor::Sensor *>> sensors_{};
|
||||||
|
#endif
|
||||||
|
LazyCallbackManager<void(JsonObject, StringRef)> json_callbacks_;
|
||||||
|
LazyCallbackManager<void(StringRef)> data_callbacks_;
|
||||||
|
uint16_t buffer_pos_{0};
|
||||||
|
std::array<char, MAX_LINE_LENGTH + 1> buffer_{};
|
||||||
|
};
|
||||||
|
|
||||||
|
// Action to send command to emonTx
|
||||||
|
template<typename... Ts> class EmonTxSendCommandAction : public Action<Ts...>, public Parented<EmonTx> {
|
||||||
|
public:
|
||||||
|
TEMPLATABLE_VALUE(std::string, command)
|
||||||
|
|
||||||
|
void play(const Ts &...x) override { this->parent_->send_command(this->command_.value(x...)); }
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace esphome::emontx
|
||||||
@@ -0,0 +1,133 @@
|
|||||||
|
import esphome.codegen as cg
|
||||||
|
from esphome.components import sensor
|
||||||
|
import esphome.config_validation as cv
|
||||||
|
from esphome.const import (
|
||||||
|
CONF_ACCURACY_DECIMALS,
|
||||||
|
CONF_DEVICE_CLASS,
|
||||||
|
CONF_ID,
|
||||||
|
CONF_STATE_CLASS,
|
||||||
|
CONF_UNIT_OF_MEASUREMENT,
|
||||||
|
DEVICE_CLASS_CURRENT,
|
||||||
|
DEVICE_CLASS_ENERGY,
|
||||||
|
DEVICE_CLASS_POWER,
|
||||||
|
DEVICE_CLASS_POWER_FACTOR,
|
||||||
|
DEVICE_CLASS_TEMPERATURE,
|
||||||
|
DEVICE_CLASS_VOLTAGE,
|
||||||
|
STATE_CLASS_MEASUREMENT,
|
||||||
|
STATE_CLASS_TOTAL_INCREASING,
|
||||||
|
UNIT_AMPERE,
|
||||||
|
UNIT_CELSIUS,
|
||||||
|
UNIT_EMPTY,
|
||||||
|
UNIT_PULSES,
|
||||||
|
UNIT_VOLT,
|
||||||
|
UNIT_WATT,
|
||||||
|
UNIT_WATT_HOURS,
|
||||||
|
)
|
||||||
|
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)
|
||||||
|
|
||||||
|
# Define sensor type configurations by prefix
|
||||||
|
SENSOR_CONFIGS = {
|
||||||
|
"P": {
|
||||||
|
CONF_UNIT_OF_MEASUREMENT: UNIT_WATT,
|
||||||
|
CONF_DEVICE_CLASS: DEVICE_CLASS_POWER,
|
||||||
|
CONF_STATE_CLASS: STATE_CLASS_MEASUREMENT,
|
||||||
|
CONF_ACCURACY_DECIMALS: 0,
|
||||||
|
},
|
||||||
|
"E": {
|
||||||
|
CONF_UNIT_OF_MEASUREMENT: UNIT_WATT_HOURS,
|
||||||
|
CONF_DEVICE_CLASS: DEVICE_CLASS_ENERGY,
|
||||||
|
CONF_STATE_CLASS: STATE_CLASS_TOTAL_INCREASING,
|
||||||
|
CONF_ACCURACY_DECIMALS: 0,
|
||||||
|
},
|
||||||
|
"V": {
|
||||||
|
CONF_UNIT_OF_MEASUREMENT: UNIT_VOLT,
|
||||||
|
CONF_DEVICE_CLASS: DEVICE_CLASS_VOLTAGE,
|
||||||
|
CONF_STATE_CLASS: STATE_CLASS_MEASUREMENT,
|
||||||
|
CONF_ACCURACY_DECIMALS: 2,
|
||||||
|
},
|
||||||
|
"I": {
|
||||||
|
CONF_UNIT_OF_MEASUREMENT: UNIT_AMPERE,
|
||||||
|
CONF_DEVICE_CLASS: DEVICE_CLASS_CURRENT,
|
||||||
|
CONF_STATE_CLASS: STATE_CLASS_MEASUREMENT,
|
||||||
|
CONF_ACCURACY_DECIMALS: 2,
|
||||||
|
},
|
||||||
|
"T": {
|
||||||
|
CONF_UNIT_OF_MEASUREMENT: UNIT_CELSIUS,
|
||||||
|
CONF_DEVICE_CLASS: DEVICE_CLASS_TEMPERATURE,
|
||||||
|
CONF_STATE_CLASS: STATE_CLASS_MEASUREMENT,
|
||||||
|
CONF_ACCURACY_DECIMALS: 2,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
# Pattern-based configurations
|
||||||
|
PATTERN_CONFIGS = {
|
||||||
|
"PULSE": {
|
||||||
|
CONF_UNIT_OF_MEASUREMENT: UNIT_PULSES,
|
||||||
|
CONF_DEVICE_CLASS: DEVICE_CLASS_ENERGY,
|
||||||
|
CONF_ACCURACY_DECIMALS: 0,
|
||||||
|
},
|
||||||
|
"PF": {
|
||||||
|
CONF_UNIT_OF_MEASUREMENT: UNIT_EMPTY,
|
||||||
|
CONF_DEVICE_CLASS: DEVICE_CLASS_POWER_FACTOR,
|
||||||
|
CONF_STATE_CLASS: STATE_CLASS_MEASUREMENT,
|
||||||
|
CONF_ACCURACY_DECIMALS: 2,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
# Create a base schema that's flexible for any tag
|
||||||
|
BASE_SCHEMA = sensor.sensor_schema(
|
||||||
|
EmonTxSensor,
|
||||||
|
state_class=STATE_CLASS_MEASUREMENT,
|
||||||
|
accuracy_decimals=0,
|
||||||
|
).extend(
|
||||||
|
{
|
||||||
|
cv.GenerateID(CONF_EMONTX_ID): cv.use_id(EmonTx),
|
||||||
|
cv.Required(CONF_TAG_NAME): cv.string,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def apply_tag_defaults(config: ConfigType) -> ConfigType:
|
||||||
|
"""Apply defaults based on tag prefix if applicable, but don't restrict any tags."""
|
||||||
|
tag = config[CONF_TAG_NAME]
|
||||||
|
|
||||||
|
# Skip if tag is too short
|
||||||
|
if len(tag) < 2:
|
||||||
|
return config
|
||||||
|
|
||||||
|
# Check if this tag starts with a known prefix
|
||||||
|
tag_upper = tag.upper()
|
||||||
|
|
||||||
|
for pattern, pattern_config in PATTERN_CONFIGS.items():
|
||||||
|
if tag_upper.startswith(pattern):
|
||||||
|
# Apply pattern defaults if not overridden by user
|
||||||
|
for key, value in pattern_config.items():
|
||||||
|
if key not in config:
|
||||||
|
config[key] = value
|
||||||
|
return config
|
||||||
|
|
||||||
|
# Only apply defaults for known prefixes with numeric indices
|
||||||
|
prefix = tag_upper[0]
|
||||||
|
if prefix in SENSOR_CONFIGS and len(tag) > 1 and tag[1:].isdigit():
|
||||||
|
# Apply defaults for known tag types, but only if not overridden by user
|
||||||
|
defaults = SENSOR_CONFIGS[prefix]
|
||||||
|
for key, value in defaults.items():
|
||||||
|
if key not in config:
|
||||||
|
config[key] = value
|
||||||
|
|
||||||
|
return config
|
||||||
|
|
||||||
|
|
||||||
|
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))
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
#include "emontx_sensor.h"
|
||||||
|
#include "esphome/core/log.h"
|
||||||
|
|
||||||
|
namespace esphome::emontx {
|
||||||
|
|
||||||
|
static const char *const TAG = "emontx_sensor";
|
||||||
|
|
||||||
|
void EmonTxSensor::dump_config() { LOG_SENSOR(" ", "EmonTx Sensor", this); }
|
||||||
|
|
||||||
|
} // namespace esphome::emontx
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "esphome/components/sensor/sensor.h"
|
||||||
|
#include "esphome/core/component.h"
|
||||||
|
|
||||||
|
namespace esphome::emontx {
|
||||||
|
|
||||||
|
class EmonTxSensor : public sensor::Sensor, public Component {
|
||||||
|
public:
|
||||||
|
void dump_config() override;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace esphome::emontx
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
button:
|
||||||
|
- platform: template
|
||||||
|
name: Send command test
|
||||||
|
on_press:
|
||||||
|
- emontx.send_command:
|
||||||
|
id: test_emontx
|
||||||
|
command: "v"
|
||||||
|
|
||||||
|
emontx:
|
||||||
|
id: test_emontx
|
||||||
|
on_json:
|
||||||
|
- then:
|
||||||
|
- logger.log: "Got JSON"
|
||||||
|
on_data:
|
||||||
|
- then:
|
||||||
|
- logger.log:
|
||||||
|
format: "Got data: %s"
|
||||||
|
args: [data.c_str()]
|
||||||
|
|
||||||
|
sensor:
|
||||||
|
- platform: emontx
|
||||||
|
name: Power
|
||||||
|
tag_name: P1
|
||||||
|
emontx_id: test_emontx
|
||||||
|
unit_of_measurement: W
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
packages:
|
||||||
|
uart: !include ../../test_build_components/common/uart_115200/esp32-idf.yaml
|
||||||
|
|
||||||
|
<<: !include common.yaml
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
packages:
|
||||||
|
uart: !include ../../test_build_components/common/uart_115200/esp8266-ard.yaml
|
||||||
|
|
||||||
|
<<: !include common.yaml
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
packages:
|
||||||
|
uart: !include ../../test_build_components/common/uart_115200/rp2040-ard.yaml
|
||||||
|
|
||||||
|
<<: !include common.yaml
|
||||||
@@ -9,3 +9,4 @@ uart:
|
|||||||
tx_pin: ${tx_pin}
|
tx_pin: ${tx_pin}
|
||||||
rx_pin: ${rx_pin}
|
rx_pin: ${rx_pin}
|
||||||
baud_rate: 115200
|
baud_rate: 115200
|
||||||
|
rx_buffer_size: 2048
|
||||||
|
|||||||
@@ -9,3 +9,4 @@ uart:
|
|||||||
tx_pin: ${tx_pin}
|
tx_pin: ${tx_pin}
|
||||||
rx_pin: ${rx_pin}
|
rx_pin: ${rx_pin}
|
||||||
baud_rate: 115200
|
baud_rate: 115200
|
||||||
|
rx_buffer_size: 2048
|
||||||
|
|||||||
@@ -10,3 +10,4 @@ uart:
|
|||||||
tx_pin: ${tx_pin}
|
tx_pin: ${tx_pin}
|
||||||
rx_pin: ${rx_pin}
|
rx_pin: ${rx_pin}
|
||||||
baud_rate: 115200
|
baud_rate: 115200
|
||||||
|
rx_buffer_size: 2048
|
||||||
|
|||||||
@@ -10,3 +10,4 @@ uart:
|
|||||||
tx_pin: ${tx_pin}
|
tx_pin: ${tx_pin}
|
||||||
rx_pin: ${rx_pin}
|
rx_pin: ${rx_pin}
|
||||||
baud_rate: 115200
|
baud_rate: 115200
|
||||||
|
rx_buffer_size: 2048
|
||||||
|
|||||||
@@ -9,3 +9,4 @@ uart:
|
|||||||
tx_pin: ${tx_pin}
|
tx_pin: ${tx_pin}
|
||||||
rx_pin: ${rx_pin}
|
rx_pin: ${rx_pin}
|
||||||
baud_rate: 115200
|
baud_rate: 115200
|
||||||
|
rx_buffer_size: 2048
|
||||||
|
|||||||
@@ -9,3 +9,4 @@ uart:
|
|||||||
tx_pin: ${tx_pin}
|
tx_pin: ${tx_pin}
|
||||||
rx_pin: ${rx_pin}
|
rx_pin: ${rx_pin}
|
||||||
baud_rate: 115200
|
baud_rate: 115200
|
||||||
|
rx_buffer_size: 2048
|
||||||
|
|||||||
Reference in New Issue
Block a user