diff --git a/esphome/components/remote_base/__init__.py b/esphome/components/remote_base/__init__.py index c4cdc98bbb..c9f1c611a8 100644 --- a/esphome/components/remote_base/__init__.py +++ b/esphome/components/remote_base/__init__.py @@ -234,6 +234,49 @@ async def build_dumpers(config): return dumpers +# Dish +DishData, DishBinarySensor, DishTrigger, DishAction, DishDumper = declare_protocol( + "Dish" +) +DISH_SCHEMA = cv.Schema( + { + cv.Optional(CONF_ADDRESS, default=1): cv.int_range(min=1, max=16), + cv.Required(CONF_COMMAND): cv.int_range(min=0, max=63), + } +) + + +@register_binary_sensor("dish", DishBinarySensor, DISH_SCHEMA) +def dish_binary_sensor(var, config): + cg.add( + var.set_data( + cg.StructInitializer( + DishData, + ("address", config[CONF_ADDRESS]), + ("command", config[CONF_COMMAND]), + ) + ) + ) + + +@register_trigger("dish", DishTrigger, DishData) +def dish_trigger(var, config): + pass + + +@register_dumper("dish", DishDumper) +def dish_dumper(var, config): + pass + + +@register_action("dish", DishAction, DISH_SCHEMA) +async def dish_action(var, config, args): + template_ = await cg.templatable(config[CONF_ADDRESS], args, cg.uint8) + cg.add(var.set_address(template_)) + template_ = await cg.templatable(config[CONF_COMMAND], args, cg.uint8) + cg.add(var.set_command(template_)) + + # JVC JVCData, JVCBinarySensor, JVCTrigger, JVCAction, JVCDumper = declare_protocol("JVC") JVC_SCHEMA = cv.Schema({cv.Required(CONF_DATA): cv.hex_uint32_t}) diff --git a/esphome/components/remote_base/dish_protocol.cpp b/esphome/components/remote_base/dish_protocol.cpp new file mode 100644 index 0000000000..1257e22a45 --- /dev/null +++ b/esphome/components/remote_base/dish_protocol.cpp @@ -0,0 +1,92 @@ +#include "dish_protocol.h" +#include "esphome/core/log.h" + +namespace esphome { +namespace remote_base { + +static const char *const TAG = "remote.dish"; + +static const uint32_t HEADER_HIGH_US = 400; +static const uint32_t HEADER_LOW_US = 6100; +static const uint32_t BIT_HIGH_US = 400; +static const uint32_t BIT_ONE_LOW_US = 1700; +static const uint32_t BIT_ZERO_LOW_US = 2800; + +void DishProtocol::encode(RemoteTransmitData *dst, const DishData &data) { + dst->reserve(138); + dst->set_carrier_frequency(57600); + + // HEADER + dst->item(HEADER_HIGH_US, HEADER_LOW_US); + + // Typically a DISH device needs to get a command a total of + // at least 4 times to accept it. + for (uint i = 0; i < 4; i++) { + // COMMAND (function, in MSB) + for (uint8_t mask = 1UL << 5; mask; mask >>= 1) { + if (data.command & mask) + dst->item(BIT_HIGH_US, BIT_ONE_LOW_US); + else + dst->item(BIT_HIGH_US, BIT_ZERO_LOW_US); + } + + // ADDRESS (unit code, in LSB) + for (uint8_t mask = 1UL; mask < 1UL << 4; mask <<= 1) { + if ((data.address - 1) & mask) + dst->item(BIT_HIGH_US, BIT_ONE_LOW_US); + else + dst->item(BIT_HIGH_US, BIT_ZERO_LOW_US); + } + // PADDING + for (uint j = 0; j < 6; j++) + dst->item(BIT_HIGH_US, BIT_ZERO_LOW_US); + + // FOOTER + dst->item(HEADER_HIGH_US, HEADER_LOW_US); + } +} +optional DishProtocol::decode(RemoteReceiveData src) { + DishData data{ + .address = 0, + .command = 0, + }; + if (!src.expect_item(HEADER_HIGH_US, HEADER_LOW_US)) + return {}; + + for (uint8_t mask = 1UL << 5; mask != 0; mask >>= 1) { + if (src.expect_item(BIT_HIGH_US, BIT_ONE_LOW_US)) { + data.command |= mask; + } else if (src.expect_item(BIT_HIGH_US, BIT_ZERO_LOW_US)) { + data.command &= ~mask; + } else { + return {}; + } + } + + for (uint8_t mask = 1UL; mask < 1UL << 5; mask <<= 1) { + if (src.expect_item(BIT_HIGH_US, BIT_ONE_LOW_US)) { + data.address |= mask; + } else if (src.expect_item(BIT_HIGH_US, BIT_ZERO_LOW_US)) { + data.address &= ~mask; + } else { + return {}; + } + } + for (uint j = 0; j < 6; j++) { + if (!src.expect_item(BIT_HIGH_US, BIT_ZERO_LOW_US)) { + return {}; + } + } + data.address++; + + src.expect_item(HEADER_HIGH_US, HEADER_LOW_US); + + return data; +} + +void DishProtocol::dump(const DishData &data) { + ESP_LOGD(TAG, "Received Dish: address=0x%02X, command=0x%02X", data.address, data.command); +} + +} // namespace remote_base +} // namespace esphome diff --git a/esphome/components/remote_base/dish_protocol.h b/esphome/components/remote_base/dish_protocol.h new file mode 100644 index 0000000000..ca4d04ed34 --- /dev/null +++ b/esphome/components/remote_base/dish_protocol.h @@ -0,0 +1,38 @@ +#pragma once + +#include "remote_base.h" + +namespace esphome { +namespace remote_base { + +struct DishData { + uint8_t address; + uint8_t command; + + bool operator==(const DishData &rhs) const { return address == rhs.address && command == rhs.command; } +}; + +class DishProtocol : public RemoteProtocol { + public: + void encode(RemoteTransmitData *dst, const DishData &data) override; + optional decode(RemoteReceiveData src) override; + void dump(const DishData &data) override; +}; + +DECLARE_REMOTE_PROTOCOL(Dish) + +template class DishAction : public RemoteTransmitterActionBase { + public: + TEMPLATABLE_VALUE(uint8_t, address) + TEMPLATABLE_VALUE(uint8_t, command) + + void encode(RemoteTransmitData *dst, Ts... x) override { + DishData data{}; + data.address = this->address_.value(x...); + data.command = this->command_.value(x...); + DishProtocol().encode(dst, data); + } +}; + +} // namespace remote_base +} // namespace esphome