Remote magiquest protocol (#2963)

Co-authored-by: Aaron Hertz <aaron@rockforest.org>
Co-authored-by: Otto Winter <otto@otto-winter.com>
This commit is contained in:
ImSorryButWho 2022-02-18 16:22:41 -05:00 committed by GitHub
parent 1a8f8adc2a
commit f137cc10f4
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 190 additions and 0 deletions

View file

@ -27,6 +27,8 @@ from esphome.const import (
CONF_CARRIER_FREQUENCY, CONF_CARRIER_FREQUENCY,
CONF_RC_CODE_1, CONF_RC_CODE_1,
CONF_RC_CODE_2, CONF_RC_CODE_2,
CONF_MAGNITUDE,
CONF_WAND_ID,
CONF_LEVEL, CONF_LEVEL,
) )
from esphome.core import coroutine from esphome.core import coroutine
@ -391,6 +393,54 @@ async def lg_action(var, config, args):
cg.add(var.set_nbits(template_)) cg.add(var.set_nbits(template_))
# MagiQuest
(
MagiQuestData,
MagiQuestBinarySensor,
MagiQuestTrigger,
MagiQuestAction,
MagiQuestDumper,
) = declare_protocol("MagiQuest")
MAGIQUEST_SCHEMA = cv.Schema(
{
cv.Required(CONF_WAND_ID): cv.hex_uint32_t,
cv.Optional(CONF_MAGNITUDE, default=0xFFFF): cv.hex_uint16_t,
}
)
@register_binary_sensor("magiquest", MagiQuestBinarySensor, MAGIQUEST_SCHEMA)
def magiquest_binary_sensor(var, config):
cg.add(
var.set_data(
cg.StructInitializer(
MagiQuestData,
("magnitude", config[CONF_MAGNITUDE]),
("wand_id", config[CONF_WAND_ID]),
)
)
)
@register_trigger("magiquest", MagiQuestTrigger, MagiQuestData)
def magiquest_trigger(var, config):
pass
@register_dumper("magiquest", MagiQuestDumper)
def magiquest_dumper(var, config):
pass
@register_action("magiquest", MagiQuestAction, MAGIQUEST_SCHEMA)
async def magiquest_action(var, config, args):
template_ = await cg.templatable(config[CONF_WAND_ID], args, cg.uint32)
cg.add(var.set_wand_id(template_))
template_ = await cg.templatable(config[CONF_MAGNITUDE], args, cg.uint16)
cg.add(var.set_magnitude(template_))
# NEC # NEC
NECData, NECBinarySensor, NECTrigger, NECAction, NECDumper = declare_protocol("NEC") NECData, NECBinarySensor, NECTrigger, NECAction, NECDumper = declare_protocol("NEC")
NEC_SCHEMA = cv.Schema( NEC_SCHEMA = cv.Schema(

View file

@ -0,0 +1,83 @@
#include "magiquest_protocol.h"
#include "esphome/core/log.h"
/* Based on protocol analysis from
* https://arduino-irremote.github.io/Arduino-IRremote/ir__MagiQuest_8cpp_source.html
*/
namespace esphome {
namespace remote_base {
static const char *const TAG = "remote.magiquest";
static const uint32_t MAGIQUEST_UNIT = 288; // us
static const uint32_t MAGIQUEST_ONE_MARK = 2 * MAGIQUEST_UNIT;
static const uint32_t MAGIQUEST_ONE_SPACE = 2 * MAGIQUEST_UNIT;
static const uint32_t MAGIQUEST_ZERO_MARK = MAGIQUEST_UNIT;
static const uint32_t MAGIQUEST_ZERO_SPACE = 3 * MAGIQUEST_UNIT;
void MagiQuestProtocol::encode(RemoteTransmitData *dst, const MagiQuestData &data) {
dst->reserve(101); // 2 start bits, 48 data bits, 1 stop bit
dst->set_carrier_frequency(38000);
// 2 start bits
dst->item(MAGIQUEST_ZERO_MARK, MAGIQUEST_ZERO_SPACE);
dst->item(MAGIQUEST_ZERO_MARK, MAGIQUEST_ZERO_SPACE);
for (uint32_t mask = 1 << 31; mask; mask >>= 1) {
if (data.wand_id & mask) {
dst->item(MAGIQUEST_ONE_MARK, MAGIQUEST_ONE_SPACE);
} else {
dst->item(MAGIQUEST_ZERO_MARK, MAGIQUEST_ZERO_SPACE);
}
}
for (uint16_t mask = 1 << 15; mask; mask >>= 1) {
if (data.magnitude & mask) {
dst->item(MAGIQUEST_ONE_MARK, MAGIQUEST_ONE_SPACE);
} else {
dst->item(MAGIQUEST_ZERO_MARK, MAGIQUEST_ZERO_SPACE);
}
}
dst->mark(MAGIQUEST_UNIT);
}
optional<MagiQuestData> MagiQuestProtocol::decode(RemoteReceiveData src) {
MagiQuestData data{
.magnitude = 0,
.wand_id = 0,
};
// Two start bits
if (!src.expect_item(MAGIQUEST_ZERO_MARK, MAGIQUEST_ZERO_SPACE) ||
!src.expect_item(MAGIQUEST_ZERO_MARK, MAGIQUEST_ZERO_SPACE)) {
return {};
}
for (uint32_t mask = 1 << 31; mask; mask >>= 1) {
if (src.expect_item(MAGIQUEST_ONE_MARK, MAGIQUEST_ONE_SPACE)) {
data.wand_id |= mask;
} else if (src.expect_item(MAGIQUEST_ZERO_MARK, MAGIQUEST_ZERO_SPACE)) {
data.wand_id &= ~mask;
} else {
return {};
}
}
for (uint16_t mask = 1 << 15; mask; mask >>= 1) {
if (src.expect_item(MAGIQUEST_ONE_MARK, MAGIQUEST_ONE_SPACE)) {
data.magnitude |= mask;
} else if (src.expect_item(MAGIQUEST_ZERO_MARK, MAGIQUEST_ZERO_SPACE)) {
data.magnitude &= ~mask;
} else {
return {};
}
}
src.expect_mark(MAGIQUEST_UNIT);
return data;
}
void MagiQuestProtocol::dump(const MagiQuestData &data) {
ESP_LOGD(TAG, "Received MagiQuest: wand_id=0x%08X, magnitude=0x%04X", data.wand_id, data.magnitude);
}
} // namespace remote_base
} // namespace esphome

View file

@ -0,0 +1,50 @@
#pragma once
#include "remote_base.h"
/* Based on protocol analysis from
* https://arduino-irremote.github.io/Arduino-IRremote/ir__MagiQuest_8cpp_source.html
*/
namespace esphome {
namespace remote_base {
struct MagiQuestData {
uint16_t magnitude;
uint32_t wand_id;
bool operator==(const MagiQuestData &rhs) const {
// Treat 0xffff as a special, wildcard magnitude
// In testing, the wand never produces this value, and this allows us to match
// on just the wand_id if wanted.
if (rhs.wand_id != this->wand_id) {
return false;
}
return (this->wand_id == 0xffff || rhs.wand_id == 0xffff || this->wand_id == rhs.wand_id);
}
};
class MagiQuestProtocol : public RemoteProtocol<MagiQuestData> {
public:
void encode(RemoteTransmitData *dst, const MagiQuestData &data) override;
optional<MagiQuestData> decode(RemoteReceiveData src) override;
void dump(const MagiQuestData &data) override;
};
DECLARE_REMOTE_PROTOCOL(MagiQuest)
template<typename... Ts> class MagiQuestAction : public RemoteTransmitterActionBase<Ts...> {
public:
TEMPLATABLE_VALUE(uint16_t, magnitude)
TEMPLATABLE_VALUE(uint32_t, wand_id)
void encode(RemoteTransmitData *dst, Ts... x) override {
MagiQuestData data{};
data.magnitude = this->magnitude_.value(x...);
data.wand_id = this->wand_id_.value(x...);
MagiQuestProtocol().encode(dst, data);
}
};
} // namespace remote_base
} // namespace esphome

View file

@ -344,6 +344,7 @@ CONF_LOOP_TIME = "loop_time"
CONF_LOW = "low" CONF_LOW = "low"
CONF_LOW_VOLTAGE_REFERENCE = "low_voltage_reference" CONF_LOW_VOLTAGE_REFERENCE = "low_voltage_reference"
CONF_MAC_ADDRESS = "mac_address" CONF_MAC_ADDRESS = "mac_address"
CONF_MAGNITUDE = "magnitude"
CONF_MAINS_FILTER = "mains_filter" CONF_MAINS_FILTER = "mains_filter"
CONF_MAKE_ID = "make_id" CONF_MAKE_ID = "make_id"
CONF_MANUAL_IP = "manual_ip" CONF_MANUAL_IP = "manual_ip"
@ -735,6 +736,7 @@ CONF_VOLTAGE_DIVIDER = "voltage_divider"
CONF_WAIT_TIME = "wait_time" CONF_WAIT_TIME = "wait_time"
CONF_WAIT_UNTIL = "wait_until" CONF_WAIT_UNTIL = "wait_until"
CONF_WAKEUP_PIN = "wakeup_pin" CONF_WAKEUP_PIN = "wakeup_pin"
CONF_WAND_ID = "wand_id"
CONF_WARM_WHITE = "warm_white" CONF_WARM_WHITE = "warm_white"
CONF_WARM_WHITE_COLOR_TEMPERATURE = "warm_white_color_temperature" CONF_WARM_WHITE_COLOR_TEMPERATURE = "warm_white_color_temperature"
CONF_WATCHDOG_THRESHOLD = "watchdog_threshold" CONF_WATCHDOG_THRESHOLD = "watchdog_threshold"

View file

@ -1861,6 +1861,11 @@ switch:
turn_on_action: turn_on_action:
remote_transmitter.transmit_jvc: remote_transmitter.transmit_jvc:
data: 0x10EF data: 0x10EF
- platform: template
name: MagiQuest
turn_on_action:
remote_transmitter.transmit_magiquest:
wand_id: 0x01234567
- platform: template - platform: template
name: NEC name: NEC
id: living_room_lights_off id: living_room_lights_off