From 90c0d3e12feceedb6be95bbb34067d555ec1cbd5 Mon Sep 17 00:00:00 2001 From: Keith Burzinski Date: Mon, 9 Aug 2021 20:21:10 -0500 Subject: [PATCH] Add Toshiba AC generic IR remote protocol (#2019) --- esphome/components/remote_base/__init__.py | 47 ++++++++ .../remote_base/toshiba_ac_protocol.cpp | 111 ++++++++++++++++++ .../remote_base/toshiba_ac_protocol.h | 39 ++++++ tests/test1.yaml | 6 + 4 files changed, 203 insertions(+) create mode 100644 esphome/components/remote_base/toshiba_ac_protocol.cpp create mode 100644 esphome/components/remote_base/toshiba_ac_protocol.h diff --git a/esphome/components/remote_base/__init__.py b/esphome/components/remote_base/__init__.py index 7c27c1b736..c4cdc98bbb 100644 --- a/esphome/components/remote_base/__init__.py +++ b/esphome/components/remote_base/__init__.py @@ -950,6 +950,53 @@ async def samsung36_action(var, config, args): cg.add(var.set_command(template_)) +# Toshiba AC +( + ToshibaAcData, + ToshibaAcBinarySensor, + ToshibaAcTrigger, + ToshibaAcAction, + ToshibaAcDumper, +) = declare_protocol("ToshibaAc") +TOSHIBAAC_SCHEMA = cv.Schema( + { + cv.Required(CONF_RC_CODE_1): cv.hex_uint64_t, + cv.Optional(CONF_RC_CODE_2, default=0): cv.hex_uint64_t, + } +) + + +@register_binary_sensor("toshiba_ac", ToshibaAcBinarySensor, TOSHIBAAC_SCHEMA) +def toshibaac_binary_sensor(var, config): + cg.add( + var.set_data( + cg.StructInitializer( + ToshibaAcData, + ("rc_code_1", config[CONF_RC_CODE_1]), + ("rc_code_2", config[CONF_RC_CODE_2]), + ) + ) + ) + + +@register_trigger("toshiba_ac", ToshibaAcTrigger, ToshibaAcData) +def toshibaac_trigger(var, config): + pass + + +@register_dumper("toshiba_ac", ToshibaAcDumper) +def toshibaac_dumper(var, config): + pass + + +@register_action("toshiba_ac", ToshibaAcAction, TOSHIBAAC_SCHEMA) +async def toshibaac_action(var, config, args): + template_ = await cg.templatable(config[CONF_RC_CODE_1], args, cg.uint64) + cg.add(var.set_rc_code_1(template_)) + template_ = await cg.templatable(config[CONF_RC_CODE_2], args, cg.uint64) + cg.add(var.set_rc_code_2(template_)) + + # Panasonic ( PanasonicData, diff --git a/esphome/components/remote_base/toshiba_ac_protocol.cpp b/esphome/components/remote_base/toshiba_ac_protocol.cpp new file mode 100644 index 0000000000..27d042ace0 --- /dev/null +++ b/esphome/components/remote_base/toshiba_ac_protocol.cpp @@ -0,0 +1,111 @@ +#include "toshiba_ac_protocol.h" +#include "esphome/core/log.h" + +namespace esphome { +namespace remote_base { + +static const char *const TAG = "remote.toshibaac"; + +static const uint32_t HEADER_HIGH_US = 4500; +static const uint32_t HEADER_LOW_US = 4500; +static const uint32_t BIT_HIGH_US = 560; +static const uint32_t BIT_ONE_LOW_US = 1690; +static const uint32_t BIT_ZERO_LOW_US = 560; +static const uint32_t FOOTER_HIGH_US = 560; +static const uint32_t FOOTER_LOW_US = 4500; +static const uint16_t PACKET_SPACE = 5500; + +void ToshibaAcProtocol::encode(RemoteTransmitData *dst, const ToshibaAcData &data) { + dst->set_carrier_frequency(38000); + dst->reserve((3 + (48 * 2)) * 3); + + for (uint8_t repeat = 0; repeat < 2; repeat++) { + dst->item(HEADER_HIGH_US, HEADER_LOW_US); + for (uint8_t bit = 48; bit > 0; bit--) { + dst->mark(BIT_HIGH_US); + if ((data.rc_code_1 >> (bit - 1)) & 1) + dst->space(BIT_ONE_LOW_US); + else + dst->space(BIT_ZERO_LOW_US); + } + dst->item(FOOTER_HIGH_US, FOOTER_LOW_US); + } + + if (data.rc_code_2 != 0) { + dst->item(HEADER_HIGH_US, HEADER_LOW_US); + for (uint8_t bit = 48; bit > 0; bit--) { + dst->mark(BIT_HIGH_US); + if ((data.rc_code_2 >> (bit - 1)) & 1) + dst->space(BIT_ONE_LOW_US); + else + dst->space(BIT_ZERO_LOW_US); + } + dst->item(FOOTER_HIGH_US, FOOTER_LOW_US); + } +} + +optional ToshibaAcProtocol::decode(RemoteReceiveData src) { + uint64_t packet = 0; + ToshibaAcData out{ + .rc_code_1 = 0, + .rc_code_2 = 0, + }; + // *** Packet 1 + if (!src.expect_item(HEADER_HIGH_US, HEADER_LOW_US)) + return {}; + for (uint8_t bit_counter = 0; bit_counter < 48; bit_counter++) { + if (src.expect_item(BIT_HIGH_US, BIT_ONE_LOW_US)) { + packet = (packet << 1) | 1; + } else if (src.expect_item(BIT_HIGH_US, BIT_ZERO_LOW_US)) { + packet = (packet << 1) | 0; + } else { + return {}; + } + } + if (!src.expect_item(FOOTER_HIGH_US, PACKET_SPACE)) + return {}; + + // *** Packet 2 + if (!src.expect_item(HEADER_HIGH_US, HEADER_LOW_US)) + return {}; + for (uint8_t bit_counter = 0; bit_counter < 48; bit_counter++) { + if (src.expect_item(BIT_HIGH_US, BIT_ONE_LOW_US)) { + out.rc_code_1 = (out.rc_code_1 << 1) | 1; + } else if (src.expect_item(BIT_HIGH_US, BIT_ZERO_LOW_US)) { + out.rc_code_1 = (out.rc_code_1 << 1) | 0; + } else { + return {}; + } + } + // The first two packets must match + if (packet != out.rc_code_1) + return {}; + // The third packet isn't always present + if (!src.expect_item(FOOTER_HIGH_US, PACKET_SPACE)) + return out; + + // *** Packet 3 + if (!src.expect_item(HEADER_HIGH_US, HEADER_LOW_US)) + return {}; + for (uint8_t bit_counter = 0; bit_counter < 48; bit_counter++) { + if (src.expect_item(BIT_HIGH_US, BIT_ONE_LOW_US)) { + out.rc_code_2 = (out.rc_code_2 << 1) | 1; + } else if (src.expect_item(BIT_HIGH_US, BIT_ZERO_LOW_US)) { + out.rc_code_2 = (out.rc_code_2 << 1) | 0; + } else { + return {}; + } + } + + return out; +} + +void ToshibaAcProtocol::dump(const ToshibaAcData &data) { + if (data.rc_code_2 != 0) + ESP_LOGD(TAG, "Received Toshiba AC: rc_code_1=0x%" PRIX64 ", rc_code_2=0x%" PRIX64, data.rc_code_1, data.rc_code_2); + else + ESP_LOGD(TAG, "Received Toshiba AC: rc_code_1=0x%" PRIX64, data.rc_code_1); +} + +} // namespace remote_base +} // namespace esphome diff --git a/esphome/components/remote_base/toshiba_ac_protocol.h b/esphome/components/remote_base/toshiba_ac_protocol.h new file mode 100644 index 0000000000..c69401c378 --- /dev/null +++ b/esphome/components/remote_base/toshiba_ac_protocol.h @@ -0,0 +1,39 @@ +#pragma once + +#include "esphome/core/component.h" +#include "remote_base.h" + +namespace esphome { +namespace remote_base { + +struct ToshibaAcData { + uint64_t rc_code_1; + uint64_t rc_code_2; + + bool operator==(const ToshibaAcData &rhs) const { return rc_code_1 == rhs.rc_code_1 && rc_code_2 == rhs.rc_code_2; } +}; + +class ToshibaAcProtocol : public RemoteProtocol { + public: + void encode(RemoteTransmitData *dst, const ToshibaAcData &data) override; + optional decode(RemoteReceiveData src) override; + void dump(const ToshibaAcData &data) override; +}; + +DECLARE_REMOTE_PROTOCOL(ToshibaAc) + +template class ToshibaAcAction : public RemoteTransmitterActionBase { + public: + TEMPLATABLE_VALUE(uint64_t, rc_code_1) + TEMPLATABLE_VALUE(uint64_t, rc_code_2) + + void encode(RemoteTransmitData *dst, Ts... x) override { + ToshibaAcData data{}; + data.rc_code_1 = this->rc_code_1_.value(x...); + data.rc_code_2 = this->rc_code_2_.value(x...); + ToshibaAcProtocol().encode(dst, data); + } +}; + +} // namespace remote_base +} // namespace esphome diff --git a/tests/test1.yaml b/tests/test1.yaml index 6d6fc113ea..54f8eb589c 100644 --- a/tests/test1.yaml +++ b/tests/test1.yaml @@ -1626,6 +1626,12 @@ switch: remote_transmitter.transmit_samsung36: address: 0x0400 command: 0x000E00FF + - platform: template + name: ToshibaAC + turn_on_action: + - remote_transmitter.transmit_toshiba_ac: + rc_code_1: 0xB24DBF4050AF + rc_code_2: 0xD5660001003C - platform: template name: Sony turn_on_action: