mirror of
https://github.com/esphome/esphome.git
synced 2026-10-14 02:02:51 +00:00
[remote_base] Generate transmit_pronto and transmit_rc6 with no C++ class (#20564)
This commit is contained in:
@@ -308,26 +308,38 @@ def register_action(
|
||||
return decorator
|
||||
|
||||
|
||||
ProtocolField = (
|
||||
tuple[str, str, SafeExpType] | automation.ApplyField | automation.ApplyCall
|
||||
)
|
||||
|
||||
|
||||
def _protocol_field(
|
||||
field: ProtocolField,
|
||||
) -> automation.ApplyField | automation.ApplyCall:
|
||||
if not isinstance(field, tuple):
|
||||
return field
|
||||
key, member, type_ = field
|
||||
return automation.ApplyField(key, f"data.{member} = {{}}", type_)
|
||||
|
||||
|
||||
def register_protocol_action(
|
||||
name: str,
|
||||
protocol: str,
|
||||
schema: cv.Schema,
|
||||
*fields: tuple[str, str, SafeExpType],
|
||||
*fields: ProtocolField,
|
||||
validate: Callable[[ConfigType], ConfigType] | None = None,
|
||||
) -> None:
|
||||
"""Register ``transmit_<name>`` with no C++ action class.
|
||||
|
||||
One generated function fills ``<protocol>Data`` from ``(conf_key, member, type_)`` fields
|
||||
and sends it; the action is a core ``ApplyAction``.
|
||||
and sends it; the action is a core ``ApplyAction``. ``ApplyField`` and ``ApplyCall`` pass through
|
||||
and target the call object, whose protocol data is ``data``.
|
||||
"""
|
||||
action, schema, validator = _transmit_action(name, schema, validate)
|
||||
builder = automation.apply_action_builder(
|
||||
action,
|
||||
schema,
|
||||
*(
|
||||
automation.ApplyField(key, f"data.{member} = {{}}", type_)
|
||||
for key, member, type_ in fields
|
||||
),
|
||||
*(_protocol_field(field) for field in fields),
|
||||
automation.ApplyField((CONF_REPEAT, CONF_TIMES), "set_send_times", cg.uint32),
|
||||
automation.ApplyField(
|
||||
(CONF_REPEAT, CONF_WAIT_TIME), "set_send_wait", cg.uint32
|
||||
@@ -1206,7 +1218,7 @@ register_protocol_action(
|
||||
ProntoData,
|
||||
ProntoBinarySensor,
|
||||
ProntoTrigger,
|
||||
ProntoAction,
|
||||
_,
|
||||
ProntoDumper,
|
||||
) = declare_protocol("Pronto")
|
||||
PRONTO_SCHEMA = cv.Schema(
|
||||
@@ -1240,10 +1252,10 @@ def pronto_dumper(var, config):
|
||||
pass
|
||||
|
||||
|
||||
@register_action("pronto", ProntoAction, PRONTO_SCHEMA)
|
||||
async def pronto_action(var, config, args):
|
||||
template_ = await cg.templatable(config[CONF_DATA], args, cg.std_string)
|
||||
cg.add(var.set_data(template_))
|
||||
# delta only matters when matching a received code
|
||||
register_protocol_action(
|
||||
"pronto", "Pronto", PRONTO_SCHEMA, (CONF_DATA, "data", cg.std_string)
|
||||
)
|
||||
|
||||
|
||||
# Gobox
|
||||
@@ -1640,7 +1652,7 @@ register_protocol_action(
|
||||
|
||||
|
||||
# RC6
|
||||
RC6Data, RC6BinarySensor, RC6Trigger, RC6Action, RC6Dumper = declare_protocol("RC6")
|
||||
RC6Data, RC6BinarySensor, RC6Trigger, _, RC6Dumper = declare_protocol("RC6")
|
||||
RC6_SCHEMA = cv.Schema(
|
||||
{
|
||||
cv.Required(CONF_ADDRESS): cv.hex_uint8_t,
|
||||
@@ -1674,12 +1686,17 @@ def rc6_dumper(var, config):
|
||||
pass
|
||||
|
||||
|
||||
@register_action("rc6", RC6Action, RC6_SCHEMA)
|
||||
async def rc6_action(var, config, args):
|
||||
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_))
|
||||
register_protocol_action(
|
||||
"rc6",
|
||||
"RC6",
|
||||
RC6_SCHEMA,
|
||||
(CONF_ADDRESS, "address", cg.uint8),
|
||||
(CONF_COMMAND, "command", cg.uint8),
|
||||
# Each action flips its own toggle bit, starting at 0: the lambda's static is per action
|
||||
automation.ApplyCall(
|
||||
"data.toggle = []() {{ static uint8_t toggle; return toggle++; }}()"
|
||||
),
|
||||
)
|
||||
|
||||
|
||||
# RC Switch Raw
|
||||
|
||||
@@ -37,17 +37,4 @@ class ProntoProtocol : public RemoteProtocol<ProntoData> {
|
||||
|
||||
DECLARE_REMOTE_PROTOCOL(Pronto)
|
||||
|
||||
template<typename... Ts> class ProntoAction : public RemoteTransmitterActionBase<Ts...> {
|
||||
public:
|
||||
TEMPLATABLE_VALUE(std::string, data)
|
||||
TEMPLATABLE_VALUE(int, delta)
|
||||
|
||||
void encode(RemoteTransmitData *dst, Ts... x) override {
|
||||
ProntoData data{};
|
||||
data.data = this->data_.value(x...);
|
||||
data.delta = this->delta_.value(x...);
|
||||
ProntoProtocol().encode(dst, data);
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace esphome::remote_base
|
||||
|
||||
@@ -22,23 +22,4 @@ class RC6Protocol : public RemoteProtocol<RC6Data> {
|
||||
|
||||
DECLARE_REMOTE_PROTOCOL(RC6)
|
||||
|
||||
template<typename... Ts> class RC6Action : public RemoteTransmitterActionBase<Ts...> {
|
||||
public:
|
||||
TEMPLATABLE_VALUE(uint8_t, address)
|
||||
TEMPLATABLE_VALUE(uint8_t, command)
|
||||
|
||||
void encode(RemoteTransmitData *dst, Ts... x) {
|
||||
RC6Data data{};
|
||||
data.mode = 0;
|
||||
data.toggle = this->toggle_;
|
||||
data.address = this->address_.value(x...);
|
||||
data.command = this->command_.value(x...);
|
||||
RC6Protocol().encode(dst, data);
|
||||
this->toggle_ = !this->toggle_;
|
||||
}
|
||||
|
||||
protected:
|
||||
uint8_t toggle_{0};
|
||||
};
|
||||
|
||||
} // namespace esphome::remote_base
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
esphome:
|
||||
name: test
|
||||
|
||||
esp8266:
|
||||
board: esp01_1m
|
||||
|
||||
remote_transmitter:
|
||||
pin: GPIO4
|
||||
carrier_duty_percent: 50%
|
||||
|
||||
button:
|
||||
- platform: template
|
||||
name: RC6 one
|
||||
on_press:
|
||||
- remote_transmitter.transmit_rc6:
|
||||
address: 0x00
|
||||
command: 0x0C
|
||||
- platform: template
|
||||
name: RC6 two
|
||||
on_press:
|
||||
- remote_transmitter.transmit_rc6:
|
||||
address: 0x00
|
||||
command: 0x0D
|
||||
- platform: template
|
||||
name: Pronto
|
||||
on_press:
|
||||
- remote_transmitter.transmit_pronto:
|
||||
data: "0000 006D 0001 0000 0015 0016"
|
||||
@@ -0,0 +1,41 @@
|
||||
"""transmit_rc6 keeps a toggle bit per action, and transmit_pronto sends its string."""
|
||||
|
||||
from collections.abc import Callable
|
||||
from pathlib import Path
|
||||
|
||||
from esphome import automation
|
||||
import esphome.codegen as cg
|
||||
from esphome.components.remote_base import _protocol_field
|
||||
from esphome.const import CONF_TEMPERATURE
|
||||
from tests.component_tests.remote_transmitter import protocol_call_bodies
|
||||
|
||||
|
||||
def test_rc6_toggle_and_pronto(
|
||||
generate_main: Callable[[str | Path], str],
|
||||
component_config_path: Callable[[str], Path],
|
||||
) -> None:
|
||||
main_cpp = generate_main(component_config_path("rc6_pronto.yaml"))
|
||||
bodies = protocol_call_bodies(main_cpp)
|
||||
assert len(bodies) == 3
|
||||
toggle = (
|
||||
"apply_call.data.toggle = []() { static uint8_t toggle; return toggle++; }();"
|
||||
)
|
||||
# Each RC6 action has its own generated function, so its own toggle static.
|
||||
assert toggle in bodies[0]
|
||||
assert toggle in bodies[1]
|
||||
assert (
|
||||
'apply_call.data.data = progmem_string(ESPHOME_F("0000 006D 0001 0000 0015 0016"));'
|
||||
in bodies[2]
|
||||
)
|
||||
assert "delta" not in bodies[2]
|
||||
|
||||
|
||||
def test_apply_objects_pass_through() -> None:
|
||||
call = automation.ApplyCall("stop()")
|
||||
assert _protocol_field(call) is call
|
||||
field = automation.ApplyField(
|
||||
CONF_TEMPERATURE, "data.set_temperature({})", cg.float_
|
||||
)
|
||||
assert _protocol_field(field) is field
|
||||
plain = _protocol_field((CONF_TEMPERATURE, "temperature", cg.float_))
|
||||
assert plain.target == "data.temperature = {}"
|
||||
Reference in New Issue
Block a user