mirror of
https://github.com/esphome/esphome.git
synced 2024-11-12 10:16:02 +01:00
Merge branch 'dev' into ltr303
This commit is contained in:
commit
125bfc8145
@ -39,4 +39,4 @@ repos:
|
||||
rev: v13.0.1
|
||||
hooks:
|
||||
- id: clang-format
|
||||
|
||||
types_or: [c, c++]
|
||||
|
13
CODEOWNERS
13
CODEOWNERS
@ -119,6 +119,7 @@ esphome/components/esp32_rmt/* @jesserockz
|
||||
esphome/components/esp32_rmt_led_strip/* @jesserockz
|
||||
esphome/components/esp8266/* @esphome/core
|
||||
esphome/components/ethernet_info/* @gtjadsonsantos
|
||||
esphome/components/event/* @nohat
|
||||
esphome/components/exposure_notifications/* @OttoWinter
|
||||
esphome/components/ezo/* @ssieb
|
||||
esphome/components/ezo_pmp/* @carlos-sarmiento
|
||||
@ -360,6 +361,7 @@ esphome/components/tee501/* @Stock-M
|
||||
esphome/components/teleinfo/* @0hax
|
||||
esphome/components/template/alarm_control_panel/* @grahambrown11 @hwstar
|
||||
esphome/components/template/datetime/* @rfdarter
|
||||
esphome/components/template/event/* @nohat
|
||||
esphome/components/template/fan/* @ssieb
|
||||
esphome/components/text/* @mauritskorse
|
||||
esphome/components/thermostat/* @kbx81
|
||||
@ -401,10 +403,21 @@ esphome/components/wake_on_lan/* @willwill2will54
|
||||
esphome/components/waveshare_epaper/* @clydebarrow
|
||||
esphome/components/web_server_base/* @OttoWinter
|
||||
esphome/components/web_server_idf/* @dentra
|
||||
esphome/components/weikai/* @DrCoolZic
|
||||
esphome/components/weikai_i2c/* @DrCoolZic
|
||||
esphome/components/weikai_spi/* @DrCoolZic
|
||||
esphome/components/whirlpool/* @glmnet
|
||||
esphome/components/whynter/* @aeonsablaze
|
||||
esphome/components/wiegand/* @ssieb
|
||||
esphome/components/wireguard/* @droscy @lhoracek @thomas0bernard
|
||||
esphome/components/wk2132_i2c/* @DrCoolZic
|
||||
esphome/components/wk2132_spi/* @DrCoolZic
|
||||
esphome/components/wk2168_i2c/* @DrCoolZic
|
||||
esphome/components/wk2168_spi/* @DrCoolZic
|
||||
esphome/components/wk2204_i2c/* @DrCoolZic
|
||||
esphome/components/wk2204_spi/* @DrCoolZic
|
||||
esphome/components/wk2212_i2c/* @DrCoolZic
|
||||
esphome/components/wk2212_spi/* @DrCoolZic
|
||||
esphome/components/wl_134/* @hobbypunk90
|
||||
esphome/components/x9c/* @EtienneMD
|
||||
esphome/components/xgzp68xx/* @gcormier
|
||||
|
@ -18,10 +18,20 @@ from esphome.util import Registry
|
||||
|
||||
|
||||
def maybe_simple_id(*validators):
|
||||
"""Allow a raw ID to be specified in place of a config block.
|
||||
If the value that's being validated is a dictionary, it's passed as-is to the specified validators. Otherwise, it's
|
||||
wrapped in a dict that looks like ``{"id": <value>}``, and that dict is then handed off to the specified validators.
|
||||
"""
|
||||
return maybe_conf(CONF_ID, *validators)
|
||||
|
||||
|
||||
def maybe_conf(conf, *validators):
|
||||
"""Allow a raw value to be specified in place of a config block.
|
||||
If the value that's being validated is a dictionary, it's passed as-is to the specified validators. Otherwise, it's
|
||||
wrapped in a dict that looks like ``{<conf>: <value>}``, and that dict is then handed off to the specified
|
||||
validators.
|
||||
(This is a general case of ``maybe_simple_id`` that allows the wrapping key to be something other than ``id``.)
|
||||
"""
|
||||
validator = cv.All(*validators)
|
||||
|
||||
@schema_extractor("maybe")
|
||||
|
@ -97,9 +97,11 @@ void Alpha3::handle_geni_response_(const uint8_t *response, uint16_t length) {
|
||||
void Alpha3::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if, esp_ble_gattc_cb_param_t *param) {
|
||||
switch (event) {
|
||||
case ESP_GATTC_OPEN_EVT: {
|
||||
this->response_offset_ = 0;
|
||||
this->response_length_ = 0;
|
||||
ESP_LOGI(TAG, "[%s] connection open", this->parent_->address_str().c_str());
|
||||
if (param->open.status == ESP_GATT_OK) {
|
||||
this->response_offset_ = 0;
|
||||
this->response_length_ = 0;
|
||||
ESP_LOGI(TAG, "[%s] connection open", this->parent_->address_str().c_str());
|
||||
}
|
||||
break;
|
||||
}
|
||||
case ESP_GATTC_CONNECT_EVT: {
|
||||
|
@ -26,7 +26,9 @@ void Am43::setup() {
|
||||
void Am43::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if, esp_ble_gattc_cb_param_t *param) {
|
||||
switch (event) {
|
||||
case ESP_GATTC_OPEN_EVT: {
|
||||
this->logged_in_ = false;
|
||||
if (param->open.status == ESP_GATT_OK) {
|
||||
this->logged_in_ = false;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case ESP_GATTC_DISCONNECT_EVT: {
|
||||
|
@ -47,6 +47,7 @@ service APIConnection {
|
||||
rpc media_player_command (MediaPlayerCommandRequest) returns (void) {}
|
||||
rpc date_command (DateCommandRequest) returns (void) {}
|
||||
rpc time_command (TimeCommandRequest) returns (void) {}
|
||||
rpc datetime_command (DateTimeCommandRequest) returns (void) {}
|
||||
|
||||
rpc subscribe_bluetooth_le_advertisements(SubscribeBluetoothLEAdvertisementsRequest) returns (void) {}
|
||||
rpc bluetooth_device_request(BluetoothDeviceRequest) returns (void) {}
|
||||
@ -1702,6 +1703,32 @@ message TimeCommandRequest {
|
||||
uint32 second = 4;
|
||||
}
|
||||
|
||||
// ==================== EVENT ====================
|
||||
message ListEntitiesEventResponse {
|
||||
option (id) = 107;
|
||||
option (source) = SOURCE_SERVER;
|
||||
option (ifdef) = "USE_EVENT";
|
||||
|
||||
string object_id = 1;
|
||||
fixed32 key = 2;
|
||||
string name = 3;
|
||||
string unique_id = 4;
|
||||
|
||||
string icon = 5;
|
||||
bool disabled_by_default = 6;
|
||||
EntityCategory entity_category = 7;
|
||||
string device_class = 8;
|
||||
|
||||
repeated string event_types = 9;
|
||||
}
|
||||
message EventResponse {
|
||||
option (id) = 108;
|
||||
option (source) = SOURCE_SERVER;
|
||||
option (ifdef) = "USE_EVENT";
|
||||
|
||||
fixed32 key = 1;
|
||||
string event_type = 2;
|
||||
}
|
||||
|
||||
// ==================== VALVE ====================
|
||||
message ListEntitiesValveResponse {
|
||||
@ -1751,3 +1778,40 @@ message ValveCommandRequest {
|
||||
float position = 3;
|
||||
bool stop = 4;
|
||||
}
|
||||
|
||||
// ==================== DATETIME DATETIME ====================
|
||||
message ListEntitiesDateTimeResponse {
|
||||
option (id) = 112;
|
||||
option (source) = SOURCE_SERVER;
|
||||
option (ifdef) = "USE_DATETIME_DATETIME";
|
||||
|
||||
string object_id = 1;
|
||||
fixed32 key = 2;
|
||||
string name = 3;
|
||||
string unique_id = 4;
|
||||
|
||||
string icon = 5;
|
||||
bool disabled_by_default = 6;
|
||||
EntityCategory entity_category = 7;
|
||||
}
|
||||
message DateTimeStateResponse {
|
||||
option (id) = 113;
|
||||
option (source) = SOURCE_SERVER;
|
||||
option (ifdef) = "USE_DATETIME_DATETIME";
|
||||
option (no_delay) = true;
|
||||
|
||||
fixed32 key = 1;
|
||||
// If the datetime does not have a valid state yet.
|
||||
// Equivalent to `!obj->has_state()` - inverse logic to make state packets smaller
|
||||
bool missing_state = 2;
|
||||
fixed32 epoch_seconds = 3;
|
||||
}
|
||||
message DateTimeCommandRequest {
|
||||
option (id) = 114;
|
||||
option (source) = SOURCE_CLIENT;
|
||||
option (ifdef) = "USE_DATETIME_DATETIME";
|
||||
option (no_delay) = true;
|
||||
|
||||
fixed32 key = 1;
|
||||
fixed32 epoch_seconds = 2;
|
||||
}
|
||||
|
@ -772,6 +772,44 @@ void APIConnection::time_command(const TimeCommandRequest &msg) {
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef USE_DATETIME_DATETIME
|
||||
bool APIConnection::send_datetime_state(datetime::DateTimeEntity *datetime) {
|
||||
if (!this->state_subscription_)
|
||||
return false;
|
||||
|
||||
DateTimeStateResponse resp{};
|
||||
resp.key = datetime->get_object_id_hash();
|
||||
resp.missing_state = !datetime->has_state();
|
||||
if (datetime->has_state()) {
|
||||
ESPTime state = datetime->state_as_esptime();
|
||||
resp.epoch_seconds = state.timestamp;
|
||||
}
|
||||
return this->send_date_time_state_response(resp);
|
||||
}
|
||||
bool APIConnection::send_datetime_info(datetime::DateTimeEntity *datetime) {
|
||||
ListEntitiesDateTimeResponse msg;
|
||||
msg.key = datetime->get_object_id_hash();
|
||||
msg.object_id = datetime->get_object_id();
|
||||
if (datetime->has_own_name())
|
||||
msg.name = datetime->get_name();
|
||||
msg.unique_id = get_default_unique_id("datetime", datetime);
|
||||
msg.icon = datetime->get_icon();
|
||||
msg.disabled_by_default = datetime->is_disabled_by_default();
|
||||
msg.entity_category = static_cast<enums::EntityCategory>(datetime->get_entity_category());
|
||||
|
||||
return this->send_list_entities_date_time_response(msg);
|
||||
}
|
||||
void APIConnection::datetime_command(const DateTimeCommandRequest &msg) {
|
||||
datetime::DateTimeEntity *datetime = App.get_datetime_by_key(msg.key);
|
||||
if (datetime == nullptr)
|
||||
return;
|
||||
|
||||
auto call = datetime->make_call();
|
||||
call.set_datetime(msg.epoch_seconds);
|
||||
call.perform();
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef USE_TEXT
|
||||
bool APIConnection::send_text_state(text::Text *text, std::string state) {
|
||||
if (!this->state_subscription_)
|
||||
@ -1209,6 +1247,30 @@ void APIConnection::alarm_control_panel_command(const AlarmControlPanelCommandRe
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef USE_EVENT
|
||||
bool APIConnection::send_event(event::Event *event, std::string event_type) {
|
||||
EventResponse resp{};
|
||||
resp.key = event->get_object_id_hash();
|
||||
resp.event_type = std::move(event_type);
|
||||
return this->send_event_response(resp);
|
||||
}
|
||||
bool APIConnection::send_event_info(event::Event *event) {
|
||||
ListEntitiesEventResponse msg;
|
||||
msg.key = event->get_object_id_hash();
|
||||
msg.object_id = event->get_object_id();
|
||||
if (event->has_own_name())
|
||||
msg.name = event->get_name();
|
||||
msg.unique_id = get_default_unique_id("event", event);
|
||||
msg.icon = event->get_icon();
|
||||
msg.disabled_by_default = event->is_disabled_by_default();
|
||||
msg.entity_category = static_cast<enums::EntityCategory>(event->get_entity_category());
|
||||
msg.device_class = event->get_device_class();
|
||||
for (const auto &event_type : event->get_event_types())
|
||||
msg.event_types.push_back(event_type);
|
||||
return this->send_list_entities_event_response(msg);
|
||||
}
|
||||
#endif
|
||||
|
||||
bool APIConnection::send_log_message(int level, const char *tag, const char *line) {
|
||||
if (this->log_subscription_ < level)
|
||||
return false;
|
||||
|
@ -82,6 +82,11 @@ class APIConnection : public APIServerConnection {
|
||||
bool send_time_info(datetime::TimeEntity *time);
|
||||
void time_command(const TimeCommandRequest &msg) override;
|
||||
#endif
|
||||
#ifdef USE_DATETIME_DATETIME
|
||||
bool send_datetime_state(datetime::DateTimeEntity *datetime);
|
||||
bool send_datetime_info(datetime::DateTimeEntity *datetime);
|
||||
void datetime_command(const DateTimeCommandRequest &msg) override;
|
||||
#endif
|
||||
#ifdef USE_TEXT
|
||||
bool send_text_state(text::Text *text, std::string state);
|
||||
bool send_text_info(text::Text *text);
|
||||
@ -153,6 +158,11 @@ class APIConnection : public APIServerConnection {
|
||||
void alarm_control_panel_command(const AlarmControlPanelCommandRequest &msg) override;
|
||||
#endif
|
||||
|
||||
#ifdef USE_EVENT
|
||||
bool send_event(event::Event *event, std::string event_type);
|
||||
bool send_event_info(event::Event *event);
|
||||
#endif
|
||||
|
||||
void on_disconnect_response(const DisconnectResponse &value) override;
|
||||
void on_ping_response(const PingResponse &value) override {
|
||||
// we initiated ping
|
||||
|
@ -7709,6 +7709,157 @@ void TimeCommandRequest::dump_to(std::string &out) const {
|
||||
out.append("}");
|
||||
}
|
||||
#endif
|
||||
bool ListEntitiesEventResponse::decode_varint(uint32_t field_id, ProtoVarInt value) {
|
||||
switch (field_id) {
|
||||
case 6: {
|
||||
this->disabled_by_default = value.as_bool();
|
||||
return true;
|
||||
}
|
||||
case 7: {
|
||||
this->entity_category = value.as_enum<enums::EntityCategory>();
|
||||
return true;
|
||||
}
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
bool ListEntitiesEventResponse::decode_length(uint32_t field_id, ProtoLengthDelimited value) {
|
||||
switch (field_id) {
|
||||
case 1: {
|
||||
this->object_id = value.as_string();
|
||||
return true;
|
||||
}
|
||||
case 3: {
|
||||
this->name = value.as_string();
|
||||
return true;
|
||||
}
|
||||
case 4: {
|
||||
this->unique_id = value.as_string();
|
||||
return true;
|
||||
}
|
||||
case 5: {
|
||||
this->icon = value.as_string();
|
||||
return true;
|
||||
}
|
||||
case 8: {
|
||||
this->device_class = value.as_string();
|
||||
return true;
|
||||
}
|
||||
case 9: {
|
||||
this->event_types.push_back(value.as_string());
|
||||
return true;
|
||||
}
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
bool ListEntitiesEventResponse::decode_32bit(uint32_t field_id, Proto32Bit value) {
|
||||
switch (field_id) {
|
||||
case 2: {
|
||||
this->key = value.as_fixed32();
|
||||
return true;
|
||||
}
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
void ListEntitiesEventResponse::encode(ProtoWriteBuffer buffer) const {
|
||||
buffer.encode_string(1, this->object_id);
|
||||
buffer.encode_fixed32(2, this->key);
|
||||
buffer.encode_string(3, this->name);
|
||||
buffer.encode_string(4, this->unique_id);
|
||||
buffer.encode_string(5, this->icon);
|
||||
buffer.encode_bool(6, this->disabled_by_default);
|
||||
buffer.encode_enum<enums::EntityCategory>(7, this->entity_category);
|
||||
buffer.encode_string(8, this->device_class);
|
||||
for (auto &it : this->event_types) {
|
||||
buffer.encode_string(9, it, true);
|
||||
}
|
||||
}
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
void ListEntitiesEventResponse::dump_to(std::string &out) const {
|
||||
__attribute__((unused)) char buffer[64];
|
||||
out.append("ListEntitiesEventResponse {\n");
|
||||
out.append(" object_id: ");
|
||||
out.append("'").append(this->object_id).append("'");
|
||||
out.append("\n");
|
||||
|
||||
out.append(" key: ");
|
||||
sprintf(buffer, "%" PRIu32, this->key);
|
||||
out.append(buffer);
|
||||
out.append("\n");
|
||||
|
||||
out.append(" name: ");
|
||||
out.append("'").append(this->name).append("'");
|
||||
out.append("\n");
|
||||
|
||||
out.append(" unique_id: ");
|
||||
out.append("'").append(this->unique_id).append("'");
|
||||
out.append("\n");
|
||||
|
||||
out.append(" icon: ");
|
||||
out.append("'").append(this->icon).append("'");
|
||||
out.append("\n");
|
||||
|
||||
out.append(" disabled_by_default: ");
|
||||
out.append(YESNO(this->disabled_by_default));
|
||||
out.append("\n");
|
||||
|
||||
out.append(" entity_category: ");
|
||||
out.append(proto_enum_to_string<enums::EntityCategory>(this->entity_category));
|
||||
out.append("\n");
|
||||
|
||||
out.append(" device_class: ");
|
||||
out.append("'").append(this->device_class).append("'");
|
||||
out.append("\n");
|
||||
|
||||
for (const auto &it : this->event_types) {
|
||||
out.append(" event_types: ");
|
||||
out.append("'").append(it).append("'");
|
||||
out.append("\n");
|
||||
}
|
||||
out.append("}");
|
||||
}
|
||||
#endif
|
||||
bool EventResponse::decode_length(uint32_t field_id, ProtoLengthDelimited value) {
|
||||
switch (field_id) {
|
||||
case 2: {
|
||||
this->event_type = value.as_string();
|
||||
return true;
|
||||
}
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
bool EventResponse::decode_32bit(uint32_t field_id, Proto32Bit value) {
|
||||
switch (field_id) {
|
||||
case 1: {
|
||||
this->key = value.as_fixed32();
|
||||
return true;
|
||||
}
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
void EventResponse::encode(ProtoWriteBuffer buffer) const {
|
||||
buffer.encode_fixed32(1, this->key);
|
||||
buffer.encode_string(2, this->event_type);
|
||||
}
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
void EventResponse::dump_to(std::string &out) const {
|
||||
__attribute__((unused)) char buffer[64];
|
||||
out.append("EventResponse {\n");
|
||||
out.append(" key: ");
|
||||
sprintf(buffer, "%" PRIu32, this->key);
|
||||
out.append(buffer);
|
||||
out.append("\n");
|
||||
|
||||
out.append(" event_type: ");
|
||||
out.append("'").append(this->event_type).append("'");
|
||||
out.append("\n");
|
||||
out.append("}");
|
||||
}
|
||||
#endif
|
||||
bool ListEntitiesValveResponse::decode_varint(uint32_t field_id, ProtoVarInt value) {
|
||||
switch (field_id) {
|
||||
case 6: {
|
||||
@ -7942,6 +8093,179 @@ void ValveCommandRequest::dump_to(std::string &out) const {
|
||||
out.append("}");
|
||||
}
|
||||
#endif
|
||||
bool ListEntitiesDateTimeResponse::decode_varint(uint32_t field_id, ProtoVarInt value) {
|
||||
switch (field_id) {
|
||||
case 6: {
|
||||
this->disabled_by_default = value.as_bool();
|
||||
return true;
|
||||
}
|
||||
case 7: {
|
||||
this->entity_category = value.as_enum<enums::EntityCategory>();
|
||||
return true;
|
||||
}
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
bool ListEntitiesDateTimeResponse::decode_length(uint32_t field_id, ProtoLengthDelimited value) {
|
||||
switch (field_id) {
|
||||
case 1: {
|
||||
this->object_id = value.as_string();
|
||||
return true;
|
||||
}
|
||||
case 3: {
|
||||
this->name = value.as_string();
|
||||
return true;
|
||||
}
|
||||
case 4: {
|
||||
this->unique_id = value.as_string();
|
||||
return true;
|
||||
}
|
||||
case 5: {
|
||||
this->icon = value.as_string();
|
||||
return true;
|
||||
}
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
bool ListEntitiesDateTimeResponse::decode_32bit(uint32_t field_id, Proto32Bit value) {
|
||||
switch (field_id) {
|
||||
case 2: {
|
||||
this->key = value.as_fixed32();
|
||||
return true;
|
||||
}
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
void ListEntitiesDateTimeResponse::encode(ProtoWriteBuffer buffer) const {
|
||||
buffer.encode_string(1, this->object_id);
|
||||
buffer.encode_fixed32(2, this->key);
|
||||
buffer.encode_string(3, this->name);
|
||||
buffer.encode_string(4, this->unique_id);
|
||||
buffer.encode_string(5, this->icon);
|
||||
buffer.encode_bool(6, this->disabled_by_default);
|
||||
buffer.encode_enum<enums::EntityCategory>(7, this->entity_category);
|
||||
}
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
void ListEntitiesDateTimeResponse::dump_to(std::string &out) const {
|
||||
__attribute__((unused)) char buffer[64];
|
||||
out.append("ListEntitiesDateTimeResponse {\n");
|
||||
out.append(" object_id: ");
|
||||
out.append("'").append(this->object_id).append("'");
|
||||
out.append("\n");
|
||||
|
||||
out.append(" key: ");
|
||||
sprintf(buffer, "%" PRIu32, this->key);
|
||||
out.append(buffer);
|
||||
out.append("\n");
|
||||
|
||||
out.append(" name: ");
|
||||
out.append("'").append(this->name).append("'");
|
||||
out.append("\n");
|
||||
|
||||
out.append(" unique_id: ");
|
||||
out.append("'").append(this->unique_id).append("'");
|
||||
out.append("\n");
|
||||
|
||||
out.append(" icon: ");
|
||||
out.append("'").append(this->icon).append("'");
|
||||
out.append("\n");
|
||||
|
||||
out.append(" disabled_by_default: ");
|
||||
out.append(YESNO(this->disabled_by_default));
|
||||
out.append("\n");
|
||||
|
||||
out.append(" entity_category: ");
|
||||
out.append(proto_enum_to_string<enums::EntityCategory>(this->entity_category));
|
||||
out.append("\n");
|
||||
out.append("}");
|
||||
}
|
||||
#endif
|
||||
bool DateTimeStateResponse::decode_varint(uint32_t field_id, ProtoVarInt value) {
|
||||
switch (field_id) {
|
||||
case 2: {
|
||||
this->missing_state = value.as_bool();
|
||||
return true;
|
||||
}
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
bool DateTimeStateResponse::decode_32bit(uint32_t field_id, Proto32Bit value) {
|
||||
switch (field_id) {
|
||||
case 1: {
|
||||
this->key = value.as_fixed32();
|
||||
return true;
|
||||
}
|
||||
case 3: {
|
||||
this->epoch_seconds = value.as_fixed32();
|
||||
return true;
|
||||
}
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
void DateTimeStateResponse::encode(ProtoWriteBuffer buffer) const {
|
||||
buffer.encode_fixed32(1, this->key);
|
||||
buffer.encode_bool(2, this->missing_state);
|
||||
buffer.encode_fixed32(3, this->epoch_seconds);
|
||||
}
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
void DateTimeStateResponse::dump_to(std::string &out) const {
|
||||
__attribute__((unused)) char buffer[64];
|
||||
out.append("DateTimeStateResponse {\n");
|
||||
out.append(" key: ");
|
||||
sprintf(buffer, "%" PRIu32, this->key);
|
||||
out.append(buffer);
|
||||
out.append("\n");
|
||||
|
||||
out.append(" missing_state: ");
|
||||
out.append(YESNO(this->missing_state));
|
||||
out.append("\n");
|
||||
|
||||
out.append(" epoch_seconds: ");
|
||||
sprintf(buffer, "%" PRIu32, this->epoch_seconds);
|
||||
out.append(buffer);
|
||||
out.append("\n");
|
||||
out.append("}");
|
||||
}
|
||||
#endif
|
||||
bool DateTimeCommandRequest::decode_32bit(uint32_t field_id, Proto32Bit value) {
|
||||
switch (field_id) {
|
||||
case 1: {
|
||||
this->key = value.as_fixed32();
|
||||
return true;
|
||||
}
|
||||
case 2: {
|
||||
this->epoch_seconds = value.as_fixed32();
|
||||
return true;
|
||||
}
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
void DateTimeCommandRequest::encode(ProtoWriteBuffer buffer) const {
|
||||
buffer.encode_fixed32(1, this->key);
|
||||
buffer.encode_fixed32(2, this->epoch_seconds);
|
||||
}
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
void DateTimeCommandRequest::dump_to(std::string &out) const {
|
||||
__attribute__((unused)) char buffer[64];
|
||||
out.append("DateTimeCommandRequest {\n");
|
||||
out.append(" key: ");
|
||||
sprintf(buffer, "%" PRIu32, this->key);
|
||||
out.append(buffer);
|
||||
out.append("\n");
|
||||
|
||||
out.append(" epoch_seconds: ");
|
||||
sprintf(buffer, "%" PRIu32, this->epoch_seconds);
|
||||
out.append(buffer);
|
||||
out.append("\n");
|
||||
out.append("}");
|
||||
}
|
||||
#endif
|
||||
|
||||
} // namespace api
|
||||
} // namespace esphome
|
||||
|
@ -1974,6 +1974,40 @@ class TimeCommandRequest : public ProtoMessage {
|
||||
bool decode_32bit(uint32_t field_id, Proto32Bit value) override;
|
||||
bool decode_varint(uint32_t field_id, ProtoVarInt value) override;
|
||||
};
|
||||
class ListEntitiesEventResponse : public ProtoMessage {
|
||||
public:
|
||||
std::string object_id{};
|
||||
uint32_t key{0};
|
||||
std::string name{};
|
||||
std::string unique_id{};
|
||||
std::string icon{};
|
||||
bool disabled_by_default{false};
|
||||
enums::EntityCategory entity_category{};
|
||||
std::string device_class{};
|
||||
std::vector<std::string> event_types{};
|
||||
void encode(ProtoWriteBuffer buffer) const override;
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
void dump_to(std::string &out) const override;
|
||||
#endif
|
||||
|
||||
protected:
|
||||
bool decode_32bit(uint32_t field_id, Proto32Bit value) override;
|
||||
bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override;
|
||||
bool decode_varint(uint32_t field_id, ProtoVarInt value) override;
|
||||
};
|
||||
class EventResponse : public ProtoMessage {
|
||||
public:
|
||||
uint32_t key{0};
|
||||
std::string event_type{};
|
||||
void encode(ProtoWriteBuffer buffer) const override;
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
void dump_to(std::string &out) const override;
|
||||
#endif
|
||||
|
||||
protected:
|
||||
bool decode_32bit(uint32_t field_id, Proto32Bit value) override;
|
||||
bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override;
|
||||
};
|
||||
class ListEntitiesValveResponse : public ProtoMessage {
|
||||
public:
|
||||
std::string object_id{};
|
||||
@ -2026,6 +2060,51 @@ class ValveCommandRequest : public ProtoMessage {
|
||||
bool decode_32bit(uint32_t field_id, Proto32Bit value) override;
|
||||
bool decode_varint(uint32_t field_id, ProtoVarInt value) override;
|
||||
};
|
||||
class ListEntitiesDateTimeResponse : public ProtoMessage {
|
||||
public:
|
||||
std::string object_id{};
|
||||
uint32_t key{0};
|
||||
std::string name{};
|
||||
std::string unique_id{};
|
||||
std::string icon{};
|
||||
bool disabled_by_default{false};
|
||||
enums::EntityCategory entity_category{};
|
||||
void encode(ProtoWriteBuffer buffer) const override;
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
void dump_to(std::string &out) const override;
|
||||
#endif
|
||||
|
||||
protected:
|
||||
bool decode_32bit(uint32_t field_id, Proto32Bit value) override;
|
||||
bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override;
|
||||
bool decode_varint(uint32_t field_id, ProtoVarInt value) override;
|
||||
};
|
||||
class DateTimeStateResponse : public ProtoMessage {
|
||||
public:
|
||||
uint32_t key{0};
|
||||
bool missing_state{false};
|
||||
uint32_t epoch_seconds{0};
|
||||
void encode(ProtoWriteBuffer buffer) const override;
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
void dump_to(std::string &out) const override;
|
||||
#endif
|
||||
|
||||
protected:
|
||||
bool decode_32bit(uint32_t field_id, Proto32Bit value) override;
|
||||
bool decode_varint(uint32_t field_id, ProtoVarInt value) override;
|
||||
};
|
||||
class DateTimeCommandRequest : public ProtoMessage {
|
||||
public:
|
||||
uint32_t key{0};
|
||||
uint32_t epoch_seconds{0};
|
||||
void encode(ProtoWriteBuffer buffer) const override;
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
void dump_to(std::string &out) const override;
|
||||
#endif
|
||||
|
||||
protected:
|
||||
bool decode_32bit(uint32_t field_id, Proto32Bit value) override;
|
||||
};
|
||||
|
||||
} // namespace api
|
||||
} // namespace esphome
|
||||
|
@ -557,6 +557,22 @@ bool APIServerConnectionBase::send_time_state_response(const TimeStateResponse &
|
||||
#endif
|
||||
#ifdef USE_DATETIME_TIME
|
||||
#endif
|
||||
#ifdef USE_EVENT
|
||||
bool APIServerConnectionBase::send_list_entities_event_response(const ListEntitiesEventResponse &msg) {
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
ESP_LOGVV(TAG, "send_list_entities_event_response: %s", msg.dump().c_str());
|
||||
#endif
|
||||
return this->send_message_<ListEntitiesEventResponse>(msg, 107);
|
||||
}
|
||||
#endif
|
||||
#ifdef USE_EVENT
|
||||
bool APIServerConnectionBase::send_event_response(const EventResponse &msg) {
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
ESP_LOGVV(TAG, "send_event_response: %s", msg.dump().c_str());
|
||||
#endif
|
||||
return this->send_message_<EventResponse>(msg, 108);
|
||||
}
|
||||
#endif
|
||||
#ifdef USE_VALVE
|
||||
bool APIServerConnectionBase::send_list_entities_valve_response(const ListEntitiesValveResponse &msg) {
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
@ -575,6 +591,24 @@ bool APIServerConnectionBase::send_valve_state_response(const ValveStateResponse
|
||||
#endif
|
||||
#ifdef USE_VALVE
|
||||
#endif
|
||||
#ifdef USE_DATETIME_DATETIME
|
||||
bool APIServerConnectionBase::send_list_entities_date_time_response(const ListEntitiesDateTimeResponse &msg) {
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
ESP_LOGVV(TAG, "send_list_entities_date_time_response: %s", msg.dump().c_str());
|
||||
#endif
|
||||
return this->send_message_<ListEntitiesDateTimeResponse>(msg, 112);
|
||||
}
|
||||
#endif
|
||||
#ifdef USE_DATETIME_DATETIME
|
||||
bool APIServerConnectionBase::send_date_time_state_response(const DateTimeStateResponse &msg) {
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
ESP_LOGVV(TAG, "send_date_time_state_response: %s", msg.dump().c_str());
|
||||
#endif
|
||||
return this->send_message_<DateTimeStateResponse>(msg, 113);
|
||||
}
|
||||
#endif
|
||||
#ifdef USE_DATETIME_DATETIME
|
||||
#endif
|
||||
bool APIServerConnectionBase::read_message(uint32_t msg_size, uint32_t msg_type, uint8_t *msg_data) {
|
||||
switch (msg_type) {
|
||||
case 1: {
|
||||
@ -1048,6 +1082,17 @@ bool APIServerConnectionBase::read_message(uint32_t msg_size, uint32_t msg_type,
|
||||
ESP_LOGVV(TAG, "on_valve_command_request: %s", msg.dump().c_str());
|
||||
#endif
|
||||
this->on_valve_command_request(msg);
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
case 114: {
|
||||
#ifdef USE_DATETIME_DATETIME
|
||||
DateTimeCommandRequest msg;
|
||||
msg.decode(msg_data, msg_size);
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
ESP_LOGVV(TAG, "on_date_time_command_request: %s", msg.dump().c_str());
|
||||
#endif
|
||||
this->on_date_time_command_request(msg);
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
@ -1363,6 +1408,19 @@ void APIServerConnection::on_time_command_request(const TimeCommandRequest &msg)
|
||||
this->time_command(msg);
|
||||
}
|
||||
#endif
|
||||
#ifdef USE_DATETIME_DATETIME
|
||||
void APIServerConnection::on_date_time_command_request(const DateTimeCommandRequest &msg) {
|
||||
if (!this->is_connection_setup()) {
|
||||
this->on_no_setup_connection();
|
||||
return;
|
||||
}
|
||||
if (!this->is_authenticated()) {
|
||||
this->on_unauthenticated_access();
|
||||
return;
|
||||
}
|
||||
this->datetime_command(msg);
|
||||
}
|
||||
#endif
|
||||
#ifdef USE_BLUETOOTH_PROXY
|
||||
void APIServerConnection::on_subscribe_bluetooth_le_advertisements_request(
|
||||
const SubscribeBluetoothLEAdvertisementsRequest &msg) {
|
||||
|
@ -280,6 +280,12 @@ class APIServerConnectionBase : public ProtoService {
|
||||
#ifdef USE_DATETIME_TIME
|
||||
virtual void on_time_command_request(const TimeCommandRequest &value){};
|
||||
#endif
|
||||
#ifdef USE_EVENT
|
||||
bool send_list_entities_event_response(const ListEntitiesEventResponse &msg);
|
||||
#endif
|
||||
#ifdef USE_EVENT
|
||||
bool send_event_response(const EventResponse &msg);
|
||||
#endif
|
||||
#ifdef USE_VALVE
|
||||
bool send_list_entities_valve_response(const ListEntitiesValveResponse &msg);
|
||||
#endif
|
||||
@ -288,6 +294,15 @@ class APIServerConnectionBase : public ProtoService {
|
||||
#endif
|
||||
#ifdef USE_VALVE
|
||||
virtual void on_valve_command_request(const ValveCommandRequest &value){};
|
||||
#endif
|
||||
#ifdef USE_DATETIME_DATETIME
|
||||
bool send_list_entities_date_time_response(const ListEntitiesDateTimeResponse &msg);
|
||||
#endif
|
||||
#ifdef USE_DATETIME_DATETIME
|
||||
bool send_date_time_state_response(const DateTimeStateResponse &msg);
|
||||
#endif
|
||||
#ifdef USE_DATETIME_DATETIME
|
||||
virtual void on_date_time_command_request(const DateTimeCommandRequest &value){};
|
||||
#endif
|
||||
protected:
|
||||
bool read_message(uint32_t msg_size, uint32_t msg_type, uint8_t *msg_data) override;
|
||||
@ -352,6 +367,9 @@ class APIServerConnection : public APIServerConnectionBase {
|
||||
#ifdef USE_DATETIME_TIME
|
||||
virtual void time_command(const TimeCommandRequest &msg) = 0;
|
||||
#endif
|
||||
#ifdef USE_DATETIME_DATETIME
|
||||
virtual void datetime_command(const DateTimeCommandRequest &msg) = 0;
|
||||
#endif
|
||||
#ifdef USE_BLUETOOTH_PROXY
|
||||
virtual void subscribe_bluetooth_le_advertisements(const SubscribeBluetoothLEAdvertisementsRequest &msg) = 0;
|
||||
#endif
|
||||
@ -447,6 +465,9 @@ class APIServerConnection : public APIServerConnectionBase {
|
||||
#ifdef USE_DATETIME_TIME
|
||||
void on_time_command_request(const TimeCommandRequest &msg) override;
|
||||
#endif
|
||||
#ifdef USE_DATETIME_DATETIME
|
||||
void on_date_time_command_request(const DateTimeCommandRequest &msg) override;
|
||||
#endif
|
||||
#ifdef USE_BLUETOOTH_PROXY
|
||||
void on_subscribe_bluetooth_le_advertisements_request(const SubscribeBluetoothLEAdvertisementsRequest &msg) override;
|
||||
#endif
|
||||
|
@ -273,6 +273,15 @@ void APIServer::on_time_update(datetime::TimeEntity *obj) {
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef USE_DATETIME_DATETIME
|
||||
void APIServer::on_datetime_update(datetime::DateTimeEntity *obj) {
|
||||
if (obj->is_internal())
|
||||
return;
|
||||
for (auto &c : this->clients_)
|
||||
c->send_datetime_state(obj);
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef USE_TEXT
|
||||
void APIServer::on_text_update(text::Text *obj, const std::string &state) {
|
||||
if (obj->is_internal())
|
||||
@ -318,6 +327,13 @@ void APIServer::on_media_player_update(media_player::MediaPlayer *obj) {
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef USE_EVENT
|
||||
void APIServer::on_event(event::Event *obj, const std::string &event_type) {
|
||||
for (auto &c : this->clients_)
|
||||
c->send_event(obj, event_type);
|
||||
}
|
||||
#endif
|
||||
|
||||
float APIServer::get_setup_priority() const { return setup_priority::AFTER_WIFI; }
|
||||
void APIServer::set_port(uint16_t port) { this->port_ = port; }
|
||||
APIServer *global_api_server = nullptr; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables)
|
||||
|
@ -72,6 +72,9 @@ class APIServer : public Component, public Controller {
|
||||
#ifdef USE_DATETIME_TIME
|
||||
void on_time_update(datetime::TimeEntity *obj) override;
|
||||
#endif
|
||||
#ifdef USE_DATETIME_DATETIME
|
||||
void on_datetime_update(datetime::DateTimeEntity *obj) override;
|
||||
#endif
|
||||
#ifdef USE_TEXT
|
||||
void on_text_update(text::Text *obj, const std::string &state) override;
|
||||
#endif
|
||||
@ -96,6 +99,9 @@ class APIServer : public Component, public Controller {
|
||||
#ifdef USE_ALARM_CONTROL_PANEL
|
||||
void on_alarm_control_panel_update(alarm_control_panel::AlarmControlPanel *obj) override;
|
||||
#endif
|
||||
#ifdef USE_EVENT
|
||||
void on_event(event::Event *obj, const std::string &event_type) override;
|
||||
#endif
|
||||
|
||||
bool is_connected() const;
|
||||
|
||||
|
@ -71,6 +71,12 @@ bool ListEntitiesIterator::on_date(datetime::DateEntity *date) { return this->cl
|
||||
bool ListEntitiesIterator::on_time(datetime::TimeEntity *time) { return this->client_->send_time_info(time); }
|
||||
#endif
|
||||
|
||||
#ifdef USE_DATETIME_DATETIME
|
||||
bool ListEntitiesIterator::on_datetime(datetime::DateTimeEntity *datetime) {
|
||||
return this->client_->send_datetime_info(datetime);
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef USE_TEXT
|
||||
bool ListEntitiesIterator::on_text(text::Text *text) { return this->client_->send_text_info(text); }
|
||||
#endif
|
||||
@ -89,6 +95,9 @@ bool ListEntitiesIterator::on_alarm_control_panel(alarm_control_panel::AlarmCont
|
||||
return this->client_->send_alarm_control_panel_info(a_alarm_control_panel);
|
||||
}
|
||||
#endif
|
||||
#ifdef USE_EVENT
|
||||
bool ListEntitiesIterator::on_event(event::Event *event) { return this->client_->send_event_info(event); }
|
||||
#endif
|
||||
|
||||
} // namespace api
|
||||
} // namespace esphome
|
||||
|
@ -52,6 +52,9 @@ class ListEntitiesIterator : public ComponentIterator {
|
||||
#ifdef USE_DATETIME_TIME
|
||||
bool on_time(datetime::TimeEntity *time) override;
|
||||
#endif
|
||||
#ifdef USE_DATETIME_DATETIME
|
||||
bool on_datetime(datetime::DateTimeEntity *datetime) override;
|
||||
#endif
|
||||
#ifdef USE_TEXT
|
||||
bool on_text(text::Text *text) override;
|
||||
#endif
|
||||
@ -69,6 +72,9 @@ class ListEntitiesIterator : public ComponentIterator {
|
||||
#endif
|
||||
#ifdef USE_ALARM_CONTROL_PANEL
|
||||
bool on_alarm_control_panel(alarm_control_panel::AlarmControlPanel *a_alarm_control_panel) override;
|
||||
#endif
|
||||
#ifdef USE_EVENT
|
||||
bool on_event(event::Event *event) override;
|
||||
#endif
|
||||
bool on_end() override;
|
||||
|
||||
|
@ -48,6 +48,11 @@ bool InitialStateIterator::on_date(datetime::DateEntity *date) { return this->cl
|
||||
#ifdef USE_DATETIME_TIME
|
||||
bool InitialStateIterator::on_time(datetime::TimeEntity *time) { return this->client_->send_time_state(time); }
|
||||
#endif
|
||||
#ifdef USE_DATETIME_DATETIME
|
||||
bool InitialStateIterator::on_datetime(datetime::DateTimeEntity *datetime) {
|
||||
return this->client_->send_datetime_state(datetime);
|
||||
}
|
||||
#endif
|
||||
#ifdef USE_TEXT
|
||||
bool InitialStateIterator::on_text(text::Text *text) { return this->client_->send_text_state(text, text->state); }
|
||||
#endif
|
||||
|
@ -49,6 +49,9 @@ class InitialStateIterator : public ComponentIterator {
|
||||
#ifdef USE_DATETIME_TIME
|
||||
bool on_time(datetime::TimeEntity *time) override;
|
||||
#endif
|
||||
#ifdef USE_DATETIME_DATETIME
|
||||
bool on_datetime(datetime::DateTimeEntity *datetime) override;
|
||||
#endif
|
||||
#ifdef USE_TEXT
|
||||
bool on_text(text::Text *text) override;
|
||||
#endif
|
||||
@ -66,6 +69,9 @@ class InitialStateIterator : public ComponentIterator {
|
||||
#endif
|
||||
#ifdef USE_ALARM_CONTROL_PANEL
|
||||
bool on_alarm_control_panel(alarm_control_panel::AlarmControlPanel *a_alarm_control_panel) override;
|
||||
#endif
|
||||
#ifdef USE_EVENT
|
||||
bool on_event(event::Event *event) override { return true; };
|
||||
#endif
|
||||
protected:
|
||||
APIConnection *client_;
|
||||
|
@ -25,9 +25,13 @@ bool BluetoothConnection::gattc_event_handler(esp_gattc_cb_event_t event, esp_ga
|
||||
this->proxy_->send_connections_free();
|
||||
break;
|
||||
}
|
||||
case ESP_GATTC_CLOSE_EVT: {
|
||||
this->proxy_->send_device_connection(this->address_, false, 0, param->close.reason);
|
||||
this->set_address(0);
|
||||
this->proxy_->send_connections_free();
|
||||
break;
|
||||
}
|
||||
case ESP_GATTC_OPEN_EVT: {
|
||||
if (param->open.conn_id != this->conn_id_)
|
||||
break;
|
||||
if (param->open.status != ESP_GATT_OK && param->open.status != ESP_GATT_ALREADY_OPEN) {
|
||||
this->proxy_->send_device_connection(this->address_, false, 0, param->open.status);
|
||||
this->set_address(0);
|
||||
@ -39,9 +43,8 @@ bool BluetoothConnection::gattc_event_handler(esp_gattc_cb_event_t event, esp_ga
|
||||
this->seen_mtu_or_services_ = false;
|
||||
break;
|
||||
}
|
||||
case ESP_GATTC_CFG_MTU_EVT: {
|
||||
if (param->cfg_mtu.conn_id != this->conn_id_)
|
||||
break;
|
||||
case ESP_GATTC_CFG_MTU_EVT:
|
||||
case ESP_GATTC_SEARCH_CMPL_EVT: {
|
||||
if (!this->seen_mtu_or_services_) {
|
||||
// We don't know if we will get the MTU or the services first, so
|
||||
// only send the device connection true if we have already received
|
||||
@ -53,24 +56,8 @@ bool BluetoothConnection::gattc_event_handler(esp_gattc_cb_event_t event, esp_ga
|
||||
this->proxy_->send_connections_free();
|
||||
break;
|
||||
}
|
||||
case ESP_GATTC_SEARCH_CMPL_EVT: {
|
||||
if (param->search_cmpl.conn_id != this->conn_id_)
|
||||
break;
|
||||
if (!this->seen_mtu_or_services_) {
|
||||
// We don't know if we will get the MTU or the services first, so
|
||||
// only send the device connection true if we have already received
|
||||
// the mtu.
|
||||
this->seen_mtu_or_services_ = true;
|
||||
break;
|
||||
}
|
||||
this->proxy_->send_device_connection(this->address_, true, this->mtu_);
|
||||
this->proxy_->send_connections_free();
|
||||
break;
|
||||
}
|
||||
case ESP_GATTC_READ_DESCR_EVT:
|
||||
case ESP_GATTC_READ_CHAR_EVT: {
|
||||
if (param->read.conn_id != this->conn_id_)
|
||||
break;
|
||||
if (param->read.status != ESP_GATT_OK) {
|
||||
ESP_LOGW(TAG, "[%d] [%s] Error reading char/descriptor at handle 0x%2X, status=%d", this->connection_index_,
|
||||
this->address_str_.c_str(), param->read.handle, param->read.status);
|
||||
@ -89,8 +76,6 @@ bool BluetoothConnection::gattc_event_handler(esp_gattc_cb_event_t event, esp_ga
|
||||
}
|
||||
case ESP_GATTC_WRITE_CHAR_EVT:
|
||||
case ESP_GATTC_WRITE_DESCR_EVT: {
|
||||
if (param->write.conn_id != this->conn_id_)
|
||||
break;
|
||||
if (param->write.status != ESP_GATT_OK) {
|
||||
ESP_LOGW(TAG, "[%d] [%s] Error writing char/descriptor at handle 0x%2X, status=%d", this->connection_index_,
|
||||
this->address_str_.c_str(), param->write.handle, param->write.status);
|
||||
@ -131,8 +116,6 @@ bool BluetoothConnection::gattc_event_handler(esp_gattc_cb_event_t event, esp_ga
|
||||
break;
|
||||
}
|
||||
case ESP_GATTC_NOTIFY_EVT: {
|
||||
if (param->notify.conn_id != this->conn_id_)
|
||||
break;
|
||||
ESP_LOGV(TAG, "[%d] [%s] ESP_GATTC_NOTIFY_EVT: handle=0x%2X", this->connection_index_, this->address_str_.c_str(),
|
||||
param->notify.handle);
|
||||
api::BluetoothGATTNotifyDataResponse resp;
|
||||
|
@ -5,17 +5,13 @@ namespace cst226 {
|
||||
|
||||
void CST226Touchscreen::setup() {
|
||||
esph_log_config(TAG, "Setting up CST226 Touchscreen...");
|
||||
if (this->reset_pin_ != nullptr) {
|
||||
this->reset_pin_->setup();
|
||||
this->reset_pin_->digital_write(true);
|
||||
delay(5);
|
||||
this->reset_pin_->digital_write(false);
|
||||
delay(5);
|
||||
this->reset_pin_->digital_write(true);
|
||||
this->set_timeout(30, [this] { this->continue_setup_(); });
|
||||
} else {
|
||||
this->continue_setup_();
|
||||
}
|
||||
this->reset_pin_->setup();
|
||||
this->reset_pin_->digital_write(true);
|
||||
delay(5);
|
||||
this->reset_pin_->digital_write(false);
|
||||
delay(5);
|
||||
this->reset_pin_->digital_write(true);
|
||||
this->set_timeout(30, [this] { this->continue_setup_(); });
|
||||
}
|
||||
|
||||
void CST226Touchscreen::update_touches() {
|
||||
|
@ -35,7 +35,7 @@ class CST226Touchscreen : public touchscreen::Touchscreen, public i2c::I2CDevice
|
||||
void continue_setup_();
|
||||
|
||||
InternalGPIOPin *interrupt_pin_{};
|
||||
GPIOPin *reset_pin_{};
|
||||
GPIOPin *reset_pin_{NULL_PIN};
|
||||
uint8_t chip_id_{};
|
||||
bool setup_complete_{};
|
||||
};
|
||||
|
@ -4,6 +4,7 @@ from esphome.components import uart
|
||||
from esphome.const import CONF_ID, CONF_ADDRESS
|
||||
|
||||
CODEOWNERS = ["@s1lvi0"]
|
||||
MULTI_CONF = True
|
||||
DEPENDENCIES = ["uart"]
|
||||
|
||||
CONF_BMS_DALY_ID = "bms_daly_id"
|
||||
|
@ -1,6 +1,5 @@
|
||||
import esphome.codegen as cg
|
||||
|
||||
# import cpp_generator as cpp
|
||||
import esphome.config_validation as cv
|
||||
from esphome import automation
|
||||
from esphome.components import mqtt, time
|
||||
@ -13,6 +12,7 @@ from esphome.const import (
|
||||
CONF_TYPE,
|
||||
CONF_MQTT_ID,
|
||||
CONF_DATE,
|
||||
CONF_DATETIME,
|
||||
CONF_TIME,
|
||||
CONF_YEAR,
|
||||
CONF_MONTH,
|
||||
@ -27,6 +27,7 @@ from esphome.cpp_helpers import setup_entity
|
||||
|
||||
|
||||
CODEOWNERS = ["@rfdarter", "@jesserockz"]
|
||||
DEPENDENCIES = ["time"]
|
||||
|
||||
IS_PLATFORM_COMPONENT = True
|
||||
|
||||
@ -34,10 +35,12 @@ datetime_ns = cg.esphome_ns.namespace("datetime")
|
||||
DateTimeBase = datetime_ns.class_("DateTimeBase", cg.EntityBase)
|
||||
DateEntity = datetime_ns.class_("DateEntity", DateTimeBase)
|
||||
TimeEntity = datetime_ns.class_("TimeEntity", DateTimeBase)
|
||||
DateTimeEntity = datetime_ns.class_("DateTimeEntity", DateTimeBase)
|
||||
|
||||
# Actions
|
||||
DateSetAction = datetime_ns.class_("DateSetAction", automation.Action)
|
||||
TimeSetAction = datetime_ns.class_("TimeSetAction", automation.Action)
|
||||
DateTimeSetAction = datetime_ns.class_("DateTimeSetAction", automation.Action)
|
||||
|
||||
DateTimeStateTrigger = datetime_ns.class_(
|
||||
"DateTimeStateTrigger", automation.Trigger.template(cg.ESPTime)
|
||||
@ -46,6 +49,12 @@ DateTimeStateTrigger = datetime_ns.class_(
|
||||
OnTimeTrigger = datetime_ns.class_(
|
||||
"OnTimeTrigger", automation.Trigger, cg.Component, cg.Parented.template(TimeEntity)
|
||||
)
|
||||
OnDateTimeTrigger = datetime_ns.class_(
|
||||
"OnDateTimeTrigger",
|
||||
automation.Trigger,
|
||||
cg.Component,
|
||||
cg.Parented.template(DateTimeEntity),
|
||||
)
|
||||
|
||||
DATETIME_MODES = [
|
||||
"DATE",
|
||||
@ -61,45 +70,55 @@ _DATETIME_SCHEMA = cv.Schema(
|
||||
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(DateTimeStateTrigger),
|
||||
}
|
||||
),
|
||||
cv.GenerateID(CONF_TIME_ID): cv.use_id(time.RealTimeClock),
|
||||
}
|
||||
).extend(cv.ENTITY_BASE_SCHEMA.extend(cv.MQTT_COMMAND_COMPONENT_SCHEMA))
|
||||
|
||||
|
||||
def date_schema(class_: MockObjClass) -> cv.Schema:
|
||||
schema = {
|
||||
cv.GenerateID(): cv.declare_id(class_),
|
||||
cv.OnlyWith(CONF_MQTT_ID, "mqtt"): cv.declare_id(mqtt.MQTTDateComponent),
|
||||
cv.Optional(CONF_TYPE, default="DATE"): cv.one_of("DATE", upper=True),
|
||||
}
|
||||
schema = cv.Schema(
|
||||
{
|
||||
cv.GenerateID(): cv.declare_id(class_),
|
||||
cv.OnlyWith(CONF_MQTT_ID, "mqtt"): cv.declare_id(mqtt.MQTTDateComponent),
|
||||
cv.Optional(CONF_TYPE, default="DATE"): cv.one_of("DATE", upper=True),
|
||||
}
|
||||
)
|
||||
return _DATETIME_SCHEMA.extend(schema)
|
||||
|
||||
|
||||
def time_schema(class_: MockObjClass) -> cv.Schema:
|
||||
schema = {
|
||||
cv.GenerateID(): cv.declare_id(class_),
|
||||
cv.OnlyWith(CONF_MQTT_ID, "mqtt"): cv.declare_id(mqtt.MQTTTimeComponent),
|
||||
cv.Optional(CONF_TYPE, default="TIME"): cv.one_of("TIME", upper=True),
|
||||
cv.Inclusive(
|
||||
CONF_ON_TIME,
|
||||
group_of_inclusion=CONF_ON_TIME,
|
||||
msg="`on_time` and `time_id` must both be specified",
|
||||
): automation.validate_automation(
|
||||
{
|
||||
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(OnTimeTrigger),
|
||||
}
|
||||
),
|
||||
cv.Inclusive(CONF_TIME_ID, group_of_inclusion=CONF_ON_TIME): cv.use_id(
|
||||
time.RealTimeClock
|
||||
),
|
||||
}
|
||||
schema = cv.Schema(
|
||||
{
|
||||
cv.GenerateID(): cv.declare_id(class_),
|
||||
cv.OnlyWith(CONF_MQTT_ID, "mqtt"): cv.declare_id(mqtt.MQTTTimeComponent),
|
||||
cv.Optional(CONF_TYPE, default="TIME"): cv.one_of("TIME", upper=True),
|
||||
cv.Optional(CONF_ON_TIME): automation.validate_automation(
|
||||
{
|
||||
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(OnTimeTrigger),
|
||||
}
|
||||
),
|
||||
}
|
||||
)
|
||||
return _DATETIME_SCHEMA.extend(schema)
|
||||
|
||||
|
||||
def datetime_schema(class_: MockObjClass) -> cv.Schema:
|
||||
schema = {
|
||||
cv.GenerateID(): cv.declare_id(class_),
|
||||
cv.Optional(CONF_TYPE, default="DATETIME"): cv.one_of("DATETIME", upper=True),
|
||||
}
|
||||
schema = cv.Schema(
|
||||
{
|
||||
cv.GenerateID(): cv.declare_id(class_),
|
||||
cv.OnlyWith(CONF_MQTT_ID, "mqtt"): cv.declare_id(
|
||||
mqtt.MQTTDateTimeComponent
|
||||
),
|
||||
cv.Optional(CONF_TYPE, default="DATETIME"): cv.one_of(
|
||||
"DATETIME", upper=True
|
||||
),
|
||||
cv.Optional(CONF_ON_TIME): automation.validate_automation(
|
||||
{
|
||||
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(OnDateTimeTrigger),
|
||||
}
|
||||
),
|
||||
}
|
||||
)
|
||||
return _DATETIME_SCHEMA.extend(schema)
|
||||
|
||||
|
||||
@ -113,13 +132,11 @@ async def setup_datetime_core_(var, config):
|
||||
trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var)
|
||||
await automation.build_automation(trigger, [(cg.ESPTime, "x")], conf)
|
||||
|
||||
rtc_id = config.get(CONF_TIME_ID)
|
||||
rtc = None
|
||||
if rtc_id is not None:
|
||||
rtc = await cg.get_variable(rtc_id)
|
||||
rtc = await cg.get_variable(config[CONF_TIME_ID])
|
||||
cg.add(var.set_rtc(rtc))
|
||||
|
||||
for conf in config.get(CONF_ON_TIME, []):
|
||||
assert rtc is not None
|
||||
trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], rtc)
|
||||
trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID])
|
||||
await automation.build_automation(trigger, [], conf)
|
||||
await cg.register_component(trigger, conf)
|
||||
await cg.register_parented(trigger, var)
|
||||
@ -161,16 +178,16 @@ async def datetime_date_set_to_code(config, action_id, template_arg, args):
|
||||
action_var = cg.new_Pvariable(action_id, template_arg)
|
||||
await cg.register_parented(action_var, config[CONF_ID])
|
||||
|
||||
date = config[CONF_DATE]
|
||||
if cg.is_template(date):
|
||||
template_ = await cg.templatable(config[CONF_DATE], [], cg.ESPTime)
|
||||
date_config = config[CONF_DATE]
|
||||
if cg.is_template(date_config):
|
||||
template_ = await cg.templatable(date_config, [], cg.ESPTime)
|
||||
cg.add(action_var.set_date(template_))
|
||||
else:
|
||||
date_struct = cg.StructInitializer(
|
||||
cg.ESPTime,
|
||||
("day_of_month", date[CONF_DAY]),
|
||||
("month", date[CONF_MONTH]),
|
||||
("year", date[CONF_YEAR]),
|
||||
("day_of_month", date_config[CONF_DAY]),
|
||||
("month", date_config[CONF_MONTH]),
|
||||
("year", date_config[CONF_YEAR]),
|
||||
)
|
||||
cg.add(action_var.set_date(date_struct))
|
||||
return action_var
|
||||
@ -194,7 +211,7 @@ async def datetime_time_set_to_code(config, action_id, template_arg, args):
|
||||
|
||||
time_config = config[CONF_TIME]
|
||||
if cg.is_template(time_config):
|
||||
template_ = await cg.templatable(config[CONF_TIME], [], cg.ESPTime)
|
||||
template_ = await cg.templatable(time_config, [], cg.ESPTime)
|
||||
cg.add(action_var.set_time(template_))
|
||||
else:
|
||||
time_struct = cg.StructInitializer(
|
||||
@ -205,3 +222,35 @@ async def datetime_time_set_to_code(config, action_id, template_arg, args):
|
||||
)
|
||||
cg.add(action_var.set_time(time_struct))
|
||||
return action_var
|
||||
|
||||
|
||||
@automation.register_action(
|
||||
"datetime.datetime.set",
|
||||
DateTimeSetAction,
|
||||
cv.Schema(
|
||||
{
|
||||
cv.Required(CONF_ID): cv.use_id(DateTimeEntity),
|
||||
cv.Required(CONF_DATETIME): cv.Any(cv.returning_lambda, cv.date_time()),
|
||||
},
|
||||
),
|
||||
)
|
||||
async def datetime_datetime_set_to_code(config, action_id, template_arg, args):
|
||||
action_var = cg.new_Pvariable(action_id, template_arg)
|
||||
await cg.register_parented(action_var, config[CONF_ID])
|
||||
|
||||
datetime_config = config[CONF_DATETIME]
|
||||
if cg.is_template(datetime_config):
|
||||
template_ = await cg.templatable(datetime_config, [], cg.ESPTime)
|
||||
cg.add(action_var.set_datetime(template_))
|
||||
else:
|
||||
datetime_struct = cg.StructInitializer(
|
||||
cg.ESPTime,
|
||||
("second", datetime_config[CONF_SECOND]),
|
||||
("minute", datetime_config[CONF_MINUTE]),
|
||||
("hour", datetime_config[CONF_HOUR]),
|
||||
("day_of_month", datetime_config[CONF_DAY]),
|
||||
("month", datetime_config[CONF_MONTH]),
|
||||
("year", datetime_config[CONF_YEAR]),
|
||||
)
|
||||
cg.add(action_var.set_datetime(datetime_struct))
|
||||
return action_var
|
||||
|
@ -40,10 +40,13 @@ void DateCall::validate_() {
|
||||
if (this->year_.has_value() && (this->year_ < 1970 || this->year_ > 3000)) {
|
||||
ESP_LOGE(TAG, "Year must be between 1970 and 3000");
|
||||
this->year_.reset();
|
||||
this->month_.reset();
|
||||
this->day_.reset();
|
||||
}
|
||||
if (this->month_.has_value() && (this->month_ < 1 || this->month_ > 12)) {
|
||||
ESP_LOGE(TAG, "Month must be between 1 and 12");
|
||||
this->month_.reset();
|
||||
this->day_.reset();
|
||||
}
|
||||
if (this->day_.has_value()) {
|
||||
uint16_t year = 0;
|
||||
|
@ -5,6 +5,8 @@
|
||||
#include "esphome/core/entity_base.h"
|
||||
#include "esphome/core/time.h"
|
||||
|
||||
#include "esphome/components/time/real_time_clock.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace datetime {
|
||||
|
||||
@ -17,9 +19,14 @@ class DateTimeBase : public EntityBase {
|
||||
|
||||
void add_on_state_callback(std::function<void()> &&callback) { this->state_callback_.add(std::move(callback)); }
|
||||
|
||||
void set_rtc(time::RealTimeClock *rtc) { this->rtc_ = rtc; }
|
||||
time::RealTimeClock *get_rtc() const { return this->rtc_; }
|
||||
|
||||
protected:
|
||||
CallbackManager<void()> state_callback_;
|
||||
|
||||
time::RealTimeClock *rtc_;
|
||||
|
||||
bool has_state_{false};
|
||||
};
|
||||
|
||||
|
252
esphome/components/datetime/datetime_entity.cpp
Normal file
252
esphome/components/datetime/datetime_entity.cpp
Normal file
@ -0,0 +1,252 @@
|
||||
#include "datetime_entity.h"
|
||||
|
||||
#ifdef USE_DATETIME_DATETIME
|
||||
|
||||
#include "esphome/core/log.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace datetime {
|
||||
|
||||
static const char *const TAG = "datetime.datetime_entity";
|
||||
|
||||
void DateTimeEntity::publish_state() {
|
||||
if (this->year_ == 0 || this->month_ == 0 || this->day_ == 0) {
|
||||
this->has_state_ = false;
|
||||
return;
|
||||
}
|
||||
if (this->year_ < 1970 || this->year_ > 3000) {
|
||||
this->has_state_ = false;
|
||||
ESP_LOGE(TAG, "Year must be between 1970 and 3000");
|
||||
return;
|
||||
}
|
||||
if (this->month_ < 1 || this->month_ > 12) {
|
||||
this->has_state_ = false;
|
||||
ESP_LOGE(TAG, "Month must be between 1 and 12");
|
||||
return;
|
||||
}
|
||||
if (this->day_ > days_in_month(this->month_, this->year_)) {
|
||||
this->has_state_ = false;
|
||||
ESP_LOGE(TAG, "Day must be between 1 and %d for month %d", days_in_month(this->month_, this->year_), this->month_);
|
||||
return;
|
||||
}
|
||||
if (this->hour_ > 23) {
|
||||
this->has_state_ = false;
|
||||
ESP_LOGE(TAG, "Hour must be between 0 and 23");
|
||||
return;
|
||||
}
|
||||
if (this->minute_ > 59) {
|
||||
this->has_state_ = false;
|
||||
ESP_LOGE(TAG, "Minute must be between 0 and 59");
|
||||
return;
|
||||
}
|
||||
if (this->second_ > 59) {
|
||||
this->has_state_ = false;
|
||||
ESP_LOGE(TAG, "Second must be between 0 and 59");
|
||||
return;
|
||||
}
|
||||
this->has_state_ = true;
|
||||
ESP_LOGD(TAG, "'%s': Sending datetime %04u-%02u-%02u %02d:%02d:%02d", this->get_name().c_str(), this->year_,
|
||||
this->month_, this->day_, this->hour_, this->minute_, this->second_);
|
||||
this->state_callback_.call();
|
||||
}
|
||||
|
||||
DateTimeCall DateTimeEntity::make_call() { return DateTimeCall(this); }
|
||||
|
||||
ESPTime DateTimeEntity::state_as_esptime() const {
|
||||
ESPTime obj;
|
||||
obj.year = this->year_;
|
||||
obj.month = this->month_;
|
||||
obj.day_of_month = this->day_;
|
||||
obj.hour = this->hour_;
|
||||
obj.minute = this->minute_;
|
||||
obj.second = this->second_;
|
||||
obj.day_of_week = 1; // Required to be valid for recalc_timestamp_local but not used.
|
||||
obj.day_of_year = 1; // Required to be valid for recalc_timestamp_local but not used.
|
||||
obj.recalc_timestamp_local(false);
|
||||
return obj;
|
||||
}
|
||||
|
||||
void DateTimeCall::validate_() {
|
||||
if (this->year_.has_value() && (this->year_ < 1970 || this->year_ > 3000)) {
|
||||
ESP_LOGE(TAG, "Year must be between 1970 and 3000");
|
||||
this->year_.reset();
|
||||
this->month_.reset();
|
||||
this->day_.reset();
|
||||
}
|
||||
if (this->month_.has_value() && (this->month_ < 1 || this->month_ > 12)) {
|
||||
ESP_LOGE(TAG, "Month must be between 1 and 12");
|
||||
this->month_.reset();
|
||||
this->day_.reset();
|
||||
}
|
||||
if (this->day_.has_value()) {
|
||||
uint16_t year = 0;
|
||||
uint8_t month = 0;
|
||||
if (this->month_.has_value()) {
|
||||
month = *this->month_;
|
||||
} else {
|
||||
if (this->parent_->month != 0) {
|
||||
month = this->parent_->month;
|
||||
} else {
|
||||
ESP_LOGE(TAG, "Month must be set to validate day");
|
||||
this->day_.reset();
|
||||
}
|
||||
}
|
||||
if (this->year_.has_value()) {
|
||||
year = *this->year_;
|
||||
} else {
|
||||
if (this->parent_->year != 0) {
|
||||
year = this->parent_->year;
|
||||
} else {
|
||||
ESP_LOGE(TAG, "Year must be set to validate day");
|
||||
this->day_.reset();
|
||||
}
|
||||
}
|
||||
if (this->day_.has_value() && *this->day_ > days_in_month(month, year)) {
|
||||
ESP_LOGE(TAG, "Day must be between 1 and %d for month %d", days_in_month(month, year), month);
|
||||
this->day_.reset();
|
||||
}
|
||||
}
|
||||
|
||||
if (this->hour_.has_value() && this->hour_ > 23) {
|
||||
ESP_LOGE(TAG, "Hour must be between 0 and 23");
|
||||
this->hour_.reset();
|
||||
}
|
||||
if (this->minute_.has_value() && this->minute_ > 59) {
|
||||
ESP_LOGE(TAG, "Minute must be between 0 and 59");
|
||||
this->minute_.reset();
|
||||
}
|
||||
if (this->second_.has_value() && this->second_ > 59) {
|
||||
ESP_LOGE(TAG, "Second must be between 0 and 59");
|
||||
this->second_.reset();
|
||||
}
|
||||
}
|
||||
|
||||
void DateTimeCall::perform() {
|
||||
this->validate_();
|
||||
ESP_LOGD(TAG, "'%s' - Setting", this->parent_->get_name().c_str());
|
||||
|
||||
if (this->year_.has_value()) {
|
||||
ESP_LOGD(TAG, " Year: %d", *this->year_);
|
||||
}
|
||||
if (this->month_.has_value()) {
|
||||
ESP_LOGD(TAG, " Month: %d", *this->month_);
|
||||
}
|
||||
if (this->day_.has_value()) {
|
||||
ESP_LOGD(TAG, " Day: %d", *this->day_);
|
||||
}
|
||||
if (this->hour_.has_value()) {
|
||||
ESP_LOGD(TAG, " Hour: %d", *this->hour_);
|
||||
}
|
||||
if (this->minute_.has_value()) {
|
||||
ESP_LOGD(TAG, " Minute: %d", *this->minute_);
|
||||
}
|
||||
if (this->second_.has_value()) {
|
||||
ESP_LOGD(TAG, " Second: %d", *this->second_);
|
||||
}
|
||||
this->parent_->control(*this);
|
||||
}
|
||||
|
||||
DateTimeCall &DateTimeCall::set_datetime(uint16_t year, uint8_t month, uint8_t day, uint8_t hour, uint8_t minute,
|
||||
uint8_t second) {
|
||||
this->year_ = year;
|
||||
this->month_ = month;
|
||||
this->day_ = day;
|
||||
this->hour_ = hour;
|
||||
this->minute_ = minute;
|
||||
this->second_ = second;
|
||||
return *this;
|
||||
};
|
||||
|
||||
DateTimeCall &DateTimeCall::set_datetime(ESPTime datetime) {
|
||||
return this->set_datetime(datetime.year, datetime.month, datetime.day_of_month, datetime.hour, datetime.minute,
|
||||
datetime.second);
|
||||
};
|
||||
|
||||
DateTimeCall &DateTimeCall::set_datetime(const std::string &datetime) {
|
||||
ESPTime val{};
|
||||
if (!ESPTime::strptime(datetime, val)) {
|
||||
ESP_LOGE(TAG, "Could not convert the time string to an ESPTime object");
|
||||
return *this;
|
||||
}
|
||||
return this->set_datetime(val);
|
||||
}
|
||||
|
||||
DateTimeCall &DateTimeCall::set_datetime(time_t epoch_seconds) {
|
||||
ESPTime val = ESPTime::from_epoch_local(epoch_seconds);
|
||||
return this->set_datetime(val);
|
||||
}
|
||||
|
||||
DateTimeCall DateTimeEntityRestoreState::to_call(DateTimeEntity *datetime) {
|
||||
DateTimeCall call = datetime->make_call();
|
||||
call.set_datetime(this->year, this->month, this->day, this->hour, this->minute, this->second);
|
||||
return call;
|
||||
}
|
||||
|
||||
void DateTimeEntityRestoreState::apply(DateTimeEntity *time) {
|
||||
time->year_ = this->year;
|
||||
time->month_ = this->month;
|
||||
time->day_ = this->day;
|
||||
time->hour_ = this->hour;
|
||||
time->minute_ = this->minute;
|
||||
time->second_ = this->second;
|
||||
time->publish_state();
|
||||
}
|
||||
|
||||
static const int MAX_TIMESTAMP_DRIFT = 900; // how far can the clock drift before we consider
|
||||
// there has been a drastic time synchronization
|
||||
|
||||
void OnDateTimeTrigger::loop() {
|
||||
if (!this->parent_->has_state()) {
|
||||
return;
|
||||
}
|
||||
ESPTime time = this->parent_->rtc_->now();
|
||||
if (!time.is_valid()) {
|
||||
return;
|
||||
}
|
||||
if (this->last_check_.has_value()) {
|
||||
if (*this->last_check_ > time && this->last_check_->timestamp - time.timestamp > MAX_TIMESTAMP_DRIFT) {
|
||||
// We went back in time (a lot), probably caused by time synchronization
|
||||
ESP_LOGW(TAG, "Time has jumped back!");
|
||||
} else if (*this->last_check_ >= time) {
|
||||
// already handled this one
|
||||
return;
|
||||
} else if (time > *this->last_check_ && time.timestamp - this->last_check_->timestamp > MAX_TIMESTAMP_DRIFT) {
|
||||
// We went ahead in time (a lot), probably caused by time synchronization
|
||||
ESP_LOGW(TAG, "Time has jumped ahead!");
|
||||
this->last_check_ = time;
|
||||
return;
|
||||
}
|
||||
|
||||
while (true) {
|
||||
this->last_check_->increment_second();
|
||||
if (*this->last_check_ >= time)
|
||||
break;
|
||||
|
||||
if (this->matches_(*this->last_check_)) {
|
||||
this->trigger();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this->last_check_ = time;
|
||||
if (!time.fields_in_range()) {
|
||||
ESP_LOGW(TAG, "Time is out of range!");
|
||||
ESP_LOGD(TAG, "Second=%02u Minute=%02u Hour=%02u Day=%02u Month=%02u Year=%04u", time.second, time.minute,
|
||||
time.hour, time.day_of_month, time.month, time.year);
|
||||
}
|
||||
|
||||
if (this->matches_(time))
|
||||
this->trigger();
|
||||
}
|
||||
|
||||
bool OnDateTimeTrigger::matches_(const ESPTime &time) const {
|
||||
return time.is_valid() && time.year == this->parent_->year && time.month == this->parent_->month &&
|
||||
time.day_of_month == this->parent_->day && time.hour == this->parent_->hour &&
|
||||
time.minute == this->parent_->minute && time.second == this->parent_->second;
|
||||
}
|
||||
|
||||
} // namespace datetime
|
||||
} // namespace esphome
|
||||
|
||||
#endif // USE_DATETIME_TIME
|
150
esphome/components/datetime/datetime_entity.h
Normal file
150
esphome/components/datetime/datetime_entity.h
Normal file
@ -0,0 +1,150 @@
|
||||
#pragma once
|
||||
|
||||
#include "esphome/core/defines.h"
|
||||
|
||||
#ifdef USE_DATETIME_DATETIME
|
||||
|
||||
#include "esphome/core/automation.h"
|
||||
#include "esphome/core/helpers.h"
|
||||
#include "esphome/core/time.h"
|
||||
|
||||
#include "datetime_base.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace datetime {
|
||||
|
||||
#define LOG_DATETIME_DATETIME(prefix, type, obj) \
|
||||
if ((obj) != nullptr) { \
|
||||
ESP_LOGCONFIG(TAG, "%s%s '%s'", prefix, LOG_STR_LITERAL(type), (obj)->get_name().c_str()); \
|
||||
if (!(obj)->get_icon().empty()) { \
|
||||
ESP_LOGCONFIG(TAG, "%s Icon: '%s'", prefix, (obj)->get_icon().c_str()); \
|
||||
} \
|
||||
}
|
||||
|
||||
class DateTimeCall;
|
||||
class DateTimeEntity;
|
||||
|
||||
struct DateTimeEntityRestoreState {
|
||||
uint16_t year;
|
||||
uint8_t month;
|
||||
uint8_t day;
|
||||
uint8_t hour;
|
||||
uint8_t minute;
|
||||
uint8_t second;
|
||||
|
||||
DateTimeCall to_call(DateTimeEntity *datetime);
|
||||
void apply(DateTimeEntity *datetime);
|
||||
} __attribute__((packed));
|
||||
|
||||
class DateTimeEntity : public DateTimeBase {
|
||||
protected:
|
||||
uint16_t year_;
|
||||
uint8_t month_;
|
||||
uint8_t day_;
|
||||
uint8_t hour_;
|
||||
uint8_t minute_;
|
||||
uint8_t second_;
|
||||
|
||||
public:
|
||||
void publish_state();
|
||||
DateTimeCall make_call();
|
||||
|
||||
ESPTime state_as_esptime() const override;
|
||||
|
||||
const uint16_t &year = year_;
|
||||
const uint8_t &month = month_;
|
||||
const uint8_t &day = day_;
|
||||
const uint8_t &hour = hour_;
|
||||
const uint8_t &minute = minute_;
|
||||
const uint8_t &second = second_;
|
||||
|
||||
protected:
|
||||
friend class DateTimeCall;
|
||||
friend struct DateTimeEntityRestoreState;
|
||||
friend class OnDateTimeTrigger;
|
||||
|
||||
virtual void control(const DateTimeCall &call) = 0;
|
||||
};
|
||||
|
||||
class DateTimeCall {
|
||||
public:
|
||||
explicit DateTimeCall(DateTimeEntity *parent) : parent_(parent) {}
|
||||
void perform();
|
||||
DateTimeCall &set_datetime(uint16_t year, uint8_t month, uint8_t day, uint8_t hour, uint8_t minute, uint8_t second);
|
||||
DateTimeCall &set_datetime(ESPTime datetime);
|
||||
DateTimeCall &set_datetime(const std::string &datetime);
|
||||
DateTimeCall &set_datetime(time_t epoch_seconds);
|
||||
|
||||
DateTimeCall &set_year(uint16_t year) {
|
||||
this->year_ = year;
|
||||
return *this;
|
||||
}
|
||||
DateTimeCall &set_month(uint8_t month) {
|
||||
this->month_ = month;
|
||||
return *this;
|
||||
}
|
||||
DateTimeCall &set_day(uint8_t day) {
|
||||
this->day_ = day;
|
||||
return *this;
|
||||
}
|
||||
DateTimeCall &set_hour(uint8_t hour) {
|
||||
this->hour_ = hour;
|
||||
return *this;
|
||||
}
|
||||
DateTimeCall &set_minute(uint8_t minute) {
|
||||
this->minute_ = minute;
|
||||
return *this;
|
||||
}
|
||||
DateTimeCall &set_second(uint8_t second) {
|
||||
this->second_ = second;
|
||||
return *this;
|
||||
}
|
||||
|
||||
optional<uint16_t> get_year() const { return this->year_; }
|
||||
optional<uint8_t> get_month() const { return this->month_; }
|
||||
optional<uint8_t> get_day() const { return this->day_; }
|
||||
optional<uint8_t> get_hour() const { return this->hour_; }
|
||||
optional<uint8_t> get_minute() const { return this->minute_; }
|
||||
optional<uint8_t> get_second() const { return this->second_; }
|
||||
|
||||
protected:
|
||||
void validate_();
|
||||
|
||||
DateTimeEntity *parent_;
|
||||
|
||||
optional<uint16_t> year_;
|
||||
optional<uint8_t> month_;
|
||||
optional<uint8_t> day_;
|
||||
optional<uint8_t> hour_;
|
||||
optional<uint8_t> minute_;
|
||||
optional<uint8_t> second_;
|
||||
};
|
||||
|
||||
template<typename... Ts> class DateTimeSetAction : public Action<Ts...>, public Parented<DateTimeEntity> {
|
||||
public:
|
||||
TEMPLATABLE_VALUE(ESPTime, datetime)
|
||||
|
||||
void play(Ts... x) override {
|
||||
auto call = this->parent_->make_call();
|
||||
|
||||
if (this->datetime_.has_value()) {
|
||||
call.set_datetime(this->datetime_.value(x...));
|
||||
}
|
||||
call.perform();
|
||||
}
|
||||
};
|
||||
|
||||
class OnDateTimeTrigger : public Trigger<>, public Component, public Parented<DateTimeEntity> {
|
||||
public:
|
||||
void loop() override;
|
||||
|
||||
protected:
|
||||
bool matches_(const ESPTime &time) const;
|
||||
|
||||
optional<ESPTime> last_check_;
|
||||
};
|
||||
|
||||
} // namespace datetime
|
||||
} // namespace esphome
|
||||
|
||||
#endif // USE_DATETIME_DATETIME
|
@ -94,8 +94,6 @@ void TimeEntityRestoreState::apply(TimeEntity *time) {
|
||||
time->publish_state();
|
||||
}
|
||||
|
||||
#ifdef USE_TIME
|
||||
|
||||
static const int MAX_TIMESTAMP_DRIFT = 900; // how far can the clock drift before we consider
|
||||
// there has been a drastic time synchronization
|
||||
|
||||
@ -103,7 +101,7 @@ void OnTimeTrigger::loop() {
|
||||
if (!this->parent_->has_state()) {
|
||||
return;
|
||||
}
|
||||
ESPTime time = this->rtc_->now();
|
||||
ESPTime time = this->parent_->rtc_->now();
|
||||
if (!time.is_valid()) {
|
||||
return;
|
||||
}
|
||||
@ -148,8 +146,6 @@ bool OnTimeTrigger::matches_(const ESPTime &time) const {
|
||||
time.second == this->parent_->second;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
} // namespace datetime
|
||||
} // namespace esphome
|
||||
|
||||
|
@ -10,10 +10,6 @@
|
||||
|
||||
#include "datetime_base.h"
|
||||
|
||||
#ifdef USE_TIME
|
||||
#include "esphome/components/time/real_time_clock.h"
|
||||
#endif
|
||||
|
||||
namespace esphome {
|
||||
namespace datetime {
|
||||
|
||||
@ -27,6 +23,7 @@ namespace datetime {
|
||||
|
||||
class TimeCall;
|
||||
class TimeEntity;
|
||||
class OnTimeTrigger;
|
||||
|
||||
struct TimeEntityRestoreState {
|
||||
uint8_t hour;
|
||||
@ -62,6 +59,7 @@ class TimeEntity : public DateTimeBase {
|
||||
protected:
|
||||
friend class TimeCall;
|
||||
friend struct TimeEntityRestoreState;
|
||||
friend class OnTimeTrigger;
|
||||
|
||||
virtual void control(const TimeCall &call) = 0;
|
||||
};
|
||||
@ -115,22 +113,16 @@ template<typename... Ts> class TimeSetAction : public Action<Ts...>, public Pare
|
||||
}
|
||||
};
|
||||
|
||||
#ifdef USE_TIME
|
||||
|
||||
class OnTimeTrigger : public Trigger<>, public Component, public Parented<TimeEntity> {
|
||||
public:
|
||||
explicit OnTimeTrigger(time::RealTimeClock *rtc) : rtc_(rtc) {}
|
||||
void loop() override;
|
||||
|
||||
protected:
|
||||
bool matches_(const ESPTime &time) const;
|
||||
|
||||
time::RealTimeClock *rtc_;
|
||||
optional<ESPTime> last_check_;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
} // namespace datetime
|
||||
} // namespace esphome
|
||||
|
||||
|
@ -38,6 +38,7 @@ DisplayOnPageChangeTrigger = display_ns.class_(
|
||||
)
|
||||
|
||||
CONF_ON_PAGE_CHANGE = "on_page_change"
|
||||
CONF_SHOW_TEST_CARD = "show_test_card"
|
||||
|
||||
DISPLAY_ROTATIONS = {
|
||||
0: display_ns.DISPLAY_ROTATION_0_DEGREES,
|
||||
@ -82,6 +83,7 @@ FULL_DISPLAY_SCHEMA = BASIC_DISPLAY_SCHEMA.extend(
|
||||
}
|
||||
),
|
||||
cv.Optional(CONF_AUTO_CLEAR_ENABLED, default=True): cv.boolean,
|
||||
cv.Optional(CONF_SHOW_TEST_CARD): cv.boolean,
|
||||
}
|
||||
)
|
||||
|
||||
@ -113,6 +115,8 @@ async def setup_display_core_(var, config):
|
||||
await automation.build_automation(
|
||||
trigger, [(DisplayPagePtr, "from"), (DisplayPagePtr, "to")], conf
|
||||
)
|
||||
if config.get(CONF_SHOW_TEST_CARD):
|
||||
cg.add(var.show_test_card())
|
||||
|
||||
|
||||
async def register_display(var, config):
|
||||
|
@ -1,7 +1,7 @@
|
||||
#include "display.h"
|
||||
|
||||
#include "display_color_utils.h"
|
||||
#include <utility>
|
||||
|
||||
#include "esphome/core/hal.h"
|
||||
#include "esphome/core/log.h"
|
||||
|
||||
namespace esphome {
|
||||
@ -507,7 +507,9 @@ void Display::do_update_() {
|
||||
if (this->auto_clear_enabled_) {
|
||||
this->clear();
|
||||
}
|
||||
if (this->page_ != nullptr) {
|
||||
if (this->show_test_card_) {
|
||||
this->test_card();
|
||||
} else if (this->page_ != nullptr) {
|
||||
this->page_->get_writer()(*this);
|
||||
} else if (this->writer_.has_value()) {
|
||||
(*this->writer_)(*this);
|
||||
@ -608,6 +610,62 @@ bool Display::clamp_y_(int y, int h, int &min_y, int &max_y) {
|
||||
return min_y < max_y;
|
||||
}
|
||||
|
||||
const uint8_t TESTCARD_FONT[3][8] PROGMEM = {{0x41, 0x7F, 0x7F, 0x09, 0x19, 0x7F, 0x66, 0x00}, // 'R'
|
||||
{0x1C, 0x3E, 0x63, 0x41, 0x51, 0x73, 0x72, 0x00}, // 'G'
|
||||
{0x41, 0x7F, 0x7F, 0x49, 0x49, 0x7F, 0x36, 0x00}}; // 'B'
|
||||
|
||||
void Display::test_card() {
|
||||
int w = get_width(), h = get_height(), image_w, image_h;
|
||||
this->clear();
|
||||
this->show_test_card_ = false;
|
||||
if (this->get_display_type() == DISPLAY_TYPE_COLOR) {
|
||||
Color r(255, 0, 0), g(0, 255, 0), b(0, 0, 255);
|
||||
image_w = std::min(w - 20, 310);
|
||||
image_h = std::min(h - 20, 255);
|
||||
|
||||
int shift_x = (w - image_w) / 2;
|
||||
int shift_y = (h - image_h) / 2;
|
||||
int line_w = (image_w - 6) / 6;
|
||||
int image_c = image_w / 2;
|
||||
for (auto i = 0; i <= image_h; i++) {
|
||||
int c = esp_scale(i, image_h);
|
||||
this->horizontal_line(shift_x + 0, shift_y + i, line_w, r.fade_to_white(c));
|
||||
this->horizontal_line(shift_x + line_w, shift_y + i, line_w, r.fade_to_black(c)); //
|
||||
|
||||
this->horizontal_line(shift_x + image_c - line_w, shift_y + i, line_w, g.fade_to_white(c));
|
||||
this->horizontal_line(shift_x + image_c, shift_y + i, line_w, g.fade_to_black(c));
|
||||
|
||||
this->horizontal_line(shift_x + image_w - (line_w * 2), shift_y + i, line_w, b.fade_to_white(c));
|
||||
this->horizontal_line(shift_x + image_w - line_w, shift_y + i, line_w, b.fade_to_black(c));
|
||||
}
|
||||
this->rectangle(shift_x, shift_y, image_w, image_h, Color(127, 127, 0));
|
||||
|
||||
uint16_t shift_r = shift_x + line_w - (8 * 3);
|
||||
uint16_t shift_g = shift_x + image_c - (8 * 3);
|
||||
uint16_t shift_b = shift_x + image_w - line_w - (8 * 3);
|
||||
shift_y = h / 2 - (8 * 3);
|
||||
for (auto i = 0; i < 8; i++) {
|
||||
uint8_t ftr = progmem_read_byte(&TESTCARD_FONT[0][i]);
|
||||
uint8_t ftg = progmem_read_byte(&TESTCARD_FONT[1][i]);
|
||||
uint8_t ftb = progmem_read_byte(&TESTCARD_FONT[2][i]);
|
||||
for (auto k = 0; k < 8; k++) {
|
||||
if ((ftr & (1 << k)) != 0) {
|
||||
this->filled_rectangle(shift_r + (i * 6), shift_y + (k * 6), 6, 6, COLOR_OFF);
|
||||
}
|
||||
if ((ftg & (1 << k)) != 0) {
|
||||
this->filled_rectangle(shift_g + (i * 6), shift_y + (k * 6), 6, 6, COLOR_OFF);
|
||||
}
|
||||
if ((ftb & (1 << k)) != 0) {
|
||||
this->filled_rectangle(shift_b + (i * 6), shift_y + (k * 6), 6, 6, COLOR_OFF);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
this->rectangle(0, 0, w, h, Color(127, 0, 127));
|
||||
this->filled_rectangle(0, 0, 10, 10, Color(255, 0, 255));
|
||||
this->stop_poller();
|
||||
}
|
||||
|
||||
DisplayPage::DisplayPage(display_writer_t writer) : writer_(std::move(writer)) {}
|
||||
void DisplayPage::show() { this->parent_->show_page(this); }
|
||||
void DisplayPage::show_next() { this->next_->show(); }
|
||||
|
@ -631,6 +631,9 @@ class Display : public PollingComponent {
|
||||
*/
|
||||
bool clip(int x, int y);
|
||||
|
||||
void test_card();
|
||||
void show_test_card() { this->show_test_card_ = true; }
|
||||
|
||||
protected:
|
||||
bool clamp_x_(int x, int w, int &min_x, int &max_x);
|
||||
bool clamp_y_(int y, int h, int &min_y, int &max_y);
|
||||
@ -659,6 +662,7 @@ class Display : public PollingComponent {
|
||||
std::vector<DisplayOnPageChangeTrigger *> on_page_change_triggers_;
|
||||
bool auto_clear_enabled_{true};
|
||||
std::vector<Rect> clipping_rectangle_;
|
||||
bool show_test_card_{false};
|
||||
};
|
||||
|
||||
class DisplayPage {
|
||||
|
@ -32,6 +32,7 @@ from esphome.const import (
|
||||
TYPE_GIT,
|
||||
TYPE_LOCAL,
|
||||
__version__,
|
||||
CONF_PLATFORM_VERSION,
|
||||
)
|
||||
from esphome.core import CORE, HexInt, TimePeriod
|
||||
import esphome.config_validation as cv
|
||||
@ -365,8 +366,6 @@ def final_validate(config):
|
||||
return config
|
||||
|
||||
|
||||
CONF_PLATFORM_VERSION = "platform_version"
|
||||
|
||||
ARDUINO_FRAMEWORK_SCHEMA = cv.All(
|
||||
cv.Schema(
|
||||
{
|
||||
|
@ -142,7 +142,7 @@ bool BLEClientBase::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_
|
||||
ESP_LOGW(TAG, "[%d] [%s] Connection failed, status=%d", this->connection_index_, this->address_str_.c_str(),
|
||||
param->open.status);
|
||||
this->set_state(espbt::ClientState::IDLE);
|
||||
return false;
|
||||
break;
|
||||
}
|
||||
auto ret = esp_ble_gattc_send_mtu_req(this->gattc_if_, param->open.conn_id);
|
||||
if (ret) {
|
||||
|
@ -12,6 +12,7 @@ from esphome.const import (
|
||||
KEY_TARGET_FRAMEWORK,
|
||||
KEY_TARGET_PLATFORM,
|
||||
PLATFORM_ESP8266,
|
||||
CONF_PLATFORM_VERSION,
|
||||
)
|
||||
from esphome.core import CORE, coroutine_with_priority
|
||||
import esphome.config_validation as cv
|
||||
@ -146,7 +147,6 @@ def _parse_platform_version(value):
|
||||
return value
|
||||
|
||||
|
||||
CONF_PLATFORM_VERSION = "platform_version"
|
||||
ARDUINO_FRAMEWORK_SCHEMA = cv.All(
|
||||
cv.Schema(
|
||||
{
|
||||
|
134
esphome/components/event/__init__.py
Normal file
134
esphome/components/event/__init__.py
Normal file
@ -0,0 +1,134 @@
|
||||
import esphome.codegen as cg
|
||||
import esphome.config_validation as cv
|
||||
from esphome import automation
|
||||
from esphome.components import mqtt
|
||||
from esphome.const import (
|
||||
CONF_DEVICE_CLASS,
|
||||
CONF_ENTITY_CATEGORY,
|
||||
CONF_ICON,
|
||||
CONF_ID,
|
||||
CONF_ON_EVENT,
|
||||
CONF_TRIGGER_ID,
|
||||
CONF_MQTT_ID,
|
||||
CONF_EVENT_TYPE,
|
||||
DEVICE_CLASS_BUTTON,
|
||||
DEVICE_CLASS_DOORBELL,
|
||||
DEVICE_CLASS_EMPTY,
|
||||
DEVICE_CLASS_MOTION,
|
||||
)
|
||||
from esphome.core import CORE, coroutine_with_priority
|
||||
from esphome.cpp_helpers import setup_entity
|
||||
from esphome.cpp_generator import MockObjClass
|
||||
|
||||
CODEOWNERS = ["@nohat"]
|
||||
IS_PLATFORM_COMPONENT = True
|
||||
|
||||
DEVICE_CLASSES = [
|
||||
DEVICE_CLASS_BUTTON,
|
||||
DEVICE_CLASS_DOORBELL,
|
||||
DEVICE_CLASS_EMPTY,
|
||||
DEVICE_CLASS_MOTION,
|
||||
]
|
||||
|
||||
event_ns = cg.esphome_ns.namespace("event")
|
||||
Event = event_ns.class_("Event", cg.EntityBase)
|
||||
EventPtr = Event.operator("ptr")
|
||||
|
||||
TriggerEventAction = event_ns.class_("TriggerEventAction", automation.Action)
|
||||
|
||||
EventTrigger = event_ns.class_("EventTrigger", automation.Trigger.template())
|
||||
|
||||
validate_device_class = cv.one_of(*DEVICE_CLASSES, lower=True, space="_")
|
||||
|
||||
EVENT_SCHEMA = cv.ENTITY_BASE_SCHEMA.extend(cv.MQTT_COMPONENT_SCHEMA).extend(
|
||||
{
|
||||
cv.OnlyWith(CONF_MQTT_ID, "mqtt"): cv.declare_id(mqtt.MQTTEventComponent),
|
||||
cv.GenerateID(): cv.declare_id(Event),
|
||||
cv.Optional(CONF_DEVICE_CLASS): validate_device_class,
|
||||
cv.Optional(CONF_ON_EVENT): automation.validate_automation(
|
||||
{
|
||||
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(EventTrigger),
|
||||
}
|
||||
),
|
||||
}
|
||||
)
|
||||
|
||||
_UNDEF = object()
|
||||
|
||||
|
||||
def event_schema(
|
||||
class_: MockObjClass = _UNDEF,
|
||||
*,
|
||||
icon: str = _UNDEF,
|
||||
entity_category: str = _UNDEF,
|
||||
device_class: str = _UNDEF,
|
||||
) -> cv.Schema:
|
||||
schema = {}
|
||||
|
||||
if class_ is not _UNDEF:
|
||||
schema[cv.GenerateID()] = cv.declare_id(class_)
|
||||
|
||||
for key, default, validator in [
|
||||
(CONF_ICON, icon, cv.icon),
|
||||
(CONF_ENTITY_CATEGORY, entity_category, cv.entity_category),
|
||||
(CONF_DEVICE_CLASS, device_class, validate_device_class),
|
||||
]:
|
||||
if default is not _UNDEF:
|
||||
schema[cv.Optional(key, default=default)] = validator
|
||||
|
||||
return EVENT_SCHEMA.extend(schema)
|
||||
|
||||
|
||||
async def setup_event_core_(var, config, *, event_types: list[str]):
|
||||
await setup_entity(var, config)
|
||||
|
||||
for conf in config.get(CONF_ON_EVENT, []):
|
||||
trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var)
|
||||
await automation.build_automation(
|
||||
trigger, [(cg.std_string, "event_type")], conf
|
||||
)
|
||||
|
||||
cg.add(var.set_event_types(event_types))
|
||||
|
||||
if (device_class := config.get(CONF_DEVICE_CLASS)) is not None:
|
||||
cg.add(var.set_device_class(device_class))
|
||||
|
||||
if mqtt_id := config.get(CONF_MQTT_ID):
|
||||
mqtt_ = cg.new_Pvariable(mqtt_id, var)
|
||||
await mqtt.register_mqtt_component(mqtt_, config)
|
||||
|
||||
|
||||
async def register_event(var, config, *, event_types: list[str]):
|
||||
if not CORE.has_id(config[CONF_ID]):
|
||||
var = cg.Pvariable(config[CONF_ID], var)
|
||||
cg.add(cg.App.register_event(var))
|
||||
await setup_event_core_(var, config, event_types=event_types)
|
||||
|
||||
|
||||
async def new_event(config, *, event_types: list[str]):
|
||||
var = cg.new_Pvariable(config[CONF_ID])
|
||||
await register_event(var, config, event_types=event_types)
|
||||
return var
|
||||
|
||||
|
||||
TRIGGER_EVENT_SCHEMA = cv.Schema(
|
||||
{
|
||||
cv.Required(CONF_ID): cv.use_id(Event),
|
||||
cv.Required(CONF_EVENT_TYPE): cv.templatable(cv.string_strict),
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
@automation.register_action("event.trigger", TriggerEventAction, TRIGGER_EVENT_SCHEMA)
|
||||
async def event_fire_to_code(config, action_id, template_arg, args):
|
||||
var = cg.new_Pvariable(action_id, template_arg)
|
||||
await cg.register_parented(var, config[CONF_ID])
|
||||
templ = await cg.templatable(config[CONF_EVENT_TYPE], args, cg.std_string)
|
||||
cg.add(var.set_event_type(templ))
|
||||
return var
|
||||
|
||||
|
||||
@coroutine_with_priority(100.0)
|
||||
async def to_code(config):
|
||||
cg.add_define("USE_EVENT")
|
||||
cg.add_global(event_ns.using)
|
25
esphome/components/event/automation.h
Normal file
25
esphome/components/event/automation.h
Normal file
@ -0,0 +1,25 @@
|
||||
#pragma once
|
||||
|
||||
#include "esphome/components/event/event.h"
|
||||
#include "esphome/core/automation.h"
|
||||
#include "esphome/core/component.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace event {
|
||||
|
||||
template<typename... Ts> class TriggerEventAction : public Action<Ts...>, public Parented<Event> {
|
||||
public:
|
||||
TEMPLATABLE_VALUE(std::string, event_type)
|
||||
|
||||
void play(Ts... x) override { this->parent_->trigger(this->event_type_.value(x...)); }
|
||||
};
|
||||
|
||||
class EventTrigger : public Trigger<std::string> {
|
||||
public:
|
||||
EventTrigger(Event *event) {
|
||||
event->add_on_event_callback([this](const std::string &event_type) { this->trigger(event_type); });
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace event
|
||||
} // namespace esphome
|
24
esphome/components/event/event.cpp
Normal file
24
esphome/components/event/event.cpp
Normal file
@ -0,0 +1,24 @@
|
||||
#include "event.h"
|
||||
|
||||
#include "esphome/core/log.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace event {
|
||||
|
||||
static const char *const TAG = "event";
|
||||
|
||||
void Event::trigger(const std::string &event_type) {
|
||||
if (types_.find(event_type) == types_.end()) {
|
||||
ESP_LOGE(TAG, "'%s': invalid event type for trigger(): %s", this->get_name().c_str(), event_type.c_str());
|
||||
return;
|
||||
}
|
||||
ESP_LOGD(TAG, "'%s' Triggered event '%s'", this->get_name().c_str(), event_type.c_str());
|
||||
this->event_callback_.call(event_type);
|
||||
}
|
||||
|
||||
void Event::add_on_event_callback(std::function<void(const std::string &event_type)> &&callback) {
|
||||
this->event_callback_.add(std::move(callback));
|
||||
}
|
||||
|
||||
} // namespace event
|
||||
} // namespace esphome
|
37
esphome/components/event/event.h
Normal file
37
esphome/components/event/event.h
Normal file
@ -0,0 +1,37 @@
|
||||
#pragma once
|
||||
|
||||
#include <set>
|
||||
#include <string>
|
||||
|
||||
#include "esphome/core/component.h"
|
||||
#include "esphome/core/entity_base.h"
|
||||
#include "esphome/core/helpers.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace event {
|
||||
|
||||
#define LOG_EVENT(prefix, type, obj) \
|
||||
if ((obj) != nullptr) { \
|
||||
ESP_LOGCONFIG(TAG, "%s%s '%s'", prefix, LOG_STR_LITERAL(type), (obj)->get_name().c_str()); \
|
||||
if (!(obj)->get_icon().empty()) { \
|
||||
ESP_LOGCONFIG(TAG, "%s Icon: '%s'", prefix, (obj)->get_icon().c_str()); \
|
||||
} \
|
||||
if (!(obj)->get_device_class().empty()) { \
|
||||
ESP_LOGCONFIG(TAG, "%s Device Class: '%s'", prefix, (obj)->get_device_class().c_str()); \
|
||||
} \
|
||||
}
|
||||
|
||||
class Event : public EntityBase, public EntityBase_DeviceClass {
|
||||
public:
|
||||
void trigger(const std::string &event_type);
|
||||
void set_event_types(const std::set<std::string> &event_types) { this->types_ = event_types; }
|
||||
std::set<std::string> get_event_types() const { return this->types_; }
|
||||
void add_on_event_callback(std::function<void(const std::string &event_type)> &&callback);
|
||||
|
||||
protected:
|
||||
CallbackManager<void(const std::string &event_type)> event_callback_;
|
||||
std::set<std::string> types_;
|
||||
};
|
||||
|
||||
} // namespace event
|
||||
} // namespace esphome
|
@ -129,7 +129,13 @@ void Font::print(int x_start, int y_start, display::Display *display, Color colo
|
||||
|
||||
uint8_t bitmask = 0;
|
||||
uint8_t pixel_data = 0;
|
||||
float bpp_max = (1 << this->bpp_) - 1;
|
||||
uint8_t bpp_max = (1 << this->bpp_) - 1;
|
||||
auto diff_r = (float) color.r - (float) background.r;
|
||||
auto diff_g = (float) color.g - (float) background.g;
|
||||
auto diff_b = (float) color.b - (float) background.b;
|
||||
auto b_r = (float) background.r;
|
||||
auto b_g = (float) background.g;
|
||||
auto b_b = (float) background.g;
|
||||
for (int glyph_y = y_start + scan_y1; glyph_y != max_y; glyph_y++) {
|
||||
for (int glyph_x = x_at + scan_x1; glyph_x != max_x; glyph_x++) {
|
||||
uint8_t pixel = 0;
|
||||
@ -146,12 +152,9 @@ void Font::print(int x_start, int y_start, display::Display *display, Color colo
|
||||
if (pixel == bpp_max) {
|
||||
display->draw_pixel_at(glyph_x, glyph_y, color);
|
||||
} else if (pixel != 0) {
|
||||
float on = (float) pixel / bpp_max;
|
||||
float off = 1.0 - on;
|
||||
Color blended;
|
||||
blended.r = color.r * on + background.r * off;
|
||||
blended.g = color.r * on + background.g * off;
|
||||
blended.b = color.r * on + background.b * off;
|
||||
auto on = (float) pixel / (float) bpp_max;
|
||||
auto blended =
|
||||
Color((uint8_t) (diff_r * on + b_r), (uint8_t) (diff_g * on + b_g), (uint8_t) (diff_b * on + b_b));
|
||||
display->draw_pixel_at(glyph_x, glyph_y, blended);
|
||||
}
|
||||
}
|
||||
|
@ -164,7 +164,7 @@ void Graph::draw(Display *buff, uint16_t x_offset, uint16_t y_offset, Color colo
|
||||
ESP_LOGV(TAG, "Updating graph. ymin %f, ymax %f", ymin, ymax);
|
||||
for (auto *trace : traces_) {
|
||||
Color c = trace->get_line_color();
|
||||
uint16_t thick = trace->get_line_thickness();
|
||||
int16_t thick = trace->get_line_thickness();
|
||||
bool continuous = trace->get_continuous();
|
||||
bool has_prev = false;
|
||||
bool prev_b = false;
|
||||
@ -178,20 +178,20 @@ void Graph::draw(Display *buff, uint16_t x_offset, uint16_t y_offset, Color colo
|
||||
if (b) {
|
||||
int16_t y = (int16_t) roundf((this->height_ - 1) * (1.0 - v)) - thick / 2 + y_offset;
|
||||
if (!continuous || !has_prev || !prev_b || (abs(y - prev_y) <= thick)) {
|
||||
for (uint16_t t = 0; t < thick; t++) {
|
||||
for (int16_t t = 0; t < thick; t++) {
|
||||
buff->draw_pixel_at(x, y + t, c);
|
||||
}
|
||||
} else {
|
||||
int16_t mid_y = (y + prev_y + thick) / 2;
|
||||
if (y > prev_y) {
|
||||
for (uint16_t t = prev_y + thick; t <= mid_y; t++)
|
||||
for (int16_t t = prev_y + thick; t <= mid_y; t++)
|
||||
buff->draw_pixel_at(x + 1, t, c);
|
||||
for (uint16_t t = mid_y + 1; t < y + thick; t++)
|
||||
for (int16_t t = mid_y + 1; t < y + thick; t++)
|
||||
buff->draw_pixel_at(x, t, c);
|
||||
} else {
|
||||
for (uint16_t t = prev_y - 1; t >= mid_y; t--)
|
||||
for (int16_t t = prev_y - 1; t >= mid_y; t--)
|
||||
buff->draw_pixel_at(x + 1, t, c);
|
||||
for (uint16_t t = mid_y - 1; t >= y; t--)
|
||||
for (int16_t t = mid_y - 1; t >= y; t--)
|
||||
buff->draw_pixel_at(x, t, c);
|
||||
}
|
||||
}
|
||||
|
@ -38,7 +38,7 @@ CONFIG_SCHEMA = DISPLAY_MENU_BASE_SCHEMA.extend(
|
||||
cv.Schema(
|
||||
{
|
||||
cv.GenerateID(): cv.declare_id(GraphicalDisplayMenu),
|
||||
cv.Optional(CONF_DISPLAY): cv.use_id(display.DisplayBuffer),
|
||||
cv.Optional(CONF_DISPLAY): cv.use_id(display.Display),
|
||||
cv.Required(CONF_FONT): cv.use_id(font.Font),
|
||||
cv.Optional(CONF_MENU_ITEM_VALUE): cv.templatable(cv.string),
|
||||
cv.Optional(CONF_FOREGROUND_COLOR): cv.use_id(color.ColorStruct),
|
||||
|
@ -104,7 +104,8 @@ void GraphicalDisplayMenu::draw(display::Display *display, const display::Rect *
|
||||
}
|
||||
|
||||
void GraphicalDisplayMenu::draw_menu_internal_(display::Display *display, const display::Rect *bounds) {
|
||||
int total_height = 0;
|
||||
int16_t total_height = 0;
|
||||
int16_t max_width = 0;
|
||||
int y_padding = 2;
|
||||
bool scroll_menu_items = false;
|
||||
std::vector<display::Rect> menu_dimensions;
|
||||
@ -118,6 +119,7 @@ void GraphicalDisplayMenu::draw_menu_internal_(display::Display *display, const
|
||||
|
||||
menu_dimensions.push_back(item_dimensions);
|
||||
total_height += item_dimensions.h + (i == 0 ? 0 : y_padding);
|
||||
max_width = std::max(max_width, item_dimensions.w);
|
||||
|
||||
if (total_height <= bounds->h) {
|
||||
number_items_fit_to_screen++;
|
||||
@ -166,7 +168,8 @@ void GraphicalDisplayMenu::draw_menu_internal_(display::Display *display, const
|
||||
// Render the items into the view port
|
||||
display->start_clipping(*bounds);
|
||||
|
||||
int y_offset = bounds->y;
|
||||
display->filled_rectangle(bounds->x, bounds->y, max_width, total_height, this->background_color_);
|
||||
auto y_offset = bounds->y;
|
||||
for (size_t i = first_item_index; i <= last_item_index; i++) {
|
||||
const auto *item = this->displayed_item_->get_item(i);
|
||||
const bool selected = i == this->cursor_index_;
|
||||
@ -176,7 +179,7 @@ void GraphicalDisplayMenu::draw_menu_internal_(display::Display *display, const
|
||||
dimensions.x = bounds->x;
|
||||
this->draw_item(display, item, &dimensions, selected);
|
||||
|
||||
y_offset = dimensions.y + dimensions.h + y_padding;
|
||||
y_offset += dimensions.h + y_padding;
|
||||
}
|
||||
|
||||
display->end_clipping();
|
||||
@ -219,9 +222,7 @@ inline void GraphicalDisplayMenu::draw_item(display::Display *display, const dis
|
||||
// int background_width = std::max(bounds->width, available_width);
|
||||
int background_width = bounds->w;
|
||||
|
||||
if (selected) {
|
||||
display->filled_rectangle(bounds->x, bounds->y, background_width, bounds->h, background_color);
|
||||
}
|
||||
display->filled_rectangle(bounds->x, bounds->y, background_width, bounds->h, background_color);
|
||||
|
||||
std::string label = item->get_text();
|
||||
if (item->has_value()) {
|
||||
@ -229,7 +230,8 @@ inline void GraphicalDisplayMenu::draw_item(display::Display *display, const dis
|
||||
label.append(this->menu_item_value_.value(&args));
|
||||
}
|
||||
|
||||
display->print(bounds->x, bounds->y, this->font_, foreground_color, display::TextAlign::TOP_LEFT, label.c_str());
|
||||
display->print(bounds->x, bounds->y, this->font_, foreground_color, display::TextAlign::TOP_LEFT, label.c_str(),
|
||||
background_color);
|
||||
}
|
||||
|
||||
void GraphicalDisplayMenu::draw_item(const display_menu_base::MenuItem *item, const uint8_t row, const bool selected) {
|
||||
|
@ -493,19 +493,16 @@ void LD2420Component::handle_ack_data_(uint8_t *buffer, int len) {
|
||||
}
|
||||
|
||||
int LD2420Component::send_cmd_from_array(CmdFrameT frame) {
|
||||
uint32_t start_millis = millis();
|
||||
uint8_t error = 0;
|
||||
uint8_t ack_buffer[64];
|
||||
uint8_t cmd_buffer[64];
|
||||
uint16_t loop_count;
|
||||
this->cmd_reply_.ack = false;
|
||||
if (frame.command != CMD_RESTART)
|
||||
this->set_cmd_active_(true); // Restart does not reply, thus no ack state required.
|
||||
uint8_t retry = 3;
|
||||
while (retry) {
|
||||
// TODO setup a dynamic method e.g. millis time count etc. to tune for non ESP32 240Mhz devices
|
||||
// this is ok for now since the module firmware is changing like the weather atm
|
||||
frame.length = 0;
|
||||
loop_count = 1250;
|
||||
uint16_t frame_data_bytes = frame.data_length + 2; // Always add two bytes for the cmd size
|
||||
|
||||
memcpy(&cmd_buffer[frame.length], &frame.header, sizeof(frame.header));
|
||||
@ -538,12 +535,13 @@ int LD2420Component::send_cmd_from_array(CmdFrameT frame) {
|
||||
this->readline_(read(), ack_buffer, sizeof(ack_buffer));
|
||||
}
|
||||
delay_microseconds_safe(1450);
|
||||
if (loop_count <= 0) {
|
||||
// Wait on an Rx from the LD2420 for up to 3 1 second loops, otherwise it could trigger a WDT.
|
||||
if ((millis() - start_millis) > 1000) {
|
||||
start_millis = millis();
|
||||
error = LD2420_ERROR_TIMEOUT;
|
||||
retry--;
|
||||
break;
|
||||
}
|
||||
loop_count--;
|
||||
}
|
||||
if (this->cmd_reply_.ack)
|
||||
retry = 0;
|
||||
|
@ -337,9 +337,12 @@ LightColorValues LightCall::validate_() {
|
||||
void LightCall::transform_parameters_() {
|
||||
auto traits = this->parent_->get_traits();
|
||||
|
||||
// Allow CWWW modes to be set with a white value and/or color temperature. This is used by HA,
|
||||
// which doesn't support CWWW modes (yet?), and for compatibility with the pre-colormode model,
|
||||
// as CWWW and RGBWW lights used to represent their values as white + color temperature.
|
||||
// Allow CWWW modes to be set with a white value and/or color temperature.
|
||||
// This is used in three cases in HA:
|
||||
// - CW/WW lights, which set the "brightness" and "color_temperature"
|
||||
// - RGBWW lights with color_interlock=true, which also sets "brightness" and
|
||||
// "color_temperature" (without color_interlock, CW/WW are set directly)
|
||||
// - Legacy Home Assistant (pre-colormode), which sets "white" and "color_temperature"
|
||||
if (((this->white_.has_value() && *this->white_ > 0.0f) || this->color_temperature_.has_value()) && //
|
||||
(*this->color_mode_ & ColorCapability::COLD_WARM_WHITE) && //
|
||||
!(*this->color_mode_ & ColorCapability::WHITE) && //
|
||||
@ -347,21 +350,17 @@ void LightCall::transform_parameters_() {
|
||||
traits.get_min_mireds() > 0.0f && traits.get_max_mireds() > 0.0f) {
|
||||
ESP_LOGD(TAG, "'%s' - Setting cold/warm white channels using white/color temperature values.",
|
||||
this->parent_->get_name().c_str());
|
||||
auto current_values = this->parent_->remote_values;
|
||||
if (this->color_temperature_.has_value()) {
|
||||
const float white =
|
||||
this->white_.value_or(fmaxf(current_values.get_cold_white(), current_values.get_warm_white()));
|
||||
const float color_temp = clamp(*this->color_temperature_, traits.get_min_mireds(), traits.get_max_mireds());
|
||||
const float ww_fraction =
|
||||
(color_temp - traits.get_min_mireds()) / (traits.get_max_mireds() - traits.get_min_mireds());
|
||||
const float cw_fraction = 1.0f - ww_fraction;
|
||||
const float max_cw_ww = std::max(ww_fraction, cw_fraction);
|
||||
this->cold_white_ = white * gamma_uncorrect(cw_fraction / max_cw_ww, this->parent_->get_gamma_correct());
|
||||
this->warm_white_ = white * gamma_uncorrect(ww_fraction / max_cw_ww, this->parent_->get_gamma_correct());
|
||||
} else {
|
||||
const float max_cw_ww = std::max(current_values.get_warm_white(), current_values.get_cold_white());
|
||||
this->cold_white_ = *this->white_ * current_values.get_cold_white() / max_cw_ww;
|
||||
this->warm_white_ = *this->white_ * current_values.get_warm_white() / max_cw_ww;
|
||||
this->cold_white_ = gamma_uncorrect(cw_fraction / max_cw_ww, this->parent_->get_gamma_correct());
|
||||
this->warm_white_ = gamma_uncorrect(ww_fraction / max_cw_ww, this->parent_->get_gamma_correct());
|
||||
}
|
||||
if (this->white_.has_value()) {
|
||||
this->brightness_ = *this->white_;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -266,6 +266,21 @@ class LightColorValues {
|
||||
/// Set the color temperature property of these light color values in mired.
|
||||
void set_color_temperature(float color_temperature) { this->color_temperature_ = color_temperature; }
|
||||
|
||||
/// Get the color temperature property of these light color values in kelvin.
|
||||
float get_color_temperature_kelvin() const {
|
||||
if (this->color_temperature_ <= 0) {
|
||||
return this->color_temperature_;
|
||||
}
|
||||
return 1000000.0 / this->color_temperature_;
|
||||
}
|
||||
/// Set the color temperature property of these light color values in kelvin.
|
||||
void set_color_temperature_kelvin(float color_temperature) {
|
||||
if (color_temperature <= 0) {
|
||||
return;
|
||||
}
|
||||
this->color_temperature_ = 1000000.0 / color_temperature;
|
||||
}
|
||||
|
||||
/// Get the cold white property of these light color values. In range 0.0 to 1.0.
|
||||
float get_cold_white() const { return this->cold_white_; }
|
||||
/// Set the cold white property of these light color values. In range 0.0 to 1.0.
|
||||
|
@ -115,10 +115,12 @@ MQTTTextSensor = mqtt_ns.class_("MQTTTextSensor", MQTTComponent)
|
||||
MQTTNumberComponent = mqtt_ns.class_("MQTTNumberComponent", MQTTComponent)
|
||||
MQTTDateComponent = mqtt_ns.class_("MQTTDateComponent", MQTTComponent)
|
||||
MQTTTimeComponent = mqtt_ns.class_("MQTTTimeComponent", MQTTComponent)
|
||||
MQTTDateTimeComponent = mqtt_ns.class_("MQTTDateTimeComponent", MQTTComponent)
|
||||
MQTTTextComponent = mqtt_ns.class_("MQTTTextComponent", MQTTComponent)
|
||||
MQTTSelectComponent = mqtt_ns.class_("MQTTSelectComponent", MQTTComponent)
|
||||
MQTTButtonComponent = mqtt_ns.class_("MQTTButtonComponent", MQTTComponent)
|
||||
MQTTLockComponent = mqtt_ns.class_("MQTTLockComponent", MQTTComponent)
|
||||
MQTTEventComponent = mqtt_ns.class_("MQTTEventComponent", MQTTComponent)
|
||||
MQTTValveComponent = mqtt_ns.class_("MQTTValveComponent", MQTTComponent)
|
||||
|
||||
MQTTDiscoveryUniqueIdGenerator = mqtt_ns.enum("MQTTDiscoveryUniqueIdGenerator")
|
||||
|
@ -9,8 +9,8 @@ namespace mqtt {
|
||||
|
||||
#ifdef USE_MQTT_ABBREVIATIONS
|
||||
|
||||
constexpr const char *const MQTT_ACTION_TOPIC = "act_t";
|
||||
constexpr const char *const MQTT_ACTION_TEMPLATE = "act_tpl";
|
||||
constexpr const char *const MQTT_ACTION_TOPIC = "act_t";
|
||||
constexpr const char *const MQTT_AUTOMATION_TYPE = "atype";
|
||||
constexpr const char *const MQTT_AUX_COMMAND_TOPIC = "aux_cmd_t";
|
||||
constexpr const char *const MQTT_AUX_STATE_TEMPLATE = "aux_stat_tpl";
|
||||
@ -21,60 +21,70 @@ constexpr const char *const MQTT_AVAILABILITY_TOPIC = "avty_t";
|
||||
constexpr const char *const MQTT_AWAY_MODE_COMMAND_TOPIC = "away_mode_cmd_t";
|
||||
constexpr const char *const MQTT_AWAY_MODE_STATE_TEMPLATE = "away_mode_stat_tpl";
|
||||
constexpr const char *const MQTT_AWAY_MODE_STATE_TOPIC = "away_mode_stat_t";
|
||||
constexpr const char *const MQTT_BATTERY_LEVEL_TEMPLATE = "bat_lev_tpl";
|
||||
constexpr const char *const MQTT_BATTERY_LEVEL_TOPIC = "bat_lev_t";
|
||||
constexpr const char *const MQTT_BLUE_TEMPLATE = "b_tpl";
|
||||
constexpr const char *const MQTT_BRIGHTNESS_COMMAND_TOPIC = "bri_cmd_t";
|
||||
constexpr const char *const MQTT_BRIGHTNESS_SCALE = "bri_scl";
|
||||
constexpr const char *const MQTT_BRIGHTNESS_STATE_TOPIC = "bri_stat_t";
|
||||
constexpr const char *const MQTT_BRIGHTNESS_TEMPLATE = "bri_tpl";
|
||||
constexpr const char *const MQTT_BRIGHTNESS_VALUE_TEMPLATE = "bri_val_tpl";
|
||||
constexpr const char *const MQTT_COLOR_TEMP_COMMAND_TEMPLATE = "clr_temp_cmd_tpl";
|
||||
constexpr const char *const MQTT_BATTERY_LEVEL_TOPIC = "bat_lev_t";
|
||||
constexpr const char *const MQTT_BATTERY_LEVEL_TEMPLATE = "bat_lev_tpl";
|
||||
constexpr const char *const MQTT_CONFIGURATION_URL = "cu";
|
||||
constexpr const char *const MQTT_CHARGING_TOPIC = "chrg_t";
|
||||
constexpr const char *const MQTT_CHARGING_TEMPLATE = "chrg_tpl";
|
||||
constexpr const char *const MQTT_CHARGING_TOPIC = "chrg_t";
|
||||
constexpr const char *const MQTT_CLEANING_TEMPLATE = "cln_tpl";
|
||||
constexpr const char *const MQTT_CLEANING_TOPIC = "cln_t";
|
||||
constexpr const char *const MQTT_CODE_ARM_REQUIRED = "cod_arm_req";
|
||||
constexpr const char *const MQTT_CODE_DISARM_REQUIRED = "cod_dis_req";
|
||||
constexpr const char *const MQTT_COLOR_MODE = "clrm";
|
||||
constexpr const char *const MQTT_COLOR_MODE_STATE_TOPIC = "clrm_stat_t";
|
||||
constexpr const char *const MQTT_COLOR_MODE_VALUE_TEMPLATE = "clrm_val_tpl";
|
||||
constexpr const char *const MQTT_COLOR_TEMP_COMMAND_TEMPLATE = "clr_temp_cmd_tpl";
|
||||
constexpr const char *const MQTT_COLOR_TEMP_COMMAND_TOPIC = "clr_temp_cmd_t";
|
||||
constexpr const char *const MQTT_COLOR_TEMP_STATE_TOPIC = "clr_temp_stat_t";
|
||||
constexpr const char *const MQTT_COLOR_TEMP_TEMPLATE = "clr_temp_tpl";
|
||||
constexpr const char *const MQTT_COLOR_TEMP_VALUE_TEMPLATE = "clr_temp_val_tpl";
|
||||
constexpr const char *const MQTT_CLEANING_TOPIC = "cln_t";
|
||||
constexpr const char *const MQTT_CLEANING_TEMPLATE = "cln_tpl";
|
||||
constexpr const char *const MQTT_COMMAND_OFF_TEMPLATE = "cmd_off_tpl";
|
||||
constexpr const char *const MQTT_COMMAND_ON_TEMPLATE = "cmd_on_tpl";
|
||||
constexpr const char *const MQTT_COMMAND_TOPIC = "cmd_t";
|
||||
constexpr const char *const MQTT_COMMAND_RETAIN = "ret";
|
||||
constexpr const char *const MQTT_COMMAND_TEMPLATE = "cmd_tpl";
|
||||
constexpr const char *const MQTT_CODE_ARM_REQUIRED = "cod_arm_req";
|
||||
constexpr const char *const MQTT_CODE_DISARM_REQUIRED = "cod_dis_req";
|
||||
constexpr const char *const MQTT_CURRENT_TEMPERATURE_TOPIC = "curr_temp_t";
|
||||
constexpr const char *const MQTT_CURRENT_TEMPERATURE_TEMPLATE = "curr_temp_tpl";
|
||||
constexpr const char *const MQTT_CURRENT_HUMIDITY_TOPIC = "curr_hum_t";
|
||||
constexpr const char *const MQTT_COMMAND_TOPIC = "cmd_t";
|
||||
constexpr const char *const MQTT_CONFIGURATION_URL = "cu";
|
||||
constexpr const char *const MQTT_CURRENT_HUMIDITY_TEMPLATE = "curr_hum_tpl";
|
||||
constexpr const char *const MQTT_CURRENT_HUMIDITY_TOPIC = "curr_hum_t";
|
||||
constexpr const char *const MQTT_CURRENT_TEMPERATURE_TEMPLATE = "curr_temp_tpl";
|
||||
constexpr const char *const MQTT_CURRENT_TEMPERATURE_TOPIC = "curr_temp_t";
|
||||
constexpr const char *const MQTT_DEVICE = "dev";
|
||||
constexpr const char *const MQTT_DEVICE_CLASS = "dev_cla";
|
||||
constexpr const char *const MQTT_DOCKED_TOPIC = "dock_t";
|
||||
constexpr const char *const MQTT_DEVICE_CONNECTIONS = "cns";
|
||||
constexpr const char *const MQTT_DEVICE_IDENTIFIERS = "ids";
|
||||
constexpr const char *const MQTT_DEVICE_MANUFACTURER = "mf";
|
||||
constexpr const char *const MQTT_DEVICE_MODEL = "mdl";
|
||||
constexpr const char *const MQTT_DEVICE_NAME = "name";
|
||||
constexpr const char *const MQTT_DEVICE_SUGGESTED_AREA = "sa";
|
||||
constexpr const char *const MQTT_DEVICE_SW_VERSION = "sw";
|
||||
constexpr const char *const MQTT_DOCKED_TEMPLATE = "dock_tpl";
|
||||
constexpr const char *const MQTT_ENABLED_BY_DEFAULT = "en";
|
||||
constexpr const char *const MQTT_ERROR_TOPIC = "err_t";
|
||||
constexpr const char *const MQTT_ERROR_TEMPLATE = "err_tpl";
|
||||
constexpr const char *const MQTT_FAN_SPEED_TOPIC = "fanspd_t";
|
||||
constexpr const char *const MQTT_FAN_SPEED_TEMPLATE = "fanspd_tpl";
|
||||
constexpr const char *const MQTT_FAN_SPEED_LIST = "fanspd_lst";
|
||||
constexpr const char *const MQTT_FLASH_TIME_LONG = "flsh_tlng";
|
||||
constexpr const char *const MQTT_FLASH_TIME_SHORT = "flsh_tsht";
|
||||
constexpr const char *const MQTT_DOCKED_TOPIC = "dock_t";
|
||||
constexpr const char *const MQTT_EFFECT_COMMAND_TOPIC = "fx_cmd_t";
|
||||
constexpr const char *const MQTT_EFFECT_LIST = "fx_list";
|
||||
constexpr const char *const MQTT_EFFECT_STATE_TOPIC = "fx_stat_t";
|
||||
constexpr const char *const MQTT_EFFECT_TEMPLATE = "fx_tpl";
|
||||
constexpr const char *const MQTT_EFFECT_VALUE_TEMPLATE = "fx_val_tpl";
|
||||
constexpr const char *const MQTT_ENABLED_BY_DEFAULT = "en";
|
||||
constexpr const char *const MQTT_ENTITY_CATEGORY = "ent_cat";
|
||||
constexpr const char *const MQTT_ERROR_TEMPLATE = "err_tpl";
|
||||
constexpr const char *const MQTT_ERROR_TOPIC = "err_t";
|
||||
constexpr const char *const MQTT_EVENT_TYPE = "event_type";
|
||||
constexpr const char *const MQTT_EVENT_TYPES = "evt_typ";
|
||||
constexpr const char *const MQTT_EXPIRE_AFTER = "exp_aft";
|
||||
constexpr const char *const MQTT_FAN_MODE_COMMAND_TEMPLATE = "fan_mode_cmd_tpl";
|
||||
constexpr const char *const MQTT_FAN_MODE_COMMAND_TOPIC = "fan_mode_cmd_t";
|
||||
constexpr const char *const MQTT_FAN_MODE_STATE_TEMPLATE = "fan_mode_stat_tpl";
|
||||
constexpr const char *const MQTT_FAN_MODE_STATE_TOPIC = "fan_mode_stat_t";
|
||||
constexpr const char *const MQTT_FAN_SPEED_LIST = "fanspd_lst";
|
||||
constexpr const char *const MQTT_FAN_SPEED_TEMPLATE = "fanspd_tpl";
|
||||
constexpr const char *const MQTT_FAN_SPEED_TOPIC = "fanspd_t";
|
||||
constexpr const char *const MQTT_FLASH_TIME_LONG = "flsh_tlng";
|
||||
constexpr const char *const MQTT_FLASH_TIME_SHORT = "flsh_tsht";
|
||||
constexpr const char *const MQTT_FORCE_UPDATE = "frc_upd";
|
||||
constexpr const char *const MQTT_GREEN_TEMPLATE = "g_tpl";
|
||||
constexpr const char *const MQTT_HOLD_COMMAND_TEMPLATE = "hold_cmd_tpl";
|
||||
@ -86,56 +96,49 @@ constexpr const char *const MQTT_HS_STATE_TOPIC = "hs_stat_t";
|
||||
constexpr const char *const MQTT_HS_VALUE_TEMPLATE = "hs_val_tpl";
|
||||
constexpr const char *const MQTT_ICON = "ic";
|
||||
constexpr const char *const MQTT_INITIAL = "init";
|
||||
constexpr const char *const MQTT_TARGET_HUMIDITY_COMMAND_TOPIC = "hum_cmd_t";
|
||||
constexpr const char *const MQTT_TARGET_HUMIDITY_COMMAND_TEMPLATE = "hum_cmd_tpl";
|
||||
constexpr const char *const MQTT_TARGET_HUMIDITY_STATE_TOPIC = "hum_stat_t";
|
||||
constexpr const char *const MQTT_TARGET_HUMIDITY_STATE_TEMPLATE = "hum_state_tpl";
|
||||
constexpr const char *const MQTT_JSON_ATTRIBUTES = "json_attr";
|
||||
constexpr const char *const MQTT_JSON_ATTRIBUTES_TOPIC = "json_attr_t";
|
||||
constexpr const char *const MQTT_JSON_ATTRIBUTES_TEMPLATE = "json_attr_tpl";
|
||||
constexpr const char *const MQTT_JSON_ATTRIBUTES_TOPIC = "json_attr_t";
|
||||
constexpr const char *const MQTT_LAST_RESET_TOPIC = "lrst_t";
|
||||
constexpr const char *const MQTT_LAST_RESET_VALUE_TEMPLATE = "lrst_val_tpl";
|
||||
constexpr const char *const MQTT_MAX = "max";
|
||||
constexpr const char *const MQTT_MIN = "min";
|
||||
constexpr const char *const MQTT_MAX_HUMIDITY = "max_hum";
|
||||
constexpr const char *const MQTT_MIN_HUMIDITY = "min_hum";
|
||||
constexpr const char *const MQTT_MAX_MIREDS = "max_mirs";
|
||||
constexpr const char *const MQTT_MIN_MIREDS = "min_mirs";
|
||||
constexpr const char *const MQTT_MAX_TEMP = "max_temp";
|
||||
constexpr const char *const MQTT_MIN = "min";
|
||||
constexpr const char *const MQTT_MIN_HUMIDITY = "min_hum";
|
||||
constexpr const char *const MQTT_MIN_MIREDS = "min_mirs";
|
||||
constexpr const char *const MQTT_MIN_TEMP = "min_temp";
|
||||
constexpr const char *const MQTT_MODE = "mode";
|
||||
constexpr const char *const MQTT_MODE_COMMAND_TEMPLATE = "mode_cmd_tpl";
|
||||
constexpr const char *const MQTT_MODE_COMMAND_TOPIC = "mode_cmd_t";
|
||||
constexpr const char *const MQTT_MODE_STATE_TOPIC = "mode_stat_t";
|
||||
constexpr const char *const MQTT_MODE_STATE_TEMPLATE = "mode_stat_tpl";
|
||||
constexpr const char *const MQTT_MODE_STATE_TOPIC = "mode_stat_t";
|
||||
constexpr const char *const MQTT_MODES = "modes";
|
||||
constexpr const char *const MQTT_NAME = "name";
|
||||
constexpr const char *const MQTT_OBJECT_ID = "obj_id";
|
||||
constexpr const char *const MQTT_OFF_DELAY = "off_dly";
|
||||
constexpr const char *const MQTT_ON_COMMAND_TYPE = "on_cmd_type";
|
||||
constexpr const char *const MQTT_OPTIONS = "ops";
|
||||
constexpr const char *const MQTT_OPTIMISTIC = "opt";
|
||||
constexpr const char *const MQTT_OSCILLATION_COMMAND_TOPIC = "osc_cmd_t";
|
||||
constexpr const char *const MQTT_OPTIONS = "ops";
|
||||
constexpr const char *const MQTT_OSCILLATION_COMMAND_TEMPLATE = "osc_cmd_tpl";
|
||||
constexpr const char *const MQTT_OSCILLATION_COMMAND_TOPIC = "osc_cmd_t";
|
||||
constexpr const char *const MQTT_OSCILLATION_STATE_TOPIC = "osc_stat_t";
|
||||
constexpr const char *const MQTT_OSCILLATION_VALUE_TEMPLATE = "osc_val_tpl";
|
||||
constexpr const char *const MQTT_PERCENTAGE_COMMAND_TOPIC = "pct_cmd_t";
|
||||
constexpr const char *const MQTT_PERCENTAGE_COMMAND_TEMPLATE = "pct_cmd_tpl";
|
||||
constexpr const char *const MQTT_PERCENTAGE_STATE_TOPIC = "pct_stat_t";
|
||||
constexpr const char *const MQTT_PERCENTAGE_VALUE_TEMPLATE = "pct_val_tpl";
|
||||
constexpr const char *const MQTT_PAYLOAD = "pl";
|
||||
constexpr const char *const MQTT_PAYLOAD_ARM_AWAY = "pl_arm_away";
|
||||
constexpr const char *const MQTT_PAYLOAD_ARM_CUSTOM_BYPASS = "pl_arm_custom_b";
|
||||
constexpr const char *const MQTT_PAYLOAD_ARM_HOME = "pl_arm_home";
|
||||
constexpr const char *const MQTT_PAYLOAD_ARM_NIGHT = "pl_arm_nite";
|
||||
constexpr const char *const MQTT_PAYLOAD_ARM_VACATION = "pl_arm_vacation";
|
||||
constexpr const char *const MQTT_PAYLOAD_ARM_CUSTOM_BYPASS = "pl_arm_custom_b";
|
||||
constexpr const char *const MQTT_PAYLOAD_AVAILABLE = "pl_avail";
|
||||
constexpr const char *const MQTT_PAYLOAD_CLEAN_SPOT = "pl_cln_sp";
|
||||
constexpr const char *const MQTT_PAYLOAD_CLOSE = "pl_cls";
|
||||
constexpr const char *const MQTT_PAYLOAD_DISARM = "pl_disarm";
|
||||
constexpr const char *const MQTT_PAYLOAD_HIGH_SPEED = "pl_hi_spd";
|
||||
constexpr const char *const MQTT_PAYLOAD_HOME = "pl_home";
|
||||
constexpr const char *const MQTT_PAYLOAD_LOCK = "pl_lock";
|
||||
constexpr const char *const MQTT_PAYLOAD_LOCATE = "pl_loc";
|
||||
constexpr const char *const MQTT_PAYLOAD_LOCK = "pl_lock";
|
||||
constexpr const char *const MQTT_PAYLOAD_LOW_SPEED = "pl_lo_spd";
|
||||
constexpr const char *const MQTT_PAYLOAD_MEDIUM_SPEED = "pl_med_spd";
|
||||
constexpr const char *const MQTT_PAYLOAD_NOT_AVAILABLE = "pl_not_avail";
|
||||
@ -152,20 +155,26 @@ constexpr const char *const MQTT_PAYLOAD_RESET_HUMIDITY = "pl_rst_hum";
|
||||
constexpr const char *const MQTT_PAYLOAD_RESET_MODE = "pl_rst_mode";
|
||||
constexpr const char *const MQTT_PAYLOAD_RESET_PERCENTAGE = "pl_rst_pct";
|
||||
constexpr const char *const MQTT_PAYLOAD_RESET_PRESET_MODE = "pl_rst_pr_mode";
|
||||
constexpr const char *const MQTT_PAYLOAD_STOP = "pl_stop";
|
||||
constexpr const char *const MQTT_PAYLOAD_RETURN_TO_BASE = "pl_ret";
|
||||
constexpr const char *const MQTT_PAYLOAD_START = "pl_strt";
|
||||
constexpr const char *const MQTT_PAYLOAD_START_PAUSE = "pl_stpa";
|
||||
constexpr const char *const MQTT_PAYLOAD_RETURN_TO_BASE = "pl_ret";
|
||||
constexpr const char *const MQTT_PAYLOAD_STOP = "pl_stop";
|
||||
constexpr const char *const MQTT_PAYLOAD_TURN_OFF = "pl_toff";
|
||||
constexpr const char *const MQTT_PAYLOAD_TURN_ON = "pl_ton";
|
||||
constexpr const char *const MQTT_PAYLOAD_UNLOCK = "pl_unlk";
|
||||
constexpr const char *const MQTT_PERCENTAGE_COMMAND_TEMPLATE = "pct_cmd_tpl";
|
||||
constexpr const char *const MQTT_PERCENTAGE_COMMAND_TOPIC = "pct_cmd_t";
|
||||
constexpr const char *const MQTT_PERCENTAGE_STATE_TOPIC = "pct_stat_t";
|
||||
constexpr const char *const MQTT_PERCENTAGE_VALUE_TEMPLATE = "pct_val_tpl";
|
||||
constexpr const char *const MQTT_POSITION_CLOSED = "pos_clsd";
|
||||
constexpr const char *const MQTT_POSITION_OPEN = "pos_open";
|
||||
constexpr const char *const MQTT_POSITION_TEMPLATE = "pos_tpl";
|
||||
constexpr const char *const MQTT_POSITION_TOPIC = "pos_t";
|
||||
constexpr const char *const MQTT_POWER_COMMAND_TOPIC = "pow_cmd_t";
|
||||
constexpr const char *const MQTT_POWER_STATE_TOPIC = "pow_stat_t";
|
||||
constexpr const char *const MQTT_POWER_STATE_TEMPLATE = "pow_stat_tpl";
|
||||
constexpr const char *const MQTT_PRESET_MODE_COMMAND_TOPIC = "pr_mode_cmd_t";
|
||||
constexpr const char *const MQTT_POWER_STATE_TOPIC = "pow_stat_t";
|
||||
constexpr const char *const MQTT_PRESET_MODE_COMMAND_TEMPLATE = "pr_mode_cmd_tpl";
|
||||
constexpr const char *const MQTT_PRESET_MODE_COMMAND_TOPIC = "pr_mode_cmd_t";
|
||||
constexpr const char *const MQTT_PRESET_MODE_STATE_TOPIC = "pr_mode_stat_t";
|
||||
constexpr const char *const MQTT_PRESET_MODE_VALUE_TEMPLATE = "pr_mode_val_tpl";
|
||||
constexpr const char *const MQTT_PRESET_MODES = "pr_modes";
|
||||
@ -188,36 +197,38 @@ constexpr const char *const MQTT_SEND_IF_OFF = "send_if_off";
|
||||
constexpr const char *const MQTT_SET_FAN_SPEED_TOPIC = "set_fan_spd_t";
|
||||
constexpr const char *const MQTT_SET_POSITION_TEMPLATE = "set_pos_tpl";
|
||||
constexpr const char *const MQTT_SET_POSITION_TOPIC = "set_pos_t";
|
||||
constexpr const char *const MQTT_POSITION_TOPIC = "pos_t";
|
||||
constexpr const char *const MQTT_POSITION_TEMPLATE = "pos_tpl";
|
||||
constexpr const char *const MQTT_SOURCE_TYPE = "src_type";
|
||||
constexpr const char *const MQTT_SPEED_COMMAND_TOPIC = "spd_cmd_t";
|
||||
constexpr const char *const MQTT_SPEED_STATE_TOPIC = "spd_stat_t";
|
||||
constexpr const char *const MQTT_SPEED_RANGE_MIN = "spd_rng_min";
|
||||
constexpr const char *const MQTT_SPEED_RANGE_MAX = "spd_rng_max";
|
||||
constexpr const char *const MQTT_SPEED_RANGE_MIN = "spd_rng_min";
|
||||
constexpr const char *const MQTT_SPEED_STATE_TOPIC = "spd_stat_t";
|
||||
constexpr const char *const MQTT_SPEED_VALUE_TEMPLATE = "spd_val_tpl";
|
||||
constexpr const char *const MQTT_SPEEDS = "spds";
|
||||
constexpr const char *const MQTT_SOURCE_TYPE = "src_type";
|
||||
constexpr const char *const MQTT_STATE_CLASS = "stat_cla";
|
||||
constexpr const char *const MQTT_STATE_CLOSED = "stat_clsd";
|
||||
constexpr const char *const MQTT_STATE_CLOSING = "stat_closing";
|
||||
constexpr const char *const MQTT_STATE_LOCKED = "stat_locked";
|
||||
constexpr const char *const MQTT_STATE_OFF = "stat_off";
|
||||
constexpr const char *const MQTT_STATE_ON = "stat_on";
|
||||
constexpr const char *const MQTT_STATE_OPEN = "stat_open";
|
||||
constexpr const char *const MQTT_STATE_OPENING = "stat_opening";
|
||||
constexpr const char *const MQTT_STATE_STOPPED = "stat_stopped";
|
||||
constexpr const char *const MQTT_STATE_LOCKED = "stat_locked";
|
||||
constexpr const char *const MQTT_STATE_UNLOCKED = "stat_unlocked";
|
||||
constexpr const char *const MQTT_STATE_TOPIC = "stat_t";
|
||||
constexpr const char *const MQTT_STATE_TEMPLATE = "stat_tpl";
|
||||
constexpr const char *const MQTT_STATE_TOPIC = "stat_t";
|
||||
constexpr const char *const MQTT_STATE_UNLOCKED = "stat_unlocked";
|
||||
constexpr const char *const MQTT_STATE_VALUE_TEMPLATE = "stat_val_tpl";
|
||||
constexpr const char *const MQTT_STEP = "step";
|
||||
constexpr const char *const MQTT_SUBTYPE = "stype";
|
||||
constexpr const char *const MQTT_SUPPORTED_FEATURES = "sup_feat";
|
||||
constexpr const char *const MQTT_SUPPORTED_COLOR_MODES = "sup_clrm";
|
||||
constexpr const char *const MQTT_SUPPORTED_FEATURES = "sup_feat";
|
||||
constexpr const char *const MQTT_SWING_MODE_COMMAND_TEMPLATE = "swing_mode_cmd_tpl";
|
||||
constexpr const char *const MQTT_SWING_MODE_COMMAND_TOPIC = "swing_mode_cmd_t";
|
||||
constexpr const char *const MQTT_SWING_MODE_STATE_TEMPLATE = "swing_mode_stat_tpl";
|
||||
constexpr const char *const MQTT_SWING_MODE_STATE_TOPIC = "swing_mode_stat_t";
|
||||
constexpr const char *const MQTT_TARGET_HUMIDITY_COMMAND_TEMPLATE = "hum_cmd_tpl";
|
||||
constexpr const char *const MQTT_TARGET_HUMIDITY_COMMAND_TOPIC = "hum_cmd_t";
|
||||
constexpr const char *const MQTT_TARGET_HUMIDITY_STATE_TEMPLATE = "hum_state_tpl";
|
||||
constexpr const char *const MQTT_TARGET_HUMIDITY_STATE_TOPIC = "hum_stat_t";
|
||||
constexpr const char *const MQTT_TEMPERATURE_COMMAND_TEMPLATE = "temp_cmd_tpl";
|
||||
constexpr const char *const MQTT_TEMPERATURE_COMMAND_TOPIC = "temp_cmd_t";
|
||||
constexpr const char *const MQTT_TEMPERATURE_HIGH_COMMAND_TEMPLATE = "temp_hi_cmd_tpl";
|
||||
@ -232,15 +243,15 @@ constexpr const char *const MQTT_TEMPERATURE_STATE_TEMPLATE = "temp_stat_tpl";
|
||||
constexpr const char *const MQTT_TEMPERATURE_STATE_TOPIC = "temp_stat_t";
|
||||
constexpr const char *const MQTT_TEMPERATURE_UNIT = "temp_unit";
|
||||
constexpr const char *const MQTT_TILT_CLOSED_VALUE = "tilt_clsd_val";
|
||||
constexpr const char *const MQTT_TILT_COMMAND_TOPIC = "tilt_cmd_t";
|
||||
constexpr const char *const MQTT_TILT_COMMAND_TEMPLATE = "tilt_cmd_tpl";
|
||||
constexpr const char *const MQTT_TILT_COMMAND_TOPIC = "tilt_cmd_t";
|
||||
constexpr const char *const MQTT_TILT_INVERT_STATE = "tilt_inv_stat";
|
||||
constexpr const char *const MQTT_TILT_MAX = "tilt_max";
|
||||
constexpr const char *const MQTT_TILT_MIN = "tilt_min";
|
||||
constexpr const char *const MQTT_TILT_OPENED_VALUE = "tilt_opnd_val";
|
||||
constexpr const char *const MQTT_TILT_OPTIMISTIC = "tilt_opt";
|
||||
constexpr const char *const MQTT_TILT_STATUS_TOPIC = "tilt_status_t";
|
||||
constexpr const char *const MQTT_TILT_STATUS_TEMPLATE = "tilt_status_tpl";
|
||||
constexpr const char *const MQTT_TILT_STATUS_TOPIC = "tilt_status_t";
|
||||
constexpr const char *const MQTT_TOPIC = "t";
|
||||
constexpr const char *const MQTT_UNIQUE_ID = "uniq_id";
|
||||
constexpr const char *const MQTT_UNIT_OF_MEASUREMENT = "unit_of_meas";
|
||||
@ -255,18 +266,10 @@ constexpr const char *const MQTT_XY_COMMAND_TOPIC = "xy_cmd_t";
|
||||
constexpr const char *const MQTT_XY_STATE_TOPIC = "xy_stat_t";
|
||||
constexpr const char *const MQTT_XY_VALUE_TEMPLATE = "xy_val_tpl";
|
||||
|
||||
constexpr const char *const MQTT_DEVICE_CONNECTIONS = "cns";
|
||||
constexpr const char *const MQTT_DEVICE_IDENTIFIERS = "ids";
|
||||
constexpr const char *const MQTT_DEVICE_NAME = "name";
|
||||
constexpr const char *const MQTT_DEVICE_MANUFACTURER = "mf";
|
||||
constexpr const char *const MQTT_DEVICE_MODEL = "mdl";
|
||||
constexpr const char *const MQTT_DEVICE_SW_VERSION = "sw";
|
||||
constexpr const char *const MQTT_DEVICE_SUGGESTED_AREA = "sa";
|
||||
|
||||
#else
|
||||
|
||||
constexpr const char *const MQTT_ACTION_TOPIC = "action_topic";
|
||||
constexpr const char *const MQTT_ACTION_TEMPLATE = "action_template";
|
||||
constexpr const char *const MQTT_ACTION_TOPIC = "action_topic";
|
||||
constexpr const char *const MQTT_AUTOMATION_TYPE = "automation_type";
|
||||
constexpr const char *const MQTT_AUX_COMMAND_TOPIC = "aux_command_topic";
|
||||
constexpr const char *const MQTT_AUX_STATE_TEMPLATE = "aux_state_template";
|
||||
@ -277,60 +280,70 @@ constexpr const char *const MQTT_AVAILABILITY_TOPIC = "availability_topic";
|
||||
constexpr const char *const MQTT_AWAY_MODE_COMMAND_TOPIC = "away_mode_command_topic";
|
||||
constexpr const char *const MQTT_AWAY_MODE_STATE_TEMPLATE = "away_mode_state_template";
|
||||
constexpr const char *const MQTT_AWAY_MODE_STATE_TOPIC = "away_mode_state_topic";
|
||||
constexpr const char *const MQTT_BATTERY_LEVEL_TEMPLATE = "battery_level_template";
|
||||
constexpr const char *const MQTT_BATTERY_LEVEL_TOPIC = "battery_level_topic";
|
||||
constexpr const char *const MQTT_BLUE_TEMPLATE = "blue_template";
|
||||
constexpr const char *const MQTT_BRIGHTNESS_COMMAND_TOPIC = "brightness_command_topic";
|
||||
constexpr const char *const MQTT_BRIGHTNESS_SCALE = "brightness_scale";
|
||||
constexpr const char *const MQTT_BRIGHTNESS_STATE_TOPIC = "brightness_state_topic";
|
||||
constexpr const char *const MQTT_BRIGHTNESS_TEMPLATE = "brightness_template";
|
||||
constexpr const char *const MQTT_BRIGHTNESS_VALUE_TEMPLATE = "brightness_value_template";
|
||||
constexpr const char *const MQTT_COLOR_TEMP_COMMAND_TEMPLATE = "color_temp_command_template";
|
||||
constexpr const char *const MQTT_BATTERY_LEVEL_TOPIC = "battery_level_topic";
|
||||
constexpr const char *const MQTT_BATTERY_LEVEL_TEMPLATE = "battery_level_template";
|
||||
constexpr const char *const MQTT_CONFIGURATION_URL = "configuration_url";
|
||||
constexpr const char *const MQTT_CHARGING_TOPIC = "charging_topic";
|
||||
constexpr const char *const MQTT_CHARGING_TEMPLATE = "charging_template";
|
||||
constexpr const char *const MQTT_CHARGING_TOPIC = "charging_topic";
|
||||
constexpr const char *const MQTT_CLEANING_TEMPLATE = "cleaning_template";
|
||||
constexpr const char *const MQTT_CLEANING_TOPIC = "cleaning_topic";
|
||||
constexpr const char *const MQTT_CODE_ARM_REQUIRED = "code_arm_required";
|
||||
constexpr const char *const MQTT_CODE_DISARM_REQUIRED = "code_disarm_required";
|
||||
constexpr const char *const MQTT_COLOR_MODE = "color_mode";
|
||||
constexpr const char *const MQTT_COLOR_MODE_STATE_TOPIC = "color_mode_state_topic";
|
||||
constexpr const char *const MQTT_COLOR_MODE_VALUE_TEMPLATE = "color_mode_value_template";
|
||||
constexpr const char *const MQTT_COLOR_TEMP_COMMAND_TEMPLATE = "color_temp_command_template";
|
||||
constexpr const char *const MQTT_COLOR_TEMP_COMMAND_TOPIC = "color_temp_command_topic";
|
||||
constexpr const char *const MQTT_COLOR_TEMP_STATE_TOPIC = "color_temp_state_topic";
|
||||
constexpr const char *const MQTT_COLOR_TEMP_TEMPLATE = "color_temp_template";
|
||||
constexpr const char *const MQTT_COLOR_TEMP_VALUE_TEMPLATE = "color_temp_value_template";
|
||||
constexpr const char *const MQTT_CLEANING_TOPIC = "cleaning_topic";
|
||||
constexpr const char *const MQTT_CLEANING_TEMPLATE = "cleaning_template";
|
||||
constexpr const char *const MQTT_COMMAND_OFF_TEMPLATE = "command_off_template";
|
||||
constexpr const char *const MQTT_COMMAND_ON_TEMPLATE = "command_on_template";
|
||||
constexpr const char *const MQTT_COMMAND_TOPIC = "command_topic";
|
||||
constexpr const char *const MQTT_COMMAND_RETAIN = "retain";
|
||||
constexpr const char *const MQTT_COMMAND_TEMPLATE = "command_template";
|
||||
constexpr const char *const MQTT_CODE_ARM_REQUIRED = "code_arm_required";
|
||||
constexpr const char *const MQTT_CODE_DISARM_REQUIRED = "code_disarm_required";
|
||||
constexpr const char *const MQTT_CURRENT_TEMPERATURE_TOPIC = "current_temperature_topic";
|
||||
constexpr const char *const MQTT_CURRENT_TEMPERATURE_TEMPLATE = "current_temperature_template";
|
||||
constexpr const char *const MQTT_CURRENT_HUMIDITY_TOPIC = "current_humidity_topic";
|
||||
constexpr const char *const MQTT_COMMAND_TOPIC = "command_topic";
|
||||
constexpr const char *const MQTT_CONFIGURATION_URL = "configuration_url";
|
||||
constexpr const char *const MQTT_CURRENT_HUMIDITY_TEMPLATE = "current_humidity_template";
|
||||
constexpr const char *const MQTT_CURRENT_HUMIDITY_TOPIC = "current_humidity_topic";
|
||||
constexpr const char *const MQTT_CURRENT_TEMPERATURE_TEMPLATE = "current_temperature_template";
|
||||
constexpr const char *const MQTT_CURRENT_TEMPERATURE_TOPIC = "current_temperature_topic";
|
||||
constexpr const char *const MQTT_DEVICE = "device";
|
||||
constexpr const char *const MQTT_DEVICE_CLASS = "device_class";
|
||||
constexpr const char *const MQTT_DOCKED_TOPIC = "docked_topic";
|
||||
constexpr const char *const MQTT_DEVICE_CONNECTIONS = "connections";
|
||||
constexpr const char *const MQTT_DEVICE_IDENTIFIERS = "identifiers";
|
||||
constexpr const char *const MQTT_DEVICE_MANUFACTURER = "manufacturer";
|
||||
constexpr const char *const MQTT_DEVICE_MODEL = "model";
|
||||
constexpr const char *const MQTT_DEVICE_NAME = "name";
|
||||
constexpr const char *const MQTT_DEVICE_SUGGESTED_AREA = "suggested_area";
|
||||
constexpr const char *const MQTT_DEVICE_SW_VERSION = "sw_version";
|
||||
constexpr const char *const MQTT_DOCKED_TEMPLATE = "docked_template";
|
||||
constexpr const char *const MQTT_ENABLED_BY_DEFAULT = "enabled_by_default";
|
||||
constexpr const char *const MQTT_ERROR_TOPIC = "error_topic";
|
||||
constexpr const char *const MQTT_ERROR_TEMPLATE = "error_template";
|
||||
constexpr const char *const MQTT_FAN_SPEED_TOPIC = "fan_speed_topic";
|
||||
constexpr const char *const MQTT_FAN_SPEED_TEMPLATE = "fan_speed_template";
|
||||
constexpr const char *const MQTT_FAN_SPEED_LIST = "fan_speed_list";
|
||||
constexpr const char *const MQTT_FLASH_TIME_LONG = "flash_time_long";
|
||||
constexpr const char *const MQTT_FLASH_TIME_SHORT = "flash_time_short";
|
||||
constexpr const char *const MQTT_DOCKED_TOPIC = "docked_topic";
|
||||
constexpr const char *const MQTT_EFFECT_COMMAND_TOPIC = "effect_command_topic";
|
||||
constexpr const char *const MQTT_EFFECT_LIST = "effect_list";
|
||||
constexpr const char *const MQTT_EFFECT_STATE_TOPIC = "effect_state_topic";
|
||||
constexpr const char *const MQTT_EFFECT_TEMPLATE = "effect_template";
|
||||
constexpr const char *const MQTT_EFFECT_VALUE_TEMPLATE = "effect_value_template";
|
||||
constexpr const char *const MQTT_ENABLED_BY_DEFAULT = "enabled_by_default";
|
||||
constexpr const char *const MQTT_ENTITY_CATEGORY = "entity_category";
|
||||
constexpr const char *const MQTT_ERROR_TEMPLATE = "error_template";
|
||||
constexpr const char *const MQTT_ERROR_TOPIC = "error_topic";
|
||||
constexpr const char *const MQTT_EVENT_TYPE = "event_type";
|
||||
constexpr const char *const MQTT_EVENT_TYPES = "event_types";
|
||||
constexpr const char *const MQTT_EXPIRE_AFTER = "expire_after";
|
||||
constexpr const char *const MQTT_FAN_MODE_COMMAND_TEMPLATE = "fan_mode_command_template";
|
||||
constexpr const char *const MQTT_FAN_MODE_COMMAND_TOPIC = "fan_mode_command_topic";
|
||||
constexpr const char *const MQTT_FAN_MODE_STATE_TEMPLATE = "fan_mode_state_template";
|
||||
constexpr const char *const MQTT_FAN_MODE_STATE_TOPIC = "fan_mode_state_topic";
|
||||
constexpr const char *const MQTT_FAN_SPEED_LIST = "fan_speed_list";
|
||||
constexpr const char *const MQTT_FAN_SPEED_TEMPLATE = "fan_speed_template";
|
||||
constexpr const char *const MQTT_FAN_SPEED_TOPIC = "fan_speed_topic";
|
||||
constexpr const char *const MQTT_FLASH_TIME_LONG = "flash_time_long";
|
||||
constexpr const char *const MQTT_FLASH_TIME_SHORT = "flash_time_short";
|
||||
constexpr const char *const MQTT_FORCE_UPDATE = "force_update";
|
||||
constexpr const char *const MQTT_GREEN_TEMPLATE = "green_template";
|
||||
constexpr const char *const MQTT_HOLD_COMMAND_TEMPLATE = "hold_command_template";
|
||||
@ -342,56 +355,49 @@ constexpr const char *const MQTT_HS_STATE_TOPIC = "hs_state_topic";
|
||||
constexpr const char *const MQTT_HS_VALUE_TEMPLATE = "hs_value_template";
|
||||
constexpr const char *const MQTT_ICON = "icon";
|
||||
constexpr const char *const MQTT_INITIAL = "initial";
|
||||
constexpr const char *const MQTT_TARGET_HUMIDITY_COMMAND_TOPIC = "target_humidity_command_topic";
|
||||
constexpr const char *const MQTT_TARGET_HUMIDITY_COMMAND_TEMPLATE = "target_humidity_command_template";
|
||||
constexpr const char *const MQTT_TARGET_HUMIDITY_STATE_TOPIC = "target_humidity_state_topic";
|
||||
constexpr const char *const MQTT_TARGET_HUMIDITY_STATE_TEMPLATE = "target_humidity_state_template";
|
||||
constexpr const char *const MQTT_JSON_ATTRIBUTES = "json_attributes";
|
||||
constexpr const char *const MQTT_JSON_ATTRIBUTES_TOPIC = "json_attributes_topic";
|
||||
constexpr const char *const MQTT_JSON_ATTRIBUTES_TEMPLATE = "json_attributes_template";
|
||||
constexpr const char *const MQTT_JSON_ATTRIBUTES_TOPIC = "json_attributes_topic";
|
||||
constexpr const char *const MQTT_LAST_RESET_TOPIC = "last_reset_topic";
|
||||
constexpr const char *const MQTT_LAST_RESET_VALUE_TEMPLATE = "last_reset_value_template";
|
||||
constexpr const char *const MQTT_MAX = "max";
|
||||
constexpr const char *const MQTT_MIN = "min";
|
||||
constexpr const char *const MQTT_MAX_HUMIDITY = "max_humidity";
|
||||
constexpr const char *const MQTT_MIN_HUMIDITY = "min_humidity";
|
||||
constexpr const char *const MQTT_MAX_MIREDS = "max_mireds";
|
||||
constexpr const char *const MQTT_MIN_MIREDS = "min_mireds";
|
||||
constexpr const char *const MQTT_MAX_TEMP = "max_temp";
|
||||
constexpr const char *const MQTT_MIN = "min";
|
||||
constexpr const char *const MQTT_MIN_HUMIDITY = "min_humidity";
|
||||
constexpr const char *const MQTT_MIN_MIREDS = "min_mireds";
|
||||
constexpr const char *const MQTT_MIN_TEMP = "min_temp";
|
||||
constexpr const char *const MQTT_MODE = "mode";
|
||||
constexpr const char *const MQTT_MODE_COMMAND_TEMPLATE = "mode_command_template";
|
||||
constexpr const char *const MQTT_MODE_COMMAND_TOPIC = "mode_command_topic";
|
||||
constexpr const char *const MQTT_MODE_STATE_TOPIC = "mode_state_topic";
|
||||
constexpr const char *const MQTT_MODE_STATE_TEMPLATE = "mode_state_template";
|
||||
constexpr const char *const MQTT_MODE_STATE_TOPIC = "mode_state_topic";
|
||||
constexpr const char *const MQTT_MODES = "modes";
|
||||
constexpr const char *const MQTT_NAME = "name";
|
||||
constexpr const char *const MQTT_OBJECT_ID = "object_id";
|
||||
constexpr const char *const MQTT_OFF_DELAY = "off_delay";
|
||||
constexpr const char *const MQTT_ON_COMMAND_TYPE = "on_command_type";
|
||||
constexpr const char *const MQTT_OPTIONS = "options";
|
||||
constexpr const char *const MQTT_OPTIMISTIC = "optimistic";
|
||||
constexpr const char *const MQTT_OSCILLATION_COMMAND_TOPIC = "oscillation_command_topic";
|
||||
constexpr const char *const MQTT_OPTIONS = "options";
|
||||
constexpr const char *const MQTT_OSCILLATION_COMMAND_TEMPLATE = "oscillation_command_template";
|
||||
constexpr const char *const MQTT_OSCILLATION_COMMAND_TOPIC = "oscillation_command_topic";
|
||||
constexpr const char *const MQTT_OSCILLATION_STATE_TOPIC = "oscillation_state_topic";
|
||||
constexpr const char *const MQTT_OSCILLATION_VALUE_TEMPLATE = "oscillation_value_template";
|
||||
constexpr const char *const MQTT_PERCENTAGE_COMMAND_TOPIC = "percentage_command_topic";
|
||||
constexpr const char *const MQTT_PERCENTAGE_COMMAND_TEMPLATE = "percentage_command_template";
|
||||
constexpr const char *const MQTT_PERCENTAGE_STATE_TOPIC = "percentage_state_topic";
|
||||
constexpr const char *const MQTT_PERCENTAGE_VALUE_TEMPLATE = "percentage_value_template";
|
||||
constexpr const char *const MQTT_PAYLOAD = "payload";
|
||||
constexpr const char *const MQTT_PAYLOAD_ARM_AWAY = "payload_arm_away";
|
||||
constexpr const char *const MQTT_PAYLOAD_ARM_CUSTOM_BYPASS = "payload_arm_custom_bypass";
|
||||
constexpr const char *const MQTT_PAYLOAD_ARM_HOME = "payload_arm_home";
|
||||
constexpr const char *const MQTT_PAYLOAD_ARM_NIGHT = "payload_arm_night";
|
||||
constexpr const char *const MQTT_PAYLOAD_ARM_VACATION = "payload_arm_vacation";
|
||||
constexpr const char *const MQTT_PAYLOAD_ARM_CUSTOM_BYPASS = "payload_arm_custom_bypass";
|
||||
constexpr const char *const MQTT_PAYLOAD_AVAILABLE = "payload_available";
|
||||
constexpr const char *const MQTT_PAYLOAD_CLEAN_SPOT = "payload_clean_spot";
|
||||
constexpr const char *const MQTT_PAYLOAD_CLOSE = "payload_close";
|
||||
constexpr const char *const MQTT_PAYLOAD_DISARM = "payload_disarm";
|
||||
constexpr const char *const MQTT_PAYLOAD_HIGH_SPEED = "payload_high_speed";
|
||||
constexpr const char *const MQTT_PAYLOAD_HOME = "payload_home";
|
||||
constexpr const char *const MQTT_PAYLOAD_LOCK = "payload_lock";
|
||||
constexpr const char *const MQTT_PAYLOAD_LOCATE = "payload_locate";
|
||||
constexpr const char *const MQTT_PAYLOAD_LOCK = "payload_lock";
|
||||
constexpr const char *const MQTT_PAYLOAD_LOW_SPEED = "payload_low_speed";
|
||||
constexpr const char *const MQTT_PAYLOAD_MEDIUM_SPEED = "payload_medium_speed";
|
||||
constexpr const char *const MQTT_PAYLOAD_NOT_AVAILABLE = "payload_not_available";
|
||||
@ -408,20 +414,26 @@ constexpr const char *const MQTT_PAYLOAD_RESET_HUMIDITY = "payload_reset_humidit
|
||||
constexpr const char *const MQTT_PAYLOAD_RESET_MODE = "payload_reset_mode";
|
||||
constexpr const char *const MQTT_PAYLOAD_RESET_PERCENTAGE = "payload_reset_percentage";
|
||||
constexpr const char *const MQTT_PAYLOAD_RESET_PRESET_MODE = "payload_reset_preset_mode";
|
||||
constexpr const char *const MQTT_PAYLOAD_STOP = "payload_stop";
|
||||
constexpr const char *const MQTT_PAYLOAD_RETURN_TO_BASE = "payload_return_to_base";
|
||||
constexpr const char *const MQTT_PAYLOAD_START = "payload_start";
|
||||
constexpr const char *const MQTT_PAYLOAD_START_PAUSE = "payload_start_pause";
|
||||
constexpr const char *const MQTT_PAYLOAD_RETURN_TO_BASE = "payload_return_to_base";
|
||||
constexpr const char *const MQTT_PAYLOAD_STOP = "payload_stop";
|
||||
constexpr const char *const MQTT_PAYLOAD_TURN_OFF = "payload_turn_off";
|
||||
constexpr const char *const MQTT_PAYLOAD_TURN_ON = "payload_turn_on";
|
||||
constexpr const char *const MQTT_PAYLOAD_UNLOCK = "payload_unlock";
|
||||
constexpr const char *const MQTT_PERCENTAGE_COMMAND_TEMPLATE = "percentage_command_template";
|
||||
constexpr const char *const MQTT_PERCENTAGE_COMMAND_TOPIC = "percentage_command_topic";
|
||||
constexpr const char *const MQTT_PERCENTAGE_STATE_TOPIC = "percentage_state_topic";
|
||||
constexpr const char *const MQTT_PERCENTAGE_VALUE_TEMPLATE = "percentage_value_template";
|
||||
constexpr const char *const MQTT_POSITION_CLOSED = "position_closed";
|
||||
constexpr const char *const MQTT_POSITION_OPEN = "position_open";
|
||||
constexpr const char *const MQTT_POSITION_TEMPLATE = "position_template";
|
||||
constexpr const char *const MQTT_POSITION_TOPIC = "position_topic";
|
||||
constexpr const char *const MQTT_POWER_COMMAND_TOPIC = "power_command_topic";
|
||||
constexpr const char *const MQTT_POWER_STATE_TOPIC = "power_state_topic";
|
||||
constexpr const char *const MQTT_POWER_STATE_TEMPLATE = "power_state_template";
|
||||
constexpr const char *const MQTT_PRESET_MODE_COMMAND_TOPIC = "preset_mode_command_topic";
|
||||
constexpr const char *const MQTT_POWER_STATE_TOPIC = "power_state_topic";
|
||||
constexpr const char *const MQTT_PRESET_MODE_COMMAND_TEMPLATE = "preset_mode_command_template";
|
||||
constexpr const char *const MQTT_PRESET_MODE_COMMAND_TOPIC = "preset_mode_command_topic";
|
||||
constexpr const char *const MQTT_PRESET_MODE_STATE_TOPIC = "preset_mode_state_topic";
|
||||
constexpr const char *const MQTT_PRESET_MODE_VALUE_TEMPLATE = "preset_mode_value_template";
|
||||
constexpr const char *const MQTT_PRESET_MODES = "preset_modes";
|
||||
@ -444,36 +456,38 @@ constexpr const char *const MQTT_SEND_IF_OFF = "send_if_off";
|
||||
constexpr const char *const MQTT_SET_FAN_SPEED_TOPIC = "set_fan_speed_topic";
|
||||
constexpr const char *const MQTT_SET_POSITION_TEMPLATE = "set_position_template";
|
||||
constexpr const char *const MQTT_SET_POSITION_TOPIC = "set_position_topic";
|
||||
constexpr const char *const MQTT_POSITION_TOPIC = "position_topic";
|
||||
constexpr const char *const MQTT_POSITION_TEMPLATE = "position_template";
|
||||
constexpr const char *const MQTT_SOURCE_TYPE = "source_type";
|
||||
constexpr const char *const MQTT_SPEED_COMMAND_TOPIC = "speed_command_topic";
|
||||
constexpr const char *const MQTT_SPEED_STATE_TOPIC = "speed_state_topic";
|
||||
constexpr const char *const MQTT_SPEED_RANGE_MIN = "speed_range_min";
|
||||
constexpr const char *const MQTT_SPEED_RANGE_MAX = "speed_range_max";
|
||||
constexpr const char *const MQTT_SPEED_RANGE_MIN = "speed_range_min";
|
||||
constexpr const char *const MQTT_SPEED_STATE_TOPIC = "speed_state_topic";
|
||||
constexpr const char *const MQTT_SPEED_VALUE_TEMPLATE = "speed_value_template";
|
||||
constexpr const char *const MQTT_SPEEDS = "speeds";
|
||||
constexpr const char *const MQTT_SOURCE_TYPE = "source_type";
|
||||
constexpr const char *const MQTT_STATE_CLASS = "state_class";
|
||||
constexpr const char *const MQTT_STATE_CLOSED = "state_closed";
|
||||
constexpr const char *const MQTT_STATE_CLOSING = "state_closing";
|
||||
constexpr const char *const MQTT_STATE_LOCKED = "state_locked";
|
||||
constexpr const char *const MQTT_STATE_OFF = "state_off";
|
||||
constexpr const char *const MQTT_STATE_ON = "state_on";
|
||||
constexpr const char *const MQTT_STATE_OPEN = "state_open";
|
||||
constexpr const char *const MQTT_STATE_OPENING = "state_opening";
|
||||
constexpr const char *const MQTT_STATE_STOPPED = "state_stopped";
|
||||
constexpr const char *const MQTT_STATE_LOCKED = "state_locked";
|
||||
constexpr const char *const MQTT_STATE_UNLOCKED = "state_unlocked";
|
||||
constexpr const char *const MQTT_STATE_TOPIC = "state_topic";
|
||||
constexpr const char *const MQTT_STATE_TEMPLATE = "state_template";
|
||||
constexpr const char *const MQTT_STATE_TOPIC = "state_topic";
|
||||
constexpr const char *const MQTT_STATE_UNLOCKED = "state_unlocked";
|
||||
constexpr const char *const MQTT_STATE_VALUE_TEMPLATE = "state_value_template";
|
||||
constexpr const char *const MQTT_STEP = "step";
|
||||
constexpr const char *const MQTT_SUBTYPE = "subtype";
|
||||
constexpr const char *const MQTT_SUPPORTED_FEATURES = "supported_features";
|
||||
constexpr const char *const MQTT_SUPPORTED_COLOR_MODES = "supported_color_modes";
|
||||
constexpr const char *const MQTT_SUPPORTED_FEATURES = "supported_features";
|
||||
constexpr const char *const MQTT_SWING_MODE_COMMAND_TEMPLATE = "swing_mode_command_template";
|
||||
constexpr const char *const MQTT_SWING_MODE_COMMAND_TOPIC = "swing_mode_command_topic";
|
||||
constexpr const char *const MQTT_SWING_MODE_STATE_TEMPLATE = "swing_mode_state_template";
|
||||
constexpr const char *const MQTT_SWING_MODE_STATE_TOPIC = "swing_mode_state_topic";
|
||||
constexpr const char *const MQTT_TARGET_HUMIDITY_COMMAND_TEMPLATE = "target_humidity_command_template";
|
||||
constexpr const char *const MQTT_TARGET_HUMIDITY_COMMAND_TOPIC = "target_humidity_command_topic";
|
||||
constexpr const char *const MQTT_TARGET_HUMIDITY_STATE_TEMPLATE = "target_humidity_state_template";
|
||||
constexpr const char *const MQTT_TARGET_HUMIDITY_STATE_TOPIC = "target_humidity_state_topic";
|
||||
constexpr const char *const MQTT_TEMPERATURE_COMMAND_TEMPLATE = "temperature_command_template";
|
||||
constexpr const char *const MQTT_TEMPERATURE_COMMAND_TOPIC = "temperature_command_topic";
|
||||
constexpr const char *const MQTT_TEMPERATURE_HIGH_COMMAND_TEMPLATE = "temperature_high_command_template";
|
||||
@ -488,15 +502,15 @@ constexpr const char *const MQTT_TEMPERATURE_STATE_TEMPLATE = "temperature_state
|
||||
constexpr const char *const MQTT_TEMPERATURE_STATE_TOPIC = "temperature_state_topic";
|
||||
constexpr const char *const MQTT_TEMPERATURE_UNIT = "temperature_unit";
|
||||
constexpr const char *const MQTT_TILT_CLOSED_VALUE = "tilt_closed_value";
|
||||
constexpr const char *const MQTT_TILT_COMMAND_TOPIC = "tilt_command_topic";
|
||||
constexpr const char *const MQTT_TILT_COMMAND_TEMPLATE = "tilt_command_template";
|
||||
constexpr const char *const MQTT_TILT_COMMAND_TOPIC = "tilt_command_topic";
|
||||
constexpr const char *const MQTT_TILT_INVERT_STATE = "tilt_invert_state";
|
||||
constexpr const char *const MQTT_TILT_MAX = "tilt_max";
|
||||
constexpr const char *const MQTT_TILT_MIN = "tilt_min";
|
||||
constexpr const char *const MQTT_TILT_OPENED_VALUE = "tilt_opened_value";
|
||||
constexpr const char *const MQTT_TILT_OPTIMISTIC = "tilt_optimistic";
|
||||
constexpr const char *const MQTT_TILT_STATUS_TOPIC = "tilt_status_topic";
|
||||
constexpr const char *const MQTT_TILT_STATUS_TEMPLATE = "tilt_status_template";
|
||||
constexpr const char *const MQTT_TILT_STATUS_TOPIC = "tilt_status_topic";
|
||||
constexpr const char *const MQTT_TOPIC = "topic";
|
||||
constexpr const char *const MQTT_UNIQUE_ID = "unique_id";
|
||||
constexpr const char *const MQTT_UNIT_OF_MEASUREMENT = "unit_of_measurement";
|
||||
@ -511,19 +525,8 @@ constexpr const char *const MQTT_XY_COMMAND_TOPIC = "xy_command_topic";
|
||||
constexpr const char *const MQTT_XY_STATE_TOPIC = "xy_state_topic";
|
||||
constexpr const char *const MQTT_XY_VALUE_TEMPLATE = "xy_value_template";
|
||||
|
||||
constexpr const char *const MQTT_DEVICE_CONNECTIONS = "connections";
|
||||
constexpr const char *const MQTT_DEVICE_IDENTIFIERS = "identifiers";
|
||||
constexpr const char *const MQTT_DEVICE_NAME = "name";
|
||||
constexpr const char *const MQTT_DEVICE_MANUFACTURER = "manufacturer";
|
||||
constexpr const char *const MQTT_DEVICE_MODEL = "model";
|
||||
constexpr const char *const MQTT_DEVICE_SW_VERSION = "sw_version";
|
||||
constexpr const char *const MQTT_DEVICE_SUGGESTED_AREA = "suggested_area";
|
||||
#endif
|
||||
|
||||
// Additional MQTT fields where no abbreviation is defined in HA source
|
||||
constexpr const char *const MQTT_ENTITY_CATEGORY = "entity_category";
|
||||
constexpr const char *const MQTT_MODE = "mode";
|
||||
|
||||
} // namespace mqtt
|
||||
} // namespace esphome
|
||||
|
||||
|
84
esphome/components/mqtt/mqtt_datetime.cpp
Normal file
84
esphome/components/mqtt/mqtt_datetime.cpp
Normal file
@ -0,0 +1,84 @@
|
||||
#include "mqtt_datetime.h"
|
||||
|
||||
#include <utility>
|
||||
#include "esphome/core/log.h"
|
||||
|
||||
#include "mqtt_const.h"
|
||||
|
||||
#ifdef USE_MQTT
|
||||
#ifdef USE_DATETIME_TIME
|
||||
|
||||
namespace esphome {
|
||||
namespace mqtt {
|
||||
|
||||
static const char *const TAG = "mqtt.datetime.time";
|
||||
|
||||
using namespace esphome::datetime;
|
||||
|
||||
MQTTDateTimeComponent::MQTTDateTimeComponent(DateTimeEntity *datetime) : datetime_(datetime) {}
|
||||
|
||||
void MQTTDateTimeComponent::setup() {
|
||||
this->subscribe_json(this->get_command_topic_(), [this](const std::string &topic, JsonObject root) {
|
||||
auto call = this->datetime_->make_call();
|
||||
if (root.containsKey("year")) {
|
||||
call.set_year(root["year"]);
|
||||
}
|
||||
if (root.containsKey("month")) {
|
||||
call.set_month(root["month"]);
|
||||
}
|
||||
if (root.containsKey("day")) {
|
||||
call.set_day(root["day"]);
|
||||
}
|
||||
if (root.containsKey("hour")) {
|
||||
call.set_hour(root["hour"]);
|
||||
}
|
||||
if (root.containsKey("minute")) {
|
||||
call.set_minute(root["minute"]);
|
||||
}
|
||||
if (root.containsKey("second")) {
|
||||
call.set_second(root["second"]);
|
||||
}
|
||||
call.perform();
|
||||
});
|
||||
this->datetime_->add_on_state_callback([this]() {
|
||||
this->publish_state(this->datetime_->year, this->datetime_->month, this->datetime_->day, this->datetime_->hour,
|
||||
this->datetime_->minute, this->datetime_->second);
|
||||
});
|
||||
}
|
||||
|
||||
void MQTTDateTimeComponent::dump_config() {
|
||||
ESP_LOGCONFIG(TAG, "MQTT DateTime '%s':", this->datetime_->get_name().c_str());
|
||||
LOG_MQTT_COMPONENT(true, true)
|
||||
}
|
||||
|
||||
std::string MQTTDateTimeComponent::component_type() const { return "datetime"; }
|
||||
const EntityBase *MQTTDateTimeComponent::get_entity() const { return this->datetime_; }
|
||||
|
||||
void MQTTDateTimeComponent::send_discovery(JsonObject root, mqtt::SendDiscoveryConfig &config) {
|
||||
// Nothing extra to add here
|
||||
}
|
||||
bool MQTTDateTimeComponent::send_initial_state() {
|
||||
if (this->datetime_->has_state()) {
|
||||
return this->publish_state(this->datetime_->year, this->datetime_->month, this->datetime_->day,
|
||||
this->datetime_->hour, this->datetime_->minute, this->datetime_->second);
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
bool MQTTDateTimeComponent::publish_state(uint16_t year, uint8_t month, uint8_t day, uint8_t hour, uint8_t minute,
|
||||
uint8_t second) {
|
||||
return this->publish_json(this->get_state_topic_(), [year, month, day, hour, minute, second](JsonObject root) {
|
||||
root["year"] = year;
|
||||
root["month"] = month;
|
||||
root["day"] = day;
|
||||
root["hour"] = hour;
|
||||
root["minute"] = minute;
|
||||
root["second"] = second;
|
||||
});
|
||||
}
|
||||
|
||||
} // namespace mqtt
|
||||
} // namespace esphome
|
||||
|
||||
#endif // USE_DATETIME_TIME
|
||||
#endif // USE_MQTT
|
45
esphome/components/mqtt/mqtt_datetime.h
Normal file
45
esphome/components/mqtt/mqtt_datetime.h
Normal file
@ -0,0 +1,45 @@
|
||||
#pragma once
|
||||
|
||||
#include "esphome/core/defines.h"
|
||||
|
||||
#ifdef USE_MQTT
|
||||
#ifdef USE_DATETIME_TIME
|
||||
|
||||
#include "esphome/components/datetime/datetime_entity.h"
|
||||
#include "mqtt_component.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace mqtt {
|
||||
|
||||
class MQTTDateTimeComponent : public mqtt::MQTTComponent {
|
||||
public:
|
||||
/** Construct this MQTTDateTimeComponent instance with the provided friendly_name and time
|
||||
*
|
||||
* @param time The time entity.
|
||||
*/
|
||||
explicit MQTTDateTimeComponent(datetime::DateTimeEntity *time);
|
||||
|
||||
// ========== INTERNAL METHODS ==========
|
||||
// (In most use cases you won't need these)
|
||||
/// Override setup.
|
||||
void setup() override;
|
||||
void dump_config() override;
|
||||
|
||||
void send_discovery(JsonObject root, mqtt::SendDiscoveryConfig &config) override;
|
||||
|
||||
bool send_initial_state() override;
|
||||
|
||||
bool publish_state(uint16_t year, uint8_t month, uint8_t day, uint8_t hour, uint8_t minute, uint8_t second);
|
||||
|
||||
protected:
|
||||
std::string component_type() const override;
|
||||
const EntityBase *get_entity() const override;
|
||||
|
||||
datetime::DateTimeEntity *datetime_;
|
||||
};
|
||||
|
||||
} // namespace mqtt
|
||||
} // namespace esphome
|
||||
|
||||
#endif // USE_DATETIME_DATE
|
||||
#endif // USE_MQTT
|
54
esphome/components/mqtt/mqtt_event.cpp
Normal file
54
esphome/components/mqtt/mqtt_event.cpp
Normal file
@ -0,0 +1,54 @@
|
||||
#include "mqtt_event.h"
|
||||
#include "esphome/core/log.h"
|
||||
|
||||
#include "mqtt_const.h"
|
||||
|
||||
#ifdef USE_MQTT
|
||||
#ifdef USE_EVENT
|
||||
|
||||
namespace esphome {
|
||||
namespace mqtt {
|
||||
|
||||
static const char *const TAG = "mqtt.event";
|
||||
|
||||
using namespace esphome::event;
|
||||
|
||||
MQTTEventComponent::MQTTEventComponent(event::Event *event) : event_(event) {}
|
||||
|
||||
void MQTTEventComponent::send_discovery(JsonObject root, mqtt::SendDiscoveryConfig &config) {
|
||||
JsonArray event_types = root.createNestedArray(MQTT_EVENT_TYPES);
|
||||
for (const auto &event_type : this->event_->get_event_types())
|
||||
event_types.add(event_type);
|
||||
|
||||
if (!this->event_->get_device_class().empty())
|
||||
root[MQTT_DEVICE_CLASS] = this->event_->get_device_class();
|
||||
|
||||
config.command_topic = false;
|
||||
}
|
||||
|
||||
void MQTTEventComponent::setup() {
|
||||
this->event_->add_on_event_callback([this](const std::string &event_type) { this->publish_event_(event_type); });
|
||||
}
|
||||
|
||||
void MQTTEventComponent::dump_config() {
|
||||
ESP_LOGCONFIG(TAG, "MQTT Event '%s': ", this->event_->get_name().c_str());
|
||||
ESP_LOGCONFIG(TAG, "Event Types: ");
|
||||
for (const auto &event_type : this->event_->get_event_types()) {
|
||||
ESP_LOGCONFIG(TAG, "- %s", event_type.c_str());
|
||||
}
|
||||
LOG_MQTT_COMPONENT(true, true);
|
||||
}
|
||||
|
||||
bool MQTTEventComponent::publish_event_(const std::string &event_type) {
|
||||
return this->publish_json(this->get_state_topic_(),
|
||||
[event_type](JsonObject root) { root[MQTT_EVENT_TYPE] = event_type; });
|
||||
}
|
||||
|
||||
std::string MQTTEventComponent::component_type() const { return "event"; }
|
||||
const EntityBase *MQTTEventComponent::get_entity() const { return this->event_; }
|
||||
|
||||
} // namespace mqtt
|
||||
} // namespace esphome
|
||||
|
||||
#endif
|
||||
#endif // USE_MQTT
|
39
esphome/components/mqtt/mqtt_event.h
Normal file
39
esphome/components/mqtt/mqtt_event.h
Normal file
@ -0,0 +1,39 @@
|
||||
#pragma once
|
||||
|
||||
#include "esphome/core/defines.h"
|
||||
|
||||
#ifdef USE_MQTT
|
||||
#ifdef USE_EVENT
|
||||
|
||||
#include "esphome/components/event/event.h"
|
||||
#include "mqtt_component.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace mqtt {
|
||||
|
||||
class MQTTEventComponent : public mqtt::MQTTComponent {
|
||||
public:
|
||||
explicit MQTTEventComponent(event::Event *event);
|
||||
|
||||
void send_discovery(JsonObject root, mqtt::SendDiscoveryConfig &config) override;
|
||||
|
||||
void setup() override;
|
||||
|
||||
void dump_config() override;
|
||||
|
||||
/// Events do not send a state so just return true.
|
||||
bool send_initial_state() override { return true; }
|
||||
|
||||
protected:
|
||||
bool publish_event_(const std::string &event_type);
|
||||
std::string component_type() const override;
|
||||
const EntityBase *get_entity() const override;
|
||||
|
||||
event::Event *event_;
|
||||
};
|
||||
|
||||
} // namespace mqtt
|
||||
} // namespace esphome
|
||||
|
||||
#endif
|
||||
#endif // USE_MQTT
|
@ -24,8 +24,10 @@ void PVVXDisplay::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t
|
||||
esp_ble_gattc_cb_param_t *param) {
|
||||
switch (event) {
|
||||
case ESP_GATTC_OPEN_EVT:
|
||||
ESP_LOGV(TAG, "[%s] Connected successfully!", this->parent_->address_str().c_str());
|
||||
this->delayed_disconnect_();
|
||||
if (param->open.status == ESP_GATT_OK) {
|
||||
ESP_LOGV(TAG, "[%s] Connected successfully!", this->parent_->address_str().c_str());
|
||||
this->delayed_disconnect_();
|
||||
}
|
||||
break;
|
||||
case ESP_GATTC_DISCONNECT_EVT:
|
||||
ESP_LOGV(TAG, "[%s] Disconnected", this->parent_->address_str().c_str());
|
||||
|
@ -77,8 +77,17 @@ void QMC5883LComponent::dump_config() {
|
||||
float QMC5883LComponent::get_setup_priority() const { return setup_priority::DATA; }
|
||||
void QMC5883LComponent::update() {
|
||||
uint8_t status = false;
|
||||
if (ESPHOME_LOG_LEVEL >= ESPHOME_LOG_LEVEL_DEBUG)
|
||||
this->read_byte(QMC5883L_REGISTER_STATUS, &status);
|
||||
this->read_byte(QMC5883L_REGISTER_STATUS, &status);
|
||||
|
||||
// Always request X,Y,Z regardless if there are sensors for them
|
||||
// to avoid https://github.com/esphome/issues/issues/5731
|
||||
uint16_t raw_x, raw_y, raw_z;
|
||||
if (!this->read_byte_16_(QMC5883L_REGISTER_DATA_X_LSB, &raw_x) ||
|
||||
!this->read_byte_16_(QMC5883L_REGISTER_DATA_Y_LSB, &raw_y) ||
|
||||
!this->read_byte_16_(QMC5883L_REGISTER_DATA_Z_LSB, &raw_z)) {
|
||||
this->status_set_warning();
|
||||
return;
|
||||
}
|
||||
|
||||
float mg_per_bit;
|
||||
switch (this->range_) {
|
||||
@ -93,36 +102,11 @@ void QMC5883LComponent::update() {
|
||||
}
|
||||
|
||||
// in µT
|
||||
float x = NAN, y = NAN, z = NAN;
|
||||
if (this->x_sensor_ != nullptr || this->heading_sensor_ != nullptr) {
|
||||
uint16_t raw_x;
|
||||
if (!this->read_byte_16_(QMC5883L_REGISTER_DATA_X_LSB, &raw_x)) {
|
||||
this->status_set_warning();
|
||||
return;
|
||||
}
|
||||
x = int16_t(raw_x) * mg_per_bit * 0.1f;
|
||||
}
|
||||
if (this->y_sensor_ != nullptr || this->heading_sensor_ != nullptr) {
|
||||
uint16_t raw_y;
|
||||
if (!this->read_byte_16_(QMC5883L_REGISTER_DATA_Y_LSB, &raw_y)) {
|
||||
this->status_set_warning();
|
||||
return;
|
||||
}
|
||||
y = int16_t(raw_y) * mg_per_bit * 0.1f;
|
||||
}
|
||||
if (this->z_sensor_ != nullptr) {
|
||||
uint16_t raw_z;
|
||||
if (!this->read_byte_16_(QMC5883L_REGISTER_DATA_Z_LSB, &raw_z)) {
|
||||
this->status_set_warning();
|
||||
return;
|
||||
}
|
||||
z = int16_t(raw_z) * mg_per_bit * 0.1f;
|
||||
}
|
||||
const float x = int16_t(raw_x) * mg_per_bit * 0.1f;
|
||||
const float y = int16_t(raw_y) * mg_per_bit * 0.1f;
|
||||
const float z = int16_t(raw_z) * mg_per_bit * 0.1f;
|
||||
|
||||
float heading = NAN;
|
||||
if (this->heading_sensor_ != nullptr) {
|
||||
heading = atan2f(0.0f - x, y) * 180.0f / M_PI;
|
||||
}
|
||||
float heading = atan2f(0.0f - x, y) * 180.0f / M_PI;
|
||||
|
||||
float temp = NAN;
|
||||
if (this->temperature_sensor_ != nullptr) {
|
||||
|
@ -881,6 +881,45 @@ async def pronto_action(var, config, args):
|
||||
cg.add(var.set_data(template_))
|
||||
|
||||
|
||||
# Roomba
|
||||
(
|
||||
RoombaData,
|
||||
RoombaBinarySensor,
|
||||
RoombaTrigger,
|
||||
RoombaAction,
|
||||
RoombaDumper,
|
||||
) = declare_protocol("Roomba")
|
||||
ROOMBA_SCHEMA = cv.Schema({cv.Required(CONF_DATA): cv.hex_uint8_t})
|
||||
|
||||
|
||||
@register_binary_sensor("roomba", RoombaBinarySensor, ROOMBA_SCHEMA)
|
||||
def roomba_binary_sensor(var, config):
|
||||
cg.add(
|
||||
var.set_data(
|
||||
cg.StructInitializer(
|
||||
RoombaData,
|
||||
("data", config[CONF_DATA]),
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
@register_trigger("roomba", RoombaTrigger, RoombaData)
|
||||
def roomba_trigger(var, config):
|
||||
pass
|
||||
|
||||
|
||||
@register_dumper("roomba", RoombaDumper)
|
||||
def roomba_dumper(var, config):
|
||||
pass
|
||||
|
||||
|
||||
@register_action("roomba", RoombaAction, ROOMBA_SCHEMA)
|
||||
async def roomba_action(var, config, args):
|
||||
template_ = await cg.templatable(config[CONF_DATA], args, cg.uint8)
|
||||
cg.add(var.set_data(template_))
|
||||
|
||||
|
||||
# Sony
|
||||
SonyData, SonyBinarySensor, SonyTrigger, SonyAction, SonyDumper = declare_protocol(
|
||||
"Sony"
|
||||
|
56
esphome/components/remote_base/roomba_protocol.cpp
Normal file
56
esphome/components/remote_base/roomba_protocol.cpp
Normal file
@ -0,0 +1,56 @@
|
||||
#include "roomba_protocol.h"
|
||||
#include "esphome/core/log.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace remote_base {
|
||||
|
||||
static const char *const TAG = "remote.roomba";
|
||||
|
||||
static const uint8_t NBITS = 8;
|
||||
static const uint32_t BIT_ONE_HIGH_US = 3000;
|
||||
static const uint32_t BIT_ONE_LOW_US = 1000;
|
||||
static const uint32_t BIT_ZERO_HIGH_US = BIT_ONE_LOW_US;
|
||||
static const uint32_t BIT_ZERO_LOW_US = BIT_ONE_HIGH_US;
|
||||
|
||||
void RoombaProtocol::encode(RemoteTransmitData *dst, const RoombaData &data) {
|
||||
dst->set_carrier_frequency(38000);
|
||||
dst->reserve(NBITS * 2u);
|
||||
|
||||
for (uint32_t mask = 1UL << (NBITS - 1); mask != 0; mask >>= 1) {
|
||||
if (data.data & mask) {
|
||||
dst->item(BIT_ONE_HIGH_US, BIT_ONE_LOW_US);
|
||||
} else {
|
||||
dst->item(BIT_ZERO_HIGH_US, BIT_ZERO_LOW_US);
|
||||
}
|
||||
}
|
||||
}
|
||||
optional<RoombaData> RoombaProtocol::decode(RemoteReceiveData src) {
|
||||
RoombaData out{.data = 0};
|
||||
|
||||
for (uint8_t i = 0; i < (NBITS - 1); i++) {
|
||||
out.data <<= 1UL;
|
||||
if (src.expect_item(BIT_ONE_HIGH_US, BIT_ONE_LOW_US)) {
|
||||
out.data |= 1UL;
|
||||
} else if (src.expect_item(BIT_ZERO_HIGH_US, BIT_ZERO_LOW_US)) {
|
||||
out.data |= 0UL;
|
||||
} else {
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
// not possible to measure space on last bit, check only mark
|
||||
out.data <<= 1UL;
|
||||
if (src.expect_mark(BIT_ONE_HIGH_US)) {
|
||||
out.data |= 1UL;
|
||||
} else if (src.expect_mark(BIT_ZERO_HIGH_US)) {
|
||||
out.data |= 0UL;
|
||||
} else {
|
||||
return {};
|
||||
}
|
||||
|
||||
return out;
|
||||
}
|
||||
void RoombaProtocol::dump(const RoombaData &data) { ESP_LOGD(TAG, "Received Roomba: data=0x%02X", data.data); }
|
||||
|
||||
} // namespace remote_base
|
||||
} // namespace esphome
|
35
esphome/components/remote_base/roomba_protocol.h
Normal file
35
esphome/components/remote_base/roomba_protocol.h
Normal file
@ -0,0 +1,35 @@
|
||||
#pragma once
|
||||
|
||||
#include "remote_base.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace remote_base {
|
||||
|
||||
struct RoombaData {
|
||||
uint8_t data;
|
||||
|
||||
bool operator==(const RoombaData &rhs) const { return data == rhs.data; }
|
||||
};
|
||||
|
||||
class RoombaProtocol : public RemoteProtocol<RoombaData> {
|
||||
public:
|
||||
void encode(RemoteTransmitData *dst, const RoombaData &data) override;
|
||||
optional<RoombaData> decode(RemoteReceiveData src) override;
|
||||
void dump(const RoombaData &data) override;
|
||||
};
|
||||
|
||||
DECLARE_REMOTE_PROTOCOL(Roomba)
|
||||
|
||||
template<typename... Ts> class RoombaAction : public RemoteTransmitterActionBase<Ts...> {
|
||||
public:
|
||||
TEMPLATABLE_VALUE(uint8_t, data)
|
||||
|
||||
void encode(RemoteTransmitData *dst, Ts... x) override {
|
||||
RoombaData data{};
|
||||
data.data = this->data_.value(x...);
|
||||
RoombaProtocol().encode(dst, data);
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace remote_base
|
||||
} // namespace esphome
|
@ -15,6 +15,7 @@ from esphome.const import (
|
||||
KEY_TARGET_FRAMEWORK,
|
||||
KEY_TARGET_PLATFORM,
|
||||
PLATFORM_RP2040,
|
||||
CONF_PLATFORM_VERSION,
|
||||
)
|
||||
from esphome.core import CORE, coroutine_with_priority, EsphomeError
|
||||
from esphome.helpers import mkdir_p, write_file, copy_file_if_changed
|
||||
@ -125,8 +126,6 @@ def _parse_platform_version(value):
|
||||
return value
|
||||
|
||||
|
||||
CONF_PLATFORM_VERSION = "platform_version"
|
||||
|
||||
ARDUINO_FRAMEWORK_SCHEMA = cv.All(
|
||||
cv.Schema(
|
||||
{
|
||||
|
@ -31,6 +31,10 @@ TemplateTime = template_ns.class_(
|
||||
"TemplateTime", datetime.TimeEntity, cg.PollingComponent
|
||||
)
|
||||
|
||||
TemplateDateTime = template_ns.class_(
|
||||
"TemplateDateTime", datetime.DateTimeEntity, cg.PollingComponent
|
||||
)
|
||||
|
||||
|
||||
def validate(config):
|
||||
config = config.copy()
|
||||
@ -78,6 +82,13 @@ CONFIG_SCHEMA = cv.All(
|
||||
cv.Optional(CONF_INITIAL_VALUE): cv.date_time(allowed_date=False),
|
||||
}
|
||||
),
|
||||
"DATETIME": datetime.datetime_schema(TemplateDateTime)
|
||||
.extend(_BASE_SCHEMA)
|
||||
.extend(
|
||||
{
|
||||
cv.Optional(CONF_INITIAL_VALUE): cv.date_time(),
|
||||
}
|
||||
),
|
||||
},
|
||||
upper=True,
|
||||
),
|
||||
@ -116,6 +127,17 @@ async def to_code(config):
|
||||
("hour", initial_value[CONF_HOUR]),
|
||||
)
|
||||
cg.add(var.set_initial_value(time_struct))
|
||||
elif config[CONF_TYPE] == "DATETIME":
|
||||
datetime_struct = cg.StructInitializer(
|
||||
cg.ESPTime,
|
||||
("second", initial_value[CONF_SECOND]),
|
||||
("minute", initial_value[CONF_MINUTE]),
|
||||
("hour", initial_value[CONF_HOUR]),
|
||||
("day_of_month", initial_value[CONF_DAY]),
|
||||
("month", initial_value[CONF_MONTH]),
|
||||
("year", initial_value[CONF_YEAR]),
|
||||
)
|
||||
cg.add(var.set_initial_value(datetime_struct))
|
||||
|
||||
if CONF_SET_ACTION in config:
|
||||
await automation.build_automation(
|
||||
|
150
esphome/components/template/datetime/template_datetime.cpp
Normal file
150
esphome/components/template/datetime/template_datetime.cpp
Normal file
@ -0,0 +1,150 @@
|
||||
#include "template_datetime.h"
|
||||
|
||||
#ifdef USE_DATETIME_DATETIME
|
||||
|
||||
#include "esphome/core/log.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace template_ {
|
||||
|
||||
static const char *const TAG = "template.datetime";
|
||||
|
||||
void TemplateDateTime::setup() {
|
||||
if (this->f_.has_value())
|
||||
return;
|
||||
|
||||
ESPTime state{};
|
||||
|
||||
if (!this->restore_value_) {
|
||||
state = this->initial_value_;
|
||||
} else {
|
||||
datetime::DateTimeEntityRestoreState temp;
|
||||
this->pref_ = global_preferences->make_preference<datetime::DateTimeEntityRestoreState>(194434090U ^
|
||||
this->get_object_id_hash());
|
||||
if (this->pref_.load(&temp)) {
|
||||
temp.apply(this);
|
||||
return;
|
||||
} else {
|
||||
// set to inital value if loading from pref failed
|
||||
state = this->initial_value_;
|
||||
}
|
||||
}
|
||||
|
||||
this->year_ = state.year;
|
||||
this->month_ = state.month;
|
||||
this->day_ = state.day_of_month;
|
||||
this->hour_ = state.hour;
|
||||
this->minute_ = state.minute;
|
||||
this->second_ = state.second;
|
||||
this->publish_state();
|
||||
}
|
||||
|
||||
void TemplateDateTime::update() {
|
||||
if (!this->f_.has_value())
|
||||
return;
|
||||
|
||||
auto val = (*this->f_)();
|
||||
if (!val.has_value())
|
||||
return;
|
||||
|
||||
this->year_ = val->year;
|
||||
this->month_ = val->month;
|
||||
this->day_ = val->day_of_month;
|
||||
this->hour_ = val->hour;
|
||||
this->minute_ = val->minute;
|
||||
this->second_ = val->second;
|
||||
this->publish_state();
|
||||
}
|
||||
|
||||
void TemplateDateTime::control(const datetime::DateTimeCall &call) {
|
||||
bool has_year = call.get_year().has_value();
|
||||
bool has_month = call.get_month().has_value();
|
||||
bool has_day = call.get_day().has_value();
|
||||
bool has_hour = call.get_hour().has_value();
|
||||
bool has_minute = call.get_minute().has_value();
|
||||
bool has_second = call.get_second().has_value();
|
||||
|
||||
ESPTime value = {};
|
||||
if (has_year)
|
||||
value.year = *call.get_year();
|
||||
|
||||
if (has_month)
|
||||
value.month = *call.get_month();
|
||||
|
||||
if (has_day)
|
||||
value.day_of_month = *call.get_day();
|
||||
|
||||
if (has_hour)
|
||||
value.hour = *call.get_hour();
|
||||
|
||||
if (has_minute)
|
||||
value.minute = *call.get_minute();
|
||||
|
||||
if (has_second)
|
||||
value.second = *call.get_second();
|
||||
|
||||
this->set_trigger_->trigger(value);
|
||||
|
||||
if (this->optimistic_) {
|
||||
if (has_year)
|
||||
this->year_ = *call.get_year();
|
||||
if (has_month)
|
||||
this->month_ = *call.get_month();
|
||||
if (has_day)
|
||||
this->day_ = *call.get_day();
|
||||
if (has_hour)
|
||||
this->hour_ = *call.get_hour();
|
||||
if (has_minute)
|
||||
this->minute_ = *call.get_minute();
|
||||
if (has_second)
|
||||
this->second_ = *call.get_second();
|
||||
this->publish_state();
|
||||
}
|
||||
|
||||
if (this->restore_value_) {
|
||||
datetime::DateTimeEntityRestoreState temp = {};
|
||||
if (has_year) {
|
||||
temp.year = *call.get_year();
|
||||
} else {
|
||||
temp.year = this->year_;
|
||||
}
|
||||
if (has_month) {
|
||||
temp.month = *call.get_month();
|
||||
} else {
|
||||
temp.month = this->month_;
|
||||
}
|
||||
if (has_day) {
|
||||
temp.day = *call.get_day();
|
||||
} else {
|
||||
temp.day = this->day_;
|
||||
}
|
||||
if (has_hour) {
|
||||
temp.hour = *call.get_hour();
|
||||
} else {
|
||||
temp.hour = this->hour_;
|
||||
}
|
||||
if (has_minute) {
|
||||
temp.minute = *call.get_minute();
|
||||
} else {
|
||||
temp.minute = this->minute_;
|
||||
}
|
||||
if (has_second) {
|
||||
temp.second = *call.get_second();
|
||||
} else {
|
||||
temp.second = this->second_;
|
||||
}
|
||||
|
||||
this->pref_.save(&temp);
|
||||
}
|
||||
}
|
||||
|
||||
void TemplateDateTime::dump_config() {
|
||||
LOG_DATETIME_DATETIME("", "Template DateTime", this);
|
||||
ESP_LOGCONFIG(TAG, " Optimistic: %s", YESNO(this->optimistic_));
|
||||
LOG_UPDATE_INTERVAL(this);
|
||||
}
|
||||
|
||||
} // namespace template_
|
||||
} // namespace esphome
|
||||
|
||||
#endif // USE_DATETIME_DATETIME
|
46
esphome/components/template/datetime/template_datetime.h
Normal file
46
esphome/components/template/datetime/template_datetime.h
Normal file
@ -0,0 +1,46 @@
|
||||
#pragma once
|
||||
|
||||
#include "esphome/core/defines.h"
|
||||
|
||||
#ifdef USE_DATETIME_TIME
|
||||
|
||||
#include "esphome/components/datetime/datetime_entity.h"
|
||||
#include "esphome/core/automation.h"
|
||||
#include "esphome/core/component.h"
|
||||
#include "esphome/core/preferences.h"
|
||||
#include "esphome/core/time.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace template_ {
|
||||
|
||||
class TemplateDateTime : public datetime::DateTimeEntity, public PollingComponent {
|
||||
public:
|
||||
void set_template(std::function<optional<ESPTime>()> &&f) { this->f_ = f; }
|
||||
|
||||
void setup() override;
|
||||
void update() override;
|
||||
void dump_config() override;
|
||||
float get_setup_priority() const override { return setup_priority::HARDWARE; }
|
||||
|
||||
Trigger<ESPTime> *get_set_trigger() const { return this->set_trigger_; }
|
||||
void set_optimistic(bool optimistic) { this->optimistic_ = optimistic; }
|
||||
|
||||
void set_initial_value(ESPTime initial_value) { this->initial_value_ = initial_value; }
|
||||
void set_restore_value(bool restore_value) { this->restore_value_ = restore_value; }
|
||||
|
||||
protected:
|
||||
void control(const datetime::DateTimeCall &call) override;
|
||||
|
||||
bool optimistic_{false};
|
||||
ESPTime initial_value_{};
|
||||
bool restore_value_{false};
|
||||
Trigger<ESPTime> *set_trigger_ = new Trigger<ESPTime>();
|
||||
optional<std::function<optional<ESPTime>()>> f_;
|
||||
|
||||
ESPPreferenceObject pref_;
|
||||
};
|
||||
|
||||
} // namespace template_
|
||||
} // namespace esphome
|
||||
|
||||
#endif // USE_DATETIME_TIME
|
24
esphome/components/template/event/__init__.py
Normal file
24
esphome/components/template/event/__init__.py
Normal file
@ -0,0 +1,24 @@
|
||||
import esphome.config_validation as cv
|
||||
|
||||
from esphome.components import event
|
||||
|
||||
import esphome.codegen as cg
|
||||
|
||||
from esphome.const import CONF_EVENT_TYPES
|
||||
|
||||
from .. import template_ns
|
||||
|
||||
CODEOWNERS = ["@nohat"]
|
||||
|
||||
TemplateEvent = template_ns.class_("TemplateEvent", event.Event, cg.Component)
|
||||
|
||||
CONFIG_SCHEMA = event.event_schema(TemplateEvent).extend(
|
||||
{
|
||||
cv.Required(CONF_EVENT_TYPES): cv.ensure_list(cv.string_strict),
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
async def to_code(config):
|
||||
var = await event.new_event(config, event_types=config[CONF_EVENT_TYPES])
|
||||
await cg.register_component(var, config)
|
12
esphome/components/template/event/template_event.h
Normal file
12
esphome/components/template/event/template_event.h
Normal file
@ -0,0 +1,12 @@
|
||||
#pragma once
|
||||
|
||||
#include "esphome/core/component.h"
|
||||
#include "esphome/components/event/event.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace template_ {
|
||||
|
||||
class TemplateEvent : public Component, public event::Event {};
|
||||
|
||||
} // namespace template_
|
||||
} // namespace esphome
|
@ -13,6 +13,8 @@
|
||||
#endif
|
||||
#include <cerrno>
|
||||
|
||||
#include <cinttypes>
|
||||
|
||||
namespace esphome {
|
||||
namespace time {
|
||||
|
||||
|
@ -23,6 +23,7 @@ class TimeBasedCover : public cover::Cover, public Component {
|
||||
void set_has_built_in_endstop(bool value) { this->has_built_in_endstop_ = value; }
|
||||
void set_manual_control(bool value) { this->manual_control_ = value; }
|
||||
void set_assumed_state(bool value) { this->assumed_state_ = value; }
|
||||
cover::CoverOperation get_last_operation() const { return this->last_operation_; }
|
||||
|
||||
protected:
|
||||
void control(const cover::CoverCall &call) override;
|
||||
|
@ -59,6 +59,7 @@ UARTWriteAction = uart_ns.class_("UARTWriteAction", automation.Action)
|
||||
UARTDebugger = uart_ns.class_("UARTDebugger", cg.Component, automation.Action)
|
||||
UARTDummyReceiver = uart_ns.class_("UARTDummyReceiver", cg.Component)
|
||||
MULTI_CONF = True
|
||||
MULTI_CONF_NO_DEFAULT = True
|
||||
|
||||
|
||||
def validate_raw_data(value):
|
||||
|
@ -49,6 +49,9 @@ WaveshareEPaper2P9InV2R2 = waveshare_epaper_ns.class_(
|
||||
"WaveshareEPaper2P9InV2R2", WaveshareEPaper
|
||||
)
|
||||
GDEY029T94 = waveshare_epaper_ns.class_("GDEY029T94", WaveshareEPaper)
|
||||
WaveshareEPaper2P9InDKE = waveshare_epaper_ns.class_(
|
||||
"WaveshareEPaper2P9InDKE", WaveshareEPaper
|
||||
)
|
||||
WaveshareEPaper4P2In = waveshare_epaper_ns.class_(
|
||||
"WaveshareEPaper4P2In", WaveshareEPaper
|
||||
)
|
||||
@ -115,6 +118,7 @@ MODELS = {
|
||||
"2.90in-b": ("b", WaveshareEPaper2P9InB),
|
||||
"2.90in-bv3": ("b", WaveshareEPaper2P9InBV3),
|
||||
"2.90inv2-r2": ("c", WaveshareEPaper2P9InV2R2),
|
||||
"2.90in-dke": ("c", WaveshareEPaper2P9InDKE),
|
||||
"4.20in": ("b", WaveshareEPaper4P2In),
|
||||
"4.20in-bv2": ("b", WaveshareEPaper4P2InBV2),
|
||||
"5.83in": ("b", WaveshareEPaper5P8In),
|
||||
|
@ -1127,6 +1127,131 @@ void WaveshareEPaper2P9InB::dump_config() {
|
||||
LOG_UPDATE_INTERVAL(this);
|
||||
}
|
||||
|
||||
// DKE 2.9
|
||||
// https://www.badge.team/docs/badges/sha2017/hardware/#e-ink-display-the-dke-group-depg0290b1
|
||||
// https://www.badge.team/docs/badges/sha2017/hardware/DEPG0290B01V3.0.pdf
|
||||
static const uint8_t LUT_SIZE_DKE = 70;
|
||||
static const uint8_t UPDATE_LUT_DKE[LUT_SIZE_DKE] = {
|
||||
0xA0, 0x90, 0x50, 0x0, 0x0, 0x0, 0x0, 0x50, 0x90, 0xA0, 0x0, 0x0, 0x0, 0x0, 0xA0, 0x90, 0x50, 0x0,
|
||||
0x0, 0x0, 0x0, 0x50, 0x90, 0xA0, 0x0, 0x0, 0x0, 0x0, 0x00, 0x00, 0x00, 0x0, 0x0, 0x0, 0x0, 0xF,
|
||||
0xF, 0x0, 0x0, 0x0, 0xF, 0xF, 0x0, 0x0, 0x02, 0xF, 0xF, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
|
||||
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
|
||||
};
|
||||
static const uint8_t PART_UPDATE_LUT_DKE[LUT_SIZE_DKE] = {
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0xa0, 0x80, 0x00, 0x00, 0x00, 0x00, 0x50, 0x10, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03,
|
||||
0x05, 0x00, 0x00, 0x00, 0x01, 0x08, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
|
||||
static const uint8_t FULL_UPDATE_LUT_DKE[LUT_SIZE_DKE] = {
|
||||
0x90, 0x50, 0xa0, 0x50, 0x50, 0x00, 0x00, 0x00, 0x00, 0x10, 0xa0, 0xa0, 0x80, 0x00, 0x90, 0x50, 0xa0, 0x50,
|
||||
0x50, 0x00, 0x00, 0x00, 0x00, 0x10, 0xa0, 0xa0, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x17,
|
||||
0x04, 0x00, 0x00, 0x00, 0x0b, 0x04, 0x00, 0x00, 0x00, 0x06, 0x05, 0x00, 0x00, 0x00, 0x04, 0x05, 0x00, 0x00,
|
||||
0x00, 0x01, 0x0e, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
|
||||
|
||||
void WaveshareEPaper2P9InDKE::initialize() {
|
||||
// Hardware reset
|
||||
delay(10);
|
||||
this->reset_pin_->digital_write(false);
|
||||
delayMicroseconds(200);
|
||||
this->reset_pin_->digital_write(true);
|
||||
delayMicroseconds(200);
|
||||
// Wait for busy low
|
||||
this->wait_until_idle_();
|
||||
// Software reset
|
||||
this->command(0x12);
|
||||
// Wait for busy low
|
||||
this->wait_until_idle_();
|
||||
// Set Analog Block Control
|
||||
this->command(0x74);
|
||||
this->data(0x54);
|
||||
// Set Digital Block Control
|
||||
this->command(0x7E);
|
||||
this->data(0x3B);
|
||||
// Set display size and driver output control
|
||||
this->command(0x01);
|
||||
// this->data(0x27);
|
||||
// this->data(0x01);
|
||||
// this->data(0x00);
|
||||
this->data(this->get_height_internal() - 1);
|
||||
this->data((this->get_height_internal() - 1) >> 8);
|
||||
this->data(0x00); // ? GD = 0, SM = 0, TB = 0
|
||||
// Ram data entry mode
|
||||
this->command(0x11);
|
||||
this->data(0x03);
|
||||
// Set Ram X address
|
||||
this->command(0x44);
|
||||
this->data(0x00);
|
||||
this->data(0x0F);
|
||||
// Set Ram Y address
|
||||
this->command(0x45);
|
||||
this->data(0x00);
|
||||
this->data(0x00);
|
||||
this->data(0x27);
|
||||
this->data(0x01);
|
||||
// Set border
|
||||
this->command(0x3C);
|
||||
// this->data(0x80);
|
||||
this->data(0x01);
|
||||
// Set VCOM value
|
||||
this->command(0x2C);
|
||||
this->data(0x26);
|
||||
// Gate voltage setting
|
||||
this->command(0x03);
|
||||
this->data(0x17);
|
||||
// Source voltage setting
|
||||
this->command(0x04);
|
||||
this->data(0x41);
|
||||
this->data(0x00);
|
||||
this->data(0x32);
|
||||
// Frame setting 50hz
|
||||
this->command(0x3A);
|
||||
this->data(0x30);
|
||||
this->command(0x3B);
|
||||
this->data(0x0A);
|
||||
// Load LUT
|
||||
this->command(0x32);
|
||||
for (uint8_t v : FULL_UPDATE_LUT_DKE)
|
||||
this->data(v);
|
||||
}
|
||||
|
||||
void HOT WaveshareEPaper2P9InDKE::display() {
|
||||
ESP_LOGI(TAG, "Performing e-paper update.");
|
||||
// Set Ram X address counter
|
||||
this->command(0x4e);
|
||||
this->data(0);
|
||||
// Set Ram Y address counter
|
||||
this->command(0x4f);
|
||||
this->data(0);
|
||||
this->data(0);
|
||||
// Load image (128/8*296)
|
||||
this->command(0x24);
|
||||
this->start_data_();
|
||||
this->write_array(this->buffer_, this->get_buffer_length_());
|
||||
this->end_data_();
|
||||
// Image update
|
||||
this->command(0x22);
|
||||
this->data(0xC7);
|
||||
this->command(0x20);
|
||||
// Wait for busy low
|
||||
this->wait_until_idle_();
|
||||
// Enter deep sleep mode
|
||||
this->command(0x10);
|
||||
this->data(0x01);
|
||||
}
|
||||
int WaveshareEPaper2P9InDKE::get_width_internal() { return 128; }
|
||||
int WaveshareEPaper2P9InDKE::get_height_internal() { return 296; }
|
||||
void WaveshareEPaper2P9InDKE::dump_config() {
|
||||
LOG_DISPLAY("", "Waveshare E-Paper", this);
|
||||
ESP_LOGCONFIG(TAG, " Model: 2.9in DKE");
|
||||
LOG_PIN(" Reset Pin: ", this->reset_pin_);
|
||||
LOG_PIN(" DC Pin: ", this->dc_pin_);
|
||||
LOG_PIN(" Busy Pin: ", this->busy_pin_);
|
||||
LOG_UPDATE_INTERVAL(this);
|
||||
}
|
||||
void WaveshareEPaper2P9InDKE::set_full_update_every(uint32_t full_update_every) {
|
||||
this->full_update_every_ = full_update_every;
|
||||
}
|
||||
|
||||
// ========================================================
|
||||
// 2.90in Type B (LUT from OTP)
|
||||
// Datasheet:
|
||||
|
@ -373,6 +373,30 @@ class WaveshareEPaper2P9InV2R2 : public WaveshareEPaper {
|
||||
void reset_();
|
||||
};
|
||||
|
||||
class WaveshareEPaper2P9InDKE : public WaveshareEPaper {
|
||||
public:
|
||||
void initialize() override;
|
||||
|
||||
void display() override;
|
||||
|
||||
void dump_config() override;
|
||||
|
||||
void deep_sleep() override {
|
||||
// COMMAND DEEP SLEEP
|
||||
this->command(0x10);
|
||||
this->data(0x01);
|
||||
}
|
||||
|
||||
void set_full_update_every(uint32_t full_update_every);
|
||||
|
||||
protected:
|
||||
uint32_t full_update_every_{30};
|
||||
uint32_t at_update_{0};
|
||||
int get_width_internal() override;
|
||||
|
||||
int get_height_internal() override;
|
||||
};
|
||||
|
||||
class WaveshareEPaper4P2In : public WaveshareEPaper {
|
||||
public:
|
||||
void initialize() override;
|
||||
|
@ -129,6 +129,15 @@ bool ListEntitiesIterator::on_time(datetime::TimeEntity *time) {
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef USE_DATETIME_DATETIME
|
||||
bool ListEntitiesIterator::on_datetime(datetime::DateTimeEntity *datetime) {
|
||||
if (this->web_server_->events_.count() == 0)
|
||||
return true;
|
||||
this->web_server_->events_.send(this->web_server_->datetime_json(datetime, DETAIL_ALL).c_str(), "state");
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef USE_TEXT
|
||||
bool ListEntitiesIterator::on_text(text::Text *text) {
|
||||
if (this->web_server_->events_.count() == 0)
|
||||
@ -159,5 +168,14 @@ bool ListEntitiesIterator::on_alarm_control_panel(alarm_control_panel::AlarmCont
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef USE_EVENT
|
||||
bool ListEntitiesIterator::on_event(event::Event *event) {
|
||||
// Null event type, since we are just iterating over entities
|
||||
const std::string null_event_type = "";
|
||||
this->web_server_->events_.send(this->web_server_->event_json(event, null_event_type, DETAIL_ALL).c_str(), "state");
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
|
||||
} // namespace web_server
|
||||
} // namespace esphome
|
||||
|
@ -47,6 +47,9 @@ class ListEntitiesIterator : public ComponentIterator {
|
||||
#ifdef USE_DATETIME_TIME
|
||||
bool on_time(datetime::TimeEntity *time) override;
|
||||
#endif
|
||||
#ifdef USE_DATETIME_DATETIME
|
||||
bool on_datetime(datetime::DateTimeEntity *datetime) override;
|
||||
#endif
|
||||
#ifdef USE_TEXT
|
||||
bool on_text(text::Text *text) override;
|
||||
#endif
|
||||
@ -62,6 +65,9 @@ class ListEntitiesIterator : public ComponentIterator {
|
||||
#ifdef USE_ALARM_CONTROL_PANEL
|
||||
bool on_alarm_control_panel(alarm_control_panel::AlarmControlPanel *a_alarm_control_panel) override;
|
||||
#endif
|
||||
#ifdef USE_EVENT
|
||||
bool on_event(event::Event *event) override;
|
||||
#endif
|
||||
|
||||
protected:
|
||||
WebServer *web_server_;
|
||||
|
@ -926,6 +926,8 @@ std::string WebServer::date_json(datetime::DateEntity *obj, JsonDetail start_con
|
||||
|
||||
#ifdef USE_DATETIME_TIME
|
||||
void WebServer::on_time_update(datetime::TimeEntity *obj) {
|
||||
if (this->events_.count() == 0)
|
||||
return;
|
||||
this->events_.send(this->time_json(obj, DETAIL_STATE).c_str(), "state");
|
||||
}
|
||||
void WebServer::handle_time_request(AsyncWebServerRequest *request, const UrlMatch &match) {
|
||||
@ -970,6 +972,55 @@ std::string WebServer::time_json(datetime::TimeEntity *obj, JsonDetail start_con
|
||||
}
|
||||
#endif // USE_DATETIME_TIME
|
||||
|
||||
#ifdef USE_DATETIME_DATETIME
|
||||
void WebServer::on_datetime_update(datetime::DateTimeEntity *obj) {
|
||||
if (this->events_.count() == 0)
|
||||
return;
|
||||
this->events_.send(this->datetime_json(obj, DETAIL_STATE).c_str(), "state");
|
||||
}
|
||||
void WebServer::handle_datetime_request(AsyncWebServerRequest *request, const UrlMatch &match) {
|
||||
for (auto *obj : App.get_datetimes()) {
|
||||
if (obj->get_object_id() != match.id)
|
||||
continue;
|
||||
if (request->method() == HTTP_GET && match.method.empty()) {
|
||||
std::string data = this->datetime_json(obj, DETAIL_STATE);
|
||||
request->send(200, "application/json", data.c_str());
|
||||
return;
|
||||
}
|
||||
if (match.method != "set") {
|
||||
request->send(404);
|
||||
return;
|
||||
}
|
||||
|
||||
auto call = obj->make_call();
|
||||
|
||||
if (!request->hasParam("value")) {
|
||||
request->send(409);
|
||||
return;
|
||||
}
|
||||
|
||||
if (request->hasParam("value")) {
|
||||
std::string value = request->getParam("value")->value().c_str();
|
||||
call.set_datetime(value);
|
||||
}
|
||||
|
||||
this->schedule_([call]() mutable { call.perform(); });
|
||||
request->send(200);
|
||||
return;
|
||||
}
|
||||
request->send(404);
|
||||
}
|
||||
std::string WebServer::datetime_json(datetime::DateTimeEntity *obj, JsonDetail start_config) {
|
||||
return json::build_json([obj, start_config](JsonObject root) {
|
||||
set_json_id(root, obj, "datetime-" + obj->get_object_id(), start_config);
|
||||
std::string value = str_sprintf("%d-%02d-%02d %02d:%02d:%02d", obj->year, obj->month, obj->day, obj->hour,
|
||||
obj->minute, obj->second);
|
||||
root["value"] = value;
|
||||
root["state"] = value;
|
||||
});
|
||||
}
|
||||
#endif // USE_DATETIME_DATETIME
|
||||
|
||||
#ifdef USE_TEXT
|
||||
void WebServer::on_text_update(text::Text *obj, const std::string &state) {
|
||||
if (this->events_.count() == 0)
|
||||
@ -1352,6 +1403,28 @@ void WebServer::handle_alarm_control_panel_request(AsyncWebServerRequest *reques
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef USE_EVENT
|
||||
void WebServer::on_event(event::Event *obj, const std::string &event_type) {
|
||||
this->events_.send(this->event_json(obj, event_type, DETAIL_STATE).c_str(), "state");
|
||||
}
|
||||
|
||||
std::string WebServer::event_json(event::Event *obj, const std::string &event_type, JsonDetail start_config) {
|
||||
return json::build_json([obj, event_type, start_config](JsonObject root) {
|
||||
set_json_id(root, obj, "event-" + obj->get_object_id(), start_config);
|
||||
if (!event_type.empty()) {
|
||||
root["event_type"] = event_type;
|
||||
}
|
||||
if (start_config == DETAIL_ALL) {
|
||||
JsonArray event_types = root.createNestedArray("event_types");
|
||||
for (auto const &event_type : obj->get_event_types()) {
|
||||
event_types.add(event_type);
|
||||
}
|
||||
root["device_class"] = obj->get_device_class();
|
||||
}
|
||||
});
|
||||
}
|
||||
#endif
|
||||
|
||||
bool WebServer::canHandle(AsyncWebServerRequest *request) {
|
||||
if (request->url() == "/")
|
||||
return true;
|
||||
@ -1436,6 +1509,11 @@ bool WebServer::canHandle(AsyncWebServerRequest *request) {
|
||||
return true;
|
||||
#endif
|
||||
|
||||
#ifdef USE_DATETIME_DATETIME
|
||||
if ((request->method() == HTTP_POST || request->method() == HTTP_GET) && match.domain == "datetime")
|
||||
return true;
|
||||
#endif
|
||||
|
||||
#ifdef USE_TEXT
|
||||
if ((request->method() == HTTP_POST || request->method() == HTTP_GET) && match.domain == "text")
|
||||
return true;
|
||||
@ -1573,6 +1651,13 @@ void WebServer::handleRequest(AsyncWebServerRequest *request) {
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef USE_DATETIME_DATETIME
|
||||
if (match.domain == "datetime") {
|
||||
this->handle_datetime_request(request, match);
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef USE_TEXT
|
||||
if (match.domain == "text") {
|
||||
this->handle_text_request(request, match);
|
||||
|
@ -239,6 +239,15 @@ class WebServer : public Controller, public Component, public AsyncWebHandler {
|
||||
std::string time_json(datetime::TimeEntity *obj, JsonDetail start_config);
|
||||
#endif
|
||||
|
||||
#ifdef USE_DATETIME_DATETIME
|
||||
void on_datetime_update(datetime::DateTimeEntity *obj) override;
|
||||
/// Handle a datetime request under '/datetime/<id>'.
|
||||
void handle_datetime_request(AsyncWebServerRequest *request, const UrlMatch &match);
|
||||
|
||||
/// Dump the datetime state with its value as a JSON string.
|
||||
std::string datetime_json(datetime::DateTimeEntity *obj, JsonDetail start_config);
|
||||
#endif
|
||||
|
||||
#ifdef USE_TEXT
|
||||
void on_text_update(text::Text *obj, const std::string &state) override;
|
||||
/// Handle a text input request under '/text/<id>'.
|
||||
@ -297,6 +306,13 @@ class WebServer : public Controller, public Component, public AsyncWebHandler {
|
||||
alarm_control_panel::AlarmControlPanelState value, JsonDetail start_config);
|
||||
#endif
|
||||
|
||||
#ifdef USE_EVENT
|
||||
void on_event(event::Event *obj, const std::string &event_type) override;
|
||||
|
||||
/// Dump the event details with its value as a JSON string.
|
||||
std::string event_json(event::Event *obj, const std::string &event_type, JsonDetail start_config);
|
||||
#endif
|
||||
|
||||
/// Override the web handler's canHandle method.
|
||||
bool canHandle(AsyncWebServerRequest *request) override;
|
||||
/// Override the web handler's handleRequest method.
|
||||
|
108
esphome/components/weikai/__init__.py
Normal file
108
esphome/components/weikai/__init__.py
Normal file
@ -0,0 +1,108 @@
|
||||
import esphome.codegen as cg
|
||||
import esphome.config_validation as cv
|
||||
from esphome.components import uart
|
||||
from esphome.const import (
|
||||
CONF_BAUD_RATE,
|
||||
CONF_CHANNEL,
|
||||
CONF_ID,
|
||||
CONF_INPUT,
|
||||
CONF_INVERTED,
|
||||
CONF_MODE,
|
||||
CONF_NUMBER,
|
||||
CONF_OUTPUT,
|
||||
)
|
||||
|
||||
CODEOWNERS = ["@DrCoolZic"]
|
||||
AUTO_LOAD = ["uart"]
|
||||
|
||||
MULTI_CONF = True
|
||||
CONF_STOP_BITS = "stop_bits"
|
||||
CONF_PARITY = "parity"
|
||||
CONF_CRYSTAL = "crystal"
|
||||
CONF_UART = "uart"
|
||||
CONF_TEST_MODE = "test_mode"
|
||||
|
||||
weikai_ns = cg.esphome_ns.namespace("weikai")
|
||||
WeikaiComponent = weikai_ns.class_("WeikaiComponent", cg.Component)
|
||||
WeikaiChannel = weikai_ns.class_("WeikaiChannel", uart.UARTComponent)
|
||||
|
||||
|
||||
def check_channel_max(value, max):
|
||||
channel_uniq = []
|
||||
channel_dup = []
|
||||
for x in value[CONF_UART]:
|
||||
if x[CONF_CHANNEL] > max - 1:
|
||||
raise cv.Invalid(f"Invalid channel number: {x[CONF_CHANNEL]}")
|
||||
if x[CONF_CHANNEL] not in channel_uniq:
|
||||
channel_uniq.append(x[CONF_CHANNEL])
|
||||
else:
|
||||
channel_dup.append(x[CONF_CHANNEL])
|
||||
if len(channel_dup) > 0:
|
||||
raise cv.Invalid(f"Duplicate channel list: {channel_dup}")
|
||||
return value
|
||||
|
||||
|
||||
def check_channel_max_4(value):
|
||||
return check_channel_max(value, 4)
|
||||
|
||||
|
||||
def check_channel_max_2(value):
|
||||
return check_channel_max(value, 2)
|
||||
|
||||
|
||||
WKBASE_SCHEMA = cv.Schema(
|
||||
{
|
||||
cv.GenerateID(): cv.declare_id(WeikaiComponent),
|
||||
cv.Optional(CONF_CRYSTAL, default=14745600): cv.int_,
|
||||
cv.Optional(CONF_TEST_MODE, default=0): cv.int_,
|
||||
cv.Required(CONF_UART): cv.ensure_list(
|
||||
{
|
||||
cv.Required(CONF_ID): cv.declare_id(WeikaiChannel),
|
||||
cv.Optional(CONF_CHANNEL, default=0): cv.int_range(min=0, max=3),
|
||||
cv.Required(CONF_BAUD_RATE): cv.int_range(min=1),
|
||||
cv.Optional(CONF_STOP_BITS, default=1): cv.one_of(1, 2, int=True),
|
||||
cv.Optional(CONF_PARITY, default="NONE"): cv.enum(
|
||||
uart.UART_PARITY_OPTIONS, upper=True
|
||||
),
|
||||
}
|
||||
),
|
||||
}
|
||||
).extend(cv.COMPONENT_SCHEMA)
|
||||
|
||||
|
||||
async def register_weikai(var, config):
|
||||
"""Register an weikai device with the given config."""
|
||||
cg.add(var.set_crystal(config[CONF_CRYSTAL]))
|
||||
cg.add(var.set_test_mode(config[CONF_TEST_MODE]))
|
||||
await cg.register_component(var, config)
|
||||
for uart_elem in config[CONF_UART]:
|
||||
chan = cg.new_Pvariable(uart_elem[CONF_ID])
|
||||
cg.add(chan.set_channel_name(str(uart_elem[CONF_ID])))
|
||||
cg.add(chan.set_parent(var))
|
||||
cg.add(chan.set_channel(uart_elem[CONF_CHANNEL]))
|
||||
cg.add(chan.set_baud_rate(uart_elem[CONF_BAUD_RATE]))
|
||||
cg.add(chan.set_stop_bits(uart_elem[CONF_STOP_BITS]))
|
||||
cg.add(chan.set_parity(uart_elem[CONF_PARITY]))
|
||||
|
||||
|
||||
def validate_pin_mode(value):
|
||||
"""Checks input/output mode inconsistency"""
|
||||
if not (value[CONF_MODE][CONF_INPUT] or value[CONF_MODE][CONF_OUTPUT]):
|
||||
raise cv.Invalid("Mode must be either input or output")
|
||||
if value[CONF_MODE][CONF_INPUT] and value[CONF_MODE][CONF_OUTPUT]:
|
||||
raise cv.Invalid("Mode must be either input or output")
|
||||
return value
|
||||
|
||||
|
||||
WEIKAI_PIN_SCHEMA = cv.Schema(
|
||||
{
|
||||
cv.Required(CONF_NUMBER): cv.int_range(min=0, max=7),
|
||||
cv.Optional(CONF_MODE, default={}): cv.All(
|
||||
{
|
||||
cv.Optional(CONF_INPUT, default=False): cv.boolean,
|
||||
cv.Optional(CONF_OUTPUT, default=False): cv.boolean,
|
||||
},
|
||||
),
|
||||
cv.Optional(CONF_INVERTED, default=False): cv.boolean,
|
||||
}
|
||||
)
|
615
esphome/components/weikai/weikai.cpp
Normal file
615
esphome/components/weikai/weikai.cpp
Normal file
@ -0,0 +1,615 @@
|
||||
/// @file weikai.cpp
|
||||
/// @brief WeiKai component family - classes implementation
|
||||
/// @date Last Modified: 2024/04/06 15:13:11
|
||||
/// @details The classes declared in this file can be used by the Weikai family
|
||||
|
||||
#include "weikai.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace weikai {
|
||||
|
||||
/*! @mainpage Weikai source code documentation
|
||||
This documentation provides information about the implementation of the family of WeiKai Components in ESPHome.
|
||||
Here is the class diagram related to Weikai family of components:
|
||||
@image html weikai_class.png
|
||||
|
||||
@section WKRingBuffer_ The WKRingBuffer template class
|
||||
The WKRingBuffer template class has it names implies implement a simple ring buffer helper class. This straightforward
|
||||
container implements FIFO functionality, enabling bytes to be pushed into one side and popped from the other in the
|
||||
order of entry. Implementation is classic and therefore not described in any details.
|
||||
|
||||
@section WeikaiRegister_ The WeikaiRegister class
|
||||
The WeikaiRegister helper class creates objects that act as proxies to the device registers.
|
||||
@details This is an abstract virtual class (interface) that provides all the necessary access to registers while hiding
|
||||
the actual implementation. The access to the registers can be made through an I²C bus in for example for wk2168_i2c
|
||||
component or through a SPI bus for example in the case of the wk2168_spi component. Derived classes will actually
|
||||
performs the specific bus operations.
|
||||
|
||||
@section WeikaiRegisterI2C_ WeikaiRegisterI2C
|
||||
The weikai_i2c::WeikaiRegisterI2C class implements the virtual methods of the WeikaiRegister class for an I2C bus.
|
||||
|
||||
@section WeikaiRegisterSPI_ WeikaiRegisterSPI
|
||||
The weikai_spi::WeikaiRegisterSPI class implements the virtual methods of the WeikaiRegister class for an SPI bus.
|
||||
|
||||
@section WeikaiComponent_ The WeikaiComponent class
|
||||
The WeikaiComponent class stores the information global to a WeiKai family component and provides methods to set/access
|
||||
this information. It also serves as a container for WeikaiChannel instances. This is done by maintaining an array of
|
||||
references these WeikaiChannel instances. This class derives from the esphome::Component classes. This class override
|
||||
esphome::Component::loop() method to facilitate the seamless transfer of accumulated bytes from the receive
|
||||
FIFO into the ring buffer. This process ensures quick access to the stored bytes, enhancing the overall efficiency of
|
||||
the component.
|
||||
|
||||
@section WeikaiComponentI2C_ WeikaiComponentI2C
|
||||
The weikai_i2c::WeikaiComponentI2C class implements the virtual methods of the WeikaiComponent class for an I2C bus.
|
||||
|
||||
@section WeikaiComponentSPI_ WeikaiComponentSPI
|
||||
The weikai_spi::WeikaiComponentSPI class implements the virtual methods of the WeikaiComponent class for an SPI bus.
|
||||
|
||||
@section WeikaiGPIOPin_ WeikaiGPIOPin class
|
||||
The WeikaiGPIOPin class is an helper class to expose the GPIO pins of WK family components as if they were internal
|
||||
GPIO pins. It also provides the setup() and dump_summary() methods.
|
||||
|
||||
@section WeikaiChannel_ The WeikaiChannel class
|
||||
The WeikaiChannel class is used to implement all the virtual methods of the ESPHome uart::UARTComponent class. An
|
||||
individual instance of this class is created for each UART channel. It has a link back to the WeikaiComponent object it
|
||||
belongs to. This class derives from the uart::UARTComponent class. It collaborates through an aggregation with
|
||||
WeikaiComponent. This implies that WeikaiComponent acts as a container, housing several WeikaiChannel instances.
|
||||
Furthermore, the WeikaiChannel class derives from the ESPHome uart::UARTComponent class, it also has an association
|
||||
relationship with the WKRingBuffer and WeikaiRegister helper classes. Consequently, when a WeikaiChannel instance is
|
||||
destroyed, the associated WKRingBuffer instance is also destroyed.
|
||||
|
||||
*/
|
||||
|
||||
static const char *const TAG = "weikai";
|
||||
|
||||
/// @brief convert an int to binary representation as C++ std::string
|
||||
/// @param val integer to convert
|
||||
/// @return a std::string
|
||||
inline std::string i2s(uint8_t val) { return std::bitset<8>(val).to_string(); }
|
||||
/// Convert std::string to C string
|
||||
#define I2S2CS(val) (i2s(val).c_str())
|
||||
|
||||
/// @brief measure the time elapsed between two calls
|
||||
/// @param last_time time of the previous call
|
||||
/// @return the elapsed time in milliseconds
|
||||
uint32_t elapsed_ms(uint32_t &last_time) {
|
||||
uint32_t e = millis() - last_time;
|
||||
last_time = millis();
|
||||
return e;
|
||||
};
|
||||
|
||||
/// @brief Converts the parity enum value to a C string
|
||||
/// @param parity enum
|
||||
/// @return the string
|
||||
const char *p2s(uart::UARTParityOptions parity) {
|
||||
using namespace uart;
|
||||
switch (parity) {
|
||||
case UART_CONFIG_PARITY_NONE:
|
||||
return "NONE";
|
||||
case UART_CONFIG_PARITY_EVEN:
|
||||
return "EVEN";
|
||||
case UART_CONFIG_PARITY_ODD:
|
||||
return "ODD";
|
||||
default:
|
||||
return "UNKNOWN";
|
||||
}
|
||||
}
|
||||
|
||||
/// @brief Display a buffer in hexadecimal format (32 hex values / line) for debug
|
||||
void print_buffer(const uint8_t *data, size_t length) {
|
||||
char hex_buffer[100];
|
||||
hex_buffer[(3 * 32) + 1] = 0;
|
||||
for (size_t i = 0; i < length; i++) {
|
||||
snprintf(&hex_buffer[3 * (i % 32)], sizeof(hex_buffer), "%02X ", data[i]);
|
||||
if (i % 32 == 31) {
|
||||
ESP_LOGVV(TAG, " %s", hex_buffer);
|
||||
}
|
||||
}
|
||||
if (length % 32) {
|
||||
// null terminate if incomplete line
|
||||
hex_buffer[3 * (length % 32) + 2] = 0;
|
||||
ESP_LOGVV(TAG, " %s", hex_buffer);
|
||||
}
|
||||
}
|
||||
|
||||
static const char *const REG_TO_STR_P0[16] = {"GENA", "GRST", "GMUT", "SPAGE", "SCR", "LCR", "FCR", "SIER",
|
||||
"SIFR", "TFCNT", "RFCNT", "FSR", "LSR", "FDAT", "FWCR", "RS485"};
|
||||
static const char *const REG_TO_STR_P1[16] = {"GENA", "GRST", "GMUT", "SPAGE", "BAUD1", "BAUD0", "PRES", "RFTL",
|
||||
"TFTL", "FWTH", "FWTL", "XON1", "XOFF1", "SADR", "SAEN", "RTSDLY"};
|
||||
|
||||
// method to print a register value as text: used in the log messages ...
|
||||
const char *reg_to_str(int reg, bool page1) {
|
||||
if (reg == WKREG_GPDAT) {
|
||||
return "GPDAT";
|
||||
} else if (reg == WKREG_GPDIR) {
|
||||
return "GPDIR";
|
||||
} else {
|
||||
return page1 ? REG_TO_STR_P1[reg & 0x0F] : REG_TO_STR_P0[reg & 0x0F];
|
||||
}
|
||||
}
|
||||
|
||||
enum RegType { REG = 0, FIFO = 1 }; ///< Register or FIFO
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// The WeikaiRegister methods
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
WeikaiRegister &WeikaiRegister::operator=(uint8_t value) {
|
||||
write_reg(value);
|
||||
return *this;
|
||||
}
|
||||
|
||||
WeikaiRegister &WeikaiRegister::operator&=(uint8_t value) {
|
||||
value &= read_reg();
|
||||
write_reg(value);
|
||||
return *this;
|
||||
}
|
||||
|
||||
WeikaiRegister &WeikaiRegister::operator|=(uint8_t value) {
|
||||
value |= read_reg();
|
||||
write_reg(value);
|
||||
return *this;
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// The WeikaiComponent methods
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
void WeikaiComponent::loop() {
|
||||
if ((this->component_state_ & COMPONENT_STATE_MASK) != COMPONENT_STATE_LOOP)
|
||||
return;
|
||||
|
||||
// If there are some bytes in the receive FIFO we transfers them to the ring buffers
|
||||
size_t transferred = 0;
|
||||
for (auto *child : this->children_) {
|
||||
// we look if some characters has been received in the fifo
|
||||
transferred += child->xfer_fifo_to_buffer_();
|
||||
}
|
||||
if (transferred > 0) {
|
||||
ESP_LOGV(TAG, "we transferred %d bytes from fifo to buffer...", transferred);
|
||||
}
|
||||
|
||||
#ifdef TEST_COMPONENT
|
||||
static uint32_t loop_time = 0;
|
||||
static uint32_t loop_count = 0;
|
||||
uint32_t time = 0;
|
||||
|
||||
if (test_mode_ == 1) { // test component in loopback
|
||||
ESP_LOGI(TAG, "Component loop %" PRIu32 " for %s : %" PRIu32 " ms since last call ...", loop_count++,
|
||||
this->get_name(), millis() - loop_time);
|
||||
loop_time = millis();
|
||||
char message[64];
|
||||
elapsed_ms(time); // set time to now
|
||||
for (int i = 0; i < this->children_.size(); i++) {
|
||||
if (i != ((loop_count - 1) % this->children_.size())) // we do only one per loop
|
||||
continue;
|
||||
snprintf(message, sizeof(message), "%s:%s", this->get_name(), children_[i]->get_channel_name());
|
||||
children_[i]->uart_send_test_(message);
|
||||
uint32_t const start_time = millis();
|
||||
while (children_[i]->tx_fifo_is_not_empty_()) { // wait until buffer empty
|
||||
if (millis() - start_time > 1500) {
|
||||
ESP_LOGE(TAG, "timeout while flushing - %d bytes left in buffer...", children_[i]->tx_in_fifo_());
|
||||
break;
|
||||
}
|
||||
yield(); // reschedule our thread to avoid blocking
|
||||
}
|
||||
bool status = children_[i]->uart_receive_test_(message);
|
||||
ESP_LOGI(TAG, "Test %s => send/received %u bytes %s - execution time %" PRIu32 " ms...", message,
|
||||
RING_BUFFER_SIZE, status ? "correctly" : "with error", elapsed_ms(time));
|
||||
}
|
||||
}
|
||||
|
||||
if (this->test_mode_ == 2) { // test component in echo mode
|
||||
for (auto *child : this->children_) {
|
||||
uint8_t data = 0;
|
||||
if (child->available()) {
|
||||
child->read_byte(&data);
|
||||
ESP_LOGI(TAG, "echo mode: read -> send %02X", data);
|
||||
child->write_byte(data);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (test_mode_ == 3) {
|
||||
test_gpio_input_();
|
||||
}
|
||||
|
||||
if (test_mode_ == 4) {
|
||||
test_gpio_output_();
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
#if defined(TEST_COMPONENT)
|
||||
void WeikaiComponent::test_gpio_input_() {
|
||||
static bool init_input{false};
|
||||
static uint8_t state{0};
|
||||
uint8_t value;
|
||||
if (!init_input) {
|
||||
init_input = true;
|
||||
// set all pins in input mode
|
||||
this->reg(WKREG_GPDIR, 0) = 0x00;
|
||||
ESP_LOGI(TAG, "initializing all pins to input mode");
|
||||
state = this->reg(WKREG_GPDAT, 0);
|
||||
ESP_LOGI(TAG, "initial input data state = %02X (%s)", state, I2S2CS(state));
|
||||
}
|
||||
value = this->reg(WKREG_GPDAT, 0);
|
||||
if (value != state) {
|
||||
ESP_LOGI(TAG, "Input data changed from %02X to %02X (%s)", state, value, I2S2CS(value));
|
||||
state = value;
|
||||
}
|
||||
}
|
||||
|
||||
void WeikaiComponent::test_gpio_output_() {
|
||||
static bool init_output{false};
|
||||
static uint8_t state{0};
|
||||
if (!init_output) {
|
||||
init_output = true;
|
||||
// set all pins in output mode
|
||||
this->reg(WKREG_GPDIR, 0) = 0xFF;
|
||||
ESP_LOGI(TAG, "initializing all pins to output mode");
|
||||
this->reg(WKREG_GPDAT, 0) = state;
|
||||
ESP_LOGI(TAG, "setting all outputs to 0");
|
||||
}
|
||||
state = ~state;
|
||||
this->reg(WKREG_GPDAT, 0) = state;
|
||||
ESP_LOGI(TAG, "Flipping all outputs to %02X (%s)", state, I2S2CS(state));
|
||||
delay(100); // NOLINT
|
||||
}
|
||||
#endif
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// The WeikaiGPIOPin methods
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
bool WeikaiComponent::read_pin_val_(uint8_t pin) {
|
||||
this->input_state_ = this->reg(WKREG_GPDAT, 0);
|
||||
ESP_LOGVV(TAG, "reading input pin %u = %u in_state %s", pin, this->input_state_ & (1 << pin), I2S2CS(input_state_));
|
||||
return this->input_state_ & (1 << pin);
|
||||
}
|
||||
|
||||
void WeikaiComponent::write_pin_val_(uint8_t pin, bool value) {
|
||||
if (value) {
|
||||
this->output_state_ |= (1 << pin);
|
||||
} else {
|
||||
this->output_state_ &= ~(1 << pin);
|
||||
}
|
||||
ESP_LOGVV(TAG, "writing output pin %d with %d out_state %s", pin, uint8_t(value), I2S2CS(this->output_state_));
|
||||
this->reg(WKREG_GPDAT, 0) = this->output_state_;
|
||||
}
|
||||
|
||||
void WeikaiComponent::set_pin_direction_(uint8_t pin, gpio::Flags flags) {
|
||||
if (flags == gpio::FLAG_INPUT) {
|
||||
this->pin_config_ &= ~(1 << pin); // clear bit (input mode)
|
||||
} else {
|
||||
if (flags == gpio::FLAG_OUTPUT) {
|
||||
this->pin_config_ |= 1 << pin; // set bit (output mode)
|
||||
} else {
|
||||
ESP_LOGE(TAG, "pin %d direction invalid", pin);
|
||||
}
|
||||
}
|
||||
ESP_LOGVV(TAG, "setting pin %d direction to %d pin_config=%s", pin, flags, I2S2CS(this->pin_config_));
|
||||
this->reg(WKREG_GPDIR, 0) = this->pin_config_; // TODO check ~
|
||||
}
|
||||
|
||||
void WeikaiGPIOPin::setup() {
|
||||
ESP_LOGCONFIG(TAG, "Setting GPIO pin %d mode to %s", this->pin_,
|
||||
flags_ == gpio::FLAG_INPUT ? "Input"
|
||||
: this->flags_ == gpio::FLAG_OUTPUT ? "Output"
|
||||
: "NOT SPECIFIED");
|
||||
// ESP_LOGCONFIG(TAG, "Setting GPIO pins mode to '%s' %02X", I2S2CS(this->flags_), this->flags_);
|
||||
this->pin_mode(this->flags_);
|
||||
}
|
||||
|
||||
std::string WeikaiGPIOPin::dump_summary() const {
|
||||
char buffer[32];
|
||||
snprintf(buffer, sizeof(buffer), "%u via WeiKai %s", this->pin_, this->parent_->get_name());
|
||||
return buffer;
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// The WeikaiChannel methods
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
void WeikaiChannel::setup_channel() {
|
||||
ESP_LOGCONFIG(TAG, " Setting up UART %s:%s ...", this->parent_->get_name(), this->get_channel_name());
|
||||
// we enable transmit and receive on this channel
|
||||
if (this->check_channel_down()) {
|
||||
ESP_LOGCONFIG(TAG, " Error channel %s not working...", this->get_channel_name());
|
||||
}
|
||||
this->reset_fifo_();
|
||||
this->receive_buffer_.clear();
|
||||
this->set_line_param_();
|
||||
this->set_baudrate_();
|
||||
}
|
||||
|
||||
void WeikaiChannel::dump_channel() {
|
||||
ESP_LOGCONFIG(TAG, " UART %s ...", this->get_channel_name());
|
||||
ESP_LOGCONFIG(TAG, " Baud rate: %" PRIu32 " Bd", this->baud_rate_);
|
||||
ESP_LOGCONFIG(TAG, " Data bits: %u", this->data_bits_);
|
||||
ESP_LOGCONFIG(TAG, " Stop bits: %u", this->stop_bits_);
|
||||
ESP_LOGCONFIG(TAG, " Parity: %s", p2s(this->parity_));
|
||||
}
|
||||
|
||||
void WeikaiChannel::reset_fifo_() {
|
||||
// enable transmission and reception
|
||||
this->reg(WKREG_SCR) = SCR_RXEN | SCR_TXEN;
|
||||
// we reset and enable transmit and receive FIFO
|
||||
this->reg(WKREG_FCR) = FCR_TFEN | FCR_RFEN | FCR_TFRST | FCR_RFRST;
|
||||
}
|
||||
|
||||
void WeikaiChannel::set_line_param_() {
|
||||
this->data_bits_ = 8; // always equal to 8 for WeiKai (cant be changed)
|
||||
uint8_t lcr = 0;
|
||||
if (this->stop_bits_ == 2)
|
||||
lcr |= LCR_STPL;
|
||||
switch (this->parity_) { // parity selection settings
|
||||
case uart::UART_CONFIG_PARITY_ODD:
|
||||
lcr |= (LCR_PAEN | LCR_PAR_ODD);
|
||||
break;
|
||||
case uart::UART_CONFIG_PARITY_EVEN:
|
||||
lcr |= (LCR_PAEN | LCR_PAR_EVEN);
|
||||
break;
|
||||
default:
|
||||
break; // no parity 000x
|
||||
}
|
||||
this->reg(WKREG_LCR) = lcr; // write LCR
|
||||
ESP_LOGV(TAG, " line config: %d data_bits, %d stop_bits, parity %s register [%s]", this->data_bits_,
|
||||
this->stop_bits_, p2s(this->parity_), I2S2CS(lcr));
|
||||
}
|
||||
|
||||
void WeikaiChannel::set_baudrate_() {
|
||||
if (this->baud_rate_ > this->parent_->crystal_ / 16) {
|
||||
baud_rate_ = this->parent_->crystal_ / 16;
|
||||
ESP_LOGE(TAG, " Requested baudrate too high for crystal=%" PRIu32 " Hz. Has been reduced to %" PRIu32 " Bd",
|
||||
this->parent_->crystal_, this->baud_rate_);
|
||||
};
|
||||
uint16_t const val_int = this->parent_->crystal_ / (this->baud_rate_ * 16) - 1;
|
||||
uint16_t val_dec = (this->parent_->crystal_ % (this->baud_rate_ * 16)) / (this->baud_rate_ * 16);
|
||||
uint8_t const baud_high = (uint8_t) (val_int >> 8);
|
||||
uint8_t const baud_low = (uint8_t) (val_int & 0xFF);
|
||||
while (val_dec > 0x0A)
|
||||
val_dec /= 0x0A;
|
||||
uint8_t const baud_dec = (uint8_t) (val_dec);
|
||||
|
||||
this->parent_->page1_ = true; // switch to page 1
|
||||
this->reg(WKREG_SPAGE) = 1;
|
||||
this->reg(WKREG_BRH) = baud_high;
|
||||
this->reg(WKREG_BRL) = baud_low;
|
||||
this->reg(WKREG_BRD) = baud_dec;
|
||||
this->parent_->page1_ = false; // switch back to page 0
|
||||
this->reg(WKREG_SPAGE) = 0;
|
||||
|
||||
ESP_LOGV(TAG, " Crystal=%d baudrate=%d => registers [%d %d %d]", this->parent_->crystal_, this->baud_rate_,
|
||||
baud_high, baud_low, baud_dec);
|
||||
}
|
||||
|
||||
inline bool WeikaiChannel::tx_fifo_is_not_empty_() { return this->reg(WKREG_FSR) & FSR_TFDAT; }
|
||||
|
||||
size_t WeikaiChannel::tx_in_fifo_() {
|
||||
size_t tfcnt = this->reg(WKREG_TFCNT);
|
||||
if (tfcnt == 0) {
|
||||
uint8_t const fsr = this->reg(WKREG_FSR);
|
||||
if (fsr & FSR_TFFULL) {
|
||||
ESP_LOGVV(TAG, "tx FIFO full FSR=%s", I2S2CS(fsr));
|
||||
tfcnt = FIFO_SIZE;
|
||||
}
|
||||
}
|
||||
ESP_LOGVV(TAG, "tx FIFO contains %d bytes", tfcnt);
|
||||
return tfcnt;
|
||||
}
|
||||
|
||||
size_t WeikaiChannel::rx_in_fifo_() {
|
||||
size_t available = this->reg(WKREG_RFCNT);
|
||||
uint8_t const fsr = this->reg(WKREG_FSR);
|
||||
if (fsr & (FSR_RFOE | FSR_RFLB | FSR_RFFE | FSR_RFPE)) {
|
||||
if (fsr & FSR_RFOE)
|
||||
ESP_LOGE(TAG, "Receive data overflow FSR=%s", I2S2CS(fsr));
|
||||
if (fsr & FSR_RFLB)
|
||||
ESP_LOGE(TAG, "Receive line break FSR=%s", I2S2CS(fsr));
|
||||
if (fsr & FSR_RFFE)
|
||||
ESP_LOGE(TAG, "Receive frame error FSR=%s", I2S2CS(fsr));
|
||||
if (fsr & FSR_RFPE)
|
||||
ESP_LOGE(TAG, "Receive parity error FSR=%s", I2S2CS(fsr));
|
||||
}
|
||||
if ((available == 0) && (fsr & FSR_RFDAT)) {
|
||||
// here we should be very careful because we can have something like this:
|
||||
// - at time t0 we read RFCNT=0 because nothing yet received
|
||||
// - at time t0+delta we might read FIFO not empty because one byte has just been received
|
||||
// - so to be sure we need to do another read of RFCNT and if it is still zero -> buffer full
|
||||
available = this->reg(WKREG_RFCNT);
|
||||
if (available == 0) { // still zero ?
|
||||
ESP_LOGV(TAG, "rx FIFO is full FSR=%s", I2S2CS(fsr));
|
||||
available = FIFO_SIZE;
|
||||
}
|
||||
}
|
||||
ESP_LOGVV(TAG, "rx FIFO contain %d bytes - FSR status=%s", available, I2S2CS(fsr));
|
||||
return available;
|
||||
}
|
||||
|
||||
bool WeikaiChannel::check_channel_down() {
|
||||
// to check if we channel is up we write to the LCR W/R register
|
||||
// note that this will put a break on the tx line for few ms
|
||||
WeikaiRegister &lcr = this->reg(WKREG_LCR);
|
||||
lcr = 0x3F;
|
||||
uint8_t val = lcr;
|
||||
if (val != 0x3F) {
|
||||
ESP_LOGE(TAG, "R/W of register failed expected 0x3F received 0x%02X", val);
|
||||
return true;
|
||||
}
|
||||
lcr = 0;
|
||||
val = lcr;
|
||||
if (val != 0x00) {
|
||||
ESP_LOGE(TAG, "R/W of register failed expected 0x00 received 0x%02X", val);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool WeikaiChannel::peek_byte(uint8_t *buffer) {
|
||||
auto available = this->receive_buffer_.count();
|
||||
if (!available)
|
||||
xfer_fifo_to_buffer_();
|
||||
return this->receive_buffer_.peek(*buffer);
|
||||
}
|
||||
|
||||
int WeikaiChannel::available() {
|
||||
size_t available = this->receive_buffer_.count();
|
||||
if (!available)
|
||||
available = xfer_fifo_to_buffer_();
|
||||
return available;
|
||||
}
|
||||
|
||||
bool WeikaiChannel::read_array(uint8_t *buffer, size_t length) {
|
||||
bool status = true;
|
||||
auto available = this->receive_buffer_.count();
|
||||
if (length > available) {
|
||||
ESP_LOGW(TAG, "read_array: buffer underflow requested %d bytes only %d bytes available...", length, available);
|
||||
length = available;
|
||||
status = false;
|
||||
}
|
||||
// retrieve the bytes from ring buffer
|
||||
for (size_t i = 0; i < length; i++) {
|
||||
this->receive_buffer_.pop(buffer[i]);
|
||||
}
|
||||
ESP_LOGVV(TAG, "read_array(ch=%d buffer[0]=%02X, length=%d): status %s", this->channel_, *buffer, length,
|
||||
status ? "OK" : "ERROR");
|
||||
return status;
|
||||
}
|
||||
|
||||
void WeikaiChannel::write_array(const uint8_t *buffer, size_t length) {
|
||||
if (length > XFER_MAX_SIZE) {
|
||||
ESP_LOGE(TAG, "Write_array: invalid call - requested %d bytes but max size %d ...", length, XFER_MAX_SIZE);
|
||||
length = XFER_MAX_SIZE;
|
||||
}
|
||||
this->reg(0).write_fifo(const_cast<uint8_t *>(buffer), length);
|
||||
}
|
||||
|
||||
void WeikaiChannel::flush() {
|
||||
uint32_t const start_time = millis();
|
||||
while (this->tx_fifo_is_not_empty_()) { // wait until buffer empty
|
||||
if (millis() - start_time > 200) {
|
||||
ESP_LOGW(TAG, "WARNING flush timeout - still %d bytes not sent after 200 ms...", this->tx_in_fifo_());
|
||||
return;
|
||||
}
|
||||
yield(); // reschedule our thread to avoid blocking
|
||||
}
|
||||
}
|
||||
|
||||
size_t WeikaiChannel::xfer_fifo_to_buffer_() {
|
||||
size_t to_transfer;
|
||||
size_t free;
|
||||
while ((to_transfer = this->rx_in_fifo_()) && (free = this->receive_buffer_.free())) {
|
||||
// while bytes in fifo and some room in the buffer we transfer
|
||||
if (to_transfer > XFER_MAX_SIZE)
|
||||
to_transfer = XFER_MAX_SIZE; // we can only do so much
|
||||
if (to_transfer > free)
|
||||
to_transfer = free; // we'll do the rest next time
|
||||
if (to_transfer) {
|
||||
uint8_t data[to_transfer];
|
||||
this->reg(0).read_fifo(data, to_transfer);
|
||||
for (size_t i = 0; i < to_transfer; i++)
|
||||
this->receive_buffer_.push(data[i]);
|
||||
}
|
||||
} // while work to do
|
||||
return to_transfer;
|
||||
}
|
||||
|
||||
///
|
||||
// TEST COMPONENT
|
||||
//
|
||||
#ifdef TEST_COMPONENT
|
||||
/// @addtogroup test_ Test component information
|
||||
/// @{
|
||||
|
||||
/// @brief An increment "Functor" (i.e. a class object that acts like a method with state!)
|
||||
///
|
||||
/// Functors are objects that can be treated as though they are a function or function pointer.
|
||||
class Increment {
|
||||
public:
|
||||
/// @brief constructor: initialize current value to 0
|
||||
Increment() : i_(0) {}
|
||||
/// @brief overload of the parenthesis operator.
|
||||
/// Returns the current value and auto increment it
|
||||
/// @return the current value.
|
||||
uint8_t operator()() { return i_++; }
|
||||
|
||||
private:
|
||||
uint8_t i_;
|
||||
};
|
||||
|
||||
/// @brief Hex converter to print/display a buffer in hexadecimal format (32 hex values / line).
|
||||
/// @param buffer contains the values to display
|
||||
void print_buffer(std::vector<uint8_t> buffer) {
|
||||
char hex_buffer[100];
|
||||
hex_buffer[(3 * 32) + 1] = 0;
|
||||
for (size_t i = 0; i < buffer.size(); i++) {
|
||||
snprintf(&hex_buffer[3 * (i % 32)], sizeof(hex_buffer), "%02X ", buffer[i]);
|
||||
if (i % 32 == 31)
|
||||
ESP_LOGI(TAG, " %s", hex_buffer);
|
||||
}
|
||||
if (buffer.size() % 32) {
|
||||
// null terminate if incomplete line
|
||||
hex_buffer[3 * (buffer.size() % 32) + 1] = 0;
|
||||
ESP_LOGI(TAG, " %s", hex_buffer);
|
||||
}
|
||||
}
|
||||
|
||||
/// @brief test the write_array method
|
||||
void WeikaiChannel::uart_send_test_(char *message) {
|
||||
auto start_exec = micros();
|
||||
std::vector<uint8_t> output_buffer(XFER_MAX_SIZE);
|
||||
generate(output_buffer.begin(), output_buffer.end(), Increment()); // fill with incrementing number
|
||||
size_t to_send = RING_BUFFER_SIZE;
|
||||
while (to_send) {
|
||||
this->write_array(&output_buffer[0], XFER_MAX_SIZE); // we send the buffer
|
||||
this->flush();
|
||||
to_send -= XFER_MAX_SIZE;
|
||||
}
|
||||
ESP_LOGV(TAG, "%s => sent %d bytes - exec time %d µs ...", message, RING_BUFFER_SIZE, micros() - start_exec);
|
||||
}
|
||||
|
||||
/// @brief test read_array method
|
||||
bool WeikaiChannel::uart_receive_test_(char *message) {
|
||||
auto start_exec = micros();
|
||||
bool status = true;
|
||||
size_t received = 0;
|
||||
std::vector<uint8_t> buffer(RING_BUFFER_SIZE);
|
||||
|
||||
// we wait until we have received all the bytes
|
||||
uint32_t const start_time = millis();
|
||||
status = true;
|
||||
while (received < RING_BUFFER_SIZE) {
|
||||
while (XFER_MAX_SIZE > this->available()) {
|
||||
this->xfer_fifo_to_buffer_();
|
||||
if (millis() - start_time > 1500) {
|
||||
ESP_LOGE(TAG, "uart_receive_test_() timeout: only %d bytes received...", this->available());
|
||||
break;
|
||||
}
|
||||
yield(); // reschedule our thread to avoid blocking
|
||||
}
|
||||
status = this->read_array(&buffer[received], XFER_MAX_SIZE) && status;
|
||||
received += XFER_MAX_SIZE;
|
||||
}
|
||||
|
||||
uint8_t peek_value = 0;
|
||||
this->peek_byte(&peek_value);
|
||||
if (peek_value != 0) {
|
||||
ESP_LOGE(TAG, "Peek first byte value error...");
|
||||
status = false;
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < RING_BUFFER_SIZE; i++) {
|
||||
if (buffer[i] != i % XFER_MAX_SIZE) {
|
||||
ESP_LOGE(TAG, "Read buffer contains error...b=%x i=%x", buffer[i], i % XFER_MAX_SIZE);
|
||||
print_buffer(buffer);
|
||||
status = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
ESP_LOGV(TAG, "%s => received %d bytes status %s - exec time %d µs ...", message, received, status ? "OK" : "ERROR",
|
||||
micros() - start_exec);
|
||||
return status;
|
||||
}
|
||||
|
||||
/// @}
|
||||
#endif
|
||||
|
||||
} // namespace weikai
|
||||
} // namespace esphome
|
443
esphome/components/weikai/weikai.h
Normal file
443
esphome/components/weikai/weikai.h
Normal file
@ -0,0 +1,443 @@
|
||||
/// @file weikai.h
|
||||
/// @author DrCoolZic
|
||||
/// @brief WeiKai component family - classes declaration
|
||||
/// @date Last Modified: 2024/04/06 14:44:17
|
||||
/// @details The classes declared in this file can be used by the Weikai family
|
||||
/// of UART and GPIO expander components. As of today it provides support for
|
||||
/// wk2124_spi, wk2132_spi, wk2168_spi, wk2204_spi, wk2212_spi,
|
||||
/// wk2132_i2c, wk2168_i2c, wk2204_i2c, wk2212_i2c
|
||||
|
||||
#pragma once
|
||||
#include <bitset>
|
||||
#include <memory>
|
||||
#include <cinttypes>
|
||||
#include "esphome/core/component.h"
|
||||
#include "esphome/components/uart/uart.h"
|
||||
#include "wk_reg_def.h"
|
||||
|
||||
/// When the TEST_COMPONENT flag is defined we include some auto-test methods. Used to test the software during
|
||||
/// development but can also be used in situ to test if the component is working correctly. For release we do
|
||||
/// not set it by default but you can set it by using the following lines in you configuration file:
|
||||
/// @code
|
||||
/// esphome:
|
||||
/// platformio_options:
|
||||
/// build_flags:
|
||||
/// - -DTEST_COMPONENT
|
||||
/// @endcode
|
||||
// #define TEST_COMPONENT
|
||||
|
||||
namespace esphome {
|
||||
namespace weikai {
|
||||
|
||||
/// @brief XFER_MAX_SIZE defines the maximum number of bytes allowed during one transfer.
|
||||
#if defined(I2C_BUFFER_LENGTH)
|
||||
constexpr size_t XFER_MAX_SIZE = I2C_BUFFER_LENGTH;
|
||||
#else
|
||||
constexpr size_t XFER_MAX_SIZE = 128;
|
||||
#endif
|
||||
|
||||
/// @brief size of the internal WeiKai FIFO
|
||||
constexpr size_t FIFO_SIZE = 256;
|
||||
|
||||
/// @brief size of the ring buffer set to size of the FIFO
|
||||
constexpr size_t RING_BUFFER_SIZE = FIFO_SIZE;
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief This is an helper class that provides a simple ring buffers that works as a FIFO
|
||||
/// @details This ring buffer is used to buffer the bytes received in the FIFO of the Weika device. The best way to read
|
||||
/// characters from the device FIFO, is to first check how many bytes were received and then read them all at once.
|
||||
/// Unfortunately in all the code I have reviewed the characters are read one by one in a while loop by checking if
|
||||
/// bytes are available then reading the byte until no more byte available. This is pretty inefficient for two reasons:
|
||||
/// - Fist you need to perform a test for each byte to read
|
||||
/// - and second you call the read byte method for each character.
|
||||
/// .
|
||||
/// Assuming you need to read 100 bytes that results into 200 calls. This is to compare to 2 calls (one to find the
|
||||
/// number of bytes available plus one to read all the bytes) in the best case! If the registers you read are located on
|
||||
/// the micro-controller this is acceptable because the registers can be accessed fast. But when the registers are
|
||||
/// located on a remote device accessing them requires several cycles on a slow bus. As it it not possible to fix this
|
||||
/// problem by asking users to rewrite their code, I have implemented this ring buffer that store the bytes received
|
||||
/// locally.
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
template<typename T, size_t SIZE> class WKRingBuffer {
|
||||
public:
|
||||
/// @brief pushes an item at the tail of the fifo
|
||||
/// @param item item to push
|
||||
/// @return true if item has been pushed, false il item could not pushed (buffer full)
|
||||
bool push(const T item) {
|
||||
if (is_full())
|
||||
return false;
|
||||
this->rb_[this->head_] = item;
|
||||
this->head_ = (this->head_ + 1) % SIZE;
|
||||
this->count_++;
|
||||
return true;
|
||||
}
|
||||
|
||||
/// @brief return and remove the item at head of the fifo
|
||||
/// @param item item read
|
||||
/// @return true if an item has been retrieved, false il no item available (buffer empty)
|
||||
bool pop(T &item) {
|
||||
if (is_empty())
|
||||
return false;
|
||||
item = this->rb_[this->tail_];
|
||||
this->tail_ = (this->tail_ + 1) % SIZE;
|
||||
this->count_--;
|
||||
return true;
|
||||
}
|
||||
|
||||
/// @brief return the value of the item at fifo's head without removing it
|
||||
/// @param item pointer to item to return
|
||||
/// @return true if item has been retrieved, false il no item available (buffer empty)
|
||||
bool peek(T &item) {
|
||||
if (is_empty())
|
||||
return false;
|
||||
item = this->rb_[this->tail_];
|
||||
return true;
|
||||
}
|
||||
|
||||
/// @brief test is the Ring Buffer is empty ?
|
||||
/// @return true if empty
|
||||
inline bool is_empty() { return (this->count_ == 0); }
|
||||
|
||||
/// @brief test is the ring buffer is full ?
|
||||
/// @return true if full
|
||||
inline bool is_full() { return (this->count_ == SIZE); }
|
||||
|
||||
/// @brief return the number of item in the ring buffer
|
||||
/// @return the number of items
|
||||
inline size_t count() { return this->count_; }
|
||||
|
||||
/// @brief returns the number of free positions in the buffer
|
||||
/// @return how many items can be added
|
||||
inline size_t free() { return SIZE - this->count_; }
|
||||
|
||||
/// @brief clear the buffer content
|
||||
inline void clear() { this->head_ = this->tail_ = this->count_ = 0; }
|
||||
|
||||
protected:
|
||||
std::array<T, SIZE> rb_{0}; ///< the ring buffer
|
||||
int tail_{0}; ///< position of the next element to read
|
||||
int head_{0}; ///< position of the next element to write
|
||||
size_t count_{0}; ///< count number of element in the buffer
|
||||
};
|
||||
|
||||
class WeikaiComponent;
|
||||
// class WeikaiComponentSPI;
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief WeikaiRegister objects acts as proxies to access remote register independently of the bus type.
|
||||
/// @details This is an abstract interface class that provides many operations to access to registers while hiding
|
||||
/// the actual implementation. This allow to accesses the registers in the Weikai component abstract class independently
|
||||
/// of the actual bus (I2C, SPI). The derived classes will actually implements the specific bus operations dependant of
|
||||
/// the bus used.
|
||||
/// @n typical usage of WeikaiRegister:
|
||||
/// @code
|
||||
/// WeikaiRegister reg_X {&WeikaiComponent, ADDR_REGISTER_X, CHANNEL_NUM} // declaration
|
||||
/// reg_X |= 0x01; // set bit 0 of the weikai register
|
||||
/// reg_X &= ~0x01; // reset bit 0 of the weikai register
|
||||
/// reg_X = 10; // Set the value of weikai register
|
||||
/// uint val = reg_X; // get the value of weikai register
|
||||
/// @endcode
|
||||
class WeikaiRegister {
|
||||
public:
|
||||
/// @brief WeikaiRegister constructor.
|
||||
/// @param comp our parent WeikaiComponent
|
||||
/// @param reg address of the register
|
||||
/// @param channel the channel of this register
|
||||
WeikaiRegister(WeikaiComponent *const comp, uint8_t reg, uint8_t channel)
|
||||
: comp_(comp), register_(reg), channel_(channel) {}
|
||||
virtual ~WeikaiRegister() {}
|
||||
|
||||
/// @brief overloads the = operator. This is used to set a value into the weikai register
|
||||
/// @param value to be set
|
||||
/// @return this object
|
||||
WeikaiRegister &operator=(uint8_t value);
|
||||
|
||||
/// @brief overloads the compound &= operator. This is often used to reset bits in the weikai register
|
||||
/// @param value performs an & operation with value and store the result
|
||||
/// @return this object
|
||||
WeikaiRegister &operator&=(uint8_t value);
|
||||
|
||||
/// @brief overloads the compound |= operator. This is often used to set bits in the weikai register
|
||||
/// @param value performs an | operation with value and store the result
|
||||
/// @return this object
|
||||
WeikaiRegister &operator|=(uint8_t value);
|
||||
|
||||
/// @brief cast operator that returns the content of the weikai register
|
||||
operator uint8_t() const { return read_reg(); }
|
||||
|
||||
/// @brief reads the register
|
||||
/// @return the value read from the register
|
||||
virtual uint8_t read_reg() const = 0;
|
||||
|
||||
/// @brief writes the register
|
||||
/// @param value to write in the register
|
||||
virtual void write_reg(uint8_t value) = 0;
|
||||
|
||||
/// @brief read an array of bytes from the receiver fifo
|
||||
/// @param data pointer to data buffer
|
||||
/// @param length number of bytes to read
|
||||
virtual void read_fifo(uint8_t *data, size_t length) const = 0;
|
||||
|
||||
/// @brief write an array of bytes to the transmitter fifo
|
||||
/// @param data pointer to data buffer
|
||||
/// @param length number of bytes to write
|
||||
virtual void write_fifo(uint8_t *data, size_t length) = 0;
|
||||
|
||||
WeikaiComponent *const comp_; ///< pointer to our parent (aggregation)
|
||||
uint8_t register_; ///< address of the register
|
||||
uint8_t channel_; ///< channel for this register
|
||||
};
|
||||
|
||||
class WeikaiChannel; // forward declaration
|
||||
////////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief The WeikaiComponent class stores the information global to the WeiKai component
|
||||
/// and provides methods to set/access this information. It is also the container of
|
||||
/// the WeikaiChannel children objects. This class is derived from esphome::Component
|
||||
/// class.
|
||||
////////////////////////////////////////////////////////////////////////////////////
|
||||
class WeikaiComponent : public Component {
|
||||
public:
|
||||
/// @brief virtual destructor
|
||||
virtual ~WeikaiComponent() {}
|
||||
|
||||
/// @brief store crystal frequency
|
||||
/// @param crystal frequency
|
||||
void set_crystal(uint32_t crystal) { this->crystal_ = crystal; }
|
||||
|
||||
/// @brief store if the component is in test mode
|
||||
/// @param test_mode 0=normal mode any other values mean component in test mode
|
||||
void set_test_mode(int test_mode) { this->test_mode_ = test_mode; }
|
||||
|
||||
/// @brief store the name for the component
|
||||
/// @param name the name as defined by the python code generator
|
||||
void set_name(std::string name) { this->name_ = std::move(name); }
|
||||
|
||||
/// @brief Get the name of the component
|
||||
/// @return the name
|
||||
const char *get_name() { return this->name_.c_str(); }
|
||||
|
||||
/// @brief override the Component loop()
|
||||
void loop() override;
|
||||
|
||||
bool page1() { return page1_; }
|
||||
|
||||
/// @brief Factory method to create a Register object
|
||||
/// @param reg address of the register
|
||||
/// @param channel channel associated with this register
|
||||
/// @return a reference to WeikaiRegister
|
||||
virtual WeikaiRegister ®(uint8_t reg, uint8_t channel) = 0;
|
||||
|
||||
protected:
|
||||
friend class WeikaiChannel;
|
||||
|
||||
/// @brief Get the priority of the component
|
||||
/// @return the priority
|
||||
/// @details The priority is set below setup_priority::BUS because we use
|
||||
/// the spi/i2c busses (which has a priority of BUS) to communicate and the WeiKai
|
||||
/// therefore it is seen by our client almost as if it was a bus.
|
||||
float get_setup_priority() const override { return setup_priority::BUS - 0.1F; }
|
||||
|
||||
friend class WeikaiGPIOPin;
|
||||
/// Helper method to read the value of a pin.
|
||||
bool read_pin_val_(uint8_t pin);
|
||||
|
||||
/// Helper method to write the value of a pin.
|
||||
void write_pin_val_(uint8_t pin, bool value);
|
||||
|
||||
/// Helper method to set the pin mode of a pin.
|
||||
void set_pin_direction_(uint8_t pin, gpio::Flags flags);
|
||||
|
||||
#ifdef TEST_COMPONENT
|
||||
/// @defgroup test_ Test component information
|
||||
/// @brief Contains information about the auto-tests of the component
|
||||
/// @{
|
||||
void test_gpio_input_();
|
||||
void test_gpio_output_();
|
||||
/// @}
|
||||
#endif
|
||||
|
||||
uint8_t pin_config_{0x00}; ///< pin config mask: 1 means OUTPUT, 0 means INPUT
|
||||
uint8_t output_state_{0x00}; ///< output state: 1 means HIGH, 0 means LOW
|
||||
uint8_t input_state_{0x00}; ///< input pin states: 1 means HIGH, 0 means LOW
|
||||
uint32_t crystal_; ///< crystal value;
|
||||
int test_mode_; ///< test mode value (0 -> no tests)
|
||||
bool page1_{false}; ///< set to true when in "page1 mode"
|
||||
std::vector<WeikaiChannel *> children_{}; ///< the list of WeikaiChannel UART children
|
||||
std::string name_; ///< name of entity
|
||||
};
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief Helper class to expose a WeiKai family IO pin as an internal GPIO pin.
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
class WeikaiGPIOPin : public GPIOPin {
|
||||
public:
|
||||
void set_parent(WeikaiComponent *parent) { this->parent_ = parent; }
|
||||
void set_pin(uint8_t pin) { this->pin_ = pin; }
|
||||
void set_inverted(bool inverted) { this->inverted_ = inverted; }
|
||||
void set_flags(gpio::Flags flags) { this->flags_ = flags; }
|
||||
|
||||
void setup() override;
|
||||
std::string dump_summary() const override;
|
||||
void pin_mode(gpio::Flags flags) override { this->parent_->set_pin_direction_(this->pin_, flags); }
|
||||
bool digital_read() override { return this->parent_->read_pin_val_(this->pin_) != this->inverted_; }
|
||||
void digital_write(bool value) override { this->parent_->write_pin_val_(this->pin_, value != this->inverted_); }
|
||||
|
||||
protected:
|
||||
WeikaiComponent *parent_{nullptr};
|
||||
uint8_t pin_;
|
||||
bool inverted_;
|
||||
gpio::Flags flags_;
|
||||
};
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief The WeikaiChannel class is used to implement all the virtual methods of the ESPHome
|
||||
/// uart::UARTComponent virtual class. This class is common to the different members of the Weikai
|
||||
/// components family and therefore avoid code duplication.
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
class WeikaiChannel : public uart::UARTComponent {
|
||||
public:
|
||||
/// @brief We belongs to this WeikaiComponent
|
||||
/// @param parent pointer to the component we belongs to
|
||||
void set_parent(WeikaiComponent *parent) {
|
||||
this->parent_ = parent;
|
||||
this->parent_->children_.push_back(this); // add ourself to the list (vector)
|
||||
}
|
||||
|
||||
/// @brief Sets the channel number
|
||||
/// @param channel number
|
||||
void set_channel(uint8_t channel) { this->channel_ = channel; }
|
||||
|
||||
/// @brief The name as generated by the Python code generator
|
||||
/// @param name of the channel
|
||||
void set_channel_name(std::string name) { this->name_ = std::move(name); }
|
||||
|
||||
/// @brief Get the channel name
|
||||
/// @return the name
|
||||
const char *get_channel_name() { return this->name_.c_str(); }
|
||||
|
||||
/// @brief Setup the channel
|
||||
void virtual setup_channel();
|
||||
|
||||
/// @brief dump channel information
|
||||
void virtual dump_channel();
|
||||
|
||||
/// @brief Factory method to create a WeikaiRegister proxy object
|
||||
/// @param reg address of the register
|
||||
/// @return a reference to WeikaiRegister
|
||||
WeikaiRegister ®(uint8_t reg) { return this->parent_->reg(reg, channel_); }
|
||||
|
||||
//
|
||||
// we implements/overrides the virtual class from UARTComponent
|
||||
//
|
||||
|
||||
/// @brief Writes a specified number of bytes to a serial port
|
||||
/// @param buffer pointer to the buffer
|
||||
/// @param length number of bytes to write
|
||||
/// @details This method sends 'length' characters from the buffer to the serial line. Unfortunately (unlike the
|
||||
/// Arduino equivalent) this method does not return any flag and therefore it is not possible to know if any/all bytes
|
||||
/// have been transmitted correctly. Another problem is that it is not possible to know ahead of time how many bytes
|
||||
/// we can safely send as there is no tx_available() method provided! To avoid overrun when using the write method you
|
||||
/// can use the flush() method to wait until the transmit fifo is empty.
|
||||
/// @n Typical usage could be:
|
||||
/// @code
|
||||
/// // ...
|
||||
/// uint8_t buffer[128];
|
||||
/// // ...
|
||||
/// write_array(&buffer, length);
|
||||
/// flush();
|
||||
/// // ...
|
||||
/// @endcode
|
||||
void write_array(const uint8_t *buffer, size_t length) override;
|
||||
|
||||
/// @brief Reads a specified number of bytes from a serial port
|
||||
/// @param buffer buffer to store the bytes
|
||||
/// @param length number of bytes to read
|
||||
/// @return true if succeed, false otherwise
|
||||
/// @details Typical usage:
|
||||
/// @code
|
||||
/// // ...
|
||||
/// auto length = available();
|
||||
/// uint8_t buffer[128];
|
||||
/// if (length > 0) {
|
||||
/// auto status = read_array(&buffer, length)
|
||||
/// // test status ...
|
||||
/// }
|
||||
/// @endcode
|
||||
bool read_array(uint8_t *buffer, size_t length) override;
|
||||
|
||||
/// @brief Reads the first byte in FIFO without removing it
|
||||
/// @param buffer pointer to the byte
|
||||
/// @return true if succeed reading one byte, false if no character available
|
||||
/// @details This method returns the next byte from receiving buffer without removing it from the internal fifo. It
|
||||
/// returns true if a character is available and has been read, false otherwise.\n
|
||||
bool peek_byte(uint8_t *buffer) override;
|
||||
|
||||
/// @brief Returns the number of bytes in the receive buffer
|
||||
/// @return the number of bytes available in the receiver fifo
|
||||
int available() override;
|
||||
|
||||
/// @brief Flush the output fifo.
|
||||
/// @details If we refer to Serial.flush() in Arduino it says: ** Waits for the transmission of outgoing serial data
|
||||
/// to complete. (Prior to Arduino 1.0, this the method was removing any buffered incoming serial data.). ** Therefore
|
||||
/// we wait until all bytes are gone with a timeout of 100 ms
|
||||
void flush() override;
|
||||
|
||||
protected:
|
||||
friend class WeikaiComponent;
|
||||
|
||||
/// @brief this cannot happen with external uart therefore we do nothing
|
||||
void check_logger_conflict() override {}
|
||||
|
||||
/// @brief reset the weikai internal FIFO
|
||||
void reset_fifo_();
|
||||
|
||||
/// @brief set the line parameters
|
||||
void set_line_param_();
|
||||
|
||||
/// @brief set the baud rate
|
||||
void set_baudrate_();
|
||||
|
||||
/// @brief Returns the number of bytes in the receive fifo
|
||||
/// @return the number of bytes in the fifo
|
||||
size_t rx_in_fifo_();
|
||||
|
||||
/// @brief Returns the number of bytes in the transmit fifo
|
||||
/// @return the number of bytes in the fifo
|
||||
size_t tx_in_fifo_();
|
||||
|
||||
/// @brief test if transmit buffer is not empty in the status register (optimization)
|
||||
/// @return true if not emptygroup test_
|
||||
bool tx_fifo_is_not_empty_();
|
||||
|
||||
/// @brief transfer bytes from the weikai internal FIFO to the buffer (if any)
|
||||
/// @return number of bytes transferred
|
||||
size_t xfer_fifo_to_buffer_();
|
||||
|
||||
/// @brief check if channel is alive
|
||||
/// @return true if OK
|
||||
bool virtual check_channel_down();
|
||||
|
||||
#ifdef TEST_COMPONENT
|
||||
/// @ingroup test_
|
||||
/// @{
|
||||
|
||||
/// @brief Test the write_array() method
|
||||
/// @param message to display
|
||||
void uart_send_test_(char *message);
|
||||
|
||||
/// @brief Test the read_array() method
|
||||
/// @param message to display
|
||||
/// @return true if success
|
||||
bool uart_receive_test_(char *message);
|
||||
/// @}
|
||||
#endif
|
||||
|
||||
/// @brief the buffer where we store temporarily the bytes received
|
||||
WKRingBuffer<uint8_t, RING_BUFFER_SIZE> receive_buffer_;
|
||||
WeikaiComponent *parent_; ///< our WK2168component parent
|
||||
uint8_t channel_; ///< our Channel number
|
||||
uint8_t data_; ///< a one byte buffer for register read storage
|
||||
std::string name_; ///< name of the entity
|
||||
};
|
||||
|
||||
} // namespace weikai
|
||||
} // namespace esphome
|
304
esphome/components/weikai/wk_reg_def.h
Normal file
304
esphome/components/weikai/wk_reg_def.h
Normal file
@ -0,0 +1,304 @@
|
||||
/// @file wk_reg_def.h
|
||||
/// @author DrCoolZic
|
||||
/// @brief WeiKai component family - registers' definition
|
||||
/// @date Last Modified: 2024/02/18 15:49:18
|
||||
#pragma once
|
||||
|
||||
namespace esphome {
|
||||
namespace weikai {
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////
|
||||
/// Definition of the WeiKai registers
|
||||
////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
/// @defgroup wk2168_gr_ WeiKai Global Registers
|
||||
/// This section groups all **Global Registers**: these registers are global to the
|
||||
/// the WeiKai chip (i.e. independent of the UART channel used)
|
||||
/// @note only registers and parameters used have been fully documented
|
||||
/// @{
|
||||
|
||||
/// @brief Global Control Register - 00 0000
|
||||
/// @details @code
|
||||
/// -------------------------------------------------------------------------
|
||||
/// | b7 | b6 | b5 | b4 | b3 | b2 | b1 | b0 | bit
|
||||
/// -------------------------------------------------------------------------
|
||||
/// | M0 | M1 | RSV | C4EN | C3EN | C2EN | C1EN | name
|
||||
/// -------------------------------------------------------------------------
|
||||
/// | R | R | R | R | W/R | W/R | W/R | W/R | type
|
||||
/// -------------------------------------------------------------------------
|
||||
/// | 1 | 1 | 1 | 1 | 0 | 0 | 0 | 0 | reset
|
||||
/// -------------------------------------------------------------------------
|
||||
/// @endcode
|
||||
constexpr uint8_t WKREG_GENA = 0x00;
|
||||
/// @brief Channel 4 enable clock (0: disable, 1: enable)
|
||||
constexpr uint8_t GENA_C4EN = 1 << 3;
|
||||
/// @brief Channel 3 enable clock (0: disable, 1: enable)
|
||||
constexpr uint8_t GENA_C3EN = 1 << 2;
|
||||
/// @brief Channel 2 enable clock (0: disable, 1: enable)
|
||||
constexpr uint8_t GENA_C2EN = 1 << 1;
|
||||
/// @brief Channel 1 enable clock (0: disable, 1: enable)
|
||||
constexpr uint8_t GENA_C1EN = 1 << 0;
|
||||
|
||||
/// @brief Global Reset Register - 00 0001
|
||||
/// @details @code
|
||||
/// -------------------------------------------------------------------------
|
||||
/// | b7 | b6 | b5 | b4 | b3 | b2 | b1 | b0 | bit
|
||||
/// -------------------------------------------------------------------------
|
||||
/// | C4SLEEP| C3SLEEP| C2SLEEP| C1SLEEP| C4RST | C3RST | C2RST | C1RST | name
|
||||
/// -------------------------------------------------------------------------
|
||||
/// | R | R | R | R | W1/R0 | W1/R0 | W1/R0 | W1/R0 | type
|
||||
/// -------------------------------------------------------------------------
|
||||
/// | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | reset
|
||||
/// -------------------------------------------------------------------------
|
||||
/// @endcode
|
||||
constexpr uint8_t WKREG_GRST = 0x01;
|
||||
/// @brief Channel 4 soft reset (0: not reset, 1: reset)
|
||||
constexpr uint8_t GRST_C4RST = 1 << 3;
|
||||
/// @brief Channel 3 soft reset (0: not reset, 1: reset)
|
||||
constexpr uint8_t GRST_C3RST = 1 << 2;
|
||||
/// @brief Channel 2 soft reset (0: not reset, 1: reset)
|
||||
constexpr uint8_t GRST_C2RST = 1 << 1;
|
||||
/// @brief Channel 1 soft reset (0: not reset, 1: reset)
|
||||
constexpr uint8_t GRST_C1RST = 1 << 0;
|
||||
|
||||
/// @brief Global Master channel control register (not used) - 000010
|
||||
constexpr uint8_t WKREG_GMUT = 0x02;
|
||||
|
||||
/// Global interrupt register (not used) - 01 0000
|
||||
constexpr uint8_t WKREG_GIER = 0x10;
|
||||
|
||||
/// Global interrupt flag register (not used) 01 0001
|
||||
constexpr uint8_t WKREG_GIFR = 0x11;
|
||||
|
||||
/// @brief Global GPIO direction register - 10 0001
|
||||
/// @details @code
|
||||
/// -------------------------------------------------------------------------
|
||||
/// | b7 | b6 | b5 | b4 | b3 | b2 | b1 | b0 | bit
|
||||
/// -------------------------------------------------------------------------
|
||||
/// | PD7 | PD6 | PD5 | PD4 | PD3 | PD2 | PD1 | PD0 | name
|
||||
/// -------------------------------------------------------------------------
|
||||
/// | W/R | W/R | W/R | W/R | W/R | W/R | W/R | W/R | type
|
||||
/// -------------------------------------------------------------------------
|
||||
/// | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | reset
|
||||
/// -------------------------------------------------------------------------
|
||||
/// @endcode
|
||||
constexpr uint8_t WKREG_GPDIR = 0x21;
|
||||
|
||||
/// @brief Global GPIO data register - 11 0001
|
||||
/// @details @code
|
||||
/// -------------------------------------------------------------------------
|
||||
/// | b7 | b6 | b5 | b4 | b3 | b2 | b1 | b0 | bit
|
||||
/// -------------------------------------------------------------------------
|
||||
/// | PV7 | PV6 | PV5 | PV4 | PV3 | PV2 | PV1 | PV0 | name
|
||||
/// -------------------------------------------------------------------------
|
||||
/// | W/R | W/R | W/R | W/R | W/R | W/R | W/R | W/R | type
|
||||
/// -------------------------------------------------------------------------
|
||||
/// | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | reset
|
||||
/// -------------------------------------------------------------------------
|
||||
/// @endcode
|
||||
constexpr uint8_t WKREG_GPDAT = 0x31;
|
||||
|
||||
/// @}
|
||||
/// @defgroup WeiKai_cr_ WeiKai Channel Registers
|
||||
/// @brief Definition of the register linked to a particular channel
|
||||
/// @details This topic groups all the **Channel Registers**: these registers are specific
|
||||
/// to the a specific channel i.e. each channel has its own set of registers
|
||||
/// @note only registers and parameters used have been documented
|
||||
/// @{
|
||||
|
||||
/// @defgroup cr_p0 Channel registers when SPAGE=0
|
||||
/// @brief Definition of the register linked to a particular channel when SPAGE=0
|
||||
/// @details The channel registers are further splitted into two groups.
|
||||
/// This first group is defined when the Global register WKREG_SPAGE is 0
|
||||
/// @{
|
||||
|
||||
/// @brief Global Page register c0/c1 0011
|
||||
/// @details @code
|
||||
/// -------------------------------------------------------------------------
|
||||
/// | b7 | b6 | b5 | b4 | b3 | b2 | b1 | b0 | bit
|
||||
/// -------------------------------------------------------------------------
|
||||
/// | RSV | PAGE | name
|
||||
/// -------------------------------------------------------------------------
|
||||
/// | R | R | R | R | R | R | R | W/R | type
|
||||
/// -------------------------------------------------------------------------
|
||||
/// | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | reset
|
||||
/// -------------------------------------------------------------------------
|
||||
/// @endcode
|
||||
constexpr uint8_t WKREG_SPAGE = 0x03;
|
||||
|
||||
/// @brief Serial Control Register - c0/c1 0100
|
||||
/// @details @code
|
||||
/// -------------------------------------------------------------------------
|
||||
/// | b7 | b6 | b5 | b4 | b3 | b2 | b1 | b0 | bit
|
||||
/// -------------------------------------------------------------------------
|
||||
/// | RSV | SLPEN | TXEN | RXEN | name
|
||||
/// -------------------------------------------------------------------------
|
||||
/// | R | R | R | R | R | R/W | R/W | W/R | type
|
||||
/// -------------------------------------------------------------------------
|
||||
/// | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | reset
|
||||
/// -------------------------------------------------------------------------
|
||||
/// @endcode
|
||||
constexpr uint8_t WKREG_SCR = 0x04;
|
||||
/// @brief transmission control (0: enable, 1: disable)
|
||||
constexpr uint8_t SCR_TXEN = 1 << 1;
|
||||
/// @brief receiving control (0: enable, 1: disable)
|
||||
constexpr uint8_t SCR_RXEN = 1 << 0;
|
||||
|
||||
/// @brief Line Configuration Register - c0/c1 0101
|
||||
/// @details @code
|
||||
/// -------------------------------------------------------------------------
|
||||
/// | b7 | b6 | b5 | b4 | b3 | b2 | b1 | b0 | bit
|
||||
/// -------------------------------------------------------------------------
|
||||
/// | RSV | BREAK | IREN | PAEN | PARITY | STPL | name
|
||||
/// -------------------------------------------------------------------------
|
||||
/// | W/R | W/R | W/R | W/R | W/R | W/R | W/R | W/R | type
|
||||
/// -------------------------------------------------------------------------
|
||||
/// | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | reset
|
||||
/// -------------------------------------------------------------------------
|
||||
/// @endcode
|
||||
constexpr uint8_t WKREG_LCR = 0x05;
|
||||
/// @brief Parity enable (0: no check, 1: check)
|
||||
constexpr uint8_t LCR_PAEN = 1 << 3;
|
||||
/// @brief Parity force 0
|
||||
constexpr uint8_t LCR_PAR_F0 = 0 << 1;
|
||||
/// @brief Parity odd
|
||||
constexpr uint8_t LCR_PAR_ODD = 1 << 1;
|
||||
/// @brief Parity even
|
||||
constexpr uint8_t LCR_PAR_EVEN = 2 << 1;
|
||||
/// @brief Parity force 1
|
||||
constexpr uint8_t LCR_PAR_F1 = 3 << 1;
|
||||
/// @brief Stop length (0: 1 bit, 1: 2 bits)
|
||||
constexpr uint8_t LCR_STPL = 1 << 0;
|
||||
|
||||
/// @brief FIFO Control Register - c0/c1 0110
|
||||
/// @details @code
|
||||
/// -------------------------------------------------------------------------
|
||||
/// | b7 | b6 | b5 | b4 | b3 | b2 | b1 | b0 | bit
|
||||
/// -------------------------------------------------------------------------
|
||||
/// | TFTRIG | RFTRIG | TFEN | RFEN | TFRST | RFRST | name
|
||||
/// -------------------------------------------------------------------------
|
||||
/// | W/R | W/R | W/R | W/R | W/R | W/R | W/R | W/R | type
|
||||
/// -------------------------------------------------------------------------
|
||||
/// | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | reset
|
||||
/// -------------------------------------------------------------------------
|
||||
/// @endcode
|
||||
constexpr uint8_t WKREG_FCR = 0x06;
|
||||
/// @brief Transmitter FIFO enable
|
||||
constexpr uint8_t FCR_TFEN = 1 << 3;
|
||||
/// @brief Receiver FIFO enable
|
||||
constexpr uint8_t FCR_RFEN = 1 << 2;
|
||||
/// @brief Transmitter FIFO reset
|
||||
constexpr uint8_t FCR_TFRST = 1 << 1;
|
||||
/// @brief Receiver FIFO reset
|
||||
constexpr uint8_t FCR_RFRST = 1 << 0;
|
||||
|
||||
/// @brief Serial Interrupt Enable Register (not used) - c0/c1 0111
|
||||
constexpr uint8_t WKREG_SIER = 0x07;
|
||||
|
||||
/// @brief Serial Interrupt Flag Register (not used) - c0/c1 1000
|
||||
constexpr uint8_t WKREG_SIFR = 0x08;
|
||||
|
||||
/// @brief Transmitter FIFO Count - c0/c1 1001
|
||||
/// @details @code
|
||||
/// -------------------------------------------------------------------------
|
||||
/// | b7 | b6 | b5 | b4 | b3 | b2 | b1 | b0 |
|
||||
/// -------------------------------------------------------------------------
|
||||
/// | NUMBER OF DATA IN TRANSMITTER FIFO |
|
||||
/// -------------------------------------------------------------------------
|
||||
/// @endcode
|
||||
constexpr uint8_t WKREG_TFCNT = 0x09;
|
||||
|
||||
/// @brief Receiver FIFO count - c0/c1 1010
|
||||
/// @details @code
|
||||
/// -------------------------------------------------------------------------
|
||||
/// | b7 | b6 | b5 | b4 | b3 | b2 | b1 | b0 |
|
||||
/// -------------------------------------------------------------------------
|
||||
/// | NUMBER OF DATA IN RECEIVER FIFO |
|
||||
/// -------------------------------------------------------------------------
|
||||
/// @endcode
|
||||
constexpr uint8_t WKREG_RFCNT = 0x0A;
|
||||
|
||||
/// @brief FIFO Status Register - c0/c1 1011
|
||||
/// @details @code
|
||||
/// -------------------------------------------------------------------------
|
||||
/// | b7 | b6 | b5 | b4 | b3 | b2 | b1 | b0 | bit
|
||||
/// -------------------------------------------------------------------------
|
||||
/// | RFOE | RFLB | RFFE | RFPE | RFDAT | TFDAT | TFFULL | TBUSY | name
|
||||
/// -------------------------------------------------------------------------
|
||||
/// | R | W/R | W/R | W/R | W/R | W/R | W/R | W/R | type
|
||||
/// -------------------------------------------------------------------------
|
||||
/// | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | reset
|
||||
/// -------------------------------------------------------------------------
|
||||
/// @endcode
|
||||
/// @warning The received buffer can hold 256 bytes. However, as the RFCNT reg
|
||||
/// is 8 bits, if we have 256 byte in the register this is reported as 0 ! Therefore
|
||||
/// RFCNT=0 can indicate that there are 0 **or** 256 bytes in the buffer. If we
|
||||
/// have RFDAT = 1 and RFCNT = 0 it should be interpreted as 256 bytes in the FIFO.
|
||||
/// @note Note that in case of overflow the RFOE goes to one **but** as soon as you read
|
||||
/// the FSR this bit is cleared. Therefore Overflow can be read only once.
|
||||
/// @n The same problem applies to the transmit buffer but here we have to check the
|
||||
/// TFFULL flag. So if TFFULL is set and TFCNT is 0 this should be interpreted as 256
|
||||
constexpr uint8_t WKREG_FSR = 0x0B;
|
||||
/// @brief Receiver FIFO Overflow Error (0: no OE, 1: OE)
|
||||
constexpr uint8_t FSR_RFOE = 1 << 7;
|
||||
/// @brief Receiver FIFO Line Break (0: no LB, 1: LB)
|
||||
constexpr uint8_t FSR_RFLB = 1 << 6;
|
||||
/// @brief Receiver FIFO Frame Error (0: no FE, 1: FE)
|
||||
constexpr uint8_t FSR_RFFE = 1 << 5;
|
||||
/// @brief Receiver Parity Error (0: no PE, 1: PE)
|
||||
constexpr uint8_t FSR_RFPE = 1 << 4;
|
||||
/// @brief Receiver FIFO count (0: empty, 1: not empty)
|
||||
constexpr uint8_t FSR_RFDAT = 1 << 3;
|
||||
/// @brief Transmitter FIFO count (0: empty, 1: not empty)
|
||||
constexpr uint8_t FSR_TFDAT = 1 << 2;
|
||||
/// @brief Transmitter FIFO full (0: not full, 1: full)
|
||||
constexpr uint8_t FSR_TFFULL = 1 << 1;
|
||||
/// @brief Transmitter busy (0 nothing to transmit, 1: transmitter busy sending)
|
||||
constexpr uint8_t FSR_TBUSY = 1 << 0;
|
||||
|
||||
/// @brief Line Status Register (not used - using FIFO)
|
||||
constexpr uint8_t WKREG_LSR = 0x0C;
|
||||
|
||||
/// @brief FIFO Data Register (not used - does not seems to work)
|
||||
constexpr uint8_t WKREG_FDAT = 0x0D;
|
||||
|
||||
/// @}
|
||||
/// @defgroup cr_p1 Channel registers for SPAGE=1
|
||||
/// @brief Definition of the register linked to a particular channel when SPAGE=1
|
||||
/// @details The channel registers are further splitted into two groups.
|
||||
/// This second group is defined when the Global register WKREG_SPAGE is 1
|
||||
/// @{
|
||||
|
||||
/// @brief Baud rate configuration register: high byte - c0/c1 0100
|
||||
/// @details @code
|
||||
/// -------------------------------------------------------------------------
|
||||
/// | b7 | b6 | b5 | b4 | b3 | b2 | b1 | b0 |
|
||||
/// -------------------------------------------------------------------------
|
||||
/// | High byte of the baud rate |
|
||||
/// -------------------------------------------------------------------------
|
||||
/// @endcode
|
||||
constexpr uint8_t WKREG_BRH = 0x04;
|
||||
|
||||
/// @brief Baud rate configuration register: low byte - c0/c1 0101
|
||||
/// @details @code
|
||||
/// -------------------------------------------------------------------------
|
||||
/// | b7 | b6 | b5 | b4 | b3 | b2 | b1 | b0 |
|
||||
/// -------------------------------------------------------------------------
|
||||
/// | Low byte of the baud rate |
|
||||
/// -------------------------------------------------------------------------
|
||||
/// @endcode
|
||||
constexpr uint8_t WKREG_BRL = 0x05;
|
||||
|
||||
/// @brief Baud rate configuration register decimal part - c0/c1 0110
|
||||
constexpr uint8_t WKREG_BRD = 0x06;
|
||||
|
||||
/// @brief Receive FIFO Interrupt trigger configuration (not used) - c0/c1 0111
|
||||
constexpr uint8_t WKREG_RFI = 0x07;
|
||||
|
||||
/// @brief Transmit FIFO interrupt trigger configuration (not used) - c0/c1 1000
|
||||
constexpr uint8_t WKREG_TFI = 0x08;
|
||||
|
||||
/// @}
|
||||
/// @}
|
||||
} // namespace weikai
|
||||
} // namespace esphome
|
1
esphome/components/weikai_i2c/__init__.py
Normal file
1
esphome/components/weikai_i2c/__init__.py
Normal file
@ -0,0 +1 @@
|
||||
CODEOWNERS = ["@DrCoolZic"]
|
177
esphome/components/weikai_i2c/weikai_i2c.cpp
Normal file
177
esphome/components/weikai_i2c/weikai_i2c.cpp
Normal file
@ -0,0 +1,177 @@
|
||||
/// @file weikai_i2c.cpp
|
||||
/// @brief WeiKai component family - classes implementation
|
||||
/// @date Last Modified: 2024/04/06 14:43:31
|
||||
/// @details The classes declared in this file can be used by the Weikai family
|
||||
|
||||
#include "weikai_i2c.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace weikai_i2c {
|
||||
static const char *const TAG = "weikai_i2c";
|
||||
|
||||
/// @brief Display a buffer in hexadecimal format (32 hex values / line).
|
||||
void print_buffer(const uint8_t *data, size_t length) {
|
||||
char hex_buffer[100];
|
||||
hex_buffer[(3 * 32) + 1] = 0;
|
||||
for (size_t i = 0; i < length; i++) {
|
||||
snprintf(&hex_buffer[3 * (i % 32)], sizeof(hex_buffer), "%02X ", data[i]);
|
||||
if (i % 32 == 31) {
|
||||
ESP_LOGVV(TAG, " %s", hex_buffer);
|
||||
}
|
||||
}
|
||||
if (length % 32) {
|
||||
// null terminate if incomplete line
|
||||
hex_buffer[3 * (length % 32) + 2] = 0;
|
||||
ESP_LOGVV(TAG, " %s", hex_buffer);
|
||||
}
|
||||
}
|
||||
|
||||
static const char *const REG_TO_STR_P0[16] = {"GENA", "GRST", "GMUT", "SPAGE", "SCR", "LCR", "FCR", "SIER",
|
||||
"SIFR", "TFCNT", "RFCNT", "FSR", "LSR", "FDAT", "FWCR", "RS485"};
|
||||
static const char *const REG_TO_STR_P1[16] = {"GENA", "GRST", "GMUT", "SPAGE", "BAUD1", "BAUD0", "PRES", "RFTL",
|
||||
"TFTL", "FWTH", "FWTL", "XON1", "XOFF1", "SADR", "SAEN", "RTSDLY"};
|
||||
using namespace weikai;
|
||||
// method to print a register value as text: used in the log messages ...
|
||||
const char *reg_to_str(int reg, bool page1) {
|
||||
if (reg == WKREG_GPDAT) {
|
||||
return "GPDAT";
|
||||
} else if (reg == WKREG_GPDIR) {
|
||||
return "GPDIR";
|
||||
} else {
|
||||
return page1 ? REG_TO_STR_P1[reg & 0x0F] : REG_TO_STR_P0[reg & 0x0F];
|
||||
}
|
||||
}
|
||||
enum RegType { REG = 0, FIFO = 1 }; ///< Register or FIFO
|
||||
|
||||
/// @brief Computes the I²C bus's address used to access the component
|
||||
/// @param base_address the base address of the component - set by the A1 A0 pins
|
||||
/// @param channel (0-3) the UART channel
|
||||
/// @param fifo (0-1) 0 = access to internal register, 1 = direct access to fifo
|
||||
/// @return the i2c address to use
|
||||
inline uint8_t i2c_address(uint8_t base_address, uint8_t channel, RegType fifo) {
|
||||
// the address of the device is:
|
||||
// +----+----+----+----+----+----+----+----+
|
||||
// | 0 | A1 | A0 | 1 | 0 | C1 | C0 | F |
|
||||
// +----+----+----+----+----+----+----+----+
|
||||
// where:
|
||||
// - A1,A0 is the address read from A1,A0 switch
|
||||
// - C1,C0 is the channel number (in practice only 00 or 01)
|
||||
// - F is: 0 when accessing register, one when accessing FIFO
|
||||
uint8_t const addr = base_address | channel << 1 | fifo << 0;
|
||||
return addr;
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// The WeikaiRegisterI2C methods
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
uint8_t WeikaiRegisterI2C::read_reg() const {
|
||||
uint8_t value = 0x00;
|
||||
WeikaiComponentI2C *comp_i2c = static_cast<WeikaiComponentI2C *>(this->comp_);
|
||||
uint8_t address = i2c_address(comp_i2c->base_address_, this->channel_, REG);
|
||||
comp_i2c->set_i2c_address(address);
|
||||
auto error = comp_i2c->read_register(this->register_, &value, 1);
|
||||
if (error == i2c::NO_ERROR) {
|
||||
this->comp_->status_clear_warning();
|
||||
ESP_LOGVV(TAG, "WeikaiRegisterI2C::read_reg() @%02X reg=%s ch=%u I2C_code:%d, buf=%02X", address,
|
||||
reg_to_str(this->register_, comp_i2c->page1()), this->channel_, (int) error, value);
|
||||
} else { // error
|
||||
this->comp_->status_set_warning();
|
||||
ESP_LOGE(TAG, "WeikaiRegisterI2C::read_reg() @%02X reg=%s ch=%u I2C_code:%d, buf=%02X", address,
|
||||
reg_to_str(this->register_, comp_i2c->page1()), this->channel_, (int) error, value);
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
void WeikaiRegisterI2C::read_fifo(uint8_t *data, size_t length) const {
|
||||
WeikaiComponentI2C *comp_i2c = static_cast<WeikaiComponentI2C *>(this->comp_);
|
||||
uint8_t address = i2c_address(comp_i2c->base_address_, this->channel_, FIFO);
|
||||
comp_i2c->set_i2c_address(address);
|
||||
auto error = comp_i2c->read(data, length);
|
||||
if (error == i2c::NO_ERROR) {
|
||||
this->comp_->status_clear_warning();
|
||||
#ifdef ESPHOME_LOG_HAS_VERY_VERBOSE
|
||||
ESP_LOGVV(TAG, "WeikaiRegisterI2C::read_fifo() @%02X ch=%d I2C_code:%d len=%d buffer", address, this->channel_,
|
||||
(int) error, length);
|
||||
print_buffer(data, length);
|
||||
#endif
|
||||
} else { // error
|
||||
this->comp_->status_set_warning();
|
||||
ESP_LOGE(TAG, "WeikaiRegisterI2C::read_fifo() @%02X reg=N/A ch=%d I2C_code:%d len=%d buf=%02X...", address,
|
||||
this->channel_, (int) error, length, data[0]);
|
||||
}
|
||||
}
|
||||
|
||||
void WeikaiRegisterI2C::write_reg(uint8_t value) {
|
||||
WeikaiComponentI2C *comp_i2c = static_cast<WeikaiComponentI2C *>(this->comp_);
|
||||
uint8_t address = i2c_address(comp_i2c->base_address_, this->channel_, REG); // update the i2c bus
|
||||
comp_i2c->set_i2c_address(address);
|
||||
auto error = comp_i2c->write_register(this->register_, &value, 1);
|
||||
if (error == i2c::NO_ERROR) {
|
||||
this->comp_->status_clear_warning();
|
||||
ESP_LOGVV(TAG, "WK2168Reg::write_reg() @%02X reg=%s ch=%d I2C_code:%d buf=%02X", address,
|
||||
reg_to_str(this->register_, comp_i2c->page1()), this->channel_, (int) error, value);
|
||||
} else { // error
|
||||
this->comp_->status_set_warning();
|
||||
ESP_LOGE(TAG, "WK2168Reg::write_reg() @%02X reg=%s ch=%d I2C_code:%d buf=%d", address,
|
||||
reg_to_str(this->register_, comp_i2c->page1()), this->channel_, (int) error, value);
|
||||
}
|
||||
}
|
||||
|
||||
void WeikaiRegisterI2C::write_fifo(uint8_t *data, size_t length) {
|
||||
WeikaiComponentI2C *comp_i2c = static_cast<WeikaiComponentI2C *>(this->comp_);
|
||||
uint8_t address = i2c_address(comp_i2c->base_address_, this->channel_, FIFO); // set fifo flag
|
||||
comp_i2c->set_i2c_address(address);
|
||||
auto error = comp_i2c->write(data, length);
|
||||
if (error == i2c::NO_ERROR) {
|
||||
this->comp_->status_clear_warning();
|
||||
#ifdef ESPHOME_LOG_HAS_VERY_VERBOSE
|
||||
ESP_LOGVV(TAG, "WK2168Reg::write_fifo() @%02X ch=%d I2C_code:%d len=%d buffer", address, this->channel_,
|
||||
(int) error, length);
|
||||
print_buffer(data, length);
|
||||
#endif
|
||||
} else { // error
|
||||
this->comp_->status_set_warning();
|
||||
ESP_LOGE(TAG, "WK2168Reg::write_fifo() @%02X reg=N/A, ch=%d I2C_code:%d len=%d, buf=%02X...", address,
|
||||
this->channel_, (int) error, length, data[0]);
|
||||
}
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// The WeikaiComponentI2C methods
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
void WeikaiComponentI2C::setup() {
|
||||
// before any manipulation we store the address to base_address_ for future use
|
||||
this->base_address_ = this->address_;
|
||||
ESP_LOGCONFIG(TAG, "Setting up wk2168_i2c: %s with %d UARTs at @%02X ...", this->get_name(), this->children_.size(),
|
||||
this->base_address_);
|
||||
|
||||
// enable all channels
|
||||
this->reg(WKREG_GENA, 0) = GENA_C1EN | GENA_C2EN | GENA_C3EN | GENA_C4EN;
|
||||
// reset all channels
|
||||
this->reg(WKREG_GRST, 0) = GRST_C1RST | GRST_C2RST | GRST_C3RST | GRST_C4RST;
|
||||
// initialize the spage register to page 0
|
||||
this->reg(WKREG_SPAGE, 0) = 0;
|
||||
this->page1_ = false;
|
||||
|
||||
// we setup our children channels
|
||||
for (auto *child : this->children_) {
|
||||
child->setup_channel();
|
||||
}
|
||||
}
|
||||
|
||||
void WeikaiComponentI2C::dump_config() {
|
||||
ESP_LOGCONFIG(TAG, "Initialization of %s with %d UARTs completed", this->get_name(), this->children_.size());
|
||||
ESP_LOGCONFIG(TAG, " Crystal: %" PRIu32, this->crystal_);
|
||||
if (test_mode_)
|
||||
ESP_LOGCONFIG(TAG, " Test mode: %d", test_mode_);
|
||||
ESP_LOGCONFIG(TAG, " Transfer buffer size: %d", XFER_MAX_SIZE);
|
||||
this->address_ = this->base_address_; // we restore the base_address before display (less confusing)
|
||||
LOG_I2C_DEVICE(this);
|
||||
|
||||
for (auto *child : this->children_) {
|
||||
child->dump_channel();
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace weikai_i2c
|
||||
} // namespace esphome
|
61
esphome/components/weikai_i2c/weikai_i2c.h
Normal file
61
esphome/components/weikai_i2c/weikai_i2c.h
Normal file
@ -0,0 +1,61 @@
|
||||
/// @file weikai_i2c.h
|
||||
/// @author DrCoolZic
|
||||
/// @brief WeiKai component family - classes declaration
|
||||
/// @date Last Modified: 2024/03/01 13:31:57
|
||||
/// @details The classes declared in this file can be used by the Weikai family
|
||||
/// wk2132_i2c, wk2168_i2c, wk2204_i2c, wk2212_i2c
|
||||
|
||||
#pragma once
|
||||
#include <bitset>
|
||||
#include <memory>
|
||||
#include "esphome/core/component.h"
|
||||
#include "esphome/components/uart/uart.h"
|
||||
#include "esphome/components/i2c/i2c.h"
|
||||
#include "esphome/components/weikai/weikai.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace weikai_i2c {
|
||||
|
||||
class WeikaiComponentI2C;
|
||||
|
||||
// using namespace weikai;
|
||||
////////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief WeikaiRegisterI2C objects acts as proxies to access remote register through an I2C Bus
|
||||
////////////////////////////////////////////////////////////////////////////////////
|
||||
class WeikaiRegisterI2C : public weikai::WeikaiRegister {
|
||||
public:
|
||||
uint8_t read_reg() const override;
|
||||
void write_reg(uint8_t value) override;
|
||||
void read_fifo(uint8_t *data, size_t length) const override;
|
||||
void write_fifo(uint8_t *data, size_t length) override;
|
||||
|
||||
protected:
|
||||
friend WeikaiComponentI2C;
|
||||
WeikaiRegisterI2C(weikai::WeikaiComponent *const comp, uint8_t reg, uint8_t channel)
|
||||
: weikai::WeikaiRegister(comp, reg, channel) {}
|
||||
};
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief The WeikaiComponentI2C class stores the information to the WeiKai component
|
||||
/// connected through an I2C bus.
|
||||
////////////////////////////////////////////////////////////////////////////////////
|
||||
class WeikaiComponentI2C : public weikai::WeikaiComponent, public i2c::I2CDevice {
|
||||
public:
|
||||
weikai::WeikaiRegister ®(uint8_t reg, uint8_t channel) override {
|
||||
reg_i2c_.register_ = reg;
|
||||
reg_i2c_.channel_ = channel;
|
||||
return reg_i2c_;
|
||||
}
|
||||
|
||||
//
|
||||
// override Component methods
|
||||
//
|
||||
void setup() override;
|
||||
void dump_config() override;
|
||||
|
||||
uint8_t base_address_; ///< base address of I2C device
|
||||
WeikaiRegisterI2C reg_i2c_{this, 0, 0}; ///< init to this component
|
||||
};
|
||||
|
||||
} // namespace weikai_i2c
|
||||
} // namespace esphome
|
1
esphome/components/weikai_spi/__init__.py
Normal file
1
esphome/components/weikai_spi/__init__.py
Normal file
@ -0,0 +1 @@
|
||||
CODEOWNERS = ["@DrCoolZic"]
|
189
esphome/components/weikai_spi/weikai_spi.cpp
Normal file
189
esphome/components/weikai_spi/weikai_spi.cpp
Normal file
@ -0,0 +1,189 @@
|
||||
/// @file weikai_spi.cpp
|
||||
/// @brief WeiKai component family - classes implementation
|
||||
/// @date Last Modified: 2024/04/06 14:46:09
|
||||
/// @details The classes declared in this file can be used by the Weikai family
|
||||
|
||||
#include "weikai_spi.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace weikai_spi {
|
||||
using namespace weikai;
|
||||
static const char *const TAG = "weikai_spi";
|
||||
|
||||
/// @brief convert an int to binary representation as C++ std::string
|
||||
/// @param val integer to convert
|
||||
/// @return a std::string
|
||||
inline std::string i2s(uint8_t val) { return std::bitset<8>(val).to_string(); }
|
||||
/// Convert std::string to C string
|
||||
#define I2S2CS(val) (i2s(val).c_str())
|
||||
|
||||
/// @brief measure the time elapsed between two calls
|
||||
/// @param last_time time of the previous call
|
||||
/// @return the elapsed time in microseconds
|
||||
uint32_t elapsed_ms(uint32_t &last_time) {
|
||||
uint32_t e = millis() - last_time;
|
||||
last_time = millis();
|
||||
return e;
|
||||
};
|
||||
|
||||
/// @brief Converts the parity enum value to a C string
|
||||
/// @param parity enum
|
||||
/// @return the string
|
||||
const char *p2s(uart::UARTParityOptions parity) {
|
||||
using namespace uart;
|
||||
switch (parity) {
|
||||
case UART_CONFIG_PARITY_NONE:
|
||||
return "NONE";
|
||||
case UART_CONFIG_PARITY_EVEN:
|
||||
return "EVEN";
|
||||
case UART_CONFIG_PARITY_ODD:
|
||||
return "ODD";
|
||||
default:
|
||||
return "UNKNOWN";
|
||||
}
|
||||
}
|
||||
|
||||
/// @brief Display a buffer in hexadecimal format (32 hex values / line).
|
||||
void print_buffer(const uint8_t *data, size_t length) {
|
||||
char hex_buffer[100];
|
||||
hex_buffer[(3 * 32) + 1] = 0;
|
||||
for (size_t i = 0; i < length; i++) {
|
||||
snprintf(&hex_buffer[3 * (i % 32)], sizeof(hex_buffer), "%02X ", data[i]);
|
||||
if (i % 32 == 31) {
|
||||
ESP_LOGVV(TAG, " %s", hex_buffer);
|
||||
}
|
||||
}
|
||||
if (length % 32) {
|
||||
// null terminate if incomplete line
|
||||
hex_buffer[3 * (length % 32) + 2] = 0;
|
||||
ESP_LOGVV(TAG, " %s", hex_buffer);
|
||||
}
|
||||
}
|
||||
|
||||
static const char *const REG_TO_STR_P0[16] = {"GENA", "GRST", "GMUT", "SPAGE", "SCR", "LCR", "FCR", "SIER",
|
||||
"SIFR", "TFCNT", "RFCNT", "FSR", "LSR", "FDAT", "FWCR", "RS485"};
|
||||
static const char *const REG_TO_STR_P1[16] = {"GENA", "GRST", "GMUT", "SPAGE", "BAUD1", "BAUD0", "PRES", "RFTL",
|
||||
"TFTL", "FWTH", "FWTL", "XON1", "XOFF1", "SADR", "SAEN", "RTSDLY"};
|
||||
|
||||
// method to print a register value as text: used in the log messages ...
|
||||
const char *reg_to_str(int reg, bool page1) {
|
||||
if (reg == WKREG_GPDAT) {
|
||||
return "GPDAT";
|
||||
} else if (reg == WKREG_GPDIR) {
|
||||
return "GPDIR";
|
||||
} else {
|
||||
return page1 ? REG_TO_STR_P1[reg & 0x0F] : REG_TO_STR_P0[reg & 0x0F];
|
||||
}
|
||||
}
|
||||
|
||||
enum RegType { REG = 0, FIFO = 1 }; ///< Register or FIFO
|
||||
enum CmdType { WRITE_CMD = 0, READ_CMD = 1 }; ///< Read or Write transfer
|
||||
|
||||
/// @brief Computes the SPI command byte
|
||||
/// @param transfer_type read or write command
|
||||
/// @param reg (0-15) the address of the register
|
||||
/// @param channel (0-3) the UART channel
|
||||
/// @param fifo (0-1) 0 = access to internal register, 1 = direct access to fifo
|
||||
/// @return the spi command byte
|
||||
/// @details
|
||||
/// +------+------+------+------+------+------+------+------+
|
||||
/// | FIFO | R/W | C1-C0 | A3-A0 |
|
||||
/// +------+------+-------------+---------------------------+
|
||||
/// FIFO: 0 = register, 1 = FIFO
|
||||
/// R/W: 0 = write, 1 = read
|
||||
/// C1-C0: Channel (0-1)
|
||||
/// A3-A0: Address (0-F)
|
||||
inline static uint8_t cmd_byte(RegType fifo, CmdType transfer_type, uint8_t channel, uint8_t reg) {
|
||||
return (fifo << 7 | transfer_type << 6 | channel << 4 | reg << 0);
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// The WeikaiRegisterSPI methods
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
uint8_t WeikaiRegisterSPI::read_reg() const {
|
||||
auto *spi_comp = static_cast<WeikaiComponentSPI *>(this->comp_);
|
||||
uint8_t cmd = cmd_byte(REG, READ_CMD, this->channel_, this->register_);
|
||||
spi_comp->enable();
|
||||
spi_comp->write_byte(cmd);
|
||||
uint8_t val = spi_comp->read_byte();
|
||||
spi_comp->disable();
|
||||
ESP_LOGVV(TAG, "WeikaiRegisterSPI::read_reg() cmd=%s(%02X) reg=%s ch=%d buf=%02X", I2S2CS(cmd), cmd,
|
||||
reg_to_str(this->register_, this->comp_->page1()), this->channel_, val);
|
||||
return val;
|
||||
}
|
||||
|
||||
void WeikaiRegisterSPI::read_fifo(uint8_t *data, size_t length) const {
|
||||
auto *spi_comp = static_cast<WeikaiComponentSPI *>(this->comp_);
|
||||
uint8_t cmd = cmd_byte(FIFO, READ_CMD, this->channel_, this->register_);
|
||||
spi_comp->enable();
|
||||
spi_comp->write_byte(cmd);
|
||||
spi_comp->read_array(data, length);
|
||||
spi_comp->disable();
|
||||
#ifdef ESPHOME_LOG_HAS_VERY_VERBOSE
|
||||
ESP_LOGVV(TAG, "WeikaiRegisterSPI::read_fifo() cmd=%s(%02X) ch=%d len=%d buffer", I2S2CS(cmd), cmd, this->channel_,
|
||||
length);
|
||||
print_buffer(data, length);
|
||||
#endif
|
||||
}
|
||||
|
||||
void WeikaiRegisterSPI::write_reg(uint8_t value) {
|
||||
auto *spi_comp = static_cast<WeikaiComponentSPI *>(this->comp_);
|
||||
uint8_t buf[2]{cmd_byte(REG, WRITE_CMD, this->channel_, this->register_), value};
|
||||
spi_comp->enable();
|
||||
spi_comp->write_array(buf, 2);
|
||||
spi_comp->disable();
|
||||
ESP_LOGVV(TAG, "WeikaiRegisterSPI::write_reg() cmd=%s(%02X) reg=%s ch=%d buf=%02X", I2S2CS(buf[0]), buf[0],
|
||||
reg_to_str(this->register_, this->comp_->page1()), this->channel_, buf[1]);
|
||||
}
|
||||
|
||||
void WeikaiRegisterSPI::write_fifo(uint8_t *data, size_t length) {
|
||||
auto *spi_comp = static_cast<WeikaiComponentSPI *>(this->comp_);
|
||||
uint8_t cmd = cmd_byte(FIFO, WRITE_CMD, this->channel_, this->register_);
|
||||
spi_comp->enable();
|
||||
spi_comp->write_byte(cmd);
|
||||
spi_comp->write_array(data, length);
|
||||
spi_comp->disable();
|
||||
|
||||
#ifdef ESPHOME_LOG_HAS_VERY_VERBOSE
|
||||
ESP_LOGVV(TAG, "WeikaiRegisterSPI::write_fifo() cmd=%s(%02X) ch=%d len=%d buffer", I2S2CS(cmd), cmd, this->channel_,
|
||||
length);
|
||||
print_buffer(data, length);
|
||||
#endif
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// The WeikaiComponentSPI methods
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
void WeikaiComponentSPI::setup() {
|
||||
using namespace weikai;
|
||||
ESP_LOGCONFIG(TAG, "Setting up wk2168_spi: %s with %d UARTs...", this->get_name(), this->children_.size());
|
||||
this->spi_setup();
|
||||
// enable all channels
|
||||
this->reg(WKREG_GENA, 0) = GENA_C1EN | GENA_C2EN | GENA_C3EN | GENA_C4EN;
|
||||
// reset all channels
|
||||
this->reg(WKREG_GRST, 0) = GRST_C1RST | GRST_C2RST | GRST_C3RST | GRST_C4RST;
|
||||
// initialize the spage register to page 0
|
||||
this->reg(WKREG_SPAGE, 0) = 0;
|
||||
this->page1_ = false;
|
||||
|
||||
// we setup our children channels
|
||||
for (auto *child : this->children_) {
|
||||
child->setup_channel();
|
||||
}
|
||||
}
|
||||
|
||||
void WeikaiComponentSPI::dump_config() {
|
||||
ESP_LOGCONFIG(TAG, "Initialization of %s with %d UARTs completed", this->get_name(), this->children_.size());
|
||||
ESP_LOGCONFIG(TAG, " Crystal: %" PRIu32 "", this->crystal_);
|
||||
if (test_mode_)
|
||||
ESP_LOGCONFIG(TAG, " Test mode: %d", test_mode_);
|
||||
ESP_LOGCONFIG(TAG, " Transfer buffer size: %d", XFER_MAX_SIZE);
|
||||
LOG_PIN(" CS Pin: ", this->cs_);
|
||||
|
||||
for (auto *child : this->children_) {
|
||||
child->dump_channel();
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace weikai_spi
|
||||
} // namespace esphome
|
54
esphome/components/weikai_spi/weikai_spi.h
Normal file
54
esphome/components/weikai_spi/weikai_spi.h
Normal file
@ -0,0 +1,54 @@
|
||||
/// @file weikai.h
|
||||
/// @author DrCoolZic
|
||||
/// @brief WeiKai component family - classes declaration
|
||||
/// @date Last Modified: 2024/02/29 17:20:32
|
||||
/// @details The classes declared in this file can be used by the Weikai family
|
||||
/// wk2124_spi, wk2132_spi, wk2168_spi, wk2204_spi, wk2212_spi,
|
||||
|
||||
#pragma once
|
||||
#include <bitset>
|
||||
#include <memory>
|
||||
#include "esphome/core/component.h"
|
||||
#include "esphome/components/uart/uart.h"
|
||||
#include "esphome/components/spi/spi.h"
|
||||
#include "esphome/components/weikai/weikai.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace weikai_spi {
|
||||
////////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief WeikaiRegisterSPI objects acts as proxies to access remote register through an SPI Bus
|
||||
////////////////////////////////////////////////////////////////////////////////////
|
||||
class WeikaiRegisterSPI : public weikai::WeikaiRegister {
|
||||
public:
|
||||
WeikaiRegisterSPI(weikai::WeikaiComponent *const comp, uint8_t reg, uint8_t channel)
|
||||
: weikai::WeikaiRegister(comp, reg, channel) {}
|
||||
|
||||
uint8_t read_reg() const override;
|
||||
void write_reg(uint8_t value) override;
|
||||
void read_fifo(uint8_t *data, size_t length) const override;
|
||||
void write_fifo(uint8_t *data, size_t length) override;
|
||||
};
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief The WeikaiComponentSPI class stores the information to the WeiKai component
|
||||
/// connected through an SPI bus.
|
||||
////////////////////////////////////////////////////////////////////////////////////
|
||||
class WeikaiComponentSPI : public weikai::WeikaiComponent,
|
||||
public spi::SPIDevice<spi::BIT_ORDER_MSB_FIRST, spi::CLOCK_POLARITY_LOW,
|
||||
spi::CLOCK_PHASE_LEADING, spi::DATA_RATE_1MHZ> {
|
||||
public:
|
||||
weikai::WeikaiRegister ®(uint8_t reg, uint8_t channel) override {
|
||||
reg_spi_.register_ = reg;
|
||||
reg_spi_.channel_ = channel;
|
||||
return reg_spi_;
|
||||
}
|
||||
|
||||
void setup() override;
|
||||
void dump_config() override;
|
||||
|
||||
protected:
|
||||
WeikaiRegisterSPI reg_spi_{this, 0, 0}; ///< init to this component
|
||||
};
|
||||
|
||||
} // namespace weikai_spi
|
||||
} // namespace esphome
|
30
esphome/components/wk2132_i2c/__init__.py
Normal file
30
esphome/components/wk2132_i2c/__init__.py
Normal file
@ -0,0 +1,30 @@
|
||||
import esphome.codegen as cg
|
||||
import esphome.config_validation as cv
|
||||
from esphome.components import i2c, weikai
|
||||
from esphome.const import CONF_ID
|
||||
|
||||
CODEOWNERS = ["@DrCoolZic"]
|
||||
DEPENDENCIES = ["i2c"]
|
||||
AUTO_LOAD = ["weikai", "weikai_i2c"]
|
||||
MULTI_CONF = True
|
||||
|
||||
weikai_i2c_ns = cg.esphome_ns.namespace("weikai_i2c")
|
||||
WeikaiComponentI2C = weikai_i2c_ns.class_(
|
||||
"WeikaiComponentI2C", weikai.WeikaiComponent, i2c.I2CDevice
|
||||
)
|
||||
|
||||
CONFIG_SCHEMA = cv.All(
|
||||
weikai.WKBASE_SCHEMA.extend(
|
||||
{
|
||||
cv.GenerateID(): cv.declare_id(WeikaiComponentI2C),
|
||||
}
|
||||
).extend(i2c.i2c_device_schema(0x2C)),
|
||||
weikai.check_channel_max_2,
|
||||
)
|
||||
|
||||
|
||||
async def to_code(config):
|
||||
var = cg.new_Pvariable(config[CONF_ID])
|
||||
cg.add(var.set_name(str(config[CONF_ID])))
|
||||
await weikai.register_weikai(var, config)
|
||||
await i2c.register_i2c_device(var, config)
|
4
esphome/components/wk2132_i2c/wk2132_i2c.cpp
Normal file
4
esphome/components/wk2132_i2c/wk2132_i2c.cpp
Normal file
@ -0,0 +1,4 @@
|
||||
/* compiling with esp-idf framework requires a .cpp file for some reason ? */
|
||||
namespace esphome {
|
||||
namespace wk2132_i2c {}
|
||||
} // namespace esphome
|
31
esphome/components/wk2132_spi/__init__.py
Normal file
31
esphome/components/wk2132_spi/__init__.py
Normal file
@ -0,0 +1,31 @@
|
||||
import esphome.codegen as cg
|
||||
import esphome.config_validation as cv
|
||||
from esphome.components import spi, weikai
|
||||
|
||||
from esphome.const import CONF_ID
|
||||
|
||||
CODEOWNERS = ["@DrCoolZic"]
|
||||
DEPENDENCIES = ["spi"]
|
||||
AUTO_LOAD = ["weikai", "weikai_spi"]
|
||||
MULTI_CONF = True
|
||||
|
||||
weikai_spi_ns = cg.esphome_ns.namespace("weikai_spi")
|
||||
WeikaiComponentSPI = weikai_spi_ns.class_(
|
||||
"WeikaiComponentSPI", weikai.WeikaiComponent, spi.SPIDevice
|
||||
)
|
||||
|
||||
CONFIG_SCHEMA = cv.All(
|
||||
weikai.WKBASE_SCHEMA.extend(
|
||||
{
|
||||
cv.GenerateID(): cv.declare_id(WeikaiComponentSPI),
|
||||
}
|
||||
).extend(spi.spi_device_schema()),
|
||||
weikai.check_channel_max_2,
|
||||
)
|
||||
|
||||
|
||||
async def to_code(config):
|
||||
var = cg.new_Pvariable(config[CONF_ID])
|
||||
cg.add(var.set_name(str(config[CONF_ID])))
|
||||
await weikai.register_weikai(var, config)
|
||||
await spi.register_spi_device(var, config)
|
64
esphome/components/wk2168_i2c/__init__.py
Normal file
64
esphome/components/wk2168_i2c/__init__.py
Normal file
@ -0,0 +1,64 @@
|
||||
import esphome.codegen as cg
|
||||
import esphome.config_validation as cv
|
||||
from esphome import pins
|
||||
from esphome.components import i2c, weikai
|
||||
from esphome.const import (
|
||||
CONF_ID,
|
||||
CONF_INVERTED,
|
||||
CONF_MODE,
|
||||
CONF_NUMBER,
|
||||
)
|
||||
|
||||
CODEOWNERS = ["@DrCoolZic"]
|
||||
DEPENDENCIES = ["i2c"]
|
||||
AUTO_LOAD = ["weikai", "weikai_i2c"]
|
||||
MULTI_CONF = True
|
||||
CONF_WK2168_I2C = "wk2168_i2c"
|
||||
|
||||
weikai_ns = cg.esphome_ns.namespace("weikai")
|
||||
weikai_i2c_ns = cg.esphome_ns.namespace("weikai_i2c")
|
||||
WeikaiComponentI2C = weikai_i2c_ns.class_(
|
||||
"WeikaiComponentI2C", weikai.WeikaiComponent, i2c.I2CDevice
|
||||
)
|
||||
WeikaiGPIOPin = weikai_ns.class_(
|
||||
"WeikaiGPIOPin", cg.GPIOPin, cg.Parented.template(WeikaiComponentI2C)
|
||||
)
|
||||
|
||||
CONFIG_SCHEMA = cv.All(
|
||||
weikai.WKBASE_SCHEMA.extend(
|
||||
{
|
||||
cv.GenerateID(): cv.declare_id(WeikaiComponentI2C),
|
||||
}
|
||||
).extend(i2c.i2c_device_schema(0x2C)),
|
||||
weikai.check_channel_max_4,
|
||||
)
|
||||
|
||||
|
||||
async def to_code(config):
|
||||
var = cg.new_Pvariable(config[CONF_ID])
|
||||
cg.add(var.set_name(str(config[CONF_ID])))
|
||||
await weikai.register_weikai(var, config)
|
||||
await i2c.register_i2c_device(var, config)
|
||||
|
||||
|
||||
WK2168_PIN_SCHEMA = cv.All(
|
||||
weikai.WEIKAI_PIN_SCHEMA.extend(
|
||||
{
|
||||
cv.GenerateID(): cv.declare_id(WeikaiGPIOPin),
|
||||
cv.Required(CONF_WK2168_I2C): cv.use_id(WeikaiComponentI2C),
|
||||
}
|
||||
),
|
||||
weikai.validate_pin_mode,
|
||||
)
|
||||
|
||||
|
||||
@pins.PIN_SCHEMA_REGISTRY.register(CONF_WK2168_I2C, WK2168_PIN_SCHEMA)
|
||||
async def sc16is75x_pin_to_code(config):
|
||||
var = cg.new_Pvariable(config[CONF_ID])
|
||||
parent = await cg.get_variable(config[CONF_WK2168_I2C])
|
||||
cg.add(var.set_parent(parent))
|
||||
num = config[CONF_NUMBER]
|
||||
cg.add(var.set_pin(num))
|
||||
cg.add(var.set_inverted(config[CONF_INVERTED]))
|
||||
cg.add(var.set_flags(pins.gpio_flags_expr(config[CONF_MODE])))
|
||||
return var
|
62
esphome/components/wk2168_spi/__init__.py
Normal file
62
esphome/components/wk2168_spi/__init__.py
Normal file
@ -0,0 +1,62 @@
|
||||
import esphome.codegen as cg
|
||||
import esphome.config_validation as cv
|
||||
from esphome import pins
|
||||
from esphome.components import spi, weikai
|
||||
from esphome.const import (
|
||||
CONF_ID,
|
||||
CONF_INVERTED,
|
||||
CONF_MODE,
|
||||
CONF_NUMBER,
|
||||
)
|
||||
|
||||
CODEOWNERS = ["@DrCoolZic"]
|
||||
DEPENDENCIES = ["spi"]
|
||||
AUTO_LOAD = ["weikai", "weikai_spi"]
|
||||
MULTI_CONF = True
|
||||
CONF_WK2168_SPI = "wk2168_spi"
|
||||
|
||||
weikai_spi_ns = cg.esphome_ns.namespace("weikai_spi")
|
||||
weikai_ns = cg.esphome_ns.namespace("weikai")
|
||||
WeikaiComponentSPI = weikai_spi_ns.class_(
|
||||
"WeikaiComponentSPI", weikai.WeikaiComponent, spi.SPIDevice
|
||||
)
|
||||
WeikaiGPIOPin = weikai_ns.class_(
|
||||
"WeikaiGPIOPin", cg.GPIOPin, cg.Parented.template(WeikaiComponentSPI)
|
||||
)
|
||||
|
||||
CONFIG_SCHEMA = cv.All(
|
||||
weikai.WKBASE_SCHEMA.extend(
|
||||
{cv.GenerateID(): cv.declare_id(WeikaiComponentSPI)}
|
||||
).extend(spi.spi_device_schema()),
|
||||
weikai.check_channel_max_4,
|
||||
)
|
||||
|
||||
|
||||
async def to_code(config):
|
||||
var = cg.new_Pvariable(config[CONF_ID])
|
||||
cg.add(var.set_name(str(config[CONF_ID])))
|
||||
await weikai.register_weikai(var, config)
|
||||
await spi.register_spi_device(var, config)
|
||||
|
||||
|
||||
WK2168_PIN_SCHEMA = cv.All(
|
||||
weikai.WEIKAI_PIN_SCHEMA.extend(
|
||||
{
|
||||
cv.GenerateID(): cv.declare_id(WeikaiGPIOPin),
|
||||
cv.Required(CONF_WK2168_SPI): cv.use_id(WeikaiComponentSPI),
|
||||
},
|
||||
),
|
||||
weikai.validate_pin_mode,
|
||||
)
|
||||
|
||||
|
||||
@pins.PIN_SCHEMA_REGISTRY.register(CONF_WK2168_SPI, WK2168_PIN_SCHEMA)
|
||||
async def sc16is75x_pin_to_code(config):
|
||||
var = cg.new_Pvariable(config[CONF_ID])
|
||||
parent = await cg.get_variable(config[CONF_WK2168_SPI])
|
||||
cg.add(var.set_parent(parent))
|
||||
num = config[CONF_NUMBER]
|
||||
cg.add(var.set_pin(num))
|
||||
cg.add(var.set_inverted(config[CONF_INVERTED]))
|
||||
cg.add(var.set_flags(pins.gpio_flags_expr(config[CONF_MODE])))
|
||||
return var
|
30
esphome/components/wk2204_i2c/__init__.py
Normal file
30
esphome/components/wk2204_i2c/__init__.py
Normal file
@ -0,0 +1,30 @@
|
||||
import esphome.codegen as cg
|
||||
import esphome.config_validation as cv
|
||||
from esphome.components import i2c, weikai
|
||||
from esphome.const import CONF_ID
|
||||
|
||||
CODEOWNERS = ["@DrCoolZic"]
|
||||
DEPENDENCIES = ["i2c"]
|
||||
AUTO_LOAD = ["weikai", "weikai_i2c"]
|
||||
MULTI_CONF = True
|
||||
|
||||
weikai_i2c_ns = cg.esphome_ns.namespace("weikai_i2c")
|
||||
WeikaiComponentI2C = weikai_i2c_ns.class_(
|
||||
"WeikaiComponentI2C", weikai.WeikaiComponent, i2c.I2CDevice
|
||||
)
|
||||
|
||||
CONFIG_SCHEMA = cv.All(
|
||||
weikai.WKBASE_SCHEMA.extend(
|
||||
{
|
||||
cv.GenerateID(): cv.declare_id(WeikaiComponentI2C),
|
||||
}
|
||||
).extend(i2c.i2c_device_schema(0x2C)),
|
||||
weikai.check_channel_max_4,
|
||||
)
|
||||
|
||||
|
||||
async def to_code(config):
|
||||
var = cg.new_Pvariable(config[CONF_ID])
|
||||
cg.add(var.set_name(str(config[CONF_ID])))
|
||||
await weikai.register_weikai(var, config)
|
||||
await i2c.register_i2c_device(var, config)
|
30
esphome/components/wk2204_spi/__init__.py
Normal file
30
esphome/components/wk2204_spi/__init__.py
Normal file
@ -0,0 +1,30 @@
|
||||
import esphome.codegen as cg
|
||||
import esphome.config_validation as cv
|
||||
from esphome.components import spi, weikai
|
||||
from esphome.const import CONF_ID
|
||||
|
||||
CODEOWNERS = ["@DrCoolZic"]
|
||||
DEPENDENCIES = ["spi"]
|
||||
AUTO_LOAD = ["weikai", "weikai_spi"]
|
||||
MULTI_CONF = True
|
||||
|
||||
weikai_spi_ns = cg.esphome_ns.namespace("weikai_spi")
|
||||
WeikaiComponentSPI = weikai_spi_ns.class_(
|
||||
"WeikaiComponentSPI", weikai.WeikaiComponent, spi.SPIDevice
|
||||
)
|
||||
|
||||
CONFIG_SCHEMA = cv.All(
|
||||
weikai.WKBASE_SCHEMA.extend(
|
||||
{
|
||||
cv.GenerateID(): cv.declare_id(WeikaiComponentSPI),
|
||||
}
|
||||
).extend(spi.spi_device_schema()),
|
||||
weikai.check_channel_max_4,
|
||||
)
|
||||
|
||||
|
||||
async def to_code(config):
|
||||
var = cg.new_Pvariable(config[CONF_ID])
|
||||
cg.add(var.set_name(str(config[CONF_ID])))
|
||||
await weikai.register_weikai(var, config)
|
||||
await spi.register_spi_device(var, config)
|
64
esphome/components/wk2212_i2c/__init__.py
Normal file
64
esphome/components/wk2212_i2c/__init__.py
Normal file
@ -0,0 +1,64 @@
|
||||
import esphome.codegen as cg
|
||||
import esphome.config_validation as cv
|
||||
from esphome import pins
|
||||
from esphome.components import i2c, weikai
|
||||
from esphome.const import (
|
||||
CONF_ID,
|
||||
CONF_INVERTED,
|
||||
CONF_MODE,
|
||||
CONF_NUMBER,
|
||||
)
|
||||
|
||||
CODEOWNERS = ["@DrCoolZic"]
|
||||
DEPENDENCIES = ["i2c"]
|
||||
AUTO_LOAD = ["weikai", "weikai_i2c"]
|
||||
MULTI_CONF = True
|
||||
CONF_WK2212_I2C = "wk2212_i2c"
|
||||
|
||||
weikai_ns = cg.esphome_ns.namespace("weikai")
|
||||
weikai_i2c_ns = cg.esphome_ns.namespace("weikai_i2c")
|
||||
WeikaiComponentI2C = weikai_i2c_ns.class_(
|
||||
"WeikaiComponentI2C", weikai.WeikaiComponent, i2c.I2CDevice
|
||||
)
|
||||
WeikaiGPIOPin = weikai_ns.class_(
|
||||
"WeikaiGPIOPin", cg.GPIOPin, cg.Parented.template(WeikaiComponentI2C)
|
||||
)
|
||||
|
||||
CONFIG_SCHEMA = cv.All(
|
||||
weikai.WKBASE_SCHEMA.extend(
|
||||
{
|
||||
cv.GenerateID(): cv.declare_id(WeikaiComponentI2C),
|
||||
}
|
||||
).extend(i2c.i2c_device_schema(0x2C)),
|
||||
weikai.check_channel_max_2,
|
||||
)
|
||||
|
||||
|
||||
async def to_code(config):
|
||||
var = cg.new_Pvariable(config[CONF_ID])
|
||||
cg.add(var.set_name(str(config[CONF_ID])))
|
||||
await weikai.register_weikai(var, config)
|
||||
await i2c.register_i2c_device(var, config)
|
||||
|
||||
|
||||
WK2212_PIN_SCHEMA = cv.All(
|
||||
weikai.WEIKAI_PIN_SCHEMA.extend(
|
||||
{
|
||||
cv.GenerateID(): cv.declare_id(WeikaiGPIOPin),
|
||||
cv.Required(CONF_WK2212_I2C): cv.use_id(WeikaiComponentI2C),
|
||||
}
|
||||
),
|
||||
weikai.validate_pin_mode,
|
||||
)
|
||||
|
||||
|
||||
@pins.PIN_SCHEMA_REGISTRY.register(CONF_WK2212_I2C, WK2212_PIN_SCHEMA)
|
||||
async def sc16is75x_pin_to_code(config):
|
||||
var = cg.new_Pvariable(config[CONF_ID])
|
||||
parent = await cg.get_variable(config[CONF_WK2212_I2C])
|
||||
cg.add(var.set_parent(parent))
|
||||
num = config[CONF_NUMBER]
|
||||
cg.add(var.set_pin(num))
|
||||
cg.add(var.set_inverted(config[CONF_INVERTED]))
|
||||
cg.add(var.set_flags(pins.gpio_flags_expr(config[CONF_MODE])))
|
||||
return var
|
62
esphome/components/wk2212_spi/__init__.py
Normal file
62
esphome/components/wk2212_spi/__init__.py
Normal file
@ -0,0 +1,62 @@
|
||||
import esphome.codegen as cg
|
||||
import esphome.config_validation as cv
|
||||
from esphome import pins
|
||||
from esphome.components import spi, weikai
|
||||
from esphome.const import (
|
||||
CONF_ID,
|
||||
CONF_INVERTED,
|
||||
CONF_MODE,
|
||||
CONF_NUMBER,
|
||||
)
|
||||
|
||||
CODEOWNERS = ["@DrCoolZic"]
|
||||
DEPENDENCIES = ["spi"]
|
||||
AUTO_LOAD = ["weikai", "weikai_spi"]
|
||||
MULTI_CONF = True
|
||||
CONF_WK2212_SPI = "wk2212_spi"
|
||||
|
||||
weikai_ns = cg.esphome_ns.namespace("weikai")
|
||||
weikai_spi_ns = cg.esphome_ns.namespace("weikai_spi")
|
||||
WeikaiComponentSPI = weikai_spi_ns.class_(
|
||||
"WeikaiComponentSPI", weikai.WeikaiComponent, spi.SPIDevice
|
||||
)
|
||||
WeikaiGPIOPin = weikai_ns.class_(
|
||||
"WeikaiGPIOPin", cg.GPIOPin, cg.Parented.template(WeikaiComponentSPI)
|
||||
)
|
||||
|
||||
CONFIG_SCHEMA = cv.All(
|
||||
weikai.WKBASE_SCHEMA.extend(
|
||||
{cv.GenerateID(): cv.declare_id(WeikaiComponentSPI)}
|
||||
).extend(spi.spi_device_schema()),
|
||||
weikai.check_channel_max_2,
|
||||
)
|
||||
|
||||
|
||||
async def to_code(config):
|
||||
var = cg.new_Pvariable(config[CONF_ID])
|
||||
cg.add(var.set_name(str(config[CONF_ID])))
|
||||
await weikai.register_weikai(var, config)
|
||||
await spi.register_spi_device(var, config)
|
||||
|
||||
|
||||
WK2212_PIN_SCHEMA = cv.All(
|
||||
weikai.WEIKAI_PIN_SCHEMA.extend(
|
||||
{
|
||||
cv.GenerateID(): cv.declare_id(WeikaiGPIOPin),
|
||||
cv.Required(CONF_WK2212_SPI): cv.use_id(WeikaiComponentSPI),
|
||||
},
|
||||
),
|
||||
weikai.validate_pin_mode,
|
||||
)
|
||||
|
||||
|
||||
@pins.PIN_SCHEMA_REGISTRY.register(CONF_WK2212_SPI, WK2212_PIN_SCHEMA)
|
||||
async def sc16is75x_pin_to_code(config):
|
||||
var = cg.new_Pvariable(config[CONF_ID])
|
||||
parent = await cg.get_variable(config[CONF_WK2212_SPI])
|
||||
cg.add(var.set_parent(parent))
|
||||
num = config[CONF_NUMBER]
|
||||
cg.add(var.set_pin(num))
|
||||
cg.add(var.set_inverted(config[CONF_INVERTED]))
|
||||
cg.add(var.set_flags(pins.gpio_flags_expr(config[CONF_MODE])))
|
||||
return var
|
@ -304,7 +304,7 @@ def string(value):
|
||||
"""Validate that a configuration value is a string. If not, automatically converts to a string.
|
||||
|
||||
Note that this can be lossy, for example the input value 60.00 (float) will be turned into
|
||||
"60.0" (string). For values where this could be a problem `string_string` has to be used.
|
||||
"60.0" (string). For values where this could be a problem `string_strict` has to be used.
|
||||
"""
|
||||
check_not_templatable(value)
|
||||
if isinstance(value, (dict, list)):
|
||||
|
@ -179,6 +179,7 @@ CONF_DATA_PINS = "data_pins"
|
||||
CONF_DATA_RATE = "data_rate"
|
||||
CONF_DATA_TEMPLATE = "data_template"
|
||||
CONF_DATE = "date"
|
||||
CONF_DATETIME = "datetime"
|
||||
CONF_DAY = "day"
|
||||
CONF_DAYS_OF_MONTH = "days_of_month"
|
||||
CONF_DAYS_OF_WEEK = "days_of_week"
|
||||
@ -251,6 +252,8 @@ CONF_ESP8266_DISABLE_SSL_SUPPORT = "esp8266_disable_ssl_support"
|
||||
CONF_ESPHOME = "esphome"
|
||||
CONF_ETHERNET = "ethernet"
|
||||
CONF_EVENT = "event"
|
||||
CONF_EVENT_TYPE = "event_type"
|
||||
CONF_EVENT_TYPES = "event_types"
|
||||
CONF_EXPIRE_AFTER = "expire_after"
|
||||
CONF_EXPORT_ACTIVE_ENERGY = "export_active_energy"
|
||||
CONF_EXPORT_REACTIVE_ENERGY = "export_reactive_energy"
|
||||
@ -517,6 +520,7 @@ CONF_ON_DOUBLE_CLICK = "on_double_click"
|
||||
CONF_ON_ENROLLMENT_DONE = "on_enrollment_done"
|
||||
CONF_ON_ENROLLMENT_FAILED = "on_enrollment_failed"
|
||||
CONF_ON_ENROLLMENT_SCAN = "on_enrollment_scan"
|
||||
CONF_ON_EVENT = "on_event"
|
||||
CONF_ON_FINGER_SCAN_INVALID = "on_finger_scan_invalid"
|
||||
CONF_ON_FINGER_SCAN_MATCHED = "on_finger_scan_matched"
|
||||
CONF_ON_FINGER_SCAN_MISPLACED = "on_finger_scan_misplaced"
|
||||
@ -594,6 +598,7 @@ CONF_PIN_D = "pin_d"
|
||||
CONF_PINS = "pins"
|
||||
CONF_PIXEL_MAPPER = "pixel_mapper"
|
||||
CONF_PLATFORM = "platform"
|
||||
CONF_PLATFORM_VERSION = "platform_version"
|
||||
CONF_PLATFORMIO_OPTIONS = "platformio_options"
|
||||
CONF_PM_0_3UM = "pm_0_3um"
|
||||
CONF_PM_0_5UM = "pm_0_5um"
|
||||
@ -1024,6 +1029,7 @@ DEVICE_CLASS_AWNING = "awning"
|
||||
DEVICE_CLASS_BATTERY = "battery"
|
||||
DEVICE_CLASS_BATTERY_CHARGING = "battery_charging"
|
||||
DEVICE_CLASS_BLIND = "blind"
|
||||
DEVICE_CLASS_BUTTON = "button"
|
||||
DEVICE_CLASS_CARBON_DIOXIDE = "carbon_dioxide"
|
||||
DEVICE_CLASS_CARBON_MONOXIDE = "carbon_monoxide"
|
||||
DEVICE_CLASS_COLD = "cold"
|
||||
@ -1036,6 +1042,7 @@ DEVICE_CLASS_DATA_SIZE = "data_size"
|
||||
DEVICE_CLASS_DATE = "date"
|
||||
DEVICE_CLASS_DISTANCE = "distance"
|
||||
DEVICE_CLASS_DOOR = "door"
|
||||
DEVICE_CLASS_DOORBELL = "doorbell"
|
||||
DEVICE_CLASS_DURATION = "duration"
|
||||
DEVICE_CLASS_EMPTY = ""
|
||||
DEVICE_CLASS_ENERGY = "energy"
|
||||
|
@ -45,6 +45,9 @@
|
||||
#ifdef USE_DATETIME_TIME
|
||||
#include "esphome/components/datetime/time_entity.h"
|
||||
#endif
|
||||
#ifdef USE_DATETIME_DATETIME
|
||||
#include "esphome/components/datetime/datetime_entity.h"
|
||||
#endif
|
||||
#ifdef USE_TEXT
|
||||
#include "esphome/components/text/text.h"
|
||||
#endif
|
||||
@ -63,6 +66,9 @@
|
||||
#ifdef USE_ALARM_CONTROL_PANEL
|
||||
#include "esphome/components/alarm_control_panel/alarm_control_panel.h"
|
||||
#endif
|
||||
#ifdef USE_EVENT
|
||||
#include "esphome/components/event/event.h"
|
||||
#endif
|
||||
|
||||
namespace esphome {
|
||||
|
||||
@ -138,6 +144,10 @@ class Application {
|
||||
void register_time(datetime::TimeEntity *time) { this->times_.push_back(time); }
|
||||
#endif
|
||||
|
||||
#ifdef USE_DATETIME_DATETIME
|
||||
void register_datetime(datetime::DateTimeEntity *datetime) { this->datetimes_.push_back(datetime); }
|
||||
#endif
|
||||
|
||||
#ifdef USE_TEXT
|
||||
void register_text(text::Text *text) { this->texts_.push_back(text); }
|
||||
#endif
|
||||
@ -164,6 +174,10 @@ class Application {
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef USE_EVENT
|
||||
void register_event(event::Event *event) { this->events_.push_back(event); }
|
||||
#endif
|
||||
|
||||
/// Register the component in this Application instance.
|
||||
template<class C> C *register_component(C *c) {
|
||||
static_assert(std::is_base_of<Component, C>::value, "Only Component subclasses can be registered");
|
||||
@ -328,6 +342,15 @@ class Application {
|
||||
return nullptr;
|
||||
}
|
||||
#endif
|
||||
#ifdef USE_DATETIME_DATETIME
|
||||
const std::vector<datetime::DateTimeEntity *> &get_datetimes() { return this->datetimes_; }
|
||||
datetime::DateTimeEntity *get_datetime_by_key(uint32_t key, bool include_internal = false) {
|
||||
for (auto *obj : this->datetimes_)
|
||||
if (obj->get_object_id_hash() == key && (include_internal || !obj->is_internal()))
|
||||
return obj;
|
||||
return nullptr;
|
||||
}
|
||||
#endif
|
||||
#ifdef USE_TEXT
|
||||
const std::vector<text::Text *> &get_texts() { return this->texts_; }
|
||||
text::Text *get_text_by_key(uint32_t key, bool include_internal = false) {
|
||||
@ -386,6 +409,16 @@ class Application {
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef USE_EVENT
|
||||
const std::vector<event::Event *> &get_events() { return this->events_; }
|
||||
event::Event *get_event_by_key(uint32_t key, bool include_internal = false) {
|
||||
for (auto *obj : this->events_)
|
||||
if (obj->get_object_id_hash() == key && (include_internal || !obj->is_internal()))
|
||||
return obj;
|
||||
return nullptr;
|
||||
}
|
||||
#endif
|
||||
|
||||
Scheduler scheduler;
|
||||
|
||||
protected:
|
||||
@ -409,6 +442,9 @@ class Application {
|
||||
#ifdef USE_BUTTON
|
||||
std::vector<button::Button *> buttons_{};
|
||||
#endif
|
||||
#ifdef USE_EVENT
|
||||
std::vector<event::Event *> events_{};
|
||||
#endif
|
||||
#ifdef USE_SENSOR
|
||||
std::vector<sensor::Sensor *> sensors_{};
|
||||
#endif
|
||||
@ -436,6 +472,9 @@ class Application {
|
||||
#ifdef USE_DATETIME_TIME
|
||||
std::vector<datetime::TimeEntity *> times_{};
|
||||
#endif
|
||||
#ifdef USE_DATETIME_DATETIME
|
||||
std::vector<datetime::DateTimeEntity *> datetimes_{};
|
||||
#endif
|
||||
#ifdef USE_SELECT
|
||||
std::vector<select::Select *> selects_{};
|
||||
#endif
|
||||
|
@ -62,6 +62,7 @@ struct Color {
|
||||
return Color(esp_scale8(this->red, scale), esp_scale8(this->green, scale), esp_scale8(this->blue, scale),
|
||||
esp_scale8(this->white, scale));
|
||||
}
|
||||
inline Color operator~() const ALWAYS_INLINE { return Color(255 - this->red, 255 - this->green, 255 - this->blue); }
|
||||
inline Color &operator*=(uint8_t scale) ALWAYS_INLINE {
|
||||
this->red = esp_scale8(this->red, scale);
|
||||
this->green = esp_scale8(this->green, scale);
|
||||
|
@ -85,7 +85,7 @@ class Component {
|
||||
|
||||
/** priority of setup(). higher -> executed earlier
|
||||
*
|
||||
* Defaults to 0.
|
||||
* Defaults to setup_priority::DATA, i.e. 600.
|
||||
*
|
||||
* @return The setup priority of this component
|
||||
*/
|
||||
|
@ -232,6 +232,21 @@ void ComponentIterator::advance() {
|
||||
}
|
||||
break;
|
||||
#endif
|
||||
#ifdef USE_DATETIME_DATETIME
|
||||
case IteratorState::DATETIME_DATETIME:
|
||||
if (this->at_ >= App.get_datetimes().size()) {
|
||||
advance_platform = true;
|
||||
} else {
|
||||
auto *datetime = App.get_datetimes()[this->at_];
|
||||
if (datetime->is_internal() && !this->include_internal_) {
|
||||
success = true;
|
||||
break;
|
||||
} else {
|
||||
success = this->on_datetime(datetime);
|
||||
}
|
||||
}
|
||||
break;
|
||||
#endif
|
||||
#ifdef USE_TEXT
|
||||
case IteratorState::TEXT:
|
||||
if (this->at_ >= App.get_texts().size()) {
|
||||
@ -321,6 +336,21 @@ void ComponentIterator::advance() {
|
||||
}
|
||||
}
|
||||
break;
|
||||
#endif
|
||||
#ifdef USE_EVENT
|
||||
case IteratorState::EVENT:
|
||||
if (this->at_ >= App.get_events().size()) {
|
||||
advance_platform = true;
|
||||
} else {
|
||||
auto *event = App.get_events()[this->at_];
|
||||
if (event->is_internal() && !this->include_internal_) {
|
||||
success = true;
|
||||
break;
|
||||
} else {
|
||||
success = this->on_event(event);
|
||||
}
|
||||
}
|
||||
break;
|
||||
#endif
|
||||
case IteratorState::MAX:
|
||||
if (this->on_end()) {
|
||||
|
@ -63,6 +63,9 @@ class ComponentIterator {
|
||||
#ifdef USE_DATETIME_TIME
|
||||
virtual bool on_time(datetime::TimeEntity *time) = 0;
|
||||
#endif
|
||||
#ifdef USE_DATETIME_DATETIME
|
||||
virtual bool on_datetime(datetime::DateTimeEntity *datetime) = 0;
|
||||
#endif
|
||||
#ifdef USE_TEXT
|
||||
virtual bool on_text(text::Text *text) = 0;
|
||||
#endif
|
||||
@ -80,6 +83,9 @@ class ComponentIterator {
|
||||
#endif
|
||||
#ifdef USE_ALARM_CONTROL_PANEL
|
||||
virtual bool on_alarm_control_panel(alarm_control_panel::AlarmControlPanel *a_alarm_control_panel) = 0;
|
||||
#endif
|
||||
#ifdef USE_EVENT
|
||||
virtual bool on_event(event::Event *event) = 0;
|
||||
#endif
|
||||
virtual bool on_end();
|
||||
|
||||
@ -129,6 +135,9 @@ class ComponentIterator {
|
||||
#ifdef USE_DATETIME_TIME
|
||||
DATETIME_TIME,
|
||||
#endif
|
||||
#ifdef USE_DATETIME_DATETIME
|
||||
DATETIME_DATETIME,
|
||||
#endif
|
||||
#ifdef USE_TEXT
|
||||
TEXT,
|
||||
#endif
|
||||
@ -146,6 +155,9 @@ class ComponentIterator {
|
||||
#endif
|
||||
#ifdef USE_ALARM_CONTROL_PANEL
|
||||
ALARM_CONTROL_PANEL,
|
||||
#endif
|
||||
#ifdef USE_EVENT
|
||||
EVENT,
|
||||
#endif
|
||||
MAX,
|
||||
} state_{IteratorState::NONE};
|
||||
|
@ -71,6 +71,12 @@ void Controller::setup_controller(bool include_internal) {
|
||||
obj->add_on_state_callback([this, obj]() { this->on_time_update(obj); });
|
||||
}
|
||||
#endif
|
||||
#ifdef USE_DATETIME_DATETIME
|
||||
for (auto *obj : App.get_datetimes()) {
|
||||
if (include_internal || !obj->is_internal())
|
||||
obj->add_on_state_callback([this, obj]() { this->on_datetime_update(obj); });
|
||||
}
|
||||
#endif
|
||||
#ifdef USE_TEXT
|
||||
for (auto *obj : App.get_texts()) {
|
||||
if (include_internal || !obj->is_internal())
|
||||
@ -109,6 +115,12 @@ void Controller::setup_controller(bool include_internal) {
|
||||
obj->add_on_state_callback([this, obj]() { this->on_alarm_control_panel_update(obj); });
|
||||
}
|
||||
#endif
|
||||
#ifdef USE_EVENT
|
||||
for (auto *obj : App.get_events()) {
|
||||
if (include_internal || !obj->is_internal())
|
||||
obj->add_on_event_callback([this, obj](const std::string &event_type) { this->on_event(obj, event_type); });
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
} // namespace esphome
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user