Add support for SDMXXX energy meters (#1260)

Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com>
This commit is contained in:
polyfaces 2021-05-31 12:05:49 +08:00 committed by GitHub
parent 9a2cf05c5f
commit bb759d52c8
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
12 changed files with 525 additions and 2 deletions

View file

@ -81,6 +81,7 @@ esphome/components/restart/* @esphome/core
esphome/components/rf_bridge/* @jesserockz esphome/components/rf_bridge/* @jesserockz
esphome/components/rtttl/* @glmnet esphome/components/rtttl/* @glmnet
esphome/components/script/* @esphome/core esphome/components/script/* @esphome/core
esphome/components/sdm_meter/* @jesserockz @polyfaces
esphome/components/sensor/* @esphome/core esphome/components/sensor/* @esphome/core
esphome/components/sgp40/* @SenexCrenshaw esphome/components/sgp40/* @SenexCrenshaw
esphome/components/sht4x/* @sjtrny esphome/components/sht4x/* @sjtrny

View file

@ -3,6 +3,7 @@ import esphome.config_validation as cv
from esphome.components import sensor, spi from esphome.components import sensor, spi
from esphome.const import ( from esphome.const import (
CONF_ID, CONF_ID,
CONF_REACTIVE_POWER,
CONF_VOLTAGE, CONF_VOLTAGE,
CONF_CURRENT, CONF_CURRENT,
CONF_POWER, CONF_POWER,
@ -34,7 +35,6 @@ CONF_PHASE_A = "phase_a"
CONF_PHASE_B = "phase_b" CONF_PHASE_B = "phase_b"
CONF_PHASE_C = "phase_c" CONF_PHASE_C = "phase_c"
CONF_REACTIVE_POWER = "reactive_power"
CONF_LINE_FREQUENCY = "line_frequency" CONF_LINE_FREQUENCY = "line_frequency"
CONF_CHIP_TEMPERATURE = "chip_temperature" CONF_CHIP_TEMPERATURE = "chip_temperature"
CONF_GAIN_PGA = "gain_pga" CONF_GAIN_PGA = "gain_pga"

View file

@ -1,7 +1,9 @@
import esphome.codegen as cg import esphome.codegen as cg
import esphome.config_validation as cv import esphome.config_validation as cv
from esphome.cpp_helpers import gpio_pin_expression
from esphome.components import uart from esphome.components import uart
from esphome.const import CONF_ID, CONF_ADDRESS from esphome.const import CONF_FLOW_CONTROL_PIN, CONF_ID, CONF_ADDRESS
from esphome import pins
DEPENDENCIES = ["uart"] DEPENDENCIES = ["uart"]
@ -15,6 +17,7 @@ CONFIG_SCHEMA = (
cv.Schema( cv.Schema(
{ {
cv.GenerateID(): cv.declare_id(Modbus), cv.GenerateID(): cv.declare_id(Modbus),
cv.Optional(CONF_FLOW_CONTROL_PIN): pins.gpio_output_pin_schema,
} }
) )
.extend(cv.COMPONENT_SCHEMA) .extend(cv.COMPONENT_SCHEMA)
@ -29,6 +32,10 @@ async def to_code(config):
await uart.register_uart_device(var, config) await uart.register_uart_device(var, config)
if CONF_FLOW_CONTROL_PIN in config:
pin = await gpio_pin_expression(config[CONF_FLOW_CONTROL_PIN])
cg.add(var.set_flow_control_pin(pin))
def modbus_device_schema(default_address): def modbus_device_schema(default_address):
schema = { schema = {

View file

@ -6,6 +6,11 @@ namespace modbus {
static const char *TAG = "modbus"; static const char *TAG = "modbus";
void Modbus::setup() {
if (this->flow_control_pin_ != nullptr) {
this->flow_control_pin_->setup();
}
}
void Modbus::loop() { void Modbus::loop() {
const uint32_t now = millis(); const uint32_t now = millis();
if (now - this->last_modbus_byte_ > 50) { if (now - this->last_modbus_byte_ > 50) {
@ -94,6 +99,7 @@ bool Modbus::parse_modbus_byte_(uint8_t byte) {
void Modbus::dump_config() { void Modbus::dump_config() {
ESP_LOGCONFIG(TAG, "Modbus:"); ESP_LOGCONFIG(TAG, "Modbus:");
LOG_PIN(" Flow Control Pin: ", this->flow_control_pin_);
this->check_uart_settings(9600, 2); this->check_uart_settings(9600, 2);
} }
float Modbus::get_setup_priority() const { float Modbus::get_setup_priority() const {
@ -112,7 +118,14 @@ void Modbus::send(uint8_t address, uint8_t function, uint16_t start_address, uin
frame[6] = crc >> 0; frame[6] = crc >> 0;
frame[7] = crc >> 8; frame[7] = crc >> 8;
if (this->flow_control_pin_ != nullptr)
this->flow_control_pin_->digital_write(true);
this->write_array(frame, 8); this->write_array(frame, 8);
this->flush();
if (this->flow_control_pin_ != nullptr)
this->flow_control_pin_->digital_write(false);
} }
} // namespace modbus } // namespace modbus

View file

@ -12,6 +12,8 @@ class Modbus : public uart::UARTDevice, public Component {
public: public:
Modbus() = default; Modbus() = default;
void setup() override;
void loop() override; void loop() override;
void dump_config() override; void dump_config() override;
@ -22,7 +24,11 @@ class Modbus : public uart::UARTDevice, public Component {
void send(uint8_t address, uint8_t function, uint16_t start_address, uint16_t register_count); void send(uint8_t address, uint8_t function, uint16_t start_address, uint16_t register_count);
void set_flow_control_pin(GPIOPin *flow_control_pin) { this->flow_control_pin_ = flow_control_pin; }
protected: protected:
GPIOPin *flow_control_pin_{nullptr};
bool parse_modbus_byte_(uint8_t byte); bool parse_modbus_byte_(uint8_t byte);
std::vector<uint8_t> rx_buffer_; std::vector<uint8_t> rx_buffer_;

View file

View file

@ -0,0 +1,106 @@
#include "sdm_meter.h"
#include "sdm_meter_registers.h"
#include "esphome/core/log.h"
namespace esphome {
namespace sdm_meter {
static const char *TAG = "sdm_meter";
static const uint8_t MODBUS_CMD_READ_IN_REGISTERS = 0x04;
static const uint8_t MODBUS_REGISTER_COUNT = 80; // 74 x 16-bit registers
void SDMMeter::on_modbus_data(const std::vector<uint8_t> &data) {
if (data.size() < MODBUS_REGISTER_COUNT * 2) {
ESP_LOGW(TAG, "Invalid size for SDMMeter!");
return;
}
auto sdm_meter_get_float = [&](size_t i) -> float {
uint32_t temp = encode_uint32(data[i], data[i + 1], data[i + 2], data[i + 3]);
float f;
memcpy(&f, &temp, sizeof(f));
return f;
};
for (uint8_t i = 0; i < 3; i++) {
auto phase = this->phases_[i];
if (!phase.setup)
continue;
float voltage = sdm_meter_get_float(SDM_PHASE_1_VOLTAGE * 2 + (i * 4));
float current = sdm_meter_get_float(SDM_PHASE_1_CURRENT * 2 + (i * 4));
float active_power = sdm_meter_get_float(SDM_PHASE_1_ACTIVE_POWER * 2 + (i * 4));
float apparent_power = sdm_meter_get_float(SDM_PHASE_1_APPARENT_POWER * 2 + (i * 4));
float reactive_power = sdm_meter_get_float(SDM_PHASE_1_REACTIVE_POWER * 2 + (i * 4));
float power_factor = sdm_meter_get_float(SDM_PHASE_1_POWER_FACTOR * 2 + (i * 4));
float phase_angle = sdm_meter_get_float(SDM_PHASE_1_ANGLE * 2 + (i * 4));
ESP_LOGD(
TAG,
"SDMMeter Phase %c: V=%.3f V, I=%.3f A, Active P=%.3f W, Apparent P=%.3f VA, Reactive P=%.3f VAR, PF=%.3f, "
"PA=%.3f °",
i + 'A', voltage, current, active_power, apparent_power, reactive_power, power_factor, phase_angle);
if (phase.voltage_sensor_ != nullptr)
phase.voltage_sensor_->publish_state(voltage);
if (phase.current_sensor_ != nullptr)
phase.current_sensor_->publish_state(current);
if (phase.active_power_sensor_ != nullptr)
phase.active_power_sensor_->publish_state(active_power);
if (phase.apparent_power_sensor_ != nullptr)
phase.apparent_power_sensor_->publish_state(apparent_power);
if (phase.reactive_power_sensor_ != nullptr)
phase.reactive_power_sensor_->publish_state(reactive_power);
if (phase.power_factor_sensor_ != nullptr)
phase.power_factor_sensor_->publish_state(power_factor);
if (phase.phase_angle_sensor_ != nullptr)
phase.phase_angle_sensor_->publish_state(phase_angle);
}
float frequency = sdm_meter_get_float(SDM_FREQUENCY * 2);
float import_active_energy = sdm_meter_get_float(SDM_IMPORT_ACTIVE_ENERGY * 2);
float export_active_energy = sdm_meter_get_float(SDM_EXPORT_ACTIVE_ENERGY * 2);
float import_reactive_energy = sdm_meter_get_float(SDM_IMPORT_REACTIVE_ENERGY * 2);
float export_reactive_energy = sdm_meter_get_float(SDM_EXPORT_REACTIVE_ENERGY * 2);
ESP_LOGD(TAG, "SDMMeter: F=%.3f Hz, Im.A.E=%.3f Wh, Ex.A.E=%.3f Wh, Im.R.E=%.3f VARh, Ex.R.E=%.3f VARh", frequency,
import_active_energy, export_active_energy, import_reactive_energy, export_reactive_energy);
if (this->frequency_sensor_ != nullptr)
this->frequency_sensor_->publish_state(frequency);
if (this->import_active_energy_sensor_ != nullptr)
this->import_active_energy_sensor_->publish_state(import_active_energy);
if (this->export_active_energy_sensor_ != nullptr)
this->export_active_energy_sensor_->publish_state(export_active_energy);
if (this->import_reactive_energy_sensor_ != nullptr)
this->import_reactive_energy_sensor_->publish_state(import_reactive_energy);
if (this->export_reactive_energy_sensor_ != nullptr)
this->export_reactive_energy_sensor_->publish_state(export_reactive_energy);
}
void SDMMeter::update() { this->send(MODBUS_CMD_READ_IN_REGISTERS, 0, MODBUS_REGISTER_COUNT); }
void SDMMeter::dump_config() {
ESP_LOGCONFIG(TAG, "SDM Meter:");
ESP_LOGCONFIG(TAG, " Address: 0x%02X", this->address_);
for (uint8_t i = 0; i < 3; i++) {
auto phase = this->phases_[i];
if (!phase.setup)
continue;
ESP_LOGCONFIG(TAG, " Phase %c", i + 'A');
LOG_SENSOR(" ", "Voltage", phase.voltage_sensor_);
LOG_SENSOR(" ", "Current", phase.current_sensor_);
LOG_SENSOR(" ", "Active Power", phase.active_power_sensor_);
LOG_SENSOR(" ", "Apparent Power", phase.apparent_power_sensor_);
LOG_SENSOR(" ", "Reactive Power", phase.reactive_power_sensor_);
LOG_SENSOR(" ", "Power Factor", phase.power_factor_sensor_);
LOG_SENSOR(" ", "Phase Angle", phase.phase_angle_sensor_);
}
LOG_SENSOR(" ", "Frequency", this->frequency_sensor_);
LOG_SENSOR(" ", "Import Active Energy", this->import_active_energy_sensor_);
LOG_SENSOR(" ", "Export Active Energy", this->export_active_energy_sensor_);
LOG_SENSOR(" ", "Import Reactive Energy", this->import_reactive_energy_sensor_);
LOG_SENSOR(" ", "Export Reactive Energy", this->export_reactive_energy_sensor_);
}
} // namespace sdm_meter
} // namespace esphome

View file

@ -0,0 +1,79 @@
#pragma once
#include "esphome/core/component.h"
#include "esphome/components/sensor/sensor.h"
#include "esphome/components/modbus/modbus.h"
namespace esphome {
namespace sdm_meter {
class SDMMeter : public PollingComponent, public modbus::ModbusDevice {
public:
void set_voltage_sensor(uint8_t phase, sensor::Sensor *voltage_sensor) {
this->phases_[phase].setup = true;
this->phases_[phase].voltage_sensor_ = voltage_sensor;
}
void set_current_sensor(uint8_t phase, sensor::Sensor *current_sensor) {
this->phases_[phase].setup = true;
this->phases_[phase].current_sensor_ = current_sensor;
}
void set_active_power_sensor(uint8_t phase, sensor::Sensor *active_power_sensor) {
this->phases_[phase].setup = true;
this->phases_[phase].active_power_sensor_ = active_power_sensor;
}
void set_apparent_power_sensor(uint8_t phase, sensor::Sensor *apparent_power_sensor) {
this->phases_[phase].setup = true;
this->phases_[phase].apparent_power_sensor_ = apparent_power_sensor;
}
void set_reactive_power_sensor(uint8_t phase, sensor::Sensor *reactive_power_sensor) {
this->phases_[phase].setup = true;
this->phases_[phase].reactive_power_sensor_ = reactive_power_sensor;
}
void set_power_factor_sensor(uint8_t phase, sensor::Sensor *power_factor_sensor) {
this->phases_[phase].setup = true;
this->phases_[phase].power_factor_sensor_ = power_factor_sensor;
}
void set_phase_angle_sensor(uint8_t phase, sensor::Sensor *phase_angle_sensor) {
this->phases_[phase].setup = true;
this->phases_[phase].phase_angle_sensor_ = phase_angle_sensor;
}
void set_frequency_sensor(sensor::Sensor *frequency_sensor) { this->frequency_sensor_ = frequency_sensor; }
void set_import_active_energy_sensor(sensor::Sensor *import_active_energy_sensor) {
this->import_active_energy_sensor_ = import_active_energy_sensor;
}
void set_export_active_energy_sensor(sensor::Sensor *export_active_energy_sensor) {
this->export_active_energy_sensor_ = export_active_energy_sensor;
}
void set_import_reactive_energy_sensor(sensor::Sensor *import_reactive_energy_sensor) {
this->import_reactive_energy_sensor_ = import_reactive_energy_sensor;
}
void set_export_reactive_energy_sensor(sensor::Sensor *export_reactive_energy_sensor) {
this->export_reactive_energy_sensor_ = export_reactive_energy_sensor;
}
void update() override;
void on_modbus_data(const std::vector<uint8_t> &data) override;
void dump_config() override;
protected:
struct SDMPhase {
bool setup{false};
sensor::Sensor *voltage_sensor_{nullptr};
sensor::Sensor *current_sensor_{nullptr};
sensor::Sensor *active_power_sensor_{nullptr};
sensor::Sensor *apparent_power_sensor_{nullptr};
sensor::Sensor *reactive_power_sensor_{nullptr};
sensor::Sensor *power_factor_sensor_{nullptr};
sensor::Sensor *phase_angle_sensor_{nullptr};
} phases_[3];
sensor::Sensor *frequency_sensor_{nullptr};
sensor::Sensor *import_active_energy_sensor_{nullptr};
sensor::Sensor *export_active_energy_sensor_{nullptr};
sensor::Sensor *import_reactive_energy_sensor_{nullptr};
sensor::Sensor *export_reactive_energy_sensor_{nullptr};
};
} // namespace sdm_meter
} // namespace esphome

View file

@ -0,0 +1,114 @@
#pragma once
namespace esphome {
namespace sdm_meter {
/* PHASE STATUS REGISTERS */
static const uint16_t SDM_PHASE_1_VOLTAGE = 0x0000;
static const uint16_t SDM_PHASE_2_VOLTAGE = 0x0002;
static const uint16_t SDM_PHASE_3_VOLTAGE = 0x0004;
static const uint16_t SDM_PHASE_1_CURRENT = 0x0006;
static const uint16_t SDM_PHASE_2_CURRENT = 0x0008;
static const uint16_t SDM_PHASE_3_CURRENT = 0x000A;
static const uint16_t SDM_PHASE_1_ACTIVE_POWER = 0x000C;
static const uint16_t SDM_PHASE_2_ACTIVE_POWER = 0x000E;
static const uint16_t SDM_PHASE_3_ACTIVE_POWER = 0x0010;
static const uint16_t SDM_PHASE_1_APPARENT_POWER = 0x0012;
static const uint16_t SDM_PHASE_2_APPARENT_POWER = 0x0014;
static const uint16_t SDM_PHASE_3_APPARENT_POWER = 0x0016;
static const uint16_t SDM_PHASE_1_REACTIVE_POWER = 0x0018;
static const uint16_t SDM_PHASE_2_REACTIVE_POWER = 0x001A;
static const uint16_t SDM_PHASE_3_REACTIVE_POWER = 0x001C;
static const uint16_t SDM_PHASE_1_POWER_FACTOR = 0x001E;
static const uint16_t SDM_PHASE_2_POWER_FACTOR = 0x0020;
static const uint16_t SDM_PHASE_3_POWER_FACTOR = 0x0022;
static const uint16_t SDM_PHASE_1_ANGLE = 0x0024;
static const uint16_t SDM_PHASE_2_ANGLE = 0x0026;
static const uint16_t SDM_PHASE_3_ANGLE = 0x0028;
static const uint16_t SDM_AVERAGE_L_TO_N_VOLTS = 0x002A;
static const uint16_t SDM_AVERAGE_LINE_CURRENT = 0x002E;
static const uint16_t SDM_SUM_LINE_CURRENT = 0x0030;
static const uint16_t SDM_TOTAL_SYSTEM_POWER = 0x0034;
static const uint16_t SDM_TOTAL_SYSTEM_APPARENT_POWER = 0x0038;
static const uint16_t SDM_TOTAL_SYSTEM_REACTIVE_POWER = 0x003C;
static const uint16_t SDM_TOTAL_SYSTEM_POWER_FACTOR = 0x003E;
static const uint16_t SDM_TOTAL_SYSTEM_PHASE_ANGLE = 0x0042;
static const uint16_t SDM_FREQUENCY = 0x0046;
static const uint16_t SDM_IMPORT_ACTIVE_ENERGY = 0x0048;
static const uint16_t SDM_EXPORT_ACTIVE_ENERGY = 0x004A;
static const uint16_t SDM_IMPORT_REACTIVE_ENERGY = 0x004C;
static const uint16_t SDM_EXPORT_REACTIVE_ENERGY = 0x004E;
static const uint16_t SDM_VAH_SINCE_LAST_RESET = 0x0050;
static const uint16_t SDM_AH_SINCE_LAST_RESET = 0x0052;
static const uint16_t SDM_TOTAL_SYSTEM_POWER_DEMAND = 0x0054;
static const uint16_t SDM_MAXIMUM_TOTAL_SYSTEM_POWER_DEMAND = 0x0056;
static const uint16_t SDM_CURRENT_SYSTEM_POSITIVE_POWER_DEMAND = 0x0058;
static const uint16_t SDM_MAXIMUM_SYSTEM_POSITIVE_POWER_DEMAND = 0x005A;
static const uint16_t SDM_CURRENT_SYSTEM_REVERSE_POWER_DEMAND = 0x005C;
static const uint16_t SDM_MAXIMUM_SYSTEM_REVERSE_POWER_DEMAND = 0x005E;
static const uint16_t SDM_TOTAL_SYSTEM_VA_DEMAND = 0x0064;
static const uint16_t SDM_MAXIMUM_TOTAL_SYSTEM_VA_DEMAND = 0x0066;
static const uint16_t SDM_NEUTRAL_CURRENT_DEMAND = 0x0068;
static const uint16_t SDM_MAXIMUM_NEUTRAL_CURRENT = 0x006A;
static const uint16_t SDM_LINE_1_TO_LINE_2_VOLTS = 0x00C8;
static const uint16_t SDM_LINE_2_TO_LINE_3_VOLTS = 0x00CA;
static const uint16_t SDM_LINE_3_TO_LINE_1_VOLTS = 0x00CC;
static const uint16_t SDM_AVERAGE_LINE_TO_LINE_VOLTS = 0x00CE;
static const uint16_t SDM_NEUTRAL_CURRENT = 0x00E0;
static const uint16_t SDM_PHASE_1_LN_VOLTS_THD = 0x00EA;
static const uint16_t SDM_PHASE_2_LN_VOLTS_THD = 0x00EC;
static const uint16_t SDM_PHASE_3_LN_VOLTS_THD = 0x00EE;
static const uint16_t SDM_PHASE_1_CURRENT_THD = 0x00F0;
static const uint16_t SDM_PHASE_2_CURRENT_THD = 0x00F2;
static const uint16_t SDM_PHASE_3_CURRENT_THD = 0x00F4;
static const uint16_t SDM_AVERAGE_LINE_TO_NEUTRAL_VOLTS_THD = 0x00F8;
static const uint16_t SDM_AVERAGE_LINE_CURRENT_THD = 0x00FA;
static const uint16_t SDM_TOTAL_SYSTEM_POWER_FACTOR_INV = 0x00FE;
static const uint16_t SDM_PHASE_1_CURRENT_DEMAND = 0x0102;
static const uint16_t SDM_PHASE_2_CURRENT_DEMAND = 0x0104;
static const uint16_t SDM_PHASE_3_CURRENT_DEMAND = 0x0106;
static const uint16_t SDM_MAXIMUM_PHASE_1_CURRENT_DEMAND = 0x0108;
static const uint16_t SDM_MAXIMUM_PHASE_2_CURRENT_DEMAND = 0x010A;
static const uint16_t SDM_MAXIMUM_PHASE_3_CURRENT_DEMAND = 0x010C;
static const uint16_t SDM_LINE_1_TO_LINE_2_VOLTS_THD = 0x014E;
static const uint16_t SDM_LINE_2_TO_LINE_3_VOLTS_THD = 0x0150;
static const uint16_t SDM_LINE_3_TO_LINE_1_VOLTS_THD = 0x0152;
static const uint16_t SDM_AVERAGE_LINE_TO_LINE_VOLTS_THD = 0x0154;
static const uint16_t SDM_TOTAL_ACTIVE_ENERGY = 0x0156;
static const uint16_t SDM_TOTAL_REACTIVE_ENERGY = 0x0158;
static const uint16_t SDM_L1_IMPORT_ACTIVE_ENERGY = 0x015A;
static const uint16_t SDM_L2_IMPORT_ACTIVE_ENERGY = 0x015C;
static const uint16_t SDM_L3_IMPORT_ACTIVE_ENERGY = 0x015E;
static const uint16_t SDM_L1_EXPORT_ACTIVE_ENERGY = 0x0160;
static const uint16_t SDM_L2_EXPORT_ACTIVE_ENERGY = 0x0162;
static const uint16_t SDM_L3_EXPORT_ACTIVE_ENERGY = 0x0164;
static const uint16_t SDM_L1_TOTAL_ACTIVE_ENERGY = 0x0166;
static const uint16_t SDM_L2_TOTAL_ACTIVE_ENERGY = 0x0168;
static const uint16_t SDM_L3_TOTAL_ACTIVE_ENERGY = 0x016a;
static const uint16_t SDM_L1_IMPORT_REACTIVE_ENERGY = 0x016C;
static const uint16_t SDM_L2_IMPORT_REACTIVE_ENERGY = 0x016E;
static const uint16_t SDM_L3_IMPORT_REACTIVE_ENERGY = 0x0170;
static const uint16_t SDM_L1_EXPORT_REACTIVE_ENERGY = 0x0172;
static const uint16_t SDM_L2_EXPORT_REACTIVE_ENERGY = 0x0174;
static const uint16_t SDM_L3_EXPORT_REACTIVE_ENERGY = 0x0176;
static const uint16_t SDM_L1_TOTAL_REACTIVE_ENERGY = 0x0178;
static const uint16_t SDM_L2_TOTAL_REACTIVE_ENERGY = 0x017A;
static const uint16_t SDM_L3_TOTAL_REACTIVE_ENERGY = 0x017C;
static const uint16_t SDM_CURRENT_RESETTABLE_TOTAL_ACTIVE_ENERGY = 0x0180;
static const uint16_t SDM_CURRENT_RESETTABLE_TOTAL_REACTIVE_ENERGY = 0x0182;
static const uint16_t SDM_CURRENT_RESETTABLE_IMPORT_ENERGY = 0x0184;
static const uint16_t SDM_CURRENT_RESETTABLE_EXPORT_ENERGY = 0x0186;
static const uint16_t SDM_IMPORT_POWER = 0x0500;
static const uint16_t SDM_EXPORT_POWER = 0x0502;
} // namespace sdm_meter
} // namespace esphome

View file

@ -0,0 +1,131 @@
from esphome.components.atm90e32.sensor import CONF_PHASE_A, CONF_PHASE_B, CONF_PHASE_C
import esphome.codegen as cg
import esphome.config_validation as cv
from esphome.components import sensor, modbus
from esphome.const import (
CONF_ACTIVE_POWER,
CONF_APPARENT_POWER,
CONF_CURRENT,
CONF_EXPORT_ACTIVE_ENERGY,
CONF_EXPORT_REACTIVE_ENERGY,
CONF_FREQUENCY,
CONF_ID,
CONF_IMPORT_ACTIVE_ENERGY,
CONF_IMPORT_REACTIVE_ENERGY,
CONF_PHASE_ANGLE,
CONF_POWER_FACTOR,
CONF_REACTIVE_POWER,
CONF_VOLTAGE,
DEVICE_CLASS_CURRENT,
DEVICE_CLASS_ENERGY,
DEVICE_CLASS_POWER,
DEVICE_CLASS_POWER_FACTOR,
DEVICE_CLASS_VOLTAGE,
ICON_CURRENT_AC,
ICON_EMPTY,
ICON_FLASH,
UNIT_AMPERE,
UNIT_DEGREES,
UNIT_EMPTY,
UNIT_HERTZ,
UNIT_VOLT,
UNIT_VOLT_AMPS,
UNIT_VOLT_AMPS_REACTIVE,
UNIT_VOLT_AMPS_REACTIVE_HOURS,
UNIT_WATT,
UNIT_WATT_HOURS,
)
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)
PHASE_SENSORS = {
CONF_VOLTAGE: sensor.sensor_schema(UNIT_VOLT, ICON_EMPTY, 2, DEVICE_CLASS_VOLTAGE),
CONF_CURRENT: sensor.sensor_schema(
UNIT_AMPERE, ICON_EMPTY, 3, DEVICE_CLASS_CURRENT
),
CONF_ACTIVE_POWER: sensor.sensor_schema(
UNIT_WATT, ICON_EMPTY, 2, DEVICE_CLASS_POWER
),
CONF_APPARENT_POWER: sensor.sensor_schema(
UNIT_VOLT_AMPS, ICON_EMPTY, 2, DEVICE_CLASS_POWER
),
CONF_REACTIVE_POWER: sensor.sensor_schema(
UNIT_VOLT_AMPS_REACTIVE, ICON_EMPTY, 2, DEVICE_CLASS_POWER
),
CONF_POWER_FACTOR: sensor.sensor_schema(
UNIT_EMPTY, ICON_EMPTY, 3, DEVICE_CLASS_POWER_FACTOR
),
CONF_PHASE_ANGLE: sensor.sensor_schema(UNIT_DEGREES, ICON_FLASH, 3),
}
PHASE_SCHEMA = cv.Schema(
{cv.Optional(sensor): schema for sensor, schema in PHASE_SENSORS.items()}
)
CONFIG_SCHEMA = (
cv.Schema(
{
cv.GenerateID(): cv.declare_id(SDMMeter),
cv.Optional(CONF_PHASE_A): PHASE_SCHEMA,
cv.Optional(CONF_PHASE_B): PHASE_SCHEMA,
cv.Optional(CONF_PHASE_C): PHASE_SCHEMA,
cv.Optional(CONF_FREQUENCY): sensor.sensor_schema(
UNIT_HERTZ, ICON_CURRENT_AC, 3
),
cv.Optional(CONF_IMPORT_ACTIVE_ENERGY): sensor.sensor_schema(
UNIT_WATT_HOURS, ICON_EMPTY, 2, DEVICE_CLASS_ENERGY
),
cv.Optional(CONF_EXPORT_ACTIVE_ENERGY): sensor.sensor_schema(
UNIT_WATT_HOURS, ICON_EMPTY, 2, DEVICE_CLASS_ENERGY
),
cv.Optional(CONF_IMPORT_REACTIVE_ENERGY): sensor.sensor_schema(
UNIT_VOLT_AMPS_REACTIVE_HOURS, ICON_EMPTY, 2, DEVICE_CLASS_ENERGY
),
cv.Optional(CONF_EXPORT_REACTIVE_ENERGY): sensor.sensor_schema(
UNIT_VOLT_AMPS_REACTIVE_HOURS, ICON_EMPTY, 2, DEVICE_CLASS_ENERGY
),
}
)
.extend(cv.polling_component_schema("10s"))
.extend(modbus.modbus_device_schema(0x01))
)
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)
if CONF_FREQUENCY in config:
sens = await sensor.new_sensor(config[CONF_FREQUENCY])
cg.add(var.set_frequency_sensor(sens))
if CONF_IMPORT_ACTIVE_ENERGY in config:
sens = await sensor.new_sensor(config[CONF_IMPORT_ACTIVE_ENERGY])
cg.add(var.set_import_active_energy_sensor(sens))
if CONF_EXPORT_ACTIVE_ENERGY in config:
sens = await sensor.new_sensor(config[CONF_EXPORT_ACTIVE_ENERGY])
cg.add(var.set_export_active_energy_sensor(sens))
if CONF_IMPORT_REACTIVE_ENERGY in config:
sens = await sensor.new_sensor(config[CONF_IMPORT_REACTIVE_ENERGY])
cg.add(var.set_import_reactive_energy_sensor(sens))
if CONF_EXPORT_REACTIVE_ENERGY in config:
sens = await sensor.new_sensor(config[CONF_EXPORT_REACTIVE_ENERGY])
cg.add(var.set_export_reactive_energy_sensor(sens))
for i, phase in enumerate([CONF_PHASE_A, CONF_PHASE_B, CONF_PHASE_C]):
if phase not in config:
continue
phase_config = config[phase]
for sensor_type in PHASE_SENSORS:
if sensor_type in phase_config:
sens = await sensor.new_sensor(phase_config[sensor_type])
cg.add(getattr(var, f"set_{sensor_type}_sensor")(i, sens))

View file

@ -55,12 +55,14 @@ CONF_ACCELERATION_Z = "acceleration_z"
CONF_ACCURACY = "accuracy" CONF_ACCURACY = "accuracy"
CONF_ACCURACY_DECIMALS = "accuracy_decimals" CONF_ACCURACY_DECIMALS = "accuracy_decimals"
CONF_ACTION_ID = "action_id" CONF_ACTION_ID = "action_id"
CONF_ACTIVE_POWER = "active_power"
CONF_ADDRESS = "address" CONF_ADDRESS = "address"
CONF_ADDRESSABLE_LIGHT_ID = "addressable_light_id" CONF_ADDRESSABLE_LIGHT_ID = "addressable_light_id"
CONF_ALPHA = "alpha" CONF_ALPHA = "alpha"
CONF_ALTITUDE = "altitude" CONF_ALTITUDE = "altitude"
CONF_AND = "and" CONF_AND = "and"
CONF_AP = "ap" CONF_AP = "ap"
CONF_APPARENT_POWER = "apparent_power"
CONF_ARDUINO_VERSION = "arduino_version" CONF_ARDUINO_VERSION = "arduino_version"
CONF_ARGS = "args" CONF_ARGS = "args"
CONF_ASSUMED_STATE = "assumed_state" CONF_ASSUMED_STATE = "assumed_state"
@ -194,6 +196,8 @@ CONF_ESPHOME = "esphome"
CONF_ETHERNET = "ethernet" CONF_ETHERNET = "ethernet"
CONF_EVENT = "event" CONF_EVENT = "event"
CONF_EXPIRE_AFTER = "expire_after" CONF_EXPIRE_AFTER = "expire_after"
CONF_EXPORT_ACTIVE_ENERGY = "export_active_energy"
CONF_EXPORT_REACTIVE_ENERGY = "export_reactive_energy"
CONF_EXTERNAL_COMPONENTS = "external_components" CONF_EXTERNAL_COMPONENTS = "external_components"
CONF_EXTERNAL_VCC = "external_vcc" CONF_EXTERNAL_VCC = "external_vcc"
CONF_FALLING_EDGE = "falling_edge" CONF_FALLING_EDGE = "falling_edge"
@ -218,6 +222,7 @@ CONF_FILTERS = "filters"
CONF_FINGER_ID = "finger_id" CONF_FINGER_ID = "finger_id"
CONF_FINGERPRINT_COUNT = "fingerprint_count" CONF_FINGERPRINT_COUNT = "fingerprint_count"
CONF_FLASH_LENGTH = "flash_length" CONF_FLASH_LENGTH = "flash_length"
CONF_FLOW_CONTROL_PIN = "flow_control_pin"
CONF_FOR = "for" CONF_FOR = "for"
CONF_FORCE_UPDATE = "force_update" CONF_FORCE_UPDATE = "force_update"
CONF_FORMALDEHYDE = "formaldehyde" CONF_FORMALDEHYDE = "formaldehyde"
@ -261,6 +266,8 @@ CONF_IF = "if"
CONF_IIR_FILTER = "iir_filter" CONF_IIR_FILTER = "iir_filter"
CONF_ILLUMINANCE = "illuminance" CONF_ILLUMINANCE = "illuminance"
CONF_IMPEDANCE = "impedance" CONF_IMPEDANCE = "impedance"
CONF_IMPORT_ACTIVE_ENERGY = "import_active_energy"
CONF_IMPORT_REACTIVE_ENERGY = "import_reactive_energy"
CONF_INCLUDES = "includes" CONF_INCLUDES = "includes"
CONF_INDEX = "index" CONF_INDEX = "index"
CONF_INDOOR = "indoor" CONF_INDOOR = "indoor"
@ -413,6 +420,7 @@ CONF_PAYLOAD = "payload"
CONF_PAYLOAD_AVAILABLE = "payload_available" CONF_PAYLOAD_AVAILABLE = "payload_available"
CONF_PAYLOAD_NOT_AVAILABLE = "payload_not_available" CONF_PAYLOAD_NOT_AVAILABLE = "payload_not_available"
CONF_PERIOD = "period" CONF_PERIOD = "period"
CONF_PHASE_ANGLE = "phase_angle"
CONF_PHASE_BALANCER = "phase_balancer" CONF_PHASE_BALANCER = "phase_balancer"
CONF_PIN = "pin" CONF_PIN = "pin"
CONF_PIN_A = "pin_a" CONF_PIN_A = "pin_a"
@ -455,6 +463,7 @@ CONF_RATE = "rate"
CONF_RAW = "raw" CONF_RAW = "raw"
CONF_RC_CODE_1 = "rc_code_1" CONF_RC_CODE_1 = "rc_code_1"
CONF_RC_CODE_2 = "rc_code_2" CONF_RC_CODE_2 = "rc_code_2"
CONF_REACTIVE_POWER = "reactive_power"
CONF_REBOOT_TIMEOUT = "reboot_timeout" CONF_REBOOT_TIMEOUT = "reboot_timeout"
CONF_RECEIVE_TIMEOUT = "receive_timeout" CONF_RECEIVE_TIMEOUT = "receive_timeout"
CONF_RED = "red" CONF_RED = "red"
@ -715,6 +724,7 @@ UNIT_STEPS = "steps"
UNIT_VOLT = "V" UNIT_VOLT = "V"
UNIT_VOLT_AMPS = "VA" UNIT_VOLT_AMPS = "VA"
UNIT_VOLT_AMPS_REACTIVE = "VAR" UNIT_VOLT_AMPS_REACTIVE = "VAR"
UNIT_VOLT_AMPS_REACTIVE_HOURS = "VARh"
UNIT_WATT = "W" UNIT_WATT = "W"
UNIT_WATT_HOURS = "Wh" UNIT_WATT_HOURS = "Wh"

View file

@ -469,6 +469,62 @@ sensor:
name: "Fingerprint Last Finger ID" name: "Fingerprint Last Finger ID"
last_confidence: last_confidence:
name: "Fingerprint Last Confidence" name: "Fingerprint Last Confidence"
- platform: sdm_meter
phase_a:
current:
name: 'Phase A Current'
voltage:
name: 'Phase A Voltage'
active_power:
name: 'Phase A Power'
power_factor:
name: 'Phase A Power Factor'
apparent_power:
name: 'Phase A Apparent Power'
reactive_power:
name: 'Phase A Reactive Power'
phase_angle:
name: 'Phase A Phase Angle'
phase_b:
current:
name: 'Phase B Current'
voltage:
name: 'Phase B Voltage'
active_power:
name: 'Phase B Power'
power_factor:
name: 'Phase B Power Factor'
apparent_power:
name: 'Phase B Apparent Power'
reactive_power:
name: 'Phase B Reactive Power'
phase_angle:
name: 'Phase B Phase Angle'
phase_c:
current:
name: 'Phase C Current'
voltage:
name: 'Phase C Voltage'
active_power:
name: 'Phase C Power'
power_factor:
name: 'Phase C Power Factor'
apparent_power:
name: 'Phase C Apparent Power'
reactive_power:
name: 'Phase C Reactive Power'
phase_angle:
name: 'Phase C Phase Angle'
frequency:
name: 'Frequency'
import_active_energy:
name: 'Import Active Energy'
export_active_energy:
name: 'Export Active Energy'
import_reactive_energy:
name: 'Import Reactive Energy'
export_reactive_energy:
name: 'Export Reactive Energy'
time: time:
- platform: homeassistant - platform: homeassistant