mirror of
https://github.com/esphome/esphome.git
synced 2024-11-10 01:07:45 +01:00
Pylontech integration (solar battery bank) (#4688)
Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com>
This commit is contained in:
parent
4e6d3729e1
commit
4b6fbd5db0
11 changed files with 528 additions and 0 deletions
|
@ -240,6 +240,7 @@ esphome/components/preferences/* @esphome/core
|
||||||
esphome/components/psram/* @esphome/core
|
esphome/components/psram/* @esphome/core
|
||||||
esphome/components/pulse_meter/* @TrentHouliston @cstaahl @stevebaxter
|
esphome/components/pulse_meter/* @TrentHouliston @cstaahl @stevebaxter
|
||||||
esphome/components/pvvx_mithermometer/* @pasiz
|
esphome/components/pvvx_mithermometer/* @pasiz
|
||||||
|
esphome/components/pylontech/* @functionpointer
|
||||||
esphome/components/qmp6988/* @andrewpc
|
esphome/components/qmp6988/* @andrewpc
|
||||||
esphome/components/qr_code/* @wjtje
|
esphome/components/qr_code/* @wjtje
|
||||||
esphome/components/qwiic_pir/* @kahrendt
|
esphome/components/qwiic_pir/* @kahrendt
|
||||||
|
|
46
esphome/components/pylontech/__init__.py
Normal file
46
esphome/components/pylontech/__init__.py
Normal file
|
@ -0,0 +1,46 @@
|
||||||
|
import logging
|
||||||
|
import esphome.codegen as cg
|
||||||
|
from esphome.components import uart
|
||||||
|
import esphome.config_validation as cv
|
||||||
|
from esphome.const import CONF_ID
|
||||||
|
|
||||||
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
CODEOWNERS = ["@functionpointer"]
|
||||||
|
DEPENDENCIES = ["uart"]
|
||||||
|
MULTI_CONF = True
|
||||||
|
|
||||||
|
CONF_PYLONTECH_ID = "pylontech_id"
|
||||||
|
CONF_BATTERY = "battery"
|
||||||
|
|
||||||
|
pylontech_ns = cg.esphome_ns.namespace("pylontech")
|
||||||
|
PylontechComponent = pylontech_ns.class_(
|
||||||
|
"PylontechComponent", cg.PollingComponent, uart.UARTDevice
|
||||||
|
)
|
||||||
|
PylontechBattery = pylontech_ns.class_("PylontechBattery")
|
||||||
|
|
||||||
|
CV_NUM_BATTERIES = cv.int_range(1, 6)
|
||||||
|
|
||||||
|
PYLONTECH_COMPONENT_SCHEMA = cv.Schema(
|
||||||
|
{
|
||||||
|
cv.GenerateID(CONF_PYLONTECH_ID): cv.use_id(PylontechComponent),
|
||||||
|
cv.Required(CONF_BATTERY): CV_NUM_BATTERIES,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
CONFIG_SCHEMA = cv.All(
|
||||||
|
cv.Schema(
|
||||||
|
{
|
||||||
|
cv.GenerateID(): cv.declare_id(PylontechComponent),
|
||||||
|
}
|
||||||
|
)
|
||||||
|
.extend(cv.polling_component_schema("60s"))
|
||||||
|
.extend(uart.UART_DEVICE_SCHEMA)
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
async def to_code(config):
|
||||||
|
var = cg.new_Pvariable(config[CONF_ID])
|
||||||
|
await cg.register_component(var, config)
|
||||||
|
await uart.register_uart_device(var, config)
|
91
esphome/components/pylontech/pylontech.cpp
Normal file
91
esphome/components/pylontech/pylontech.cpp
Normal file
|
@ -0,0 +1,91 @@
|
||||||
|
#include "pylontech.h"
|
||||||
|
#include "esphome/core/log.h"
|
||||||
|
|
||||||
|
namespace esphome {
|
||||||
|
namespace pylontech {
|
||||||
|
|
||||||
|
static const char *const TAG = "pylontech";
|
||||||
|
static const int MAX_DATA_LENGTH_BYTES = 256;
|
||||||
|
static const uint8_t ASCII_LF = 0x0A;
|
||||||
|
|
||||||
|
PylontechComponent::PylontechComponent() {}
|
||||||
|
|
||||||
|
void PylontechComponent::dump_config() {
|
||||||
|
this->check_uart_settings(115200, 1, esphome::uart::UART_CONFIG_PARITY_NONE, 8);
|
||||||
|
ESP_LOGCONFIG(TAG, "pylontech:");
|
||||||
|
if (this->is_failed()) {
|
||||||
|
ESP_LOGE(TAG, "Connection with pylontech failed!");
|
||||||
|
}
|
||||||
|
|
||||||
|
for (PylontechListener *listener : this->listeners_) {
|
||||||
|
listener->dump_config();
|
||||||
|
}
|
||||||
|
|
||||||
|
LOG_UPDATE_INTERVAL(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
void PylontechComponent::setup() {
|
||||||
|
ESP_LOGCONFIG(TAG, "Setting up pylontech...");
|
||||||
|
while (this->available() != 0) {
|
||||||
|
this->read();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void PylontechComponent::update() { this->write_str("pwr\n"); }
|
||||||
|
|
||||||
|
void PylontechComponent::loop() {
|
||||||
|
uint8_t data;
|
||||||
|
|
||||||
|
// pylontech sends a lot of data very suddenly
|
||||||
|
// we need to quickly put it all into our own buffer, otherwise the uart's buffer will overflow
|
||||||
|
while (this->available() > 0) {
|
||||||
|
if (this->read_byte(&data)) {
|
||||||
|
buffer_[buffer_index_write_] += (char) data;
|
||||||
|
if (buffer_[buffer_index_write_].back() == static_cast<char>(ASCII_LF) ||
|
||||||
|
buffer_[buffer_index_write_].length() >= MAX_DATA_LENGTH_BYTES) {
|
||||||
|
// complete line received
|
||||||
|
buffer_index_write_ = (buffer_index_write_ + 1) % NUM_BUFFERS;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// only process one line per call of loop() to not block esphome for too long
|
||||||
|
if (buffer_index_read_ != buffer_index_write_) {
|
||||||
|
this->process_line_(buffer_[buffer_index_read_]);
|
||||||
|
buffer_[buffer_index_read_].clear();
|
||||||
|
buffer_index_read_ = (buffer_index_read_ + 1) % NUM_BUFFERS;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void PylontechComponent::process_line_(std::string &buffer) {
|
||||||
|
ESP_LOGV(TAG, "Read from serial: %s", buffer.substr(0, buffer.size() - 2).c_str());
|
||||||
|
// clang-format off
|
||||||
|
// example line to parse:
|
||||||
|
// Power Volt Curr Tempr Tlow Thigh Vlow Vhigh Base.St Volt.St Curr.St Temp.St Coulomb Time B.V.St B.T.St MosTempr M.T.St
|
||||||
|
// 1 50548 8910 25000 24200 25000 3368 3371 Charge Normal Normal Normal 97% 2021-06-30 20:49:45 Normal Normal 22700 Normal
|
||||||
|
// clang-format on
|
||||||
|
|
||||||
|
PylontechListener::LineContents l{};
|
||||||
|
const int parsed = sscanf( // NOLINT
|
||||||
|
buffer.c_str(), "%d %d %d %d %d %d %d %d %7s %7s %7s %7s %d%% %*d-%*d-%*d %*d:%*d:%*d %*s %*s %d %*s", // NOLINT
|
||||||
|
&l.bat_num, &l.volt, &l.curr, &l.tempr, &l.tlow, &l.thigh, &l.vlow, &l.vhigh, l.base_st, l.volt_st, // NOLINT
|
||||||
|
l.curr_st, l.temp_st, &l.coulomb, &l.mostempr); // NOLINT
|
||||||
|
|
||||||
|
if (l.bat_num <= 0) {
|
||||||
|
ESP_LOGD(TAG, "invalid bat_num in line %s", buffer.substr(0, buffer.size() - 2).c_str());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (parsed != 14) {
|
||||||
|
ESP_LOGW(TAG, "invalid line: found only %d items in %s", parsed, buffer.substr(0, buffer.size() - 2).c_str());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (PylontechListener *listener : this->listeners_) {
|
||||||
|
listener->on_line_read(&l);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
float PylontechComponent::get_setup_priority() const { return setup_priority::DATA; }
|
||||||
|
|
||||||
|
} // namespace pylontech
|
||||||
|
} // namespace esphome
|
53
esphome/components/pylontech/pylontech.h
Normal file
53
esphome/components/pylontech/pylontech.h
Normal file
|
@ -0,0 +1,53 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "esphome/core/component.h"
|
||||||
|
#include "esphome/core/defines.h"
|
||||||
|
#include "esphome/components/uart/uart.h"
|
||||||
|
|
||||||
|
namespace esphome {
|
||||||
|
namespace pylontech {
|
||||||
|
|
||||||
|
static const uint8_t NUM_BUFFERS = 20;
|
||||||
|
static const uint8_t TEXT_SENSOR_MAX_LEN = 8;
|
||||||
|
|
||||||
|
class PylontechListener {
|
||||||
|
public:
|
||||||
|
struct LineContents {
|
||||||
|
int bat_num = 0, volt, curr, tempr, tlow, thigh, vlow, vhigh, coulomb, mostempr;
|
||||||
|
char base_st[TEXT_SENSOR_MAX_LEN], volt_st[TEXT_SENSOR_MAX_LEN], curr_st[TEXT_SENSOR_MAX_LEN],
|
||||||
|
temp_st[TEXT_SENSOR_MAX_LEN];
|
||||||
|
};
|
||||||
|
|
||||||
|
virtual void on_line_read(LineContents *line);
|
||||||
|
virtual void dump_config();
|
||||||
|
};
|
||||||
|
|
||||||
|
class PylontechComponent : public PollingComponent, public uart::UARTDevice {
|
||||||
|
public:
|
||||||
|
PylontechComponent();
|
||||||
|
|
||||||
|
/// Schedule data readings.
|
||||||
|
void update() override;
|
||||||
|
/// Read data once available
|
||||||
|
void loop() override;
|
||||||
|
/// Setup the sensor and test for a connection.
|
||||||
|
void setup() override;
|
||||||
|
void dump_config() override;
|
||||||
|
|
||||||
|
float get_setup_priority() const override;
|
||||||
|
|
||||||
|
void register_listener(PylontechListener *listener) { this->listeners_.push_back(listener); }
|
||||||
|
|
||||||
|
protected:
|
||||||
|
void process_line_(std::string &buffer);
|
||||||
|
|
||||||
|
// ring buffer
|
||||||
|
std::string buffer_[NUM_BUFFERS];
|
||||||
|
int buffer_index_write_ = 0;
|
||||||
|
int buffer_index_read_ = 0;
|
||||||
|
|
||||||
|
std::vector<PylontechListener *> listeners_{};
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace pylontech
|
||||||
|
} // namespace esphome
|
97
esphome/components/pylontech/sensor/__init__.py
Normal file
97
esphome/components/pylontech/sensor/__init__.py
Normal file
|
@ -0,0 +1,97 @@
|
||||||
|
import esphome.codegen as cg
|
||||||
|
import esphome.config_validation as cv
|
||||||
|
from esphome.components import sensor
|
||||||
|
from esphome.const import (
|
||||||
|
CONF_VOLTAGE,
|
||||||
|
CONF_CURRENT,
|
||||||
|
CONF_TEMPERATURE,
|
||||||
|
UNIT_VOLT,
|
||||||
|
UNIT_AMPERE,
|
||||||
|
DEVICE_CLASS_VOLTAGE,
|
||||||
|
DEVICE_CLASS_CURRENT,
|
||||||
|
DEVICE_CLASS_TEMPERATURE,
|
||||||
|
DEVICE_CLASS_BATTERY,
|
||||||
|
UNIT_CELSIUS,
|
||||||
|
UNIT_PERCENT,
|
||||||
|
CONF_ID,
|
||||||
|
)
|
||||||
|
|
||||||
|
from .. import (
|
||||||
|
CONF_PYLONTECH_ID,
|
||||||
|
PYLONTECH_COMPONENT_SCHEMA,
|
||||||
|
CONF_BATTERY,
|
||||||
|
pylontech_ns,
|
||||||
|
)
|
||||||
|
|
||||||
|
PylontechSensor = pylontech_ns.class_("PylontechSensor", cg.Component)
|
||||||
|
|
||||||
|
CONF_COULOMB = "coulomb"
|
||||||
|
CONF_TEMPERATURE_LOW = "temperature_low"
|
||||||
|
CONF_TEMPERATURE_HIGH = "temperature_high"
|
||||||
|
CONF_VOLTAGE_LOW = "voltage_low"
|
||||||
|
CONF_VOLTAGE_HIGH = "voltage_high"
|
||||||
|
CONF_MOS_TEMPERATURE = "mos_temperature"
|
||||||
|
|
||||||
|
TYPES: dict[str, cv.Schema] = {
|
||||||
|
CONF_VOLTAGE: sensor.sensor_schema(
|
||||||
|
unit_of_measurement=UNIT_VOLT,
|
||||||
|
accuracy_decimals=3,
|
||||||
|
device_class=DEVICE_CLASS_VOLTAGE,
|
||||||
|
),
|
||||||
|
CONF_CURRENT: sensor.sensor_schema(
|
||||||
|
unit_of_measurement=UNIT_AMPERE,
|
||||||
|
accuracy_decimals=3,
|
||||||
|
device_class=DEVICE_CLASS_CURRENT,
|
||||||
|
),
|
||||||
|
CONF_TEMPERATURE: sensor.sensor_schema(
|
||||||
|
unit_of_measurement=UNIT_CELSIUS,
|
||||||
|
accuracy_decimals=1,
|
||||||
|
device_class=DEVICE_CLASS_TEMPERATURE,
|
||||||
|
),
|
||||||
|
CONF_TEMPERATURE_LOW: sensor.sensor_schema(
|
||||||
|
unit_of_measurement=UNIT_CELSIUS,
|
||||||
|
accuracy_decimals=1,
|
||||||
|
device_class=DEVICE_CLASS_TEMPERATURE,
|
||||||
|
),
|
||||||
|
CONF_TEMPERATURE_HIGH: sensor.sensor_schema(
|
||||||
|
unit_of_measurement=UNIT_CELSIUS,
|
||||||
|
accuracy_decimals=1,
|
||||||
|
device_class=DEVICE_CLASS_TEMPERATURE,
|
||||||
|
),
|
||||||
|
CONF_VOLTAGE_LOW: sensor.sensor_schema(
|
||||||
|
unit_of_measurement=UNIT_CELSIUS,
|
||||||
|
accuracy_decimals=1,
|
||||||
|
device_class=DEVICE_CLASS_TEMPERATURE,
|
||||||
|
),
|
||||||
|
CONF_VOLTAGE_HIGH: sensor.sensor_schema(
|
||||||
|
unit_of_measurement=UNIT_CELSIUS,
|
||||||
|
accuracy_decimals=1,
|
||||||
|
device_class=DEVICE_CLASS_TEMPERATURE,
|
||||||
|
),
|
||||||
|
CONF_COULOMB: sensor.sensor_schema(
|
||||||
|
unit_of_measurement=UNIT_PERCENT,
|
||||||
|
accuracy_decimals=0,
|
||||||
|
device_class=DEVICE_CLASS_BATTERY,
|
||||||
|
),
|
||||||
|
CONF_MOS_TEMPERATURE: sensor.sensor_schema(
|
||||||
|
unit_of_measurement=UNIT_CELSIUS,
|
||||||
|
accuracy_decimals=1,
|
||||||
|
device_class=DEVICE_CLASS_TEMPERATURE,
|
||||||
|
),
|
||||||
|
}
|
||||||
|
|
||||||
|
CONFIG_SCHEMA = PYLONTECH_COMPONENT_SCHEMA.extend(
|
||||||
|
{cv.GenerateID(): cv.declare_id(PylontechSensor)}
|
||||||
|
).extend({cv.Optional(marker): schema for marker, schema in TYPES.items()})
|
||||||
|
|
||||||
|
|
||||||
|
async def to_code(config):
|
||||||
|
paren = await cg.get_variable(config[CONF_PYLONTECH_ID])
|
||||||
|
bat = cg.new_Pvariable(config[CONF_ID], config[CONF_BATTERY])
|
||||||
|
|
||||||
|
for marker in TYPES:
|
||||||
|
if marker_config := config.get(marker):
|
||||||
|
sens = await sensor.new_sensor(marker_config)
|
||||||
|
cg.add(getattr(bat, f"set_{marker}_sensor")(sens))
|
||||||
|
|
||||||
|
cg.add(paren.register_listener(bat))
|
60
esphome/components/pylontech/sensor/pylontech_sensor.cpp
Normal file
60
esphome/components/pylontech/sensor/pylontech_sensor.cpp
Normal file
|
@ -0,0 +1,60 @@
|
||||||
|
#include "pylontech_sensor.h"
|
||||||
|
#include "esphome/core/helpers.h"
|
||||||
|
#include "esphome/core/log.h"
|
||||||
|
|
||||||
|
namespace esphome {
|
||||||
|
namespace pylontech {
|
||||||
|
|
||||||
|
static const char *const TAG = "pylontech.sensor";
|
||||||
|
|
||||||
|
PylontechSensor::PylontechSensor(int8_t bat_num) { this->bat_num_ = bat_num; }
|
||||||
|
|
||||||
|
void PylontechSensor::dump_config() {
|
||||||
|
ESP_LOGCONFIG(TAG, "Pylontech Sensor:");
|
||||||
|
ESP_LOGCONFIG(TAG, " Battery %d", this->bat_num_);
|
||||||
|
LOG_SENSOR(" ", "Voltage", this->voltage_sensor_);
|
||||||
|
LOG_SENSOR(" ", "Current", this->current_sensor_);
|
||||||
|
LOG_SENSOR(" ", "Temperature", this->temperature_sensor_);
|
||||||
|
LOG_SENSOR(" ", "Temperature low", this->temperature_low_sensor_);
|
||||||
|
LOG_SENSOR(" ", "Temperature high", this->temperature_high_sensor_);
|
||||||
|
LOG_SENSOR(" ", "Voltage low", this->voltage_low_sensor_);
|
||||||
|
LOG_SENSOR(" ", "Voltage high", this->voltage_high_sensor_);
|
||||||
|
LOG_SENSOR(" ", "Coulomb", this->coulomb_sensor_);
|
||||||
|
LOG_SENSOR(" ", "MOS Temperature", this->mos_temperature_sensor_);
|
||||||
|
}
|
||||||
|
|
||||||
|
void PylontechSensor::on_line_read(PylontechListener::LineContents *line) {
|
||||||
|
if (this->bat_num_ != line->bat_num) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (this->voltage_sensor_ != nullptr) {
|
||||||
|
this->voltage_sensor_->publish_state(((float) line->volt) / 1000.0f);
|
||||||
|
}
|
||||||
|
if (this->current_sensor_ != nullptr) {
|
||||||
|
this->current_sensor_->publish_state(((float) line->curr) / 1000.0f);
|
||||||
|
}
|
||||||
|
if (this->temperature_sensor_ != nullptr) {
|
||||||
|
this->temperature_sensor_->publish_state(((float) line->tempr) / 1000.0f);
|
||||||
|
}
|
||||||
|
if (this->temperature_low_sensor_ != nullptr) {
|
||||||
|
this->temperature_low_sensor_->publish_state(((float) line->tlow) / 1000.0f);
|
||||||
|
}
|
||||||
|
if (this->temperature_high_sensor_ != nullptr) {
|
||||||
|
this->temperature_high_sensor_->publish_state(((float) line->thigh) / 1000.0f);
|
||||||
|
}
|
||||||
|
if (this->voltage_low_sensor_ != nullptr) {
|
||||||
|
this->voltage_low_sensor_->publish_state(((float) line->vlow) / 1000.0f);
|
||||||
|
}
|
||||||
|
if (this->voltage_high_sensor_ != nullptr) {
|
||||||
|
this->voltage_high_sensor_->publish_state(((float) line->vhigh) / 1000.0f);
|
||||||
|
}
|
||||||
|
if (this->coulomb_sensor_ != nullptr) {
|
||||||
|
this->coulomb_sensor_->publish_state(line->coulomb);
|
||||||
|
}
|
||||||
|
if (this->mos_temperature_sensor_ != nullptr) {
|
||||||
|
this->mos_temperature_sensor_->publish_state(((float) line->mostempr) / 1000.0f);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace pylontech
|
||||||
|
} // namespace esphome
|
32
esphome/components/pylontech/sensor/pylontech_sensor.h
Normal file
32
esphome/components/pylontech/sensor/pylontech_sensor.h
Normal file
|
@ -0,0 +1,32 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "../pylontech.h"
|
||||||
|
#include "esphome/components/sensor/sensor.h"
|
||||||
|
|
||||||
|
namespace esphome {
|
||||||
|
namespace pylontech {
|
||||||
|
|
||||||
|
class PylontechSensor : public PylontechListener, public Component {
|
||||||
|
public:
|
||||||
|
PylontechSensor(int8_t bat_num);
|
||||||
|
void dump_config() override;
|
||||||
|
|
||||||
|
SUB_SENSOR(voltage)
|
||||||
|
SUB_SENSOR(current)
|
||||||
|
SUB_SENSOR(temperature)
|
||||||
|
SUB_SENSOR(temperature_low)
|
||||||
|
SUB_SENSOR(temperature_high)
|
||||||
|
SUB_SENSOR(voltage_low)
|
||||||
|
SUB_SENSOR(voltage_high)
|
||||||
|
|
||||||
|
SUB_SENSOR(coulomb)
|
||||||
|
SUB_SENSOR(mos_temperature)
|
||||||
|
|
||||||
|
void on_line_read(LineContents *line) override;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
int8_t bat_num_;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace pylontech
|
||||||
|
} // namespace esphome
|
41
esphome/components/pylontech/text_sensor/__init__.py
Normal file
41
esphome/components/pylontech/text_sensor/__init__.py
Normal file
|
@ -0,0 +1,41 @@
|
||||||
|
import esphome.codegen as cg
|
||||||
|
import esphome.config_validation as cv
|
||||||
|
from esphome.components import text_sensor
|
||||||
|
from esphome.const import CONF_ID
|
||||||
|
|
||||||
|
from .. import (
|
||||||
|
CONF_PYLONTECH_ID,
|
||||||
|
PYLONTECH_COMPONENT_SCHEMA,
|
||||||
|
CONF_BATTERY,
|
||||||
|
pylontech_ns,
|
||||||
|
)
|
||||||
|
|
||||||
|
PylontechTextSensor = pylontech_ns.class_("PylontechTextSensor", cg.Component)
|
||||||
|
|
||||||
|
CONF_BASE_STATE = "base_state"
|
||||||
|
CONF_VOLTAGE_STATE = "voltage_state"
|
||||||
|
CONF_CURRENT_STATE = "current_state"
|
||||||
|
CONF_TEMPERATURE_STATE = "temperature_state"
|
||||||
|
|
||||||
|
MARKERS: list[str] = [
|
||||||
|
CONF_BASE_STATE,
|
||||||
|
CONF_VOLTAGE_STATE,
|
||||||
|
CONF_CURRENT_STATE,
|
||||||
|
CONF_TEMPERATURE_STATE,
|
||||||
|
]
|
||||||
|
|
||||||
|
CONFIG_SCHEMA = PYLONTECH_COMPONENT_SCHEMA.extend(
|
||||||
|
{cv.GenerateID(): cv.declare_id(PylontechTextSensor)}
|
||||||
|
).extend({cv.Optional(marker): text_sensor.text_sensor_schema() for marker in MARKERS})
|
||||||
|
|
||||||
|
|
||||||
|
async def to_code(config):
|
||||||
|
paren = await cg.get_variable(config[CONF_PYLONTECH_ID])
|
||||||
|
bat = cg.new_Pvariable(config[CONF_ID], config[CONF_BATTERY])
|
||||||
|
|
||||||
|
for marker in MARKERS:
|
||||||
|
if marker_config := config.get(marker):
|
||||||
|
var = await text_sensor.new_text_sensor(marker_config)
|
||||||
|
cg.add(getattr(bat, f"set_{marker}_text_sensor")(var))
|
||||||
|
|
||||||
|
cg.add(paren.register_listener(bat))
|
|
@ -0,0 +1,40 @@
|
||||||
|
#include "pylontech_text_sensor.h"
|
||||||
|
#include "esphome/core/helpers.h"
|
||||||
|
#include "esphome/core/log.h"
|
||||||
|
|
||||||
|
namespace esphome {
|
||||||
|
namespace pylontech {
|
||||||
|
|
||||||
|
static const char *const TAG = "pylontech.textsensor";
|
||||||
|
|
||||||
|
PylontechTextSensor::PylontechTextSensor(int8_t bat_num) { this->bat_num_ = bat_num; }
|
||||||
|
|
||||||
|
void PylontechTextSensor::dump_config() {
|
||||||
|
ESP_LOGCONFIG(TAG, "Pylontech Text Sensor:");
|
||||||
|
ESP_LOGCONFIG(TAG, " Battery %d", this->bat_num_);
|
||||||
|
LOG_TEXT_SENSOR(" ", "Base state", this->base_state_text_sensor_);
|
||||||
|
LOG_TEXT_SENSOR(" ", "Voltage state", this->voltage_state_text_sensor_);
|
||||||
|
LOG_TEXT_SENSOR(" ", "Current state", this->current_state_text_sensor_);
|
||||||
|
LOG_TEXT_SENSOR(" ", "Temperature state", this->temperature_state_text_sensor_);
|
||||||
|
}
|
||||||
|
|
||||||
|
void PylontechTextSensor::on_line_read(PylontechListener::LineContents *line) {
|
||||||
|
if (this->bat_num_ != line->bat_num) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (this->base_state_text_sensor_ != nullptr) {
|
||||||
|
this->base_state_text_sensor_->publish_state(std::string(line->base_st));
|
||||||
|
}
|
||||||
|
if (this->voltage_state_text_sensor_ != nullptr) {
|
||||||
|
this->voltage_state_text_sensor_->publish_state(std::string(line->volt_st));
|
||||||
|
}
|
||||||
|
if (this->current_state_text_sensor_ != nullptr) {
|
||||||
|
this->current_state_text_sensor_->publish_state(std::string(line->curr_st));
|
||||||
|
}
|
||||||
|
if (this->temperature_state_text_sensor_ != nullptr) {
|
||||||
|
this->temperature_state_text_sensor_->publish_state(std::string(line->temp_st));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace pylontech
|
||||||
|
} // namespace esphome
|
|
@ -0,0 +1,26 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "../pylontech.h"
|
||||||
|
#include "esphome/components/text_sensor/text_sensor.h"
|
||||||
|
|
||||||
|
namespace esphome {
|
||||||
|
namespace pylontech {
|
||||||
|
|
||||||
|
class PylontechTextSensor : public PylontechListener, public Component {
|
||||||
|
public:
|
||||||
|
PylontechTextSensor(int8_t bat_num);
|
||||||
|
void dump_config() override;
|
||||||
|
|
||||||
|
SUB_TEXT_SENSOR(base_state)
|
||||||
|
SUB_TEXT_SENSOR(voltage_state)
|
||||||
|
SUB_TEXT_SENSOR(current_state)
|
||||||
|
SUB_TEXT_SENSOR(temperature_state)
|
||||||
|
|
||||||
|
void on_line_read(LineContents *line) override;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
int8_t bat_num_;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace pylontech
|
||||||
|
} // namespace esphome
|
|
@ -99,6 +99,12 @@ pipsolar:
|
||||||
id: inverter0
|
id: inverter0
|
||||||
uart_id: uart115200
|
uart_id: uart115200
|
||||||
|
|
||||||
|
pylontech:
|
||||||
|
- id: pylontech0
|
||||||
|
uart_id: uart115200
|
||||||
|
- id: pylontech1
|
||||||
|
uart_id: uart115200
|
||||||
|
|
||||||
sx1509:
|
sx1509:
|
||||||
- id: sx1509_hub
|
- id: sx1509_hub
|
||||||
address: 0x3E
|
address: 0x3E
|
||||||
|
@ -113,6 +119,30 @@ dac7678:
|
||||||
internal_reference: true
|
internal_reference: true
|
||||||
|
|
||||||
sensor:
|
sensor:
|
||||||
|
- platform: pylontech
|
||||||
|
pylontech_id: pylontech0
|
||||||
|
battery: 1
|
||||||
|
voltage:
|
||||||
|
id: pyl01_voltage
|
||||||
|
current:
|
||||||
|
id: pyl01_current
|
||||||
|
coulomb:
|
||||||
|
id: pyl01_soc
|
||||||
|
mos_temperature:
|
||||||
|
id: pyl01_mos_temperature
|
||||||
|
- platform: pylontech
|
||||||
|
pylontech_id: pylontech1
|
||||||
|
battery: 1
|
||||||
|
voltage:
|
||||||
|
id: pyl13_voltage
|
||||||
|
temperature_low:
|
||||||
|
id: pyl13_temperature_low
|
||||||
|
temperature_high:
|
||||||
|
id: pyl13_temperature_high
|
||||||
|
voltage_low:
|
||||||
|
id: pyl13_voltage_low
|
||||||
|
voltage_high:
|
||||||
|
id: pyl13_voltage_high
|
||||||
- platform: homeassistant
|
- platform: homeassistant
|
||||||
entity_id: sensor.hello_world
|
entity_id: sensor.hello_world
|
||||||
id: ha_hello_world
|
id: ha_hello_world
|
||||||
|
@ -589,6 +619,17 @@ number:
|
||||||
name: Tuya Number Copy
|
name: Tuya Number Copy
|
||||||
|
|
||||||
text_sensor:
|
text_sensor:
|
||||||
|
- platform: pylontech
|
||||||
|
pylontech_id: pylontech0
|
||||||
|
battery: 1
|
||||||
|
base_state:
|
||||||
|
id: pyl0_base_state
|
||||||
|
voltage_state:
|
||||||
|
id: pyl0_voltage_state
|
||||||
|
current_state:
|
||||||
|
id: pyl0_current_state
|
||||||
|
temperature_state:
|
||||||
|
id: pyl0_temperature_state
|
||||||
- platform: pipsolar
|
- platform: pipsolar
|
||||||
pipsolar_id: inverter0
|
pipsolar_id: inverter0
|
||||||
device_mode:
|
device_mode:
|
||||||
|
|
Loading…
Reference in a new issue