ir_transmitter: add support for repeated commands in NEC protocol (#4995)

Co-authored-by: Nicolas Gilles <nicolas.gilles@aiven.io>
This commit is contained in:
Nicolas Gilles 2023-12-13 00:55:20 +01:00 committed by GitHub
parent 69026f7599
commit d0bcba3b3f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 44 additions and 7 deletions

View File

@ -3,6 +3,7 @@ import esphome.config_validation as cv
from esphome import automation from esphome import automation
from esphome.components import binary_sensor from esphome.components import binary_sensor
from esphome.const import ( from esphome.const import (
CONF_COMMAND_REPEATS,
CONF_DATA, CONF_DATA,
CONF_TRIGGER_ID, CONF_TRIGGER_ID,
CONF_NBITS, CONF_NBITS,
@ -638,6 +639,7 @@ NEC_SCHEMA = cv.Schema(
{ {
cv.Required(CONF_ADDRESS): cv.hex_uint16_t, cv.Required(CONF_ADDRESS): cv.hex_uint16_t,
cv.Required(CONF_COMMAND): cv.hex_uint16_t, cv.Required(CONF_COMMAND): cv.hex_uint16_t,
cv.Optional(CONF_COMMAND_REPEATS, default=1): cv.uint16_t,
} }
) )
@ -650,6 +652,7 @@ def nec_binary_sensor(var, config):
NECData, NECData,
("address", config[CONF_ADDRESS]), ("address", config[CONF_ADDRESS]),
("command", config[CONF_COMMAND]), ("command", config[CONF_COMMAND]),
("command_repeats", config[CONF_COMMAND_REPEATS]),
) )
) )
) )
@ -671,6 +674,8 @@ async def nec_action(var, config, args):
cg.add(var.set_address(template_)) cg.add(var.set_address(template_))
template_ = await cg.templatable(config[CONF_COMMAND], args, cg.uint16) template_ = await cg.templatable(config[CONF_COMMAND], args, cg.uint16)
cg.add(var.set_command(template_)) cg.add(var.set_command(template_))
template_ = await cg.templatable(config[CONF_COMMAND_REPEATS], args, cg.uint16)
cg.add(var.set_command_repeats(template_))
# Pioneer # Pioneer

View File

@ -13,10 +13,14 @@ static const uint32_t BIT_ONE_LOW_US = 1690;
static const uint32_t BIT_ZERO_LOW_US = 560; static const uint32_t BIT_ZERO_LOW_US = 560;
void NECProtocol::encode(RemoteTransmitData *dst, const NECData &data) { void NECProtocol::encode(RemoteTransmitData *dst, const NECData &data) {
dst->reserve(68); ESP_LOGD(TAG, "Sending NEC: address=0x%04X, command=0x%04X command_repeats=%d", data.address, data.command,
data.command_repeats);
dst->reserve(2 + 32 + 32 * data.command_repeats + 2);
dst->set_carrier_frequency(38000); dst->set_carrier_frequency(38000);
dst->item(HEADER_HIGH_US, HEADER_LOW_US); dst->item(HEADER_HIGH_US, HEADER_LOW_US);
for (uint16_t mask = 1; mask; mask <<= 1) { for (uint16_t mask = 1; mask; mask <<= 1) {
if (data.address & mask) { if (data.address & mask) {
dst->item(BIT_HIGH_US, BIT_ONE_LOW_US); dst->item(BIT_HIGH_US, BIT_ONE_LOW_US);
@ -25,11 +29,13 @@ void NECProtocol::encode(RemoteTransmitData *dst, const NECData &data) {
} }
} }
for (uint16_t mask = 1; mask; mask <<= 1) { for (uint16_t repeats = 0; repeats < data.command_repeats; repeats++) {
if (data.command & mask) { for (uint16_t mask = 1; mask; mask <<= 1) {
dst->item(BIT_HIGH_US, BIT_ONE_LOW_US); if (data.command & mask) {
} else { dst->item(BIT_HIGH_US, BIT_ONE_LOW_US);
dst->item(BIT_HIGH_US, BIT_ZERO_LOW_US); } else {
dst->item(BIT_HIGH_US, BIT_ZERO_LOW_US);
}
} }
} }
@ -39,6 +45,7 @@ optional<NECData> NECProtocol::decode(RemoteReceiveData src) {
NECData data{ NECData data{
.address = 0, .address = 0,
.command = 0, .command = 0,
.command_repeats = 1,
}; };
if (!src.expect_item(HEADER_HIGH_US, HEADER_LOW_US)) if (!src.expect_item(HEADER_HIGH_US, HEADER_LOW_US))
return {}; return {};
@ -63,11 +70,32 @@ optional<NECData> NECProtocol::decode(RemoteReceiveData src) {
} }
} }
while (src.peek_item(BIT_HIGH_US, BIT_ONE_LOW_US) || src.peek_item(BIT_HIGH_US, BIT_ZERO_LOW_US)) {
uint16_t command = 0;
for (uint16_t mask = 1; mask; mask <<= 1) {
if (src.expect_item(BIT_HIGH_US, BIT_ONE_LOW_US)) {
command |= mask;
} else if (src.expect_item(BIT_HIGH_US, BIT_ZERO_LOW_US)) {
command &= ~mask;
} else {
return {};
}
}
// Make sure the extra/repeated data matches original command
if (command != data.command) {
return {};
}
data.command_repeats += 1;
}
src.expect_mark(BIT_HIGH_US); src.expect_mark(BIT_HIGH_US);
return data; return data;
} }
void NECProtocol::dump(const NECData &data) { void NECProtocol::dump(const NECData &data) {
ESP_LOGI(TAG, "Received NEC: address=0x%04X, command=0x%04X", data.address, data.command); ESP_LOGI(TAG, "Received NEC: address=0x%04X, command=0x%04X command_repeats=%d", data.address, data.command,
data.command_repeats);
} }
} // namespace remote_base } // namespace remote_base

View File

@ -8,6 +8,7 @@ namespace remote_base {
struct NECData { struct NECData {
uint16_t address; uint16_t address;
uint16_t command; uint16_t command;
uint16_t command_repeats;
bool operator==(const NECData &rhs) const { return address == rhs.address && command == rhs.command; } bool operator==(const NECData &rhs) const { return address == rhs.address && command == rhs.command; }
}; };
@ -25,11 +26,13 @@ template<typename... Ts> class NECAction : public RemoteTransmitterActionBase<Ts
public: public:
TEMPLATABLE_VALUE(uint16_t, address) TEMPLATABLE_VALUE(uint16_t, address)
TEMPLATABLE_VALUE(uint16_t, command) TEMPLATABLE_VALUE(uint16_t, command)
TEMPLATABLE_VALUE(uint16_t, command_repeats)
void encode(RemoteTransmitData *dst, Ts... x) override { void encode(RemoteTransmitData *dst, Ts... x) override {
NECData data{}; NECData data{};
data.address = this->address_.value(x...); data.address = this->address_.value(x...);
data.command = this->command_.value(x...); data.command = this->command_.value(x...);
data.command_repeats = this->command_repeats_.value(x...);
NECProtocol().encode(dst, data); NECProtocol().encode(dst, data);
} }
}; };

View File

@ -131,6 +131,7 @@ CONF_COLOR_PALETTE = "color_palette"
CONF_COLOR_TEMPERATURE = "color_temperature" CONF_COLOR_TEMPERATURE = "color_temperature"
CONF_COLORS = "colors" CONF_COLORS = "colors"
CONF_COMMAND = "command" CONF_COMMAND = "command"
CONF_COMMAND_REPEATS = "command_repeats"
CONF_COMMAND_RETAIN = "command_retain" CONF_COMMAND_RETAIN = "command_retain"
CONF_COMMAND_TOPIC = "command_topic" CONF_COMMAND_TOPIC = "command_topic"
CONF_COMMENT = "comment" CONF_COMMENT = "comment"