Skip to content

Commit

Permalink
refactor (commonize) vedirect entities for TEXT and HEX data (m3_vedi…
Browse files Browse the repository at this point in the history
…rect)
  • Loading branch information
krahabb committed Oct 18, 2024
1 parent 9d6f423 commit f9a7e89
Show file tree
Hide file tree
Showing 16 changed files with 289 additions and 490 deletions.
115 changes: 53 additions & 62 deletions esphome/components/m3_vedirect/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
)

CONF_VEDIRECT_ID = "vedirect_id"
CONF_VEDIRECT_ENTITIES = "vedirect_entities"
CONF_TEXTFRAME = "textframe"
CONF_HEXFRAME = "hexframe"

Expand All @@ -38,33 +39,27 @@ def validate_register_id():
return cv.hex_int_range(min=0, max=65535)


CONF_TEXT_LABEL = "text_label"
CONF_REGISTER_ID = "register_id"
CONF_DATA_TYPE = "data_type"
HexDataType = HexFrame.enum("DataType")
DATA_TYPES = {
"uint8": HexDataType.enum("u8"),
"uint16": HexDataType.enum("u16"),
"int16": HexDataType.enum("i16"),
"uint32": HexDataType.enum("u32"),
}

CONF_REGISTER_ID = "register_id"
CONF_DATA_TYPE = "data_type"
CONF_LABEL = "label"
HEX_ENTITY_SCHEMA = cv.Schema(
VEDIRECT_ENTITY_SCHEMA = cv.Schema(
{
# binds to the corresponding TEXT frame field
cv.Optional(CONF_TEXT_LABEL): cv.string,
# binds to the corresponding HEX register
cv.Required(CONF_REGISTER_ID): validate_register_id(),
cv.Optional(CONF_REGISTER_ID): validate_register_id(),
# configures the format of the HEX register
cv.Optional(CONF_DATA_TYPE): cv.enum(DATA_TYPES),
}
)

TEXT_ENTITY_SCHEMA = cv.Schema(
{
# binds to the corresponding TEXT frame field
cv.Required(CONF_LABEL): cv.string,
}
)

# root schema to group (platform) entities linked to a Manager
VEDIRECT_PLATFORM_SCHEMA = cv.Schema(
{
Expand All @@ -81,78 +76,67 @@ def vedirect_platform_schema(
)


async def new_vedirect_entity(config, *args):
var = cg.new_Pvariable(config[CONF_ID], *args)
valid = False
if CONF_TEXT_LABEL in config:
valid = True
cg.add(var.set_text_label(config[CONF_TEXT_LABEL]))
if CONF_REGISTER_ID in config:
valid = True
cg.add(var.set_register_id(config[CONF_REGISTER_ID]))
if CONF_DATA_TYPE in config:
cg.add(var.set_hex_data_type(config[CONF_DATA_TYPE]))
assert valid, f"Either {CONF_TEXT_LABEL} or {CONF_REGISTER_ID} must be provided"
return var


async def vedirect_platform_to_code(
config: dict,
platform_entities: dict[str, cv.Schema],
setup_entity_func,
setup_hfentity_func,
setup_tfentity_func,
new_vedirectentity_func,
new_entity_func,
):
manager = await cg.get_variable(config[CONF_VEDIRECT_ID])

for entity_key, entity_config in config.items():
if entity_key == CONF_HEXFRAME:
if entity_key == CONF_VEDIRECT_ENTITIES:
for _entity_config in entity_config:
var = await setup_hfentity_func(
_entity_config, manager, _entity_config[CONF_REGISTER_ID]
)
if CONF_DATA_TYPE in _entity_config:
cg.add(var.set_hex_data_type(_entity_config[CONF_DATA_TYPE]))
continue
if entity_key == CONF_TEXTFRAME:
for _entity_config in entity_config:
var = await setup_tfentity_func(
_entity_config, manager, _entity_config[CONF_LABEL]
)
await new_vedirectentity_func(_entity_config, manager)
continue
if entity_key in platform_entities:
var = await setup_entity_func(entity_config)
var = await new_entity_func(entity_config)
cg.add(getattr(manager, f"set_{entity_key}")(var))


# main component (Manager) schema
CONF_AUTO_CREATE_ENTITIES = "auto_create_entities"
CONF_PING_TIMEOUT = "ping_timeout"
CONF_DEFAULT_ENTITIES = "default_entities"
DEFAULT_ENTITIES_MAP = {
CONF_TEXTFRAME: {
"mppt": (),
"bmv60": (),
"bmv70": (),
"bmv71": (),
"inverter": (),
"charger": (),
},
CONF_HEXFRAME: {
"mppt": (),
"bmv60": (),
"bmv70": (),
"bmv71": (),
"inverter": (),
"charger": (),
},
"mppt": (),
"bmv60": (),
"bmv70": (),
"bmv71": (),
"inverter": (),
"charger": (),
}

CONF_ON_FRAME_RECEIVED = "on_frame_received"

CONFIG_SCHEMA = cv.All(
cv.Schema(
{
cv.GenerateID(): cv.declare_id(Manager),
cv.Optional(CONF_NAME): cv.string_strict,
cv.Optional(CONF_DEFAULT_ENTITIES): cv.one_of(*DEFAULT_ENTITIES_MAP),
cv.Optional(CONF_TEXTFRAME): cv.Schema(
{
cv.Optional(CONF_AUTO_CREATE_ENTITIES, default=True): cv.boolean,
cv.Optional(CONF_DEFAULT_ENTITIES): cv.one_of(
*DEFAULT_ENTITIES_MAP[CONF_TEXTFRAME]
),
cv.Optional(CONF_AUTO_CREATE_ENTITIES): cv.boolean,
}
),
cv.Optional(CONF_HEXFRAME): cv.Schema(
{
cv.Optional(CONF_AUTO_CREATE_ENTITIES, default=True): cv.boolean,
cv.Optional(CONF_DEFAULT_ENTITIES): cv.one_of(
*DEFAULT_ENTITIES_MAP[CONF_HEXFRAME]
),
cv.Optional(CONF_AUTO_CREATE_ENTITIES): cv.boolean,
cv.Optional(CONF_PING_TIMEOUT): cv.positive_time_period_seconds,
cv.Optional(CONF_ON_FRAME_RECEIVED): automation.validate_automation(
{
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(
Expand All @@ -174,16 +158,23 @@ async def to_code(config: dict):
cg.add(var.set_vedirect_id(str(var.base)))
cg.add(var.set_vedirect_name(config.get(CONF_NAME, str(var.base))))
if config_textframe := config.get(CONF_TEXTFRAME):
cg.add(
var.set_auto_create_text_entities(
config_textframe[CONF_AUTO_CREATE_ENTITIES]
if CONF_AUTO_CREATE_ENTITIES in config_textframe:
cg.add(
var.set_auto_create_text_entities(
config_textframe[CONF_AUTO_CREATE_ENTITIES]
)
)
)

if config_hexframe := config.get(CONF_HEXFRAME):
cg.add(
var.set_auto_create_hex_entities(config_hexframe[CONF_AUTO_CREATE_ENTITIES])
)
if CONF_AUTO_CREATE_ENTITIES in config_hexframe:
cg.add(
var.set_auto_create_hex_entities(
config_hexframe[CONF_AUTO_CREATE_ENTITIES]
)
)
if CONF_PING_TIMEOUT in config_hexframe:
cg.add(var.set_ping_timeout(config_hexframe[CONF_PING_TIMEOUT]))

for conf in config_hexframe.get(CONF_ON_FRAME_RECEIVED, []):
trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var)
await automation.build_automation(
Expand Down
34 changes: 17 additions & 17 deletions esphome/components/m3_vedirect/binary_sensor/__init__.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import esphome.config_validation as cv
from esphome.components import binary_sensor
import esphome.config_validation as cv

from .. import (
CONF_HEXFRAME,
CONF_TEXTFRAME,
HEX_ENTITY_SCHEMA,
TEXT_ENTITY_SCHEMA,
CONF_VEDIRECT_ENTITIES,
VEDIRECT_ENTITY_SCHEMA,
m3_vedirect_ns,
new_vedirect_entity,
vedirect_platform_schema,
vedirect_platform_to_code,
)
Expand All @@ -16,29 +16,29 @@
)

# m3_vedirect::BinarySensor mapped to HEX/TEXT data
HFBinarySensor = m3_vedirect_ns.class_("HFBinarySensor", binary_sensor.BinarySensor)
TFBinarySensor = m3_vedirect_ns.class_("TFBinarySensor", binary_sensor.BinarySensor)
HFBINARYSENSOR_SCHEMA = binary_sensor.binary_sensor_schema(HFBinarySensor).extend(
HEX_ENTITY_SCHEMA
)
TFBINARYSENSOR_SCHEMA = binary_sensor.binary_sensor_schema(TFBinarySensor).extend(
TEXT_ENTITY_SCHEMA
)
VEDirectBinarySensor = m3_vedirect_ns.class_("BinarySensor", binary_sensor.BinarySensor)
VEDIRECT_BINARY_SENSOR_SCHEMA = binary_sensor.binary_sensor_schema(
VEDirectBinarySensor
).extend(VEDIRECT_ENTITY_SCHEMA)

PLATFORM_ENTITIES = {
CONF_HEXFRAME: cv.ensure_list(HFBINARYSENSOR_SCHEMA),
CONF_TEXTFRAME: cv.ensure_list(TFBINARYSENSOR_SCHEMA),
CONF_VEDIRECT_ENTITIES: cv.ensure_list(VEDIRECT_BINARY_SENSOR_SCHEMA),
"link_connected": _diagnostic_binary_sensor_schema,
}

CONFIG_SCHEMA = vedirect_platform_schema(PLATFORM_ENTITIES)


async def new_vedirect_binary_sensor(config, *args):
var = await new_vedirect_entity(config, *args)
await binary_sensor.register_binary_sensor(var, config)
return var


async def to_code(config: dict):
await vedirect_platform_to_code(
config,
PLATFORM_ENTITIES,
binary_sensor.new_binary_sensor,
binary_sensor.new_binary_sensor,
new_vedirect_binary_sensor,
binary_sensor.new_binary_sensor,
)
20 changes: 1 addition & 19 deletions esphome/components/m3_vedirect/binary_sensor/binary_sensor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,25 +6,7 @@
namespace esphome {
namespace m3_vedirect {

void HFBinarySensor::dynamic_register() {
char *object_id = new char[7];
sprintf(object_id, "0x%04X", (int) this->id);
char *name = new char[16];
sprintf(name, "Register %s", object_id);
// name and object_id should likely need to be freed
this->manager->setup_entity_name_id(this, name, object_id);

App.register_binary_sensor(this);
if (api::global_api_server) {
add_on_state_callback([this](bool state) { api::global_api_server->on_binary_sensor_update(this, state); });
}
}

TFBinarySensor::TFBinarySensor(Manager *manager, const char *label, const DEF *def) : TFEntity(manager, label, def) {
manager->setup_entity_name_id(this, def ? def->description : label, label);
}

void TFBinarySensor::dynamic_register() {
void BinarySensor::dynamic_register() {
App.register_binary_sensor(this);
if (api::global_api_server) {
add_on_state_callback([this](bool state) { api::global_api_server->on_binary_sensor_update(this, state); });
Expand Down
13 changes: 3 additions & 10 deletions esphome/components/m3_vedirect/binary_sensor/binary_sensor.h
Original file line number Diff line number Diff line change
Expand Up @@ -6,25 +6,18 @@
namespace esphome {
namespace m3_vedirect {

class HFBinarySensor : public esphome::binary_sensor::BinarySensor, public HFEntity {
class BinarySensor : public esphome::binary_sensor::BinarySensor, public VEDirectEntity {
public:
void dynamic_register() override;
BinarySensor(Manager *manager) : VEDirectEntity(manager) {}

void parse_text_value(const char *text_value) override { publish_state(!strcasecmp(text_value, "ON")); }
void parse_hex_value(const HexFrame *hexframe) override {
if (hexframe->data_size() == sizeof(uint8_t)) {
publish_state(hexframe->data_u8());
}
};
};

class TFBinarySensor : public esphome::binary_sensor::BinarySensor, public TFEntity {
public:
TFBinarySensor(Manager *manager, const char *label, const DEF *def);
TFBinarySensor(Manager *manager, const char *label) : TFBinarySensor(manager, label, TFEntity::get_def(label)) {}

void dynamic_register() override;

void parse_text_value(const char *text_value) override { publish_state(!strcasecmp(text_value, "ON")); }
};

} // namespace m3_vedirect
Expand Down
Loading

0 comments on commit f9a7e89

Please sign in to comment.