mirror of
https://github.com/esphome/esphome.git
synced 2026-09-11 07:17:33 +00:00
Compare commits
4
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
dcd39aa8ba | ||
|
|
152bab9699 | ||
|
|
a1307f0f40 | ||
|
|
b318499666 |
@@ -1,5 +1,5 @@
|
||||
import esphome.codegen as cg
|
||||
from esphome.components import climate_ir, remote_base
|
||||
from esphome.components import climate_ir
|
||||
from esphome.types import ConfigType
|
||||
|
||||
AUTO_LOAD = ["climate_ir"]
|
||||
@@ -12,5 +12,4 @@ CONFIG_SCHEMA = climate_ir.climate_ir_with_receiver_schema(CoolixClimate)
|
||||
|
||||
|
||||
async def to_code(config: ConfigType) -> None:
|
||||
remote_base.request_protocol("coolix") # used from C++
|
||||
await climate_ir.new_climate_ir(config)
|
||||
|
||||
@@ -59,6 +59,11 @@ void Infrared::setup() {
|
||||
// Set up traits based on configuration
|
||||
this->traits_.set_supports_transmitter(this->has_transmitter());
|
||||
this->traits_.set_supports_receiver(this->has_receiver());
|
||||
|
||||
// Register as listener for received IR data
|
||||
if (this->receiver_ != nullptr) {
|
||||
this->receiver_->register_listener(this);
|
||||
}
|
||||
}
|
||||
|
||||
void Infrared::dump_config() {
|
||||
|
||||
@@ -1,9 +1,6 @@
|
||||
"""IR/RF Proxy component - provides remote_base backend for infrared platform."""
|
||||
|
||||
import esphome.codegen as cg
|
||||
from esphome.components import remote_base
|
||||
from esphome.cpp_generator import MockObj
|
||||
from esphome.types import ConfigType
|
||||
|
||||
CODEOWNERS = ["@kbx81"]
|
||||
|
||||
@@ -12,10 +9,3 @@ ir_rf_proxy_ns = cg.esphome_ns.namespace("ir_rf_proxy")
|
||||
|
||||
CONF_REMOTE_RECEIVER_ID = "remote_receiver_id"
|
||||
CONF_REMOTE_TRANSMITTER_ID = "remote_transmitter_id"
|
||||
|
||||
|
||||
async def attach_receiver(var: MockObj, config: ConfigType) -> None:
|
||||
"""Wire the configured remote_receiver to a proxy entity and register it as a listener."""
|
||||
receiver = await cg.get_variable(config[CONF_REMOTE_RECEIVER_ID])
|
||||
cg.add(var.set_receiver(receiver))
|
||||
remote_base.add_listener(receiver, var)
|
||||
|
||||
@@ -9,12 +9,7 @@ import esphome.config_validation as cv
|
||||
from esphome.const import CONF_CARRIER_DUTY_PERCENT, CONF_FREQUENCY
|
||||
import esphome.final_validate as fv
|
||||
|
||||
from . import (
|
||||
CONF_REMOTE_RECEIVER_ID,
|
||||
CONF_REMOTE_TRANSMITTER_ID,
|
||||
attach_receiver,
|
||||
ir_rf_proxy_ns,
|
||||
)
|
||||
from . import CONF_REMOTE_RECEIVER_ID, CONF_REMOTE_TRANSMITTER_ID, ir_rf_proxy_ns
|
||||
|
||||
CODEOWNERS = ["@kbx81"]
|
||||
DEPENDENCIES = ["infrared"]
|
||||
@@ -87,7 +82,8 @@ async def to_code(config: dict[str, Any]) -> None:
|
||||
|
||||
# Link receiver if specified
|
||||
if CONF_REMOTE_RECEIVER_ID in config:
|
||||
await attach_receiver(var, config)
|
||||
receiver = await cg.get_variable(config[CONF_REMOTE_RECEIVER_ID])
|
||||
cg.add(var.set_receiver(receiver))
|
||||
|
||||
# Set receiver demodulation frequency if specified (metadata only, no hardware effect)
|
||||
if CONF_RECEIVER_FREQUENCY in config:
|
||||
|
||||
@@ -97,6 +97,10 @@ void RfProxy::setup() {
|
||||
|
||||
// remote_transmitter/receiver always uses OOK (on-off keying)
|
||||
this->traits_.add_supported_modulation(radio_frequency::RadioFrequencyModulation::RADIO_FREQUENCY_MODULATION_OOK);
|
||||
|
||||
if (this->receiver_ != nullptr) {
|
||||
this->receiver_->register_listener(this);
|
||||
}
|
||||
}
|
||||
|
||||
void RfProxy::dump_config() {
|
||||
|
||||
@@ -7,12 +7,7 @@ from esphome.const import CONF_CARRIER_DUTY_PERCENT, CONF_FREQUENCY
|
||||
import esphome.final_validate as fv
|
||||
from esphome.types import ConfigType
|
||||
|
||||
from . import (
|
||||
CONF_REMOTE_RECEIVER_ID,
|
||||
CONF_REMOTE_TRANSMITTER_ID,
|
||||
attach_receiver,
|
||||
ir_rf_proxy_ns,
|
||||
)
|
||||
from . import CONF_REMOTE_RECEIVER_ID, CONF_REMOTE_TRANSMITTER_ID, ir_rf_proxy_ns
|
||||
|
||||
CODEOWNERS = ["@kbx81"]
|
||||
DEPENDENCIES = ["radio_frequency"]
|
||||
@@ -71,4 +66,5 @@ async def to_code(config: ConfigType) -> None:
|
||||
cg.add(var.set_transmitter(transmitter))
|
||||
|
||||
if CONF_REMOTE_RECEIVER_ID in config:
|
||||
await attach_receiver(var, config)
|
||||
receiver = await cg.get_variable(config[CONF_REMOTE_RECEIVER_ID])
|
||||
cg.add(var.set_receiver(receiver))
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
from esphome import automation
|
||||
import esphome.codegen as cg
|
||||
from esphome.components import climate, remote_base, remote_transmitter, sensor, uart
|
||||
from esphome.components import climate, remote_transmitter, sensor, uart
|
||||
from esphome.components.climate import ClimateMode, ClimatePreset, ClimateSwingMode
|
||||
from esphome.components.remote_base import CONF_TRANSMITTER_ID
|
||||
import esphome.config_validation as cv
|
||||
@@ -280,7 +280,6 @@ async def to_code(config):
|
||||
cg.add(var.set_response_timeout(config[CONF_TIMEOUT].total_milliseconds))
|
||||
cg.add(var.set_request_attempts(config[CONF_NUM_ATTEMPTS]))
|
||||
if CONF_TRANSMITTER_ID in config:
|
||||
remote_base.request_protocol("midea") # ir_transmitter.h uses it from C++
|
||||
cg.add_define("USE_REMOTE_TRANSMITTER")
|
||||
transmitter_ = await cg.get_variable(config[CONF_TRANSMITTER_ID])
|
||||
cg.add(var.set_transmitter(transmitter_))
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import esphome.codegen as cg
|
||||
from esphome.components import climate_ir, remote_base
|
||||
from esphome.components import climate_ir
|
||||
import esphome.config_validation as cv
|
||||
from esphome.const import CONF_USE_FAHRENHEIT
|
||||
from esphome.types import ConfigType
|
||||
@@ -19,9 +19,5 @@ CONFIG_SCHEMA = climate_ir.climate_ir_with_receiver_schema(MideaIR).extend(
|
||||
|
||||
|
||||
async def to_code(config: ConfigType) -> None:
|
||||
# midea_ir uses MideaProtocol from C++ and auto-loads coolix, whose coolix.cpp uses
|
||||
# CoolixProtocol even when no coolix climate is configured
|
||||
remote_base.request_protocol("midea")
|
||||
remote_base.request_protocol("coolix")
|
||||
var = await climate_ir.new_climate_ir(config)
|
||||
cg.add(var.set_fahrenheit(config[CONF_USE_FAHRENHEIT]))
|
||||
|
||||
@@ -1,11 +1,6 @@
|
||||
from collections.abc import Callable
|
||||
from pathlib import Path
|
||||
from typing import Any
|
||||
|
||||
from esphome import automation
|
||||
import esphome.codegen as cg
|
||||
from esphome.components import binary_sensor
|
||||
from esphome.config_helpers import filter_source_files_from_defines
|
||||
import esphome.config_validation as cv
|
||||
from esphome.const import (
|
||||
CONF_ADDRESS,
|
||||
@@ -45,9 +40,7 @@ from esphome.const import (
|
||||
CONF_ZERO,
|
||||
)
|
||||
from esphome.core import ID, coroutine
|
||||
from esphome.cpp_generator import MockObj
|
||||
from esphome.schema_extractors import SCHEMA_EXTRACT, schema_extractor
|
||||
from esphome.types import ConfigType
|
||||
from esphome.util import Registry, SimpleRegistry
|
||||
|
||||
AUTO_LOAD = ["binary_sensor"]
|
||||
@@ -97,25 +90,9 @@ REMOTE_TRANSMITTABLE_SCHEMA = cv.Schema(
|
||||
)
|
||||
|
||||
|
||||
# Listener and dumper lists are StaticVectors sized from these counts, so every
|
||||
# registration must go through add_listener / add_dumper
|
||||
_request_listener_slot = cg.slot_counter("REMOTE_BASE_LISTENER_COUNT")
|
||||
_request_dumper_slot = cg.slot_counter("REMOTE_BASE_DUMPER_COUNT")
|
||||
|
||||
|
||||
def add_listener(receiver: MockObj, listener: MockObj) -> None:
|
||||
_request_listener_slot()
|
||||
cg.add(receiver.register_listener(listener))
|
||||
|
||||
|
||||
def add_dumper(receiver: MockObj, dumper: MockObj) -> None:
|
||||
_request_dumper_slot()
|
||||
cg.add(receiver.register_dumper(dumper))
|
||||
|
||||
|
||||
async def register_listener(var: MockObj, config: ConfigType) -> None:
|
||||
async def register_listener(var, config):
|
||||
receiver = await cg.get_variable(config[CONF_RECEIVER_ID])
|
||||
add_listener(receiver, var)
|
||||
cg.add(receiver.register_listener(var))
|
||||
|
||||
|
||||
async def register_transmittable(var, config):
|
||||
@@ -123,47 +100,8 @@ async def register_transmittable(var, config):
|
||||
cg.add(var.set_transmitter(transmitter_))
|
||||
|
||||
|
||||
# Registry names that share a protocol source file
|
||||
def _protocol_stem(name: str) -> str:
|
||||
if name.startswith("rc_switch"):
|
||||
return "rc_switch"
|
||||
if name == "canalsatld":
|
||||
return "canalsat"
|
||||
return name
|
||||
|
||||
|
||||
def protocol_define(name: str) -> str:
|
||||
return f"USE_REMOTE_PROTOCOL_{_protocol_stem(name).upper()}"
|
||||
|
||||
|
||||
def request_protocol(name: str) -> None:
|
||||
"""Keep a protocol's source file in the build; components using it from C++ must call this."""
|
||||
cg.add_define(protocol_define(name))
|
||||
|
||||
|
||||
_PROTOCOL_STEMS = sorted(
|
||||
path.name.removesuffix("_protocol.cpp")
|
||||
for path in Path(__file__).parent.glob("*_protocol.cpp")
|
||||
)
|
||||
# Only the protocol sources a configuration uses are compiled
|
||||
FILTER_SOURCE_FILES = filter_source_files_from_defines(
|
||||
{f"{stem}_protocol.cpp": protocol_define(stem) for stem in _PROTOCOL_STEMS}
|
||||
)
|
||||
|
||||
|
||||
def register_binary_sensor(
|
||||
name: str, type: MockObj, schema: cv.Schema | dict
|
||||
) -> Callable[[Callable[[MockObj, ConfigType], Any]], Callable]:
|
||||
registerer = BINARY_SENSOR_REGISTRY.register(name, type, schema)
|
||||
|
||||
def decorator(func: Callable[[MockObj, ConfigType], Any]) -> Callable:
|
||||
async def new_func(var: MockObj, config: ConfigType) -> None:
|
||||
request_protocol(name)
|
||||
await coroutine(func)(var, config)
|
||||
|
||||
return registerer(new_func)
|
||||
|
||||
return decorator
|
||||
def register_binary_sensor(name, type, schema):
|
||||
return BINARY_SENSOR_REGISTRY.register(name, type, schema)
|
||||
|
||||
|
||||
def register_trigger(name, type, data_type):
|
||||
@@ -176,7 +114,6 @@ def register_trigger(name, type, data_type):
|
||||
|
||||
def decorator(func):
|
||||
async def new_func(config):
|
||||
request_protocol(name)
|
||||
var = cg.new_Pvariable(config[CONF_TRIGGER_ID])
|
||||
await coroutine(func)(var, config)
|
||||
await automation.build_automation(var, [(data_type, "x")], config)
|
||||
@@ -194,7 +131,6 @@ def register_dumper(name, type, schema=None):
|
||||
|
||||
def decorator(func):
|
||||
async def new_func(config, dumper_id):
|
||||
request_protocol(name)
|
||||
var = cg.new_Pvariable(dumper_id)
|
||||
await coroutine(func)(var, config)
|
||||
return var
|
||||
@@ -235,7 +171,6 @@ def register_action(name, type_, schema):
|
||||
|
||||
def decorator(func):
|
||||
async def new_func(config, action_id, template_arg, args):
|
||||
request_protocol(name)
|
||||
var = cg.new_Pvariable(action_id, template_arg)
|
||||
await register_transmittable(var, config)
|
||||
if CONF_REPEAT in config:
|
||||
@@ -275,21 +210,9 @@ TRIGGER_REGISTRY = SimpleRegistry()
|
||||
DUMPER_REGISTRY = Registry()
|
||||
|
||||
|
||||
def _dumper_key(item: Any) -> Any:
|
||||
"""Registry key of a dump entry in either its string or its mapping form."""
|
||||
if isinstance(item, dict) and len(item) == 1:
|
||||
return next(iter(item))
|
||||
return item
|
||||
|
||||
|
||||
def validate_dumpers(value):
|
||||
if isinstance(value, str) and value.lower() == "all":
|
||||
return validate_dumpers(list(DUMPER_REGISTRY.keys()))
|
||||
if isinstance(value, list):
|
||||
# a dumper listed twice would register twice; the receiver holds one secondary dumper
|
||||
keys = [_dumper_key(item) for item in value]
|
||||
if all(isinstance(key, str) for key in keys):
|
||||
value = list(dict(zip(keys, value, strict=True)).values())
|
||||
return cv.validate_registry("dumper", DUMPER_REGISTRY)(value)
|
||||
|
||||
|
||||
|
||||
@@ -191,9 +191,9 @@ class ABBWelcomeData {
|
||||
|
||||
class ABBWelcomeProtocol : public RemoteProtocol<ABBWelcomeData> {
|
||||
public:
|
||||
void encode(RemoteTransmitData *dst, const ABBWelcomeData &src);
|
||||
optional<ABBWelcomeData> decode(RemoteReceiveData src);
|
||||
void dump(const ABBWelcomeData &data);
|
||||
void encode(RemoteTransmitData *dst, const ABBWelcomeData &src) override;
|
||||
optional<ABBWelcomeData> decode(RemoteReceiveData src) override;
|
||||
void dump(const ABBWelcomeData &data) override;
|
||||
|
||||
protected:
|
||||
void encode_byte_(RemoteTransmitData *dst, uint8_t data) const;
|
||||
|
||||
@@ -15,9 +15,9 @@ struct AEHAData {
|
||||
|
||||
class AEHAProtocol : public RemoteProtocol<AEHAData> {
|
||||
public:
|
||||
void encode(RemoteTransmitData *dst, const AEHAData &data);
|
||||
optional<AEHAData> decode(RemoteReceiveData src);
|
||||
void dump(const AEHAData &data);
|
||||
void encode(RemoteTransmitData *dst, const AEHAData &data) override;
|
||||
optional<AEHAData> decode(RemoteReceiveData src) override;
|
||||
void dump(const AEHAData &data) override;
|
||||
|
||||
private:
|
||||
std::string format_data_(const std::vector<uint8_t> &data);
|
||||
|
||||
@@ -16,9 +16,9 @@ struct Beo4Data {
|
||||
|
||||
class Beo4Protocol : public RemoteProtocol<Beo4Data> {
|
||||
public:
|
||||
void encode(RemoteTransmitData *dst, const Beo4Data &data);
|
||||
optional<Beo4Data> decode(RemoteReceiveData src);
|
||||
void dump(const Beo4Data &data);
|
||||
void encode(RemoteTransmitData *dst, const Beo4Data &data) override;
|
||||
optional<Beo4Data> decode(RemoteReceiveData src) override;
|
||||
void dump(const Beo4Data &data) override;
|
||||
};
|
||||
|
||||
DECLARE_REMOTE_PROTOCOL(Beo4)
|
||||
|
||||
@@ -13,9 +13,9 @@ struct BrennenstuhlData {
|
||||
|
||||
class BrennenstuhlProtocol : public RemoteProtocol<BrennenstuhlData> {
|
||||
public:
|
||||
void encode(RemoteTransmitData *dst, const BrennenstuhlData &data);
|
||||
optional<BrennenstuhlData> decode(RemoteReceiveData src);
|
||||
void dump(const BrennenstuhlData &data);
|
||||
void encode(RemoteTransmitData *dst, const BrennenstuhlData &data) override;
|
||||
optional<BrennenstuhlData> decode(RemoteReceiveData src) override;
|
||||
void dump(const BrennenstuhlData &data) override;
|
||||
};
|
||||
|
||||
DECLARE_REMOTE_PROTOCOL(Brennenstuhl)
|
||||
|
||||
@@ -21,9 +21,9 @@ struct ByronSXData {
|
||||
|
||||
class ByronSXProtocol : public RemoteProtocol<ByronSXData> {
|
||||
public:
|
||||
void encode(RemoteTransmitData *dst, const ByronSXData &data);
|
||||
optional<ByronSXData> decode(RemoteReceiveData src);
|
||||
void dump(const ByronSXData &data);
|
||||
void encode(RemoteTransmitData *dst, const ByronSXData &data) override;
|
||||
optional<ByronSXData> decode(RemoteReceiveData src) override;
|
||||
void dump(const ByronSXData &data) override;
|
||||
};
|
||||
|
||||
DECLARE_REMOTE_PROTOCOL(ByronSX)
|
||||
|
||||
@@ -19,9 +19,9 @@ struct CanalSatLDData : public CanalSatData {};
|
||||
|
||||
class CanalSatBaseProtocol : public RemoteProtocol<CanalSatData> {
|
||||
public:
|
||||
void encode(RemoteTransmitData *dst, const CanalSatData &data);
|
||||
optional<CanalSatData> decode(RemoteReceiveData src);
|
||||
void dump(const CanalSatData &data);
|
||||
void encode(RemoteTransmitData *dst, const CanalSatData &data) override;
|
||||
optional<CanalSatData> decode(RemoteReceiveData src) override;
|
||||
void dump(const CanalSatData &data) override;
|
||||
|
||||
protected:
|
||||
uint16_t frequency_;
|
||||
|
||||
@@ -21,9 +21,9 @@ struct CoolixData {
|
||||
|
||||
class CoolixProtocol : public RemoteProtocol<CoolixData> {
|
||||
public:
|
||||
void encode(RemoteTransmitData *dst, const CoolixData &data);
|
||||
optional<CoolixData> decode(RemoteReceiveData data);
|
||||
void dump(const CoolixData &data);
|
||||
void encode(RemoteTransmitData *dst, const CoolixData &data) override;
|
||||
optional<CoolixData> decode(RemoteReceiveData data) override;
|
||||
void dump(const CoolixData &data) override;
|
||||
};
|
||||
|
||||
DECLARE_REMOTE_PROTOCOL(Coolix)
|
||||
|
||||
@@ -13,9 +13,9 @@ struct DishData {
|
||||
|
||||
class DishProtocol : public RemoteProtocol<DishData> {
|
||||
public:
|
||||
void encode(RemoteTransmitData *dst, const DishData &data);
|
||||
optional<DishData> decode(RemoteReceiveData src);
|
||||
void dump(const DishData &data);
|
||||
void encode(RemoteTransmitData *dst, const DishData &data) override;
|
||||
optional<DishData> decode(RemoteReceiveData src) override;
|
||||
void dump(const DishData &data) override;
|
||||
};
|
||||
|
||||
DECLARE_REMOTE_PROTOCOL(Dish)
|
||||
|
||||
@@ -20,9 +20,9 @@ struct DooyaData {
|
||||
|
||||
class DooyaProtocol : public RemoteProtocol<DooyaData> {
|
||||
public:
|
||||
void encode(RemoteTransmitData *dst, const DooyaData &data);
|
||||
optional<DooyaData> decode(RemoteReceiveData src);
|
||||
void dump(const DooyaData &data);
|
||||
void encode(RemoteTransmitData *dst, const DooyaData &data) override;
|
||||
optional<DooyaData> decode(RemoteReceiveData src) override;
|
||||
void dump(const DooyaData &data) override;
|
||||
};
|
||||
|
||||
DECLARE_REMOTE_PROTOCOL(Dooya)
|
||||
|
||||
@@ -19,9 +19,9 @@ struct DraytonData {
|
||||
|
||||
class DraytonProtocol : public RemoteProtocol<DraytonData> {
|
||||
public:
|
||||
void encode(RemoteTransmitData *dst, const DraytonData &data);
|
||||
optional<DraytonData> decode(RemoteReceiveData src);
|
||||
void dump(const DraytonData &data);
|
||||
void encode(RemoteTransmitData *dst, const DraytonData &data) override;
|
||||
optional<DraytonData> decode(RemoteReceiveData src) override;
|
||||
void dump(const DraytonData &data) override;
|
||||
};
|
||||
|
||||
DECLARE_REMOTE_PROTOCOL(Drayton)
|
||||
|
||||
@@ -21,9 +21,9 @@ struct DysonData {
|
||||
|
||||
class DysonProtocol : public RemoteProtocol<DysonData> {
|
||||
public:
|
||||
void encode(RemoteTransmitData *dst, const DysonData &data);
|
||||
optional<DysonData> decode(RemoteReceiveData src);
|
||||
void dump(const DysonData &data);
|
||||
void encode(RemoteTransmitData *dst, const DysonData &data) override;
|
||||
optional<DysonData> decode(RemoteReceiveData src) override;
|
||||
void dump(const DysonData &data) override;
|
||||
};
|
||||
|
||||
DECLARE_REMOTE_PROTOCOL(Dyson)
|
||||
|
||||
@@ -31,9 +31,9 @@ class GoboxProtocol : public RemoteProtocol<GoboxData> {
|
||||
void dump_timings_(const RawTimings &timings) const;
|
||||
|
||||
public:
|
||||
void encode(RemoteTransmitData *dst, const GoboxData &data);
|
||||
optional<GoboxData> decode(RemoteReceiveData src);
|
||||
void dump(const GoboxData &data);
|
||||
void encode(RemoteTransmitData *dst, const GoboxData &data) override;
|
||||
optional<GoboxData> decode(RemoteReceiveData src) override;
|
||||
void dump(const GoboxData &data) override;
|
||||
};
|
||||
|
||||
DECLARE_REMOTE_PROTOCOL(Gobox)
|
||||
|
||||
@@ -13,9 +13,9 @@ struct HaierData {
|
||||
|
||||
class HaierProtocol : public RemoteProtocol<HaierData> {
|
||||
public:
|
||||
void encode(RemoteTransmitData *dst, const HaierData &data);
|
||||
optional<HaierData> decode(RemoteReceiveData src);
|
||||
void dump(const HaierData &data);
|
||||
void encode(RemoteTransmitData *dst, const HaierData &data) override;
|
||||
optional<HaierData> decode(RemoteReceiveData src) override;
|
||||
void dump(const HaierData &data) override;
|
||||
|
||||
protected:
|
||||
void encode_byte_(RemoteTransmitData *dst, uint8_t item);
|
||||
|
||||
@@ -14,9 +14,9 @@ struct JVCData {
|
||||
|
||||
class JVCProtocol : public RemoteProtocol<JVCData> {
|
||||
public:
|
||||
void encode(RemoteTransmitData *dst, const JVCData &data);
|
||||
optional<JVCData> decode(RemoteReceiveData src);
|
||||
void dump(const JVCData &data);
|
||||
void encode(RemoteTransmitData *dst, const JVCData &data) override;
|
||||
optional<JVCData> decode(RemoteReceiveData src) override;
|
||||
void dump(const JVCData &data) override;
|
||||
};
|
||||
|
||||
DECLARE_REMOTE_PROTOCOL(JVC)
|
||||
|
||||
@@ -24,9 +24,9 @@ struct KeeloqData {
|
||||
|
||||
class KeeloqProtocol : public RemoteProtocol<KeeloqData> {
|
||||
public:
|
||||
void encode(RemoteTransmitData *dst, const KeeloqData &data);
|
||||
optional<KeeloqData> decode(RemoteReceiveData src);
|
||||
void dump(const KeeloqData &data);
|
||||
void encode(RemoteTransmitData *dst, const KeeloqData &data) override;
|
||||
optional<KeeloqData> decode(RemoteReceiveData src) override;
|
||||
void dump(const KeeloqData &data) override;
|
||||
};
|
||||
|
||||
DECLARE_REMOTE_PROTOCOL(Keeloq)
|
||||
|
||||
@@ -16,9 +16,9 @@ struct LGData {
|
||||
|
||||
class LGProtocol : public RemoteProtocol<LGData> {
|
||||
public:
|
||||
void encode(RemoteTransmitData *dst, const LGData &data);
|
||||
optional<LGData> decode(RemoteReceiveData src);
|
||||
void dump(const LGData &data);
|
||||
void encode(RemoteTransmitData *dst, const LGData &data) override;
|
||||
optional<LGData> decode(RemoteReceiveData src) override;
|
||||
void dump(const LGData &data) override;
|
||||
};
|
||||
|
||||
DECLARE_REMOTE_PROTOCOL(LG)
|
||||
|
||||
@@ -27,9 +27,9 @@ struct MagiQuestData {
|
||||
|
||||
class MagiQuestProtocol : public RemoteProtocol<MagiQuestData> {
|
||||
public:
|
||||
void encode(RemoteTransmitData *dst, const MagiQuestData &data);
|
||||
optional<MagiQuestData> decode(RemoteReceiveData src);
|
||||
void dump(const MagiQuestData &data);
|
||||
void encode(RemoteTransmitData *dst, const MagiQuestData &data) override;
|
||||
optional<MagiQuestData> decode(RemoteReceiveData src) override;
|
||||
void dump(const MagiQuestData &data) override;
|
||||
};
|
||||
|
||||
DECLARE_REMOTE_PROTOCOL(MagiQuest)
|
||||
|
||||
@@ -67,9 +67,9 @@ class MideaData {
|
||||
|
||||
class MideaProtocol : public RemoteProtocol<MideaData> {
|
||||
public:
|
||||
void encode(RemoteTransmitData *dst, const MideaData &src);
|
||||
optional<MideaData> decode(RemoteReceiveData src);
|
||||
void dump(const MideaData &data);
|
||||
void encode(RemoteTransmitData *dst, const MideaData &src) override;
|
||||
optional<MideaData> decode(RemoteReceiveData src) override;
|
||||
void dump(const MideaData &data) override;
|
||||
};
|
||||
|
||||
DECLARE_REMOTE_PROTOCOL(Midea)
|
||||
|
||||
@@ -13,9 +13,9 @@ struct MirageData {
|
||||
|
||||
class MirageProtocol : public RemoteProtocol<MirageData> {
|
||||
public:
|
||||
void encode(RemoteTransmitData *dst, const MirageData &data);
|
||||
optional<MirageData> decode(RemoteReceiveData src);
|
||||
void dump(const MirageData &data);
|
||||
void encode(RemoteTransmitData *dst, const MirageData &data) override;
|
||||
optional<MirageData> decode(RemoteReceiveData src) override;
|
||||
void dump(const MirageData &data) override;
|
||||
|
||||
protected:
|
||||
void encode_byte_(RemoteTransmitData *dst, uint8_t item);
|
||||
|
||||
@@ -14,9 +14,9 @@ struct NECData {
|
||||
|
||||
class NECProtocol : public RemoteProtocol<NECData> {
|
||||
public:
|
||||
void encode(RemoteTransmitData *dst, const NECData &data);
|
||||
optional<NECData> decode(RemoteReceiveData src);
|
||||
void dump(const NECData &data);
|
||||
void encode(RemoteTransmitData *dst, const NECData &data) override;
|
||||
optional<NECData> decode(RemoteReceiveData src) override;
|
||||
void dump(const NECData &data) override;
|
||||
};
|
||||
|
||||
DECLARE_REMOTE_PROTOCOL(NEC)
|
||||
|
||||
@@ -24,9 +24,9 @@ class NexaProtocol : public RemoteProtocol<NexaData> {
|
||||
void zero(RemoteTransmitData *dst) const;
|
||||
void sync(RemoteTransmitData *dst) const;
|
||||
|
||||
void encode(RemoteTransmitData *dst, const NexaData &data);
|
||||
optional<NexaData> decode(RemoteReceiveData src);
|
||||
void dump(const NexaData &data);
|
||||
void encode(RemoteTransmitData *dst, const NexaData &data) override;
|
||||
optional<NexaData> decode(RemoteReceiveData src) override;
|
||||
void dump(const NexaData &data) override;
|
||||
};
|
||||
|
||||
DECLARE_REMOTE_PROTOCOL(Nexa)
|
||||
|
||||
@@ -16,9 +16,9 @@ struct PanasonicData {
|
||||
|
||||
class PanasonicProtocol : public RemoteProtocol<PanasonicData> {
|
||||
public:
|
||||
void encode(RemoteTransmitData *dst, const PanasonicData &data);
|
||||
optional<PanasonicData> decode(RemoteReceiveData src);
|
||||
void dump(const PanasonicData &data);
|
||||
void encode(RemoteTransmitData *dst, const PanasonicData &data) override;
|
||||
optional<PanasonicData> decode(RemoteReceiveData src) override;
|
||||
void dump(const PanasonicData &data) override;
|
||||
};
|
||||
|
||||
DECLARE_REMOTE_PROTOCOL(Panasonic)
|
||||
|
||||
@@ -13,9 +13,9 @@ struct PioneerData {
|
||||
|
||||
class PioneerProtocol : public RemoteProtocol<PioneerData> {
|
||||
public:
|
||||
void encode(RemoteTransmitData *dst, const PioneerData &data);
|
||||
optional<PioneerData> decode(RemoteReceiveData src);
|
||||
void dump(const PioneerData &data);
|
||||
void encode(RemoteTransmitData *dst, const PioneerData &data) override;
|
||||
optional<PioneerData> decode(RemoteReceiveData src) override;
|
||||
void dump(const PioneerData &data) override;
|
||||
};
|
||||
|
||||
DECLARE_REMOTE_PROTOCOL(Pioneer)
|
||||
|
||||
@@ -30,9 +30,9 @@ class ProntoProtocol : public RemoteProtocol<ProntoData> {
|
||||
std::string compensate_and_dump_sequence_(const RawTimings &data, uint16_t timebase);
|
||||
|
||||
public:
|
||||
void encode(RemoteTransmitData *dst, const ProntoData &data);
|
||||
optional<ProntoData> decode(RemoteReceiveData src);
|
||||
void dump(const ProntoData &data);
|
||||
void encode(RemoteTransmitData *dst, const ProntoData &data) override;
|
||||
optional<ProntoData> decode(RemoteReceiveData src) override;
|
||||
void dump(const ProntoData &data) override;
|
||||
};
|
||||
|
||||
DECLARE_REMOTE_PROTOCOL(Pronto)
|
||||
|
||||
@@ -14,9 +14,9 @@ struct RC5Data {
|
||||
|
||||
class RC5Protocol : public RemoteProtocol<RC5Data> {
|
||||
public:
|
||||
void encode(RemoteTransmitData *dst, const RC5Data &data);
|
||||
optional<RC5Data> decode(RemoteReceiveData src);
|
||||
void dump(const RC5Data &data);
|
||||
void encode(RemoteTransmitData *dst, const RC5Data &data) override;
|
||||
optional<RC5Data> decode(RemoteReceiveData src) override;
|
||||
void dump(const RC5Data &data) override;
|
||||
};
|
||||
|
||||
DECLARE_REMOTE_PROTOCOL(RC5)
|
||||
|
||||
@@ -15,9 +15,9 @@ struct RC6Data {
|
||||
|
||||
class RC6Protocol : public RemoteProtocol<RC6Data> {
|
||||
public:
|
||||
void encode(RemoteTransmitData *dst, const RC6Data &data);
|
||||
optional<RC6Data> decode(RemoteReceiveData src);
|
||||
void dump(const RC6Data &data);
|
||||
void encode(RemoteTransmitData *dst, const RC6Data &data) override;
|
||||
optional<RC6Data> decode(RemoteReceiveData src) override;
|
||||
void dump(const RC6Data &data) override;
|
||||
};
|
||||
|
||||
DECLARE_REMOTE_PROTOCOL(RC6)
|
||||
|
||||
@@ -1,12 +1,30 @@
|
||||
#include "rc_switch_protocol.h"
|
||||
|
||||
#include <iterator>
|
||||
#include "esphome/core/log.h"
|
||||
|
||||
namespace esphome::remote_base {
|
||||
|
||||
static const char *const TAG = "remote.rc_switch";
|
||||
|
||||
const RCSwitchBase RC_SWITCH_PROTOCOLS[9] = {RCSwitchBase(0, 0, 0, 0, 0, 0, false),
|
||||
RCSwitchBase(350, 10850, 350, 1050, 1050, 350, false),
|
||||
RCSwitchBase(650, 6500, 650, 1300, 1300, 650, false),
|
||||
RCSwitchBase(3000, 7100, 400, 1100, 900, 600, false),
|
||||
RCSwitchBase(380, 2280, 380, 1140, 1140, 380, false),
|
||||
RCSwitchBase(3000, 7000, 500, 1000, 1000, 500, false),
|
||||
RCSwitchBase(10350, 450, 450, 900, 900, 450, true),
|
||||
RCSwitchBase(300, 9300, 150, 900, 900, 150, false),
|
||||
RCSwitchBase(250, 2500, 250, 1250, 250, 250, false)};
|
||||
|
||||
RCSwitchBase::RCSwitchBase(uint32_t sync_high, uint32_t sync_low, uint32_t zero_high, uint32_t zero_low,
|
||||
uint32_t one_high, uint32_t one_low, bool inverted)
|
||||
: sync_high_(sync_high),
|
||||
sync_low_(sync_low),
|
||||
zero_high_(zero_high),
|
||||
zero_low_(zero_low),
|
||||
one_high_(one_high),
|
||||
one_low_(one_low),
|
||||
inverted_(inverted) {}
|
||||
|
||||
void RCSwitchBase::one(RemoteTransmitData *dst) const {
|
||||
if (!this->inverted_) {
|
||||
dst->mark(this->one_high_);
|
||||
@@ -115,11 +133,11 @@ bool RCSwitchBase::decode(RemoteReceiveData &src, uint64_t *out_data, uint8_t *o
|
||||
optional<RCSwitchData> RCSwitchBase::decode(RemoteReceiveData &src) const {
|
||||
RCSwitchData out;
|
||||
uint8_t out_nbits;
|
||||
for (size_t i = 1; i < std::size(RC_SWITCH_PROTOCOLS); i++) {
|
||||
for (uint8_t i = 1; i <= 8; i++) {
|
||||
src.reset();
|
||||
const RCSwitchBase *protocol = &RC_SWITCH_PROTOCOLS[i];
|
||||
if (protocol->decode(src, &out.code, &out_nbits) && out_nbits >= 3) {
|
||||
out.protocol = static_cast<uint8_t>(i);
|
||||
out.protocol = i;
|
||||
return out;
|
||||
}
|
||||
}
|
||||
@@ -228,7 +246,7 @@ bool RCSwitchRawReceiver::matches(RemoteReceiveData src) {
|
||||
return decoded_nbits == this->nbits_ && (decoded_code & this->mask_) == (this->code_ & this->mask_);
|
||||
}
|
||||
bool RCSwitchDumper::dump(RemoteReceiveData src) {
|
||||
for (size_t i = 1; i < std::size(RC_SWITCH_PROTOCOLS); i++) {
|
||||
for (uint8_t i = 1; i <= 8; i++) {
|
||||
src.reset();
|
||||
uint64_t out_data;
|
||||
uint8_t out_nbits;
|
||||
@@ -239,7 +257,7 @@ bool RCSwitchDumper::dump(RemoteReceiveData src) {
|
||||
buffer[j] = (out_data & ((uint64_t) 1 << (out_nbits - j - 1))) ? '1' : '0';
|
||||
|
||||
buffer[out_nbits] = '\0';
|
||||
ESP_LOGI(TAG, "Received RCSwitch Raw: protocol=%u data='%s'", static_cast<unsigned>(i), buffer);
|
||||
ESP_LOGI(TAG, "Received RCSwitch Raw: protocol=%u data='%s'", i, buffer);
|
||||
|
||||
// only send first decoded protocol
|
||||
return true;
|
||||
|
||||
@@ -16,16 +16,9 @@ class RCSwitchBase {
|
||||
public:
|
||||
using ProtocolData = RCSwitchData;
|
||||
|
||||
constexpr RCSwitchBase() = default;
|
||||
constexpr RCSwitchBase(uint32_t sync_high, uint32_t sync_low, uint32_t zero_high, uint32_t zero_low,
|
||||
uint32_t one_high, uint32_t one_low, bool inverted)
|
||||
: sync_high_(sync_high),
|
||||
sync_low_(sync_low),
|
||||
zero_high_(zero_high),
|
||||
zero_low_(zero_low),
|
||||
one_high_(one_high),
|
||||
one_low_(one_low),
|
||||
inverted_(inverted) {}
|
||||
RCSwitchBase() = default;
|
||||
RCSwitchBase(uint32_t sync_high, uint32_t sync_low, uint32_t zero_high, uint32_t zero_low, uint32_t one_high,
|
||||
uint32_t one_low, bool inverted);
|
||||
|
||||
void one(RemoteTransmitData *dst) const;
|
||||
|
||||
@@ -65,21 +58,10 @@ class RCSwitchBase {
|
||||
uint32_t zero_low_{};
|
||||
uint32_t one_high_{};
|
||||
uint32_t one_low_{};
|
||||
uint32_t inverted_{}; // bool widened so every field is a word: the table is read from flash
|
||||
bool inverted_{};
|
||||
};
|
||||
|
||||
// Constant-initialized and kept in flash on every platform; all fields are 32-bit so ESP8266 can read it in place
|
||||
inline constexpr RCSwitchBase RC_SWITCH_PROTOCOLS[] PROGMEM = {
|
||||
{0, 0, 0, 0, 0, 0, false},
|
||||
{350, 10850, 350, 1050, 1050, 350, false},
|
||||
{650, 6500, 650, 1300, 1300, 650, false},
|
||||
{3000, 7100, 400, 1100, 900, 600, false},
|
||||
{380, 2280, 380, 1140, 1140, 380, false},
|
||||
{3000, 7000, 500, 1000, 1000, 500, false},
|
||||
{10350, 450, 450, 900, 900, 450, true},
|
||||
{300, 9300, 150, 900, 900, 150, false},
|
||||
{250, 2500, 250, 1250, 250, 250, false},
|
||||
};
|
||||
extern const RCSwitchBase RC_SWITCH_PROTOCOLS[9];
|
||||
|
||||
uint64_t decode_binary_string(const std::string &data);
|
||||
|
||||
|
||||
@@ -99,47 +99,29 @@ bool RemoteReceiverBinarySensorBase::on_receive(RemoteReceiveData src) {
|
||||
|
||||
/* RemoteReceiverBase */
|
||||
|
||||
// Slots are counted at code generation; a registration from C++ setup() has none
|
||||
#ifdef REMOTE_BASE_LISTENER_COUNT
|
||||
void RemoteReceiverBase::register_listener(RemoteReceiverListener *listener) {
|
||||
if (this->listeners_.size() == REMOTE_BASE_LISTENER_COUNT) {
|
||||
ESP_LOGE(TAG, "No %s slot: register it from to_code() with remote_base.add_%s", LOG_STR_LITERAL("listener"),
|
||||
LOG_STR_LITERAL("listener"));
|
||||
return;
|
||||
}
|
||||
this->listeners_.push_back(listener);
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef REMOTE_BASE_DUMPER_COUNT
|
||||
void RemoteReceiverBase::register_dumper(RemoteReceiverDumperBase *dumper) {
|
||||
if (dumper->is_secondary()) {
|
||||
this->secondary_dumper_ = dumper;
|
||||
return;
|
||||
this->secondary_dumpers_.push_back(dumper);
|
||||
} else {
|
||||
this->dumpers_.push_back(dumper);
|
||||
}
|
||||
if (this->dumpers_.size() == REMOTE_BASE_DUMPER_COUNT) {
|
||||
ESP_LOGE(TAG, "No %s slot: register it from to_code() with remote_base.add_%s", LOG_STR_LITERAL("dumper"),
|
||||
LOG_STR_LITERAL("dumper"));
|
||||
return;
|
||||
}
|
||||
this->dumpers_.push_back(dumper);
|
||||
}
|
||||
#endif
|
||||
|
||||
void RemoteReceiverBase::call_listeners_dumpers_() {
|
||||
#ifdef REMOTE_BASE_LISTENER_COUNT
|
||||
void RemoteReceiverBase::call_listeners_() {
|
||||
for (auto *listener : this->listeners_)
|
||||
listener->on_receive(RemoteReceiveData(this->temp_, this->tolerance_, this->tolerance_mode_));
|
||||
#endif
|
||||
#ifdef REMOTE_BASE_DUMPER_COUNT
|
||||
}
|
||||
|
||||
void RemoteReceiverBase::call_dumpers_() {
|
||||
bool success = false;
|
||||
for (auto *dumper : this->dumpers_) {
|
||||
if (dumper->dump(RemoteReceiveData(this->temp_, this->tolerance_, this->tolerance_mode_)))
|
||||
success = true;
|
||||
}
|
||||
if (!success && this->secondary_dumper_ != nullptr)
|
||||
this->secondary_dumper_->dump(RemoteReceiveData(this->temp_, this->tolerance_, this->tolerance_mode_));
|
||||
#endif
|
||||
if (!success) {
|
||||
for (auto *dumper : this->secondary_dumpers_)
|
||||
dumper->dump(RemoteReceiveData(this->temp_, this->tolerance_, this->tolerance_mode_));
|
||||
}
|
||||
}
|
||||
|
||||
void RemoteReceiverBinarySensorBase::dump_config() { LOG_BINARY_SENSOR("", "Remote Receiver Binary Sensor", this); }
|
||||
|
||||
@@ -1,14 +1,12 @@
|
||||
#pragma once
|
||||
|
||||
#include <concepts>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "esphome/components/binary_sensor/binary_sensor.h"
|
||||
#include "esphome/core/automation.h"
|
||||
#include "esphome/core/component.h"
|
||||
#include "esphome/core/hal.h"
|
||||
#include "esphome/core/helpers.h"
|
||||
|
||||
namespace esphome::remote_base {
|
||||
|
||||
@@ -143,22 +141,6 @@ class RemoteRMTChannel {
|
||||
#endif // SOC_RMT_SUPPORTED
|
||||
#endif // USE_ESP32
|
||||
|
||||
// Protocol shapes, checked where a protocol is used so a missing method fails at the use site
|
||||
// instead of deep inside a template body. Receive-only protocols such as RCSwitchBase decode
|
||||
// without encoding.
|
||||
template<typename T>
|
||||
concept RemoteProtocolDecoder = requires(T proto, RemoteReceiveData src) {
|
||||
{ proto.decode(src) } -> std::same_as<optional<typename T::ProtocolData>>;
|
||||
};
|
||||
template<typename T>
|
||||
concept RemoteProtocolDumper = RemoteProtocolDecoder<T> && requires(T proto, const typename T::ProtocolData &data) {
|
||||
proto.dump(data);
|
||||
};
|
||||
template<typename T>
|
||||
concept RemoteProtocolEncoder = requires(T proto, RemoteTransmitData *dst, const typename T::ProtocolData &data) {
|
||||
proto.encode(dst, data);
|
||||
};
|
||||
|
||||
class RemoteTransmitterBase : public RemoteComponentBase {
|
||||
public:
|
||||
RemoteTransmitterBase(InternalGPIOPin *pin) : RemoteComponentBase(pin) {}
|
||||
@@ -180,7 +162,7 @@ class RemoteTransmitterBase : public RemoteComponentBase {
|
||||
this->temp_.reset();
|
||||
return TransmitCall(this);
|
||||
}
|
||||
template<RemoteProtocolEncoder Protocol>
|
||||
template<typename Protocol>
|
||||
void transmit(const Protocol::ProtocolData &data, uint32_t send_times = 1, uint32_t send_wait = 0) {
|
||||
auto call = this->transmit();
|
||||
Protocol().encode(call.get_data(), data);
|
||||
@@ -212,37 +194,24 @@ class RemoteReceiverDumperBase {
|
||||
class RemoteReceiverBase : public RemoteComponentBase {
|
||||
public:
|
||||
RemoteReceiverBase(InternalGPIOPin *pin) : RemoteComponentBase(pin) {}
|
||||
// Slots are counted at code generation; without one the call fails at compile time with the same message
|
||||
// the runtime check logs
|
||||
#ifdef REMOTE_BASE_LISTENER_COUNT
|
||||
void register_listener(RemoteReceiverListener *listener);
|
||||
#else
|
||||
template<typename T> void register_listener(T *) {
|
||||
static_assert(sizeof(T) == 0, "No listener slot: register it from to_code() with remote_base.add_listener");
|
||||
}
|
||||
#endif
|
||||
#ifdef REMOTE_BASE_DUMPER_COUNT
|
||||
void register_listener(RemoteReceiverListener *listener) { this->listeners_.push_back(listener); }
|
||||
void register_dumper(RemoteReceiverDumperBase *dumper);
|
||||
#else
|
||||
template<typename T> void register_dumper(T *) {
|
||||
static_assert(sizeof(T) == 0, "No dumper slot: register it from to_code() with remote_base.add_dumper");
|
||||
}
|
||||
#endif
|
||||
void set_tolerance(uint32_t tolerance, ToleranceMode tolerance_mode) {
|
||||
this->tolerance_ = tolerance;
|
||||
this->tolerance_mode_ = tolerance_mode;
|
||||
}
|
||||
|
||||
protected:
|
||||
void call_listeners_dumpers_();
|
||||
void call_listeners_();
|
||||
void call_dumpers_();
|
||||
void call_listeners_dumpers_() {
|
||||
this->call_listeners_();
|
||||
this->call_dumpers_();
|
||||
}
|
||||
|
||||
#ifdef REMOTE_BASE_LISTENER_COUNT
|
||||
StaticVector<RemoteReceiverListener *, REMOTE_BASE_LISTENER_COUNT> listeners_;
|
||||
#endif
|
||||
#ifdef REMOTE_BASE_DUMPER_COUNT
|
||||
StaticVector<RemoteReceiverDumperBase *, REMOTE_BASE_DUMPER_COUNT> dumpers_;
|
||||
RemoteReceiverDumperBase *secondary_dumper_{nullptr}; // runs only when no primary dumper matched
|
||||
#endif
|
||||
std::vector<RemoteReceiverListener *> listeners_;
|
||||
std::vector<RemoteReceiverDumperBase *> dumpers_;
|
||||
std::vector<RemoteReceiverDumperBase *> secondary_dumpers_;
|
||||
RawTimings temp_;
|
||||
uint32_t tolerance_{25};
|
||||
ToleranceMode tolerance_mode_{TOLERANCE_MODE_PERCENTAGE};
|
||||
@@ -260,14 +229,15 @@ class RemoteReceiverBinarySensorBase : public binary_sensor::BinarySensorInitial
|
||||
|
||||
/* TEMPLATES */
|
||||
|
||||
// Protocols are used only through their concrete type (see the RemoteProtocol* concepts); encode/decode/dump
|
||||
// stay non-virtual so unused ones link out
|
||||
template<typename T> class RemoteProtocol {
|
||||
public:
|
||||
using ProtocolData = T;
|
||||
virtual void encode(RemoteTransmitData *dst, const ProtocolData &data) = 0;
|
||||
virtual optional<ProtocolData> decode(RemoteReceiveData src) = 0;
|
||||
virtual void dump(const ProtocolData &data) = 0;
|
||||
};
|
||||
|
||||
template<RemoteProtocolDecoder T> class RemoteReceiverBinarySensor : public RemoteReceiverBinarySensorBase {
|
||||
template<typename T> class RemoteReceiverBinarySensor : public RemoteReceiverBinarySensorBase {
|
||||
public:
|
||||
RemoteReceiverBinarySensor() : RemoteReceiverBinarySensorBase() {}
|
||||
|
||||
@@ -285,7 +255,7 @@ template<RemoteProtocolDecoder T> class RemoteReceiverBinarySensor : public Remo
|
||||
T::ProtocolData data_;
|
||||
};
|
||||
|
||||
template<RemoteProtocolDecoder T>
|
||||
template<typename T>
|
||||
class RemoteReceiverTrigger final : public Trigger<typename T::ProtocolData>, public RemoteReceiverListener {
|
||||
protected:
|
||||
bool on_receive(RemoteReceiveData src) override {
|
||||
@@ -306,7 +276,7 @@ class RemoteTransmittable {
|
||||
void set_transmitter(RemoteTransmitterBase *transmitter) { this->transmitter_ = transmitter; }
|
||||
|
||||
protected:
|
||||
template<RemoteProtocolEncoder Protocol>
|
||||
template<typename Protocol>
|
||||
void transmit_(const Protocol::ProtocolData &data, uint32_t send_times = 1, uint32_t send_wait = 0) {
|
||||
this->transmitter_->transmit<Protocol>(data, send_times, send_wait);
|
||||
}
|
||||
@@ -328,7 +298,7 @@ template<typename... Ts> class RemoteTransmitterActionBase : public RemoteTransm
|
||||
virtual void encode(RemoteTransmitData *dst, Ts... x) = 0;
|
||||
};
|
||||
|
||||
template<RemoteProtocolDumper T> class RemoteReceiverDumper : public RemoteReceiverDumperBase {
|
||||
template<typename T> class RemoteReceiverDumper : public RemoteReceiverDumperBase {
|
||||
public:
|
||||
bool dump(RemoteReceiveData src) override {
|
||||
auto proto = T();
|
||||
|
||||
@@ -12,9 +12,9 @@ struct RoombaData {
|
||||
|
||||
class RoombaProtocol : public RemoteProtocol<RoombaData> {
|
||||
public:
|
||||
void encode(RemoteTransmitData *dst, const RoombaData &data);
|
||||
optional<RoombaData> decode(RemoteReceiveData src);
|
||||
void dump(const RoombaData &data);
|
||||
void encode(RemoteTransmitData *dst, const RoombaData &data) override;
|
||||
optional<RoombaData> decode(RemoteReceiveData src) override;
|
||||
void dump(const RoombaData &data) override;
|
||||
};
|
||||
|
||||
DECLARE_REMOTE_PROTOCOL(Roomba)
|
||||
|
||||
@@ -16,9 +16,9 @@ struct Samsung36Data {
|
||||
|
||||
class Samsung36Protocol : public RemoteProtocol<Samsung36Data> {
|
||||
public:
|
||||
void encode(RemoteTransmitData *dst, const Samsung36Data &data);
|
||||
optional<Samsung36Data> decode(RemoteReceiveData src);
|
||||
void dump(const Samsung36Data &data);
|
||||
void encode(RemoteTransmitData *dst, const Samsung36Data &data) override;
|
||||
optional<Samsung36Data> decode(RemoteReceiveData src) override;
|
||||
void dump(const Samsung36Data &data) override;
|
||||
};
|
||||
|
||||
DECLARE_REMOTE_PROTOCOL(Samsung36)
|
||||
|
||||
@@ -14,9 +14,9 @@ struct SamsungData {
|
||||
|
||||
class SamsungProtocol : public RemoteProtocol<SamsungData> {
|
||||
public:
|
||||
void encode(RemoteTransmitData *dst, const SamsungData &data);
|
||||
optional<SamsungData> decode(RemoteReceiveData src);
|
||||
void dump(const SamsungData &data);
|
||||
void encode(RemoteTransmitData *dst, const SamsungData &data) override;
|
||||
optional<SamsungData> decode(RemoteReceiveData src) override;
|
||||
void dump(const SamsungData &data) override;
|
||||
};
|
||||
|
||||
DECLARE_REMOTE_PROTOCOL(Samsung)
|
||||
|
||||
@@ -16,9 +16,9 @@ struct SonyData {
|
||||
|
||||
class SonyProtocol : public RemoteProtocol<SonyData> {
|
||||
public:
|
||||
void encode(RemoteTransmitData *dst, const SonyData &data);
|
||||
optional<SonyData> decode(RemoteReceiveData src);
|
||||
void dump(const SonyData &data);
|
||||
void encode(RemoteTransmitData *dst, const SonyData &data) override;
|
||||
optional<SonyData> decode(RemoteReceiveData src) override;
|
||||
void dump(const SonyData &data) override;
|
||||
};
|
||||
|
||||
DECLARE_REMOTE_PROTOCOL(Sony)
|
||||
|
||||
@@ -17,9 +17,9 @@ struct SymphonyData {
|
||||
|
||||
class SymphonyProtocol : public RemoteProtocol<SymphonyData> {
|
||||
public:
|
||||
void encode(RemoteTransmitData *dst, const SymphonyData &data);
|
||||
optional<SymphonyData> decode(RemoteReceiveData src);
|
||||
void dump(const SymphonyData &data);
|
||||
void encode(RemoteTransmitData *dst, const SymphonyData &data) override;
|
||||
optional<SymphonyData> decode(RemoteReceiveData src) override;
|
||||
void dump(const SymphonyData &data) override;
|
||||
};
|
||||
|
||||
DECLARE_REMOTE_PROTOCOL(Symphony)
|
||||
|
||||
@@ -14,9 +14,9 @@ struct ToshibaAcData {
|
||||
|
||||
class ToshibaAcProtocol : public RemoteProtocol<ToshibaAcData> {
|
||||
public:
|
||||
void encode(RemoteTransmitData *dst, const ToshibaAcData &data);
|
||||
optional<ToshibaAcData> decode(RemoteReceiveData src);
|
||||
void dump(const ToshibaAcData &data);
|
||||
void encode(RemoteTransmitData *dst, const ToshibaAcData &data) override;
|
||||
optional<ToshibaAcData> decode(RemoteReceiveData src) override;
|
||||
void dump(const ToshibaAcData &data) override;
|
||||
};
|
||||
|
||||
DECLARE_REMOTE_PROTOCOL(ToshibaAc)
|
||||
|
||||
@@ -16,9 +16,9 @@ struct TotoData {
|
||||
|
||||
class TotoProtocol : public RemoteProtocol<TotoData> {
|
||||
public:
|
||||
void encode(RemoteTransmitData *dst, const TotoData &data);
|
||||
optional<TotoData> decode(RemoteReceiveData src);
|
||||
void dump(const TotoData &data);
|
||||
void encode(RemoteTransmitData *dst, const TotoData &data) override;
|
||||
optional<TotoData> decode(RemoteReceiveData src) override;
|
||||
void dump(const TotoData &data) override;
|
||||
};
|
||||
|
||||
DECLARE_REMOTE_PROTOCOL(Toto)
|
||||
|
||||
@@ -221,11 +221,11 @@ async def to_code(config: ConfigType) -> None:
|
||||
|
||||
dumpers = await remote_base.build_dumpers(config[CONF_DUMP])
|
||||
for dumper in dumpers:
|
||||
remote_base.add_dumper(var, dumper)
|
||||
cg.add(var.register_dumper(dumper))
|
||||
|
||||
triggers = await remote_base.build_triggers(config)
|
||||
for trigger in triggers:
|
||||
remote_base.add_listener(var, trigger)
|
||||
cg.add(var.register_listener(trigger))
|
||||
await cg.register_component(var, config)
|
||||
|
||||
cg.add(
|
||||
|
||||
@@ -64,6 +64,9 @@ class RemoteTransmitterComponent final : public remote_base::RemoteTransmitterBa
|
||||
#if defined(USE_ESP32) && SOC_RMT_SUPPORTED
|
||||
void set_with_dma(bool with_dma) { this->with_dma_ = with_dma; }
|
||||
void set_eot_level(bool eot_level) { this->eot_level_ = eot_level; }
|
||||
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 5, 1)
|
||||
void loop() override;
|
||||
#endif
|
||||
#endif
|
||||
#if (defined(USE_ESP32) && SOC_RMT_SUPPORTED) || defined(USE_LIBRETINY_VARIANT_RTL8720C) || \
|
||||
defined(REMOTE_TRANSMITTER_BK_PWM)
|
||||
@@ -145,21 +148,32 @@ class RemoteTransmitterComponent final : public remote_base::RemoteTransmitterBa
|
||||
void wait_for_rmt_();
|
||||
|
||||
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 5, 1)
|
||||
static bool tx_done_callback(rmt_channel_handle_t channel, const rmt_tx_done_event_data_t *event, void *arg);
|
||||
size_t encode_symbols_(rmt_symbol_half_t *out, uint32_t send_wait, uint32_t *offset);
|
||||
void wait_all_done_();
|
||||
void deliver_completion_();
|
||||
|
||||
RemoteTransmitterComponentStore store_{};
|
||||
std::vector<rmt_symbol_half_t> rmt_temp_;
|
||||
#else
|
||||
std::vector<rmt_symbol_word_t> rmt_temp_;
|
||||
#endif
|
||||
uint32_t current_carrier_frequency_{38000};
|
||||
bool initialized_{false};
|
||||
bool with_dma_{false};
|
||||
bool eot_level_{false};
|
||||
rmt_channel_handle_t channel_{NULL};
|
||||
rmt_encoder_handle_t encoder_{NULL};
|
||||
esp_err_t error_code_{ESP_OK};
|
||||
std::string error_string_;
|
||||
bool initialized_{false};
|
||||
bool with_dma_{false};
|
||||
bool eot_level_{false};
|
||||
bool inverted_{false};
|
||||
bool non_blocking_{false};
|
||||
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 5, 1)
|
||||
// set when a frame is handed to the hardware, cleared once its completion is reported;
|
||||
// the transmit done interrupt sets tx_done_
|
||||
bool tx_active_{false};
|
||||
volatile bool tx_done_{false};
|
||||
#endif
|
||||
#endif
|
||||
uint8_t carrier_duty_percent_{50};
|
||||
|
||||
|
||||
@@ -15,6 +15,9 @@ static const char *const TAG = "remote_transmitter";
|
||||
static constexpr uint32_t RMT_SYMBOL_DURATION_MAX = 0x7FFF;
|
||||
|
||||
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 5, 1)
|
||||
// How long a blocking wait sleeps between watchdog feeds
|
||||
static constexpr int RMT_WAIT_SLICE_MS = 50;
|
||||
|
||||
static size_t IRAM_ATTR HOT encoder_callback(const void *data, size_t size, size_t written, size_t free,
|
||||
rmt_symbol_word_t *symbols, bool *done, void *arg) {
|
||||
auto *store = static_cast<RemoteTransmitterComponentStore *>(arg);
|
||||
@@ -49,6 +52,31 @@ static size_t IRAM_ATTR HOT encoder_callback(const void *data, size_t size, size
|
||||
*done = false;
|
||||
return count;
|
||||
}
|
||||
|
||||
// Splits a duration into 15-bit symbols; with out == nullptr only counts them
|
||||
static size_t write_symbols(rmt_symbol_half_t *out, size_t pos, uint32_t ticks, bool level) {
|
||||
size_t count = 0;
|
||||
while (ticks > 0) {
|
||||
uint32_t duration = std::min(ticks, RMT_SYMBOL_DURATION_MAX);
|
||||
if (out != nullptr) {
|
||||
out[pos + count] = {
|
||||
.duration = static_cast<uint16_t>(duration),
|
||||
.level = static_cast<uint16_t>(level),
|
||||
};
|
||||
}
|
||||
ticks -= duration;
|
||||
count++;
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
bool IRAM_ATTR HOT RemoteTransmitterComponent::tx_done_callback(rmt_channel_handle_t channel,
|
||||
const rmt_tx_done_event_data_t *event, void *arg) {
|
||||
auto *self = static_cast<RemoteTransmitterComponent *>(arg);
|
||||
self->tx_done_ = true;
|
||||
self->enable_loop_soon_any_context();
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
|
||||
void RemoteTransmitterComponent::setup() {
|
||||
@@ -83,8 +111,12 @@ void RemoteTransmitterComponent::digital_write(bool value) {
|
||||
rmt_transmit_config_t config;
|
||||
memset(&config, 0, sizeof(config));
|
||||
config.flags.eot_level = value;
|
||||
config.flags.queue_nonblocking = 1;
|
||||
// a frame still on the wire finishes first and reports its completion
|
||||
this->wait_for_rmt_();
|
||||
this->store_.times = 1;
|
||||
this->store_.index = 0;
|
||||
rmt_encoder_handle_t encoder = this->encoder_;
|
||||
#else
|
||||
rmt_symbol_word_t symbol = {
|
||||
.duration0 = 1,
|
||||
@@ -95,17 +127,24 @@ void RemoteTransmitterComponent::digital_write(bool value) {
|
||||
rmt_transmit_config_t config;
|
||||
memset(&config, 0, sizeof(config));
|
||||
config.flags.eot_level = value;
|
||||
rmt_encoder_handle_t encoder = this->encoder_;
|
||||
#endif
|
||||
esp_err_t error = rmt_transmit(this->channel_, this->encoder_, &symbol, sizeof(symbol), &config);
|
||||
esp_err_t error = rmt_transmit(this->channel_, encoder, &symbol, sizeof(symbol), &config);
|
||||
if (error != ESP_OK) {
|
||||
ESP_LOGW(TAG, "rmt_transmit failed: %s", esp_err_to_name(error));
|
||||
this->status_set_warning();
|
||||
}
|
||||
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 5, 1)
|
||||
this->wait_all_done_();
|
||||
// a level write is not a frame, so its completion is not reported
|
||||
this->tx_done_ = false;
|
||||
#else
|
||||
error = rmt_tx_wait_all_done(this->channel_, -1);
|
||||
if (error != ESP_OK) {
|
||||
ESP_LOGW(TAG, "rmt_tx_wait_all_done failed: %s", esp_err_to_name(error));
|
||||
this->status_set_warning();
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void RemoteTransmitterComponent::configure_rmt_() {
|
||||
@@ -152,6 +191,17 @@ void RemoteTransmitterComponent::configure_rmt_() {
|
||||
}
|
||||
|
||||
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 5, 1)
|
||||
rmt_tx_event_callbacks_t callbacks;
|
||||
memset(&callbacks, 0, sizeof(callbacks));
|
||||
callbacks.on_trans_done = tx_done_callback;
|
||||
error = rmt_tx_register_event_callbacks(this->channel_, &callbacks, this);
|
||||
if (error != ESP_OK) {
|
||||
this->error_code_ = error;
|
||||
this->error_string_ = "in rmt_tx_register_event_callbacks";
|
||||
this->mark_failed();
|
||||
return;
|
||||
}
|
||||
|
||||
rmt_simple_encoder_config_t encoder;
|
||||
memset(&encoder, 0, sizeof(encoder));
|
||||
encoder.callback = encoder_callback;
|
||||
@@ -206,6 +256,118 @@ void RemoteTransmitterComponent::configure_rmt_() {
|
||||
}
|
||||
}
|
||||
|
||||
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 5, 1)
|
||||
// Blocks until the hardware is idle, feeding the watchdog while waiting
|
||||
void RemoteTransmitterComponent::wait_all_done_() {
|
||||
esp_err_t error;
|
||||
while ((error = rmt_tx_wait_all_done(this->channel_, RMT_WAIT_SLICE_MS)) == ESP_ERR_TIMEOUT) {
|
||||
App.feed_wdt();
|
||||
}
|
||||
if (error != ESP_OK) {
|
||||
ESP_LOGW(TAG, "rmt_tx_wait_all_done failed: %s", esp_err_to_name(error));
|
||||
this->status_set_warning();
|
||||
}
|
||||
}
|
||||
|
||||
void RemoteTransmitterComponent::deliver_completion_() {
|
||||
this->tx_done_ = false;
|
||||
this->tx_active_ = false;
|
||||
this->complete_trigger_.trigger();
|
||||
}
|
||||
|
||||
// Blocks until any frame on the wire has gone out and reports its completion
|
||||
void RemoteTransmitterComponent::wait_for_rmt_() {
|
||||
this->wait_all_done_();
|
||||
if (this->tx_active_)
|
||||
this->deliver_completion_();
|
||||
}
|
||||
|
||||
void RemoteTransmitterComponent::loop() {
|
||||
if (this->tx_done_)
|
||||
this->deliver_completion_();
|
||||
// the transmit done interrupt re-enables the loop
|
||||
this->disable_loop();
|
||||
}
|
||||
|
||||
// Encodes the repeat gap followed by the frame; with out == nullptr only counts symbols.
|
||||
// The gap leads the buffer so the encoder skips it on the first pass and replays it
|
||||
// before every repeat; offset receives the index of the first frame symbol.
|
||||
size_t RemoteTransmitterComponent::encode_symbols_(rmt_symbol_half_t *out, uint32_t send_wait, uint32_t *offset) {
|
||||
size_t count = write_symbols(out, 0, this->from_microseconds_(send_wait), this->eot_level_);
|
||||
*offset = count;
|
||||
for (int32_t value : this->temp_.get_data()) {
|
||||
bool level = value >= 0;
|
||||
if (!level) {
|
||||
value = -value;
|
||||
}
|
||||
count += write_symbols(out, count, this->from_microseconds_(static_cast<uint32_t>(value)), level ^ this->inverted_);
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
void RemoteTransmitterComponent::send_internal(uint32_t send_times, uint32_t send_wait) {
|
||||
if (this->is_failed()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (this->tx_active_ && this->tx_done_) {
|
||||
// finished, but loop() has not run yet
|
||||
this->deliver_completion_();
|
||||
}
|
||||
|
||||
if (send_times == 0 || this->tx_active_) {
|
||||
// nothing is sent, but both triggers still fire so an on_complete-sequenced automation
|
||||
// does not stall; a zero repeat count would never finish in the encoder, and a frame that
|
||||
// arrives while another is on the wire is dropped rather than blocking the loop
|
||||
if (this->tx_active_) {
|
||||
ESP_LOGW(TAG, "Transmitter busy, dropping");
|
||||
this->status_set_warning();
|
||||
}
|
||||
this->transmit_trigger_.trigger();
|
||||
this->complete_trigger_.trigger();
|
||||
return;
|
||||
}
|
||||
|
||||
if (this->current_carrier_frequency_ != this->temp_.get_carrier_frequency()) {
|
||||
this->current_carrier_frequency_ = this->temp_.get_carrier_frequency();
|
||||
this->configure_rmt_();
|
||||
}
|
||||
|
||||
uint32_t offset;
|
||||
size_t count = this->encode_symbols_(nullptr, send_wait, &offset);
|
||||
if (count <= offset) {
|
||||
ESP_LOGE(TAG, "Empty data");
|
||||
return;
|
||||
}
|
||||
this->rmt_temp_.resize(count);
|
||||
this->encode_symbols_(this->rmt_temp_.data(), send_wait, &offset);
|
||||
this->store_.times = send_times;
|
||||
this->store_.index = offset;
|
||||
|
||||
this->transmit_trigger_.trigger();
|
||||
|
||||
rmt_transmit_config_t config;
|
||||
memset(&config, 0, sizeof(config));
|
||||
config.flags.eot_level = this->eot_level_;
|
||||
config.flags.queue_nonblocking = 1;
|
||||
this->tx_done_ = false;
|
||||
this->tx_active_ = true;
|
||||
esp_err_t error = rmt_transmit(this->channel_, this->encoder_, this->rmt_temp_.data(),
|
||||
this->rmt_temp_.size() * sizeof(rmt_symbol_half_t), &config);
|
||||
if (error != ESP_OK) {
|
||||
ESP_LOGW(TAG, "rmt_transmit failed: %s", esp_err_to_name(error));
|
||||
this->status_set_warning();
|
||||
// nothing will complete, so report it now
|
||||
this->deliver_completion_();
|
||||
return;
|
||||
}
|
||||
this->status_clear_warning();
|
||||
|
||||
if (!this->non_blocking_) {
|
||||
this->wait_for_rmt_();
|
||||
}
|
||||
}
|
||||
#else
|
||||
void RemoteTransmitterComponent::wait_for_rmt_() {
|
||||
esp_err_t error = rmt_tx_wait_all_done(this->channel_, -1);
|
||||
if (error != ESP_OK) {
|
||||
@@ -216,87 +378,6 @@ void RemoteTransmitterComponent::wait_for_rmt_() {
|
||||
this->complete_trigger_.trigger();
|
||||
}
|
||||
|
||||
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 5, 1)
|
||||
void RemoteTransmitterComponent::send_internal(uint32_t send_times, uint32_t send_wait) {
|
||||
uint64_t total_duration = 0;
|
||||
|
||||
if (this->is_failed()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// if the timeout was cancelled, block until the tx is complete
|
||||
if (this->non_blocking_ && this->cancel_timeout("complete")) {
|
||||
this->wait_for_rmt_();
|
||||
}
|
||||
|
||||
if (this->current_carrier_frequency_ != this->temp_.get_carrier_frequency()) {
|
||||
this->current_carrier_frequency_ = this->temp_.get_carrier_frequency();
|
||||
this->configure_rmt_();
|
||||
}
|
||||
|
||||
this->rmt_temp_.clear();
|
||||
this->rmt_temp_.reserve(this->temp_.get_data().size() + 1);
|
||||
|
||||
// encode any delay at the start of the buffer to simplify the encoder callback
|
||||
// this will be skipped the first time around
|
||||
total_duration += send_wait * (send_times - 1);
|
||||
send_wait = this->from_microseconds_(static_cast<uint32_t>(send_wait));
|
||||
while (send_wait > 0) {
|
||||
int32_t duration = std::min(send_wait, uint32_t(RMT_SYMBOL_DURATION_MAX));
|
||||
this->rmt_temp_.push_back({
|
||||
.duration = static_cast<uint16_t>(duration),
|
||||
.level = static_cast<uint16_t>(this->eot_level_),
|
||||
});
|
||||
send_wait -= duration;
|
||||
}
|
||||
|
||||
// encode data
|
||||
size_t offset = this->rmt_temp_.size();
|
||||
for (int32_t value : this->temp_.get_data()) {
|
||||
bool level = value >= 0;
|
||||
if (!level) {
|
||||
value = -value;
|
||||
}
|
||||
total_duration += value * send_times;
|
||||
value = this->from_microseconds_(static_cast<uint32_t>(value));
|
||||
while (value > 0) {
|
||||
int32_t duration = std::min(value, int32_t(RMT_SYMBOL_DURATION_MAX));
|
||||
this->rmt_temp_.push_back({
|
||||
.duration = static_cast<uint16_t>(duration),
|
||||
.level = static_cast<uint16_t>(level ^ this->inverted_),
|
||||
});
|
||||
value -= duration;
|
||||
}
|
||||
}
|
||||
|
||||
if ((this->rmt_temp_.data() == nullptr) || this->rmt_temp_.size() <= offset) {
|
||||
ESP_LOGE(TAG, "Empty data");
|
||||
return;
|
||||
}
|
||||
|
||||
this->transmit_trigger_.trigger();
|
||||
|
||||
rmt_transmit_config_t config;
|
||||
memset(&config, 0, sizeof(config));
|
||||
config.flags.eot_level = this->eot_level_;
|
||||
this->store_.times = send_times;
|
||||
this->store_.index = offset;
|
||||
esp_err_t error = rmt_transmit(this->channel_, this->encoder_, this->rmt_temp_.data(),
|
||||
this->rmt_temp_.size() * sizeof(rmt_symbol_half_t), &config);
|
||||
if (error != ESP_OK) {
|
||||
ESP_LOGW(TAG, "rmt_transmit failed: %s", esp_err_to_name(error));
|
||||
this->status_set_warning();
|
||||
} else {
|
||||
this->status_clear_warning();
|
||||
}
|
||||
|
||||
if (this->non_blocking_) {
|
||||
this->set_timeout("complete", total_duration / 1000, [this]() { this->wait_for_rmt_(); });
|
||||
} else {
|
||||
this->wait_for_rmt_();
|
||||
}
|
||||
}
|
||||
#else
|
||||
void RemoteTransmitterComponent::send_internal(uint32_t send_times, uint32_t send_wait) {
|
||||
if (this->is_failed())
|
||||
return;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import esphome.codegen as cg
|
||||
from esphome.components import climate_ir, remote_base
|
||||
from esphome.components import climate_ir
|
||||
import esphome.config_validation as cv
|
||||
from esphome.const import CONF_MODEL
|
||||
from esphome.types import ConfigType
|
||||
@@ -26,6 +26,5 @@ CONFIG_SCHEMA = climate_ir.climate_ir_with_receiver_schema(ToshibaClimate).exten
|
||||
|
||||
|
||||
async def to_code(config: ConfigType) -> None:
|
||||
remote_base.request_protocol("toshiba_ac") # used from C++
|
||||
var = await climate_ir.new_climate_ir(config)
|
||||
cg.add(var.set_model(config[CONF_MODEL]))
|
||||
|
||||
@@ -137,43 +137,6 @@
|
||||
#define MICRONOVA_LISTENER_COUNT 1
|
||||
#define USE_MICRONOVA_WRITER
|
||||
#define MK2PVROUTER_LISTENER_COUNT 1
|
||||
#define REMOTE_BASE_DUMPER_COUNT 1
|
||||
#define REMOTE_BASE_LISTENER_COUNT 1
|
||||
#define USE_REMOTE_PROTOCOL_ABBWELCOME
|
||||
#define USE_REMOTE_PROTOCOL_AEHA
|
||||
#define USE_REMOTE_PROTOCOL_BEO4
|
||||
#define USE_REMOTE_PROTOCOL_BRENNENSTUHL
|
||||
#define USE_REMOTE_PROTOCOL_BYRONSX
|
||||
#define USE_REMOTE_PROTOCOL_CANALSAT
|
||||
#define USE_REMOTE_PROTOCOL_COOLIX
|
||||
#define USE_REMOTE_PROTOCOL_DISH
|
||||
#define USE_REMOTE_PROTOCOL_DOOYA
|
||||
#define USE_REMOTE_PROTOCOL_DRAYTON
|
||||
#define USE_REMOTE_PROTOCOL_DYSON
|
||||
#define USE_REMOTE_PROTOCOL_GOBOX
|
||||
#define USE_REMOTE_PROTOCOL_HAIER
|
||||
#define USE_REMOTE_PROTOCOL_JVC
|
||||
#define USE_REMOTE_PROTOCOL_KEELOQ
|
||||
#define USE_REMOTE_PROTOCOL_LG
|
||||
#define USE_REMOTE_PROTOCOL_MAGIQUEST
|
||||
#define USE_REMOTE_PROTOCOL_MIDEA
|
||||
#define USE_REMOTE_PROTOCOL_MIRAGE
|
||||
#define USE_REMOTE_PROTOCOL_NEC
|
||||
#define USE_REMOTE_PROTOCOL_NEXA
|
||||
#define USE_REMOTE_PROTOCOL_PANASONIC
|
||||
#define USE_REMOTE_PROTOCOL_PIONEER
|
||||
#define USE_REMOTE_PROTOCOL_PRONTO
|
||||
#define USE_REMOTE_PROTOCOL_RAW
|
||||
#define USE_REMOTE_PROTOCOL_RC5
|
||||
#define USE_REMOTE_PROTOCOL_RC6
|
||||
#define USE_REMOTE_PROTOCOL_RC_SWITCH
|
||||
#define USE_REMOTE_PROTOCOL_ROOMBA
|
||||
#define USE_REMOTE_PROTOCOL_SAMSUNG
|
||||
#define USE_REMOTE_PROTOCOL_SAMSUNG36
|
||||
#define USE_REMOTE_PROTOCOL_SONY
|
||||
#define USE_REMOTE_PROTOCOL_SYMPHONY
|
||||
#define USE_REMOTE_PROTOCOL_TOSHIBA_AC
|
||||
#define USE_REMOTE_PROTOCOL_TOTO
|
||||
#define SERIAL_PROXY_COUNT 2
|
||||
#define SNTP_SERVER_COUNT 3
|
||||
#define USE_MEDIA_PLAYER
|
||||
|
||||
@@ -1,9 +0,0 @@
|
||||
esphome:
|
||||
name: test
|
||||
|
||||
esp32:
|
||||
board: esp32dev
|
||||
|
||||
remote_receiver:
|
||||
- id: rcvr
|
||||
pin: GPIO4
|
||||
@@ -1,24 +0,0 @@
|
||||
esphome:
|
||||
name: test
|
||||
|
||||
esp32:
|
||||
board: esp32dev
|
||||
|
||||
logger:
|
||||
|
||||
remote_receiver:
|
||||
- id: rcvr
|
||||
pin: GPIO4
|
||||
dump:
|
||||
- nec
|
||||
- rc_switch
|
||||
on_nec:
|
||||
then:
|
||||
- logger.log: nec
|
||||
|
||||
binary_sensor:
|
||||
- platform: remote_receiver
|
||||
name: Remote Input
|
||||
nec:
|
||||
address: 0x1234
|
||||
command: 0x5678
|
||||
@@ -1,20 +0,0 @@
|
||||
esphome:
|
||||
name: test
|
||||
|
||||
esp32:
|
||||
board: esp32dev
|
||||
|
||||
remote_receiver:
|
||||
- id: rcvr
|
||||
pin: GPIO4
|
||||
|
||||
infrared:
|
||||
- platform: ir_rf_proxy
|
||||
name: IR Receiver
|
||||
remote_receiver_id: rcvr
|
||||
|
||||
radio_frequency:
|
||||
- platform: ir_rf_proxy
|
||||
name: RF Receiver
|
||||
frequency: 433.92MHz
|
||||
remote_receiver_id: rcvr
|
||||
@@ -1,82 +0,0 @@
|
||||
"""Listener and dumper StaticVector sizes come from codegen slot counts."""
|
||||
|
||||
from collections.abc import Callable
|
||||
from pathlib import Path
|
||||
|
||||
import pytest
|
||||
|
||||
from esphome.components import remote_base
|
||||
import esphome.config_validation as cv
|
||||
|
||||
from ..helpers import get_define_value
|
||||
|
||||
|
||||
def test_dumper_and_listener_counts(
|
||||
generate_main: Callable[[str | Path], str],
|
||||
component_config_path: Callable[[str], Path],
|
||||
) -> None:
|
||||
generate_main(component_config_path("receiver_with_dumpers.yaml"))
|
||||
# nec and rc_switch dumpers
|
||||
assert get_define_value("REMOTE_BASE_DUMPER_COUNT") == "2"
|
||||
# on_nec trigger plus the remote_receiver binary sensor
|
||||
assert get_define_value("REMOTE_BASE_LISTENER_COUNT") == "2"
|
||||
|
||||
|
||||
def test_bare_receiver_emits_no_counts(
|
||||
generate_main: Callable[[str | Path], str],
|
||||
component_config_path: Callable[[str], Path],
|
||||
) -> None:
|
||||
generate_main(component_config_path("receiver_bare.yaml"))
|
||||
assert get_define_value("REMOTE_BASE_DUMPER_COUNT") is None
|
||||
assert get_define_value("REMOTE_BASE_LISTENER_COUNT") is None
|
||||
|
||||
|
||||
def test_proxy_receivers_count_as_listeners(
|
||||
generate_main: Callable[[str | Path], str],
|
||||
component_config_path: Callable[[str], Path],
|
||||
) -> None:
|
||||
generate_main(component_config_path("receiver_with_proxies.yaml"))
|
||||
# infrared and radio_frequency ir_rf_proxy platforms each listen
|
||||
assert get_define_value("REMOTE_BASE_LISTENER_COUNT") == "2"
|
||||
assert get_define_value("REMOTE_BASE_DUMPER_COUNT") is None
|
||||
|
||||
|
||||
def test_only_used_protocol_sources_are_compiled(
|
||||
generate_main: Callable[[str | Path], str],
|
||||
component_config_path: Callable[[str], Path],
|
||||
) -> None:
|
||||
generate_main(component_config_path("receiver_with_dumpers.yaml"))
|
||||
excluded = set(remote_base.FILTER_SOURCE_FILES())
|
||||
assert "nec_protocol.cpp" not in excluded
|
||||
assert "rc_switch_protocol.cpp" not in excluded
|
||||
assert "sony_protocol.cpp" in excluded
|
||||
assert "remote_base.cpp" not in excluded
|
||||
|
||||
|
||||
def test_every_registry_name_maps_to_a_protocol_source() -> None:
|
||||
sources = {
|
||||
path.name for path in Path(remote_base.__file__).parent.glob("*_protocol.cpp")
|
||||
}
|
||||
names = (
|
||||
set(remote_base.BINARY_SENSOR_REGISTRY)
|
||||
| set(remote_base.DUMPER_REGISTRY)
|
||||
| {key.removeprefix("on_") for key in remote_base.TRIGGER_REGISTRY}
|
||||
)
|
||||
for name in names:
|
||||
stem = (
|
||||
remote_base.protocol_define(name)
|
||||
.removeprefix("USE_REMOTE_PROTOCOL_")
|
||||
.lower()
|
||||
)
|
||||
assert f"{stem}_protocol.cpp" in sources, name
|
||||
|
||||
|
||||
def test_dump_list_is_deduplicated_across_forms() -> None:
|
||||
dumpers = remote_base.validate_dumpers(["raw", {"raw": None}, "nec", "nec"])
|
||||
assert [name for name, _ in dumpers] == ["raw", "nec"]
|
||||
|
||||
|
||||
@pytest.mark.parametrize("bad", [["nec", None], [5]])
|
||||
def test_dump_list_rejects_invalid_entries_with_a_validation_error(bad: list) -> None:
|
||||
with pytest.raises(cv.Invalid):
|
||||
remote_base.validate_dumpers(bad)
|
||||
@@ -1,6 +0,0 @@
|
||||
# A receiver with no dumpers and no listeners compiles both lists out.
|
||||
# Only built while remote_receiver is tested in isolation: the counts are global defines,
|
||||
# so this variant cannot be merged with configs that register any.
|
||||
remote_receiver:
|
||||
- id: rcvr_bare
|
||||
pin: ${pin}
|
||||
@@ -1,5 +0,0 @@
|
||||
substitutions:
|
||||
pin: GPIO2
|
||||
|
||||
packages:
|
||||
bare: !include bare-common.yaml
|
||||
Reference in New Issue
Block a user