diff --git a/esphome/components/remote_base/__init__.py b/esphome/components/remote_base/__init__.py index 5730cba1eb..e4d1e115e7 100644 --- a/esphome/components/remote_base/__init__.py +++ b/esphome/components/remote_base/__init__.py @@ -1338,3 +1338,48 @@ def midea_dumper(var, config): ) async def midea_action(var, config, args): cg.add(var.set_code(config[CONF_CODE])) + + +# AEHA +AEHAData, AEHABinarySensor, AEHATrigger, AEHAAction, AEHADumper = declare_protocol( + "AEHA" +) +AEHA_SCHEMA = cv.Schema( + { + cv.Required(CONF_ADDRESS): cv.hex_uint16_t, + cv.Required(CONF_DATA): cv.All( + [cv.Any(cv.hex_uint8_t, cv.uint8_t)], + cv.Length(min=2, max=35), + ), + } +) + + +@register_binary_sensor("aeha", AEHABinarySensor, AEHA_SCHEMA) +def aeha_binary_sensor(var, config): + cg.add( + var.set_data( + cg.StructInitializer( + AEHAData, + ("address", config[CONF_ADDRESS]), + ("data", config[CONF_DATA]), + ) + ) + ) + + +@register_trigger("aeha", AEHATrigger, AEHAData) +def aeha_trigger(var, config): + pass + + +@register_dumper("aeha", AEHADumper) +def aeha_dumper(var, config): + pass + + +@register_action("aeha", AEHAAction, AEHA_SCHEMA) +async def aeha_action(var, config, args): + template_ = await cg.templatable(config[CONF_ADDRESS], args, cg.uint16) + cg.add(var.set_address(template_)) + cg.add(var.set_data(config[CONF_DATA])) diff --git a/esphome/components/remote_base/aeha_protocol.cpp b/esphome/components/remote_base/aeha_protocol.cpp new file mode 100644 index 0000000000..ee1616ed6d --- /dev/null +++ b/esphome/components/remote_base/aeha_protocol.cpp @@ -0,0 +1,103 @@ +#include "aeha_protocol.h" +#include "esphome/core/log.h" +#include + +namespace esphome { +namespace remote_base { + +static const char *const TAG = "remote.aeha"; + +static const uint16_t BITWISE = 425; +static const uint16_t HEADER_HIGH_US = BITWISE * 8; +static const uint16_t HEADER_LOW_US = BITWISE * 4; +static const uint16_t BIT_HIGH_US = BITWISE; +static const uint16_t BIT_ONE_LOW_US = BITWISE * 3; +static const uint16_t BIT_ZERO_LOW_US = BITWISE; +static const uint16_t TRAILER = BITWISE; + +void AEHAProtocol::encode(RemoteTransmitData *dst, const AEHAData &data) { + dst->set_carrier_frequency(38000); + dst->reserve(2 + 32 + (data.data.size() * 2) + 1); + + dst->item(HEADER_HIGH_US, HEADER_LOW_US); + + for (uint16_t mask = 1 << 15; mask != 0; mask >>= 1) { + if (data.address & mask) { + dst->item(BIT_HIGH_US, BIT_ONE_LOW_US); + } else { + dst->item(BIT_HIGH_US, BIT_ZERO_LOW_US); + } + } + + for (uint8_t bit : data.data) { + for (uint8_t mask = 1 << 7; mask != 0; mask >>= 1) { + if (bit & mask) { + dst->item(BIT_HIGH_US, BIT_ONE_LOW_US); + } else { + dst->item(BIT_HIGH_US, BIT_ZERO_LOW_US); + } + } + } + + dst->mark(TRAILER); +} +optional AEHAProtocol::decode(RemoteReceiveData src) { + AEHAData out{ + .address = 0, + .data = {}, + }; + if (!src.expect_item(HEADER_HIGH_US, HEADER_LOW_US)) + return {}; + + for (uint16_t mask = 1 << 15; mask != 0; mask >>= 1) { + if (src.expect_item(BIT_HIGH_US, BIT_ONE_LOW_US)) { + out.address |= mask; + } else if (src.expect_item(BIT_HIGH_US, BIT_ZERO_LOW_US)) { + out.address &= ~mask; + } else { + return {}; + } + } + + for (uint8_t pos = 0; pos < 35; pos++) { + uint8_t data = 0; + for (uint8_t mask = 1 << 7; mask != 0; mask >>= 1) { + if (src.expect_item(BIT_HIGH_US, BIT_ONE_LOW_US)) { + data |= mask; + } else if (src.expect_item(BIT_HIGH_US, BIT_ZERO_LOW_US)) { + data &= ~mask; + } else if (pos > 1 && src.expect_mark(TRAILER)) { + return out; + } else { + return {}; + } + } + + out.data.push_back(data); + } + + if (src.expect_mark(TRAILER)) { + return out; + } + + return {}; +} + +std::string AEHAProtocol::format_data_(const std::vector &data) { + std::string out; + for (uint8_t byte : data) { + char buf[6]; + sprintf(buf, "0x%02X,", byte); + out += buf; + } + out.pop_back(); + return out; +} + +void AEHAProtocol::dump(const AEHAData &data) { + auto data_str = format_data_(data.data); + ESP_LOGD(TAG, "Received AEHA: address=0x%04X, data=[%s]", data.address, data_str.c_str()); +} + +} // namespace remote_base +} // namespace esphome diff --git a/esphome/components/remote_base/aeha_protocol.h b/esphome/components/remote_base/aeha_protocol.h new file mode 100644 index 0000000000..6cb4706506 --- /dev/null +++ b/esphome/components/remote_base/aeha_protocol.h @@ -0,0 +1,42 @@ +#pragma once + +#include "remote_base.h" + +namespace esphome { +namespace remote_base { + +struct AEHAData { + uint16_t address; + std::vector data; + + bool operator==(const AEHAData &rhs) const { return address == rhs.address && data == rhs.data; } +}; + +class AEHAProtocol : public RemoteProtocol { + public: + void encode(RemoteTransmitData *dst, const AEHAData &data) override; + optional decode(RemoteReceiveData src) override; + void dump(const AEHAData &data) override; + + private: + std::string format_data_(const std::vector &data); +}; + +DECLARE_REMOTE_PROTOCOL(AEHA) + +template class AEHAAction : public RemoteTransmitterActionBase { + public: + TEMPLATABLE_VALUE(uint16_t, address) + TEMPLATABLE_VALUE(std::vector, data) + + void set_data(const std::vector &data) { data_ = data; } + void encode(RemoteTransmitData *dst, Ts... x) override { + AEHAData data{}; + data.address = this->address_.value(x...); + data.data = this->data_.value(x...); + AEHAProtocol().encode(dst, data); + } +}; + +} // namespace remote_base +} // namespace esphome diff --git a/tests/test1.yaml b/tests/test1.yaml index b741f3e255..1a9d69eac8 100644 --- a/tests/test1.yaml +++ b/tests/test1.yaml @@ -2085,6 +2085,13 @@ switch: turn_on_action: remote_transmitter.transmit_raw: code: [1000, -1000] + - platform: template + name: AEHA + id: eaha_hitachi_climate_power_on + turn_on_action: + remote_transmitter.transmit_aeha: + address: 0x8008 + data: [0x00, 0x02, 0xFD, 0xFF, 0x00, 0x33, 0xCC, 0x49, 0xB6, 0xC8, 0x37, 0x16, 0xE9, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0xCA, 0x35, 0x8F, 0x70, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF] - platform: template name: Living Room Lights id: livingroom_lights