diff --git a/esphome/components/remote_base/__init__.py b/esphome/components/remote_base/__init__.py index 3d1c10a092..daea4e5c11 100644 --- a/esphome/components/remote_base/__init__.py +++ b/esphome/components/remote_base/__init__.py @@ -1,41 +1,41 @@ -import esphome.codegen as cg -import esphome.config_validation as cv from esphome import automation +import esphome.codegen as cg from esphome.components import binary_sensor +import esphome.config_validation as cv from esphome.const import ( + CONF_ADDRESS, + CONF_BUTTON, + CONF_CARRIER_FREQUENCY, + CONF_CHANNEL, + CONF_CHECK, + CONF_CODE, + CONF_COMMAND, CONF_COMMAND_REPEATS, CONF_DATA, - CONF_TRIGGER_ID, - CONF_NBITS, - CONF_ADDRESS, - CONF_COMMAND, - CONF_CODE, - CONF_PULSE_LENGTH, - CONF_SYNC, - CONF_ZERO, - CONF_ONE, - CONF_INVERTED, - CONF_PROTOCOL, - CONF_GROUP, + CONF_DELTA, CONF_DEVICE, - CONF_SECOND, - CONF_STATE, - CONF_CHANNEL, CONF_FAMILY, - CONF_REPEAT, - CONF_WAIT_TIME, - CONF_TIMES, - CONF_TYPE_ID, - CONF_CARRIER_FREQUENCY, + CONF_GROUP, + CONF_ID, + CONF_INVERTED, + CONF_LEVEL, + CONF_MAGNITUDE, + CONF_NBITS, + CONF_ONE, + CONF_PROTOCOL, + CONF_PULSE_LENGTH, CONF_RC_CODE_1, CONF_RC_CODE_2, - CONF_MAGNITUDE, + CONF_REPEAT, + CONF_SECOND, + CONF_STATE, + CONF_SYNC, + CONF_TIMES, + CONF_TRIGGER_ID, + CONF_TYPE_ID, + CONF_WAIT_TIME, CONF_WAND_ID, - CONF_LEVEL, - CONF_DELTA, - CONF_ID, - CONF_BUTTON, - CONF_CHECK, + CONF_ZERO, ) from esphome.core import coroutine from esphome.schema_extractors import SCHEMA_EXTRACT, schema_extractor @@ -1963,3 +1963,55 @@ async def mirage_action(var, config, args): vec_ = cg.std_vector.template(cg.uint8) template_ = await cg.templatable(config[CONF_CODE], args, vec_, vec_) cg.add(var.set_code(template_)) + + +# Toto +( + TotoData, + TotoBinarySensor, + TotoTrigger, + TotoAction, + TotoDumper, +) = declare_protocol("Toto") + +TOTO_SCHEMA = cv.Schema( + { + cv.Optional(CONF_RC_CODE_1, default=0): cv.hex_int_range(0, 0xF), + cv.Optional(CONF_RC_CODE_2, default=0): cv.hex_int_range(0, 0xF), + cv.Required(CONF_COMMAND): cv.hex_uint8_t, + } +) + + +@register_binary_sensor("toto", TotoBinarySensor, TOTO_SCHEMA) +def toto_binary_sensor(var, config): + cg.add( + var.set_data( + cg.StructInitializer( + TotoData, + ("rc_code_1", config[CONF_RC_CODE_1]), + ("rc_code_2", config[CONF_RC_CODE_2]), + ("command", config[CONF_COMMAND]), + ) + ) + ) + + +@register_trigger("toto", TotoTrigger, TotoData) +def toto_trigger(var, config): + pass + + +@register_dumper("toto", TotoDumper) +def toto_dumper(var, config): + pass + + +@register_action("toto", TotoAction, TOTO_SCHEMA) +async def Toto_action(var, config, args): + template_ = await cg.templatable(config[CONF_RC_CODE_1], args, cg.uint8) + cg.add(var.set_rc_code_1(template_)) + template_ = await cg.templatable(config[CONF_RC_CODE_2], args, cg.uint8) + cg.add(var.set_rc_code_2(template_)) + template_ = await cg.templatable(config[CONF_COMMAND], args, cg.uint8) + cg.add(var.set_command(template_)) diff --git a/esphome/components/remote_base/toto_protocol.cpp b/esphome/components/remote_base/toto_protocol.cpp new file mode 100644 index 0000000000..ba21263bc8 --- /dev/null +++ b/esphome/components/remote_base/toto_protocol.cpp @@ -0,0 +1,100 @@ +#include "toto_protocol.h" +#include "esphome/core/log.h" + +namespace esphome { +namespace remote_base { + +static const char *const TAG = "remote.toto"; + +static const uint32_t PREAMBLE_HIGH_US = 6200; +static const uint32_t PREAMBLE_LOW_US = 2800; +static const uint32_t BIT_HIGH_US = 550; +static const uint32_t BIT_ONE_LOW_US = 1700; +static const uint32_t BIT_ZERO_LOW_US = 550; +static const uint32_t TOTO_HEADER = 0x2008; + +void TotoProtocol::encode(RemoteTransmitData *dst, const TotoData &data) { + uint32_t payload = 0; + + payload = data.rc_code_1 << 20; + payload |= data.rc_code_2 << 16; + payload |= data.command << 8; + payload |= ((payload & 0xFF0000) >> 16) ^ ((payload & 0x00FF00) >> 8); + + dst->reserve(80); + dst->set_carrier_frequency(38000); + dst->item(PREAMBLE_HIGH_US, PREAMBLE_LOW_US); + + for (uint32_t mask = 1UL << 14; mask; mask >>= 1) { + if (TOTO_HEADER & mask) { + dst->item(BIT_HIGH_US, BIT_ONE_LOW_US); + } else { + dst->item(BIT_HIGH_US, BIT_ZERO_LOW_US); + } + } + + for (uint32_t mask = 1UL << 23; mask; mask >>= 1) { + if (payload & mask) { + dst->item(BIT_HIGH_US, BIT_ONE_LOW_US); + } else { + dst->item(BIT_HIGH_US, BIT_ZERO_LOW_US); + } + } + + dst->mark(BIT_HIGH_US); +} +optional TotoProtocol::decode(RemoteReceiveData src) { + uint16_t header = 0; + uint32_t payload = 0; + + TotoData data{ + .rc_code_1 = 0, + .rc_code_2 = 0, + .command = 0, + }; + + if (!src.expect_item(PREAMBLE_HIGH_US, PREAMBLE_LOW_US)) { + return {}; + } + + for (uint32_t mask = 1UL << 14; mask; mask >>= 1) { + if (src.expect_item(BIT_HIGH_US, BIT_ONE_LOW_US)) { + header |= mask; + } else if (src.expect_item(BIT_HIGH_US, BIT_ZERO_LOW_US)) { + header &= ~mask; + } else { + return {}; + } + } + + if (header != TOTO_HEADER) { + return {}; + } + + for (uint32_t mask = 1UL << 23; mask; mask >>= 1) { + if (src.expect_item(BIT_HIGH_US, BIT_ONE_LOW_US)) { + payload |= mask; + } else if (src.expect_item(BIT_HIGH_US, BIT_ZERO_LOW_US)) { + payload &= ~mask; + } else { + return {}; + } + } + + if ((((payload & 0xFF0000) >> 16) ^ ((payload & 0x00FF00) >> 8)) != (payload & 0x0000FF)) { + return {}; + } + + data.rc_code_1 = (payload & 0xF00000) >> 20; + data.rc_code_2 = (payload & 0x0F0000) >> 16; + data.command = (payload & 0x00FF00) >> 8; + + return data; +} +void TotoProtocol::dump(const TotoData &data) { + ESP_LOGI(TAG, "Received Toto data: rc_code_1=0x%01X, rc_code_2=0x%01X, command=0x%02X", data.rc_code_1, + data.rc_code_2, data.command); +} + +} // namespace remote_base +} // namespace esphome diff --git a/esphome/components/remote_base/toto_protocol.h b/esphome/components/remote_base/toto_protocol.h new file mode 100644 index 0000000000..e62714bbbf --- /dev/null +++ b/esphome/components/remote_base/toto_protocol.h @@ -0,0 +1,45 @@ +#pragma once + +#include "remote_base.h" + +namespace esphome { +namespace remote_base { + +struct TotoData { + uint8_t rc_code_1 : 4; + uint8_t rc_code_2 : 4; + uint8_t command; + + bool operator==(const TotoData &rhs) const { + return (rc_code_1 == rhs.rc_code_1) && (rc_code_2 == rhs.rc_code_2) && (command == rhs.command); + } +}; + +class TotoProtocol : public RemoteProtocol { + public: + void encode(RemoteTransmitData *dst, const TotoData &data) override; + optional decode(RemoteReceiveData src) override; + void dump(const TotoData &data) override; +}; + +DECLARE_REMOTE_PROTOCOL(Toto) + +template class TotoAction : public RemoteTransmitterActionBase { + public: + TEMPLATABLE_VALUE(uint8_t, rc_code_1) + TEMPLATABLE_VALUE(uint8_t, rc_code_2) + TEMPLATABLE_VALUE(uint8_t, command) + + void encode(RemoteTransmitData *dst, Ts... x) override { + TotoData data{}; + data.rc_code_1 = this->rc_code_1_.value(x...); + data.rc_code_2 = this->rc_code_2_.value(x...); + data.command = this->command_.value(x...); + this->set_send_times(this->send_times_.value_or(x..., 3)); + this->set_send_wait(this->send_wait_.value_or(x..., 32000)); + TotoProtocol().encode(dst, data); + } +}; + +} // namespace remote_base +} // namespace esphome diff --git a/tests/components/remote_receiver/common-actions.yaml b/tests/components/remote_receiver/common-actions.yaml index 23589aed22..c1f576d20e 100644 --- a/tests/components/remote_receiver/common-actions.yaml +++ b/tests/components/remote_receiver/common-actions.yaml @@ -142,3 +142,8 @@ on_mirage: then: - lambda: |- ESP_LOGD("mirage", "Mirage data: %s", format_hex(x.data).c_str()); +on_toto: + then: + - logger.log: + format: "on_toto: %u %u %u" + args: ["x.rc_code_1", "x.rc_code_2", "x.command"] diff --git a/tests/components/remote_transmitter/common-buttons.yaml b/tests/components/remote_transmitter/common-buttons.yaml index c6a2453b20..b037c50e12 100644 --- a/tests/components/remote_transmitter/common-buttons.yaml +++ b/tests/components/remote_transmitter/common-buttons.yaml @@ -190,3 +190,10 @@ button: channel: 1 button: 1 check: 1 + - platform: template + name: Toto + on_press: + - remote_transmitter.transmit_toto: + command: 0xEC + rc_code_1: 0x0D + rc_code_2: 0x0D