Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Dooya protocol to remote_base #6488

Merged
merged 6 commits into from
Apr 9, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
54 changes: 54 additions & 0 deletions esphome/components/remote_base/__init__.py
Expand Up @@ -33,6 +33,9 @@
CONF_WAND_ID,
CONF_LEVEL,
CONF_DELTA,
CONF_ID,
CONF_BUTTON,
CONF_CHECK,
)
from esphome.core import coroutine
from esphome.schema_extractors import SCHEMA_EXTRACT, schema_extractor
Expand Down Expand Up @@ -512,6 +515,57 @@ async def dish_action(var, config, args):
cg.add(var.set_command(template_))


# Dooya
DooyaData, DooyaBinarySensor, DooyaTrigger, DooyaAction, DooyaDumper = declare_protocol(
"Dooya"
)
DOOYA_SCHEMA = cv.Schema(
{
cv.Required(CONF_ID): cv.hex_int_range(0, 16777215),
cv.Required(CONF_CHANNEL): cv.hex_int_range(0, 255),
cv.Required(CONF_BUTTON): cv.hex_int_range(0, 15),
cv.Required(CONF_CHECK): cv.hex_int_range(0, 15),
}
)


@register_binary_sensor("dooya", DooyaBinarySensor, DOOYA_SCHEMA)
def dooya_binary_sensor(var, config):
cg.add(
var.set_data(
cg.StructInitializer(
DooyaData,
("id", config[CONF_ID]),
("channel", config[CONF_CHANNEL]),
("button", config[CONF_BUTTON]),
("check", config[CONF_CHECK]),
)
)
)


@register_trigger("dooya", DooyaTrigger, DooyaData)
def dooya_trigger(var, config):
pass


@register_dumper("dooya", DooyaDumper)
def dooya_dumper(var, config):
pass


@register_action("dooya", DooyaAction, DOOYA_SCHEMA)
async def dooya_action(var, config, args):
template_ = await cg.templatable(config[CONF_ID], args, cg.uint32)
cg.add(var.set_id(template_))
template_ = await cg.templatable(config[CONF_CHANNEL], args, cg.uint8)
cg.add(var.set_channel(template_))
template_ = await cg.templatable(config[CONF_BUTTON], args, cg.uint8)
cg.add(var.set_button(template_))
template_ = await cg.templatable(config[CONF_CHECK], args, cg.uint8)
cg.add(var.set_check(template_))


# JVC
JVCData, JVCBinarySensor, JVCTrigger, JVCAction, JVCDumper = declare_protocol("JVC")
JVC_SCHEMA = cv.Schema({cv.Required(CONF_DATA): cv.hex_uint32_t})
Expand Down
120 changes: 120 additions & 0 deletions esphome/components/remote_base/dooya_protocol.cpp
@@ -0,0 +1,120 @@
#include "dooya_protocol.h"
#include "esphome/core/log.h"

namespace esphome {
namespace remote_base {

static const char *const TAG = "remote.dooya";

static const uint32_t HEADER_HIGH_US = 5000;
static const uint32_t HEADER_LOW_US = 1500;
static const uint32_t BIT_ZERO_HIGH_US = 750;
static const uint32_t BIT_ZERO_LOW_US = 350;
static const uint32_t BIT_ONE_HIGH_US = 350;
static const uint32_t BIT_ONE_LOW_US = 750;

void DooyaProtocol::encode(RemoteTransmitData *dst, const DooyaData &data) {
dst->set_carrier_frequency(0);
dst->reserve(2 + 40 * 2u);

dst->item(HEADER_HIGH_US, HEADER_LOW_US);

for (uint32_t mask = 1UL << (23); mask != 0; mask >>= 1) {
if (data.id & mask) {
dst->item(BIT_ONE_HIGH_US, BIT_ONE_LOW_US);
} else {
dst->item(BIT_ZERO_HIGH_US, BIT_ZERO_LOW_US);
}
}

for (uint32_t mask = 1UL << (7); mask != 0; mask >>= 1) {
if (data.channel & mask) {
dst->item(BIT_ONE_HIGH_US, BIT_ONE_LOW_US);
} else {
dst->item(BIT_ZERO_HIGH_US, BIT_ZERO_LOW_US);
}
}

for (uint32_t mask = 1UL << (3); mask != 0; mask >>= 1) {
if (data.button & mask) {
dst->item(BIT_ONE_HIGH_US, BIT_ONE_LOW_US);
} else {
dst->item(BIT_ZERO_HIGH_US, BIT_ZERO_LOW_US);
}
}

for (uint32_t mask = 1UL << (3); mask != 0; mask >>= 1) {
if (data.check & mask) {
dst->item(BIT_ONE_HIGH_US, BIT_ONE_LOW_US);
} else {
dst->item(BIT_ZERO_HIGH_US, BIT_ZERO_LOW_US);
}
}
}
optional<DooyaData> DooyaProtocol::decode(RemoteReceiveData src) {
DooyaData out{
.id = 0,
.channel = 0,
.button = 0,
.check = 0,
};
if (!src.expect_item(HEADER_HIGH_US, HEADER_LOW_US))
return {};

for (uint8_t i = 0; i < 24; i++) {
if (src.expect_item(BIT_ONE_HIGH_US, BIT_ONE_LOW_US)) {
out.id = (out.id << 1) | 1;
} else if (src.expect_item(BIT_ZERO_HIGH_US, BIT_ZERO_LOW_US)) {
out.id = (out.id << 1) | 0;
} else {
return {};
}
}

for (uint8_t i = 0; i < 8; i++) {
if (src.expect_item(BIT_ONE_HIGH_US, BIT_ONE_LOW_US)) {
out.channel = (out.channel << 1) | 1;
} else if (src.expect_item(BIT_ZERO_HIGH_US, BIT_ZERO_LOW_US)) {
out.channel = (out.channel << 1) | 0;
} else {
return {};
}
}

for (uint8_t i = 0; i < 4; i++) {
if (src.expect_item(BIT_ONE_HIGH_US, BIT_ONE_LOW_US)) {
out.button = (out.button << 1) | 1;
} else if (src.expect_item(BIT_ZERO_HIGH_US, BIT_ZERO_LOW_US)) {
out.button = (out.button << 1) | 0;
} else {
return {};
}
}

for (uint8_t i = 0; i < 3; i++) {
if (src.expect_item(BIT_ONE_HIGH_US, BIT_ONE_LOW_US)) {
out.check = (out.check << 1) | 1;
} else if (src.expect_item(BIT_ZERO_HIGH_US, BIT_ZERO_LOW_US)) {
out.check = (out.check << 1) | 0;
} else {
return {};
}
}
// Last bit is not received properly but can be decoded
if (src.expect_mark(BIT_ONE_HIGH_US)) {
out.check = (out.check << 1) | 1;
} else if (src.expect_mark(BIT_ZERO_HIGH_US)) {
out.check = (out.check << 1) | 0;
} else {
return {};
}

return out;
}
void DooyaProtocol::dump(const DooyaData &data) {
ESP_LOGI(TAG, "Received Dooya: id=0x%08" PRIX32 ", channel=%d, button=%d, check=%d", data.id, data.channel,
data.button, data.check);
}

} // namespace remote_base
} // namespace esphome
49 changes: 49 additions & 0 deletions esphome/components/remote_base/dooya_protocol.h
@@ -0,0 +1,49 @@
#pragma once

#include "esphome/core/component.h"
#include "remote_base.h"

#include <cinttypes>

namespace esphome {
namespace remote_base {

struct DooyaData {
uint32_t id;
uint8_t channel;
uint8_t button;
uint8_t check;

bool operator==(const DooyaData &rhs) const {
return id == rhs.id && channel == rhs.channel && button == rhs.button && check == rhs.check;
}
};

class DooyaProtocol : public RemoteProtocol<DooyaData> {
public:
void encode(RemoteTransmitData *dst, const DooyaData &data) override;
optional<DooyaData> decode(RemoteReceiveData src) override;
void dump(const DooyaData &data) override;
};

DECLARE_REMOTE_PROTOCOL(Dooya)

template<typename... Ts> class DooyaAction : public RemoteTransmitterActionBase<Ts...> {
public:
TEMPLATABLE_VALUE(uint32_t, id)
TEMPLATABLE_VALUE(uint8_t, channel)
TEMPLATABLE_VALUE(uint8_t, button)
TEMPLATABLE_VALUE(uint8_t, check)

void encode(RemoteTransmitData *dst, Ts... x) override {
DooyaData data{};
data.id = this->id_.value(x...);
data.channel = this->channel_.value(x...);
data.button = this->button_.value(x...);
data.check = this->check_.value(x...);
DooyaProtocol().encode(dst, data);
}
};

} // namespace remote_base
} // namespace esphome
3 changes: 1 addition & 2 deletions esphome/components/xiaomi_rtcgq02lm/binary_sensor.py
Expand Up @@ -8,15 +8,14 @@
DEVICE_CLASS_LIGHT,
DEVICE_CLASS_MOTION,
CONF_ID,
CONF_BUTTON,
)
from esphome.core import TimePeriod

from . import XiaomiRTCGQ02LM

DEPENDENCIES = ["xiaomi_rtcgq02lm"]

CONF_BUTTON = "button"


CONFIG_SCHEMA = cv.Schema(
{
Expand Down
3 changes: 3 additions & 0 deletions esphome/const.py
Expand Up @@ -96,6 +96,7 @@
CONF_BUILD_PATH = "build_path"
CONF_BUS_VOLTAGE = "bus_voltage"
CONF_BUSY_PIN = "busy_pin"
CONF_BUTTON = "button"
CONF_BYTES = "bytes"
CONF_CALCULATED_LUX = "calculated_lux"
CONF_CALIBRATE_LINEAR = "calibrate_linear"
Expand All @@ -110,6 +111,7 @@
CONF_CHANNEL = "channel"
CONF_CHANNELS = "channels"
CONF_CHARACTERISTIC_UUID = "characteristic_uuid"
CONF_CHECK = "check"
CONF_CHIPSET = "chipset"
CONF_CLEAR_IMPEDANCE = "clear_impedance"
CONF_CLIENT_CERTIFICATE = "client_certificate"
Expand Down Expand Up @@ -220,6 +222,7 @@
CONF_DNS1 = "dns1"
CONF_DNS2 = "dns2"
CONF_DOMAIN = "domain"
CONF_DOOYA = "dooya"
CONF_DRY_ACTION = "dry_action"
CONF_DRY_MODE = "dry_mode"
CONF_DUMMY_RECEIVER = "dummy_receiver"
Expand Down