Skip to content

Commit

Permalink
add basic send frame/command actions (m3_vedirect)
Browse files Browse the repository at this point in the history
  • Loading branch information
krahabb committed Oct 16, 2024
1 parent b50a06f commit 9d6f423
Show file tree
Hide file tree
Showing 5 changed files with 209 additions and 49 deletions.
67 changes: 49 additions & 18 deletions esphome/components/m3_vedirect/__init__.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
from functools import partial

from esphome import automation
import esphome.codegen as cg
from esphome.components import uart
Expand All @@ -19,7 +21,7 @@
HexFrame = m3_vedirect_ns.class_("HexFrame")
HexFrame_const_ref = HexFrame.operator("const").operator("ref")

HexFrameTrigger = m3_vedirect_ns.class_(
HexFrameTrigger = Manager.class_(
"HexFrameTrigger", automation.Trigger.template(HexFrame_const_ref)
)

Expand Down Expand Up @@ -193,24 +195,53 @@ async def to_code(config: dict):


# ACTIONS
HexFrameSendRawAction = m3_vedirect_ns.class_(
"HexFrameSendRawAction", automation.Action
)
HEXFRAME_SEND_SCHEMA = cv.Schema(
{
cv.Required(CONF_PAYLOAD): cv.templatable(cv.string),
cv.Optional(CONF_VEDIRECT_ID, default=""): cv.templatable(cv.string),
}
)

_CTYPE_VALIDATOR_MAP = {
cv.string: cg.std_string,
cv.int_: cg.int_,
validate_register_id: cg.uint16,
}

@automation.register_action(
"m3_vedirect.hexframe_send_raw", HexFrameSendRawAction, HEXFRAME_SEND_SCHEMA
)
async def m3_vedirect_hexframe_send_to_code(config, action_id, template_args, args):

async def action_to_code(
schema_def: dict[cv.Optional, object], config, action_id, template_args, args
):
var = cg.new_Pvariable(action_id, template_args)
template_ = await cg.templatable(config[CONF_PAYLOAD], args, cg.std_string)
cg.add(var.set_payload(template_))
template_ = await cg.templatable(config[CONF_VEDIRECT_ID], args, cg.std_string)
cg.add(var.set_vedirect_id(template_))
for _schema_key, _ctype in schema_def.items():
_key_name = _schema_key.schema
if _key_name in config:
template_ = await cg.templatable(
config[_key_name], args, _CTYPE_VALIDATOR_MAP[_ctype]
)
cg.add(getattr(var, f"set_{_key_name}")(template_))
return var


CONF_COMMAND = "command"
CONF_DATA = "data"
CONF_DATA_SIZE = "data_size"
MANAGER_ACTIONS = {
"send_hexframe": {
cv.Optional(CONF_VEDIRECT_ID, default=""): cv.string,
cv.Required(CONF_PAYLOAD): cv.string,
},
"send_command": {
cv.Optional(CONF_VEDIRECT_ID, default=""): cv.string,
cv.Required(CONF_COMMAND): cv.int_,
cv.Optional(CONF_REGISTER_ID): validate_register_id,
cv.Optional(CONF_DATA): cv.int_,
cv.Optional(CONF_DATA_SIZE): cv.int_,
},
}

for _action_name, _schema_def in MANAGER_ACTIONS.items():
_action = Manager.class_(f"Action_{_action_name}", automation.Action)
_schema = cv.Schema(
{
_schema_key: cv.templatable(_ctype)
for _schema_key, _ctype in _schema_def.items()
}
)
automation.register_action(f"m3_vedirect.{_action_name}", _action, _schema)(
partial(action_to_code, _schema_def)
)
9 changes: 9 additions & 0 deletions esphome/components/m3_vedirect/hexframe.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -206,5 +206,14 @@ void FrameHandler::decode(uint8_t *data_begin, uint8_t *data_end) {
}
}

/*
static_assert(sizeof(HexFrame::Record) == 8, "HexFrame::Record size failure");
static_assert(sizeof(HexFrame) == 20, "HexFrame size failure = ");
static_assert(sizeof(HexFrame_Get) == 40, "HexFrame_Get size failure = ");
auto _a = HexFrame_Set(0, 1);
static_assert(sizeof(_a) == 52, "HexFrame_Set size failure = ");
*/

} // namespace m3_vedirect
} // namespace esphome
67 changes: 63 additions & 4 deletions esphome/components/m3_vedirect/hexframe.h
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,26 @@ struct HexFrame {
Async = 0xA,
};

#pragma pack(push, 1)
struct Record {
Command command;
union {
uint8_t rawdata[0];
struct {
register_id_t register_id;
uint8_t flags;
union {
uint8_t data_u8;
int16_t data_i16;
uint16_t data_u16;
uint32_t data_u32;
uint8_t data[0];
};
};
};
};
#pragma pack(pop)

/// @brief Represents the format (numeric) of the data payload
enum DataType : int8_t {
unknown = 0,
Expand Down Expand Up @@ -70,9 +90,9 @@ struct HexFrame {
};

// Generic (raw) data accessors
inline Record *record() { return (Record *) this->rawframe_begin_; }
inline const uint8_t *begin() const { return this->rawframe_begin_; }
inline const uint8_t *end() const { return this->rawframe_end_; }

inline int capacity() const { return end_of_storage() - this->rawframe_begin_; }
inline int size() const { return this->rawframe_end_ - this->rawframe_begin_; }

Expand Down Expand Up @@ -115,11 +135,32 @@ struct HexFrame {
// Frame 'builders' methods
DecodeResult decode(const char *hexdigits, bool addchecksum);

inline void set_command(Command command) {
/// @brief Builds a plain command frame payload
/// @param command
inline void command(Command command) {
this->rawframe_begin_[0] = command;
this->rawframe_end_ = this->rawframe_begin_ + 1;
this->encode_();
}
/// @brief Builds a command GET register frame
/// @param register_id
inline void command_get(register_id_t register_id) {
auto record = this->record();
record->command = Command::Get;
record->register_id = register_id;
record->flags = 0;
this->rawframe_end_ = this->rawframe_begin_ + 4;
this->encode_();
}
template<typename DataType> void command_set(register_id_t register_id, DataType data) {
auto record = this->record();
record->command = Command::Set;
record->register_id = register_id;
record->flags = 0;
*(DataType *) record->data = data;
this->rawframe_end_ = this->rawframe_begin_ + 4 + sizeof(DataType);
this->encode_();
}

inline void push_back(uint8_t data) {
// warning: unchecked boundary
Expand Down Expand Up @@ -166,9 +207,27 @@ template<std::size_t HF_DATA_SIZE> struct HexFrameT : public HexFrame {
};

/// @brief Helper constructor for plain 'command' frames (no payload)
struct HexCommandFrame : public HexFrameT<0> {
struct HexFrame_Command : public HexFrameT<0> {
public:
HexCommandFrame(Command command) { this->set_command(command); }
HexFrame_Command(Command command) { this->command(command); }
};

/// @brief Helper constructor for plain 'command' frames (no payload)
struct HexFrame_Get : public HexFrameT<3> {
public:
HexFrame_Get(register_id_t register_id) { this->command_get(register_id); }
};

/// @brief Helper constructor for plain 'command' frames (no payload)
/*template <typename DataType> struct HexFrame_Set : public HexFrameT<3 + sizeof(DataType)> {
public:
HexFrame_Set(register_id_t register_id, DataType data) { this->command_set(register_id, data); }
};*/
struct HexFrame_Set : public HexFrameT<7> {
public:
template<typename DataType> HexFrame_Set(register_id_t register_id, DataType data) {
this->command_set(register_id, data);
}
};

class HexFrameDecoder {
Expand Down
27 changes: 20 additions & 7 deletions esphome/components/m3_vedirect/manager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -55,13 +55,26 @@ void Manager::loop() {
}

if (this->ping_retry_timeout_ && ((millis_ - this->millis_last_ping_tx_) > this->ping_retry_timeout_)) {
this->send_hexframe(HexCommandFrame(HexFrame::Ping));
this->send_hexframe(HexFrame_Command(HexFrame::Ping));
this->millis_last_ping_tx_ = this->millis_last_hexframe_tx_;
}
}

void Manager::dump_config() { ESP_LOGCONFIG(this->logtag_, "VEDirect:"); }

Manager *Manager::get_manager(const std::string &vedirect_id) {
if (vedirect_id.empty()) {
return managers_.front();
} else {
for (auto manager : managers_) {
if (manager->vedirect_id_ == vedirect_id) {
return manager;
}
}
}
return nullptr;
}

void Manager::setup_entity_name_id(EntityBase *entity, const char *name, const char *object_id) {
// set_name before set_object_id else it will fckup object_id generation
if (this->vedirect_name_.empty()) {
Expand All @@ -83,16 +96,16 @@ void Manager::send_hexframe(const HexFrame &hexframe) {
ESP_LOGD(this->logtag_, "HEX FRAME: sent %s", hexframe.encoded());
}

void Manager::send_hexframe(const char *hexdigits, bool addchecksum) {
void Manager::send_hexframe(const char *rawframe, bool addchecksum) {
HexFrameT<VEDIRECT_HEXFRAME_SIZE> hexframe;
if (HexFrame::DecodeResult::Valid == hexframe.decode(hexdigits, addchecksum)) {
if (HexFrame::DecodeResult::Valid == hexframe.decode(rawframe, addchecksum)) {
this->send_hexframe(hexframe);
} else {
ESP_LOGE(this->logtag_, "HEX FRAME: wrong encoding on request to send %s", hexdigits);
ESP_LOGE(this->logtag_, "HEX FRAME: wrong encoding on request to send %s", rawframe);
}
}

/*static*/ void Manager::send_hexframe(const std::string &vedirect_id, const std::string &payload) {
/* void Manager::send_hexframe(const std::string &vedirect_id, const std::string &payload) {
if (vedirect_id.empty()) {
managers_.front()->send_hexframe(payload.c_str(), false);
} else {
Expand All @@ -103,7 +116,7 @@ void Manager::send_hexframe(const char *hexdigits, bool addchecksum) {
}
}
}
}
}*/

void Manager::on_connected_() {
ESP_LOGD(this->logtag_, "LINK: connected");
Expand All @@ -112,7 +125,7 @@ void Manager::on_connected_() {
link_connected->publish_state(true);
}
if (this->auto_create_hex_entities_ || this->hex_registers_.size()) {
this->send_hexframe(HexCommandFrame(HexFrame::Ping));
this->send_hexframe(HexFrame_Command(HexFrame::Ping));
this->millis_last_ping_tx_ = this->millis_last_hexframe_tx_;
}
}
Expand Down
88 changes: 68 additions & 20 deletions esphome/components/m3_vedirect/manager.h
Original file line number Diff line number Diff line change
Expand Up @@ -46,11 +46,77 @@ class Manager : public uart::UARTDevice, public Component, protected FrameHandle
void loop() override;
void dump_config() override;

static Manager *get_manager(const std::string &vedirect_id);

void setup_entity_name_id(EntityBase *entity, const char *name, const char *object_id);

void send_hexframe(const HexFrame &hexframe);
void send_hexframe(const char *hexdigits, bool addchecksum);
static void send_hexframe(const std::string &vedirect_id, const std::string &payload);
void send_hexframe(const char *rawframe, bool addchecksum = true);
void send_hexframe(const std::string &rawframe, bool addchecksum = true) {
this->send_hexframe(rawframe.c_str(), addchecksum);
}
void send_command(HexFrame::Command command) { this->send_hexframe(HexFrame_Command(command)); }
void send_register_get(register_id_t register_id) { this->send_hexframe(HexFrame_Get(register_id)); }
template<typename DataType> void send_register_set(register_id_t register_id, DataType data) {
this->send_hexframe(HexFrame_Set(register_id, data));
}

class HexFrameTrigger : public Trigger<const HexFrame &> {
public:
explicit HexFrameTrigger(Manager *vedirect) {
vedirect->add_on_frame_callback([this](const HexFrame &hexframe) { this->trigger(hexframe); });
}
};

template<typename... Ts> class BaseAction : public Action<Ts...> {
public:
TEMPLATABLE_VALUE(std::string, vedirect_id)
};

template<typename... Ts> class Action_send_hexframe : public BaseAction<Ts...> {
public:
TEMPLATABLE_VALUE(std::string, payload)

void play(Ts... x) {
auto manager = Manager::get_manager(this->vedirect_id_.value(x...));
if (manager)
manager->send_hexframe(this->payload_.value(x...));
}
};
template<typename... Ts> class Action_send_command : public BaseAction<Ts...> {
public:
TEMPLATABLE_VALUE(uint8_t, command)
TEMPLATABLE_VALUE(register_id_t, register_id)
TEMPLATABLE_VALUE(uint32_t, data)
TEMPLATABLE_VALUE(uint8_t, data_size)

void play(Ts... x) {
auto manager = Manager::get_manager(this->vedirect_id_.value(x...));
if (manager) {
HexFrame::Command command = (HexFrame::Command) this->command_.value(x...);
switch (command) {
case HexFrame::Command::Get:
manager->send_register_get(this->register_id_.value(x...));
break;
case HexFrame::Command::Set:
switch (this->data_size_.value(x...)) {
case 1:
manager->send_register_set(this->register_id_.value(x...), (uint8_t) this->data_.value(x...));
break;
case 2:
manager->send_register_set(this->register_id_.value(x...), (uint16_t) this->data_.value(x...));
break;
default:
manager->send_register_set(this->register_id_.value(x...), this->data_.value(x...));
break;
}
break;
default:
manager->send_command(command);
}
}
}
};

protected:
// component config
Expand Down Expand Up @@ -95,23 +161,5 @@ class Manager : public uart::UARTDevice, public Component, protected FrameHandle
static std::vector<Manager *> managers_;
};

class HexFrameTrigger : public Trigger<const HexFrame &> {
public:
explicit HexFrameTrigger(Manager *vedirect) {
vedirect->add_on_frame_callback([this](const HexFrame &hexframe) { this->trigger(hexframe); });
}
};

template<typename... Ts> class HexFrameSendRawAction : public Action<Ts...> {
public:
HexFrameSendRawAction() {}
TEMPLATABLE_VALUE(std::string, vedirect_id)
TEMPLATABLE_VALUE(std::string, payload)

void play(Ts... x) { Manager::send_hexframe(this->vedirect_id_.value(x...), this->payload_.value(x...)); }

protected:
};

} // namespace m3_vedirect
} // namespace esphome

0 comments on commit 9d6f423

Please sign in to comment.