From 7810ad40d7c0b5f06cc0c65294b3be502ab5827a Mon Sep 17 00:00:00 2001 From: Eric van Blokland <8546466+Emrvb@users.noreply.github.com> Date: Thu, 9 Feb 2023 01:22:05 +0100 Subject: [PATCH] Added CanalSat and CanalSatLD protocol support (#3513) Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com> --- esphome/components/remote_base/__init__.py | 101 ++++++++++++++++ .../remote_base/canalsat_protocol.cpp | 108 ++++++++++++++++++ .../remote_base/canalsat_protocol.h | 78 +++++++++++++ 3 files changed, 287 insertions(+) create mode 100644 esphome/components/remote_base/canalsat_protocol.cpp create mode 100644 esphome/components/remote_base/canalsat_protocol.h diff --git a/esphome/components/remote_base/__init__.py b/esphome/components/remote_base/__init__.py index b979a050db..c3149ce430 100644 --- a/esphome/components/remote_base/__init__.py +++ b/esphome/components/remote_base/__init__.py @@ -237,6 +237,107 @@ async def build_dumpers(config): return dumpers +# CanalSat +( + CanalSatData, + CanalSatBinarySensor, + CanalSatTrigger, + CanalSatAction, + CanalSatDumper, +) = declare_protocol("CanalSat") +CANALSAT_SCHEMA = cv.Schema( + { + cv.Required(CONF_DEVICE): cv.hex_uint8_t, + cv.Optional(CONF_ADDRESS, default=0): cv.hex_uint8_t, + cv.Required(CONF_COMMAND): cv.hex_uint8_t, + } +) + + +@register_binary_sensor("canalsat", CanalSatBinarySensor, CANALSAT_SCHEMA) +def canalsat_binary_sensor(var, config): + cg.add( + var.set_data( + cg.StructInitializer( + CanalSatData, + ("device", config[CONF_DEVICE]), + ("address", config[CONF_ADDRESS]), + ("command", config[CONF_COMMAND]), + ) + ) + ) + + +@register_trigger("canalsat", CanalSatTrigger, CanalSatData) +def canalsat_trigger(var, config): + pass + + +@register_dumper("canalsat", CanalSatDumper) +def canalsat_dumper(var, config): + pass + + +@register_action("canalsat", CanalSatAction, CANALSAT_SCHEMA) +async def canalsat_action(var, config, args): + template_ = await cg.templatable(config[CONF_DEVICE], args, cg.uint8) + cg.add(var.set_device(template_)) + 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_)) + + +( + CanalSatLDData, + CanalSatLDBinarySensor, + CanalSatLDTrigger, + CanalSatLDAction, + CanalSatLDDumper, +) = declare_protocol("CanalSatLD") +CANALSATLD_SCHEMA = cv.Schema( + { + cv.Required(CONF_DEVICE): cv.hex_uint8_t, + cv.Optional(CONF_ADDRESS, default=0): cv.hex_uint8_t, + cv.Required(CONF_COMMAND): cv.hex_uint8_t, + } +) + + +@register_binary_sensor("canalsatld", CanalSatLDBinarySensor, CANALSAT_SCHEMA) +def canalsatld_binary_sensor(var, config): + cg.add( + var.set_data( + cg.StructInitializer( + CanalSatLDData, + ("device", config[CONF_DEVICE]), + ("address", config[CONF_ADDRESS]), + ("command", config[CONF_COMMAND]), + ) + ) + ) + + +@register_trigger("canalsatld", CanalSatLDTrigger, CanalSatLDData) +def canalsatld_trigger(var, config): + pass + + +@register_dumper("canalsatld", CanalSatLDDumper) +def canalsatld_dumper(var, config): + pass + + +@register_action("canalsatld", CanalSatLDAction, CANALSATLD_SCHEMA) +async def canalsatld_action(var, config, args): + template_ = await cg.templatable(config[CONF_DEVICE], args, cg.uint8) + cg.add(var.set_device(template_)) + 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_)) + + # Coolix ( CoolixData, diff --git a/esphome/components/remote_base/canalsat_protocol.cpp b/esphome/components/remote_base/canalsat_protocol.cpp new file mode 100644 index 0000000000..1ea47750fd --- /dev/null +++ b/esphome/components/remote_base/canalsat_protocol.cpp @@ -0,0 +1,108 @@ +#include "canalsat_protocol.h" +#include "esphome/core/log.h" + +namespace esphome { +namespace remote_base { + +static const char *const CANALSAT_TAG = "remote.canalsat"; +static const char *const CANALSATLD_TAG = "remote.canalsatld"; + +static const uint16_t CANALSAT_FREQ = 55500; +static const uint16_t CANALSATLD_FREQ = 56000; +static const uint16_t CANALSAT_UNIT = 250; +static const uint16_t CANALSATLD_UNIT = 320; + +CanalSatProtocol::CanalSatProtocol() { + this->frequency_ = CANALSAT_FREQ; + this->unit_ = CANALSAT_UNIT; + this->tag_ = CANALSAT_TAG; +} + +CanalSatLDProtocol::CanalSatLDProtocol() { + this->frequency_ = CANALSATLD_FREQ; + this->unit_ = CANALSATLD_UNIT; + this->tag_ = CANALSATLD_TAG; +} + +void CanalSatBaseProtocol::encode(RemoteTransmitData *dst, const CanalSatData &data) { + dst->reserve(48); + dst->set_carrier_frequency(this->frequency_); + + uint32_t raw{ + static_cast((1 << 23) | (data.device << 16) | (data.address << 10) | (0 << 9) | (data.command << 1))}; + bool was_high{true}; + + for (uint32_t mask = 0x800000; mask; mask >>= 1) { + if (raw & mask) { + if (was_high) { + dst->mark(this->unit_); + } + was_high = true; + if (raw & mask >> 1) { + dst->space(this->unit_); + } else { + dst->space(this->unit_ * 2); + } + } else { + if (!was_high) { + dst->space(this->unit_); + } + was_high = false; + if (raw & mask >> 1) { + dst->mark(this->unit_ * 2); + } else { + dst->mark(this->unit_); + } + } + } +} + +optional CanalSatBaseProtocol::decode(RemoteReceiveData src) { + CanalSatData data{ + .device = 0, + .address = 0, + .repeat = 0, + .command = 0, + }; + + // Check if initial mark and spaces match + if (!src.peek_mark(this->unit_) || !(src.peek_space(this->unit_, 1) || src.peek_space(this->unit_ * 2, 1))) { + return {}; + } + + uint8_t bit{1}; + uint8_t offset{1}; + uint32_t buffer{0}; + + while (offset < 24) { + buffer = buffer | (bit << (24 - offset++)); + src.advance(); + if (src.peek_mark(this->unit_) || src.peek_space(this->unit_)) { + src.advance(); + } else if (src.peek_mark(this->unit_ * 2) || src.peek_space(this->unit_ * 2)) { + bit = !bit; + } else if (offset != 24 && bit != 1) { // If last bit is high, final space is indistinguishable + return {}; + } + } + + data.device = (0xFF0000 & buffer) >> 16; + data.address = (0x00FF00 & buffer) >> 10; + data.repeat = (0x00FF00 & buffer) >> 9; + data.command = (0x0000FF & buffer) >> 1; + + return data; +} + +void CanalSatBaseProtocol::dump(const CanalSatData &data) { + if (this->tag_ == CANALSATLD_TAG) { + ESP_LOGD(this->tag_, "Received CanalSatLD: device=0x%02X, address=0x%02X, command=0x%02X, repeat=0x%X", data.device, + data.address, data.command, data.repeat); + } else { + ESP_LOGD(this->tag_, "Received CanalSat: device=0x%02X, address=0x%02X, command=0x%02X, repeat=0x%X", data.device, + data.address, data.command, data.repeat); + } +} + +} // namespace remote_base +} // namespace esphome diff --git a/esphome/components/remote_base/canalsat_protocol.h b/esphome/components/remote_base/canalsat_protocol.h new file mode 100644 index 0000000000..180989ef99 --- /dev/null +++ b/esphome/components/remote_base/canalsat_protocol.h @@ -0,0 +1,78 @@ +#pragma once + +#include "remote_base.h" + +namespace esphome { +namespace remote_base { + +struct CanalSatData { + uint8_t device : 7; + uint8_t address : 6; + uint8_t repeat : 1; + uint8_t command : 7; + + bool operator==(const CanalSatData &rhs) const { + return device == rhs.device && address == rhs.address && command == rhs.command; + } +}; + +struct CanalSatLDData : public CanalSatData {}; + +class CanalSatBaseProtocol : public RemoteProtocol { + public: + void encode(RemoteTransmitData *dst, const CanalSatData &data) override; + optional decode(RemoteReceiveData src) override; + void dump(const CanalSatData &data) override; + + protected: + uint16_t frequency_; + uint16_t unit_; + const char *tag_; +}; + +class CanalSatProtocol : public CanalSatBaseProtocol { + public: + CanalSatProtocol(); +}; + +class CanalSatLDProtocol : public CanalSatBaseProtocol { + public: + CanalSatLDProtocol(); +}; + +DECLARE_REMOTE_PROTOCOL(CanalSat) + +template class CanalSatAction : public RemoteTransmitterActionBase { + public: + TEMPLATABLE_VALUE(uint8_t, device) + TEMPLATABLE_VALUE(uint8_t, address) + TEMPLATABLE_VALUE(uint8_t, command) + + void encode(RemoteTransmitData *dst, Ts... x) { + CanalSatData data{}; + data.device = this->device_.value(x...); + data.address = this->address_.value(x...); + data.command = this->command_.value(x...); + CanalSatProtocol().encode(dst, data); + } +}; + +DECLARE_REMOTE_PROTOCOL(CanalSatLD) + +template class CanalSatLDAction : public RemoteTransmitterActionBase { + public: + TEMPLATABLE_VALUE(uint8_t, device) + TEMPLATABLE_VALUE(uint8_t, address) + TEMPLATABLE_VALUE(uint8_t, command) + + void encode(RemoteTransmitData *dst, Ts... x) { + CanalSatData data{}; + data.device = this->device_.value(x...); + data.address = this->address_.value(x...); + data.command = this->command_.value(x...); + CanalSatLDProtocol().encode(dst, data); + } +}; + +} // namespace remote_base +} // namespace esphome