[remote_base] updates to support 56 bit panasonic codes (#20260)

Co-authored-by: J. Nick Koston <nick@home-assistant.io>
This commit is contained in:
toups
2026-10-11 20:48:55 -10:00
committed by GitHub
co-authored by J. Nick Koston
parent 7fbdcb0205
commit c668ca516c
9 changed files with 268 additions and 52 deletions
+1
View File
@@ -15,6 +15,7 @@ def override_manifest(manifest: ComponentManifestOverride) -> None:
"keeloq",
"nexus",
"rc5x",
"panasonic",
):
request_protocol(protocol)
@@ -0,0 +1,100 @@
#include <gtest/gtest.h>
#include "esphome/components/remote_base/panasonic_protocol.h"
namespace esphome::remote_base::testing {
namespace {
const PanasonicData FRAME_48{0x4004, 0x0100BCBD};
const PanasonicData FRAME_56{0x4004, 0x0100BCBD, 56, 0x12};
RawTimings encode(const PanasonicData &data) {
RemoteTransmitData tx;
PanasonicProtocol().encode(&tx, data);
return tx.get_data();
}
optional<PanasonicData> decode(const RawTimings &timings) {
return PanasonicProtocol().decode(RemoteReceiveData(timings, 25, TOLERANCE_MODE_PERCENTAGE));
}
} // namespace
TEST(PanasonicProtocolTest, ForwardsTheConfiguredCarrier) {
PanasonicData data = FRAME_56;
data.carrier_frequency = 36000;
RemoteTransmitData tx;
PanasonicProtocol().encode(&tx, data);
EXPECT_EQ(tx.get_carrier_frequency(), 36000u);
}
TEST(PanasonicProtocolTest, RoundTrips48BitFrame) {
RemoteTransmitData tx;
PanasonicProtocol().encode(&tx, FRAME_48);
EXPECT_EQ(tx.get_data().size(), 2u + 2u * 48u + 1u);
EXPECT_EQ(tx.get_carrier_frequency(), 35000u);
auto decoded = decode(tx.get_data());
ASSERT_TRUE(decoded.has_value());
if (!decoded.has_value()) {
return;
}
EXPECT_EQ(*decoded, FRAME_48);
}
TEST(PanasonicProtocolTest, RoundTrips56BitFrame) {
RawTimings timings = encode(FRAME_56);
EXPECT_EQ(timings.size(), 2u + 2u * 56u + 1u);
auto decoded = decode(timings);
ASSERT_TRUE(decoded.has_value());
if (!decoded.has_value()) {
return;
}
EXPECT_EQ(*decoded, FRAME_56);
}
TEST(PanasonicProtocolTest, Decodes56BitFrameWithTrailingItems) {
RawTimings timings = encode(FRAME_56);
timings.insert(timings.end(), {-20000, 3502, -1750, 502, -400});
auto decoded = decode(timings);
ASSERT_TRUE(decoded.has_value());
if (!decoded.has_value()) {
return;
}
EXPECT_EQ(*decoded, FRAME_56);
}
TEST(PanasonicProtocolTest, Decodes48BitFrameWithTrailingItems) {
RawTimings timings = encode(FRAME_48);
// A repeat frame arriving inside the idle window.
timings.insert(timings.end(), {-20000, 3502, -1750, 502, -400});
auto decoded = decode(timings);
ASSERT_TRUE(decoded.has_value());
if (!decoded.has_value()) {
return;
}
EXPECT_EQ(*decoded, FRAME_48);
}
TEST(PanasonicProtocolTest, RejectsTruncatedFrame) {
RawTimings timings = encode(FRAME_48);
timings.resize(timings.size() - 4);
EXPECT_FALSE(decode(timings).has_value());
}
TEST(PanasonicProtocolTest, InvalidNbitsSendsNothing) {
PanasonicData data = FRAME_48;
data.nbits = 40;
EXPECT_TRUE(encode(data).empty());
}
TEST(PanasonicProtocolTest, EqualityIgnoresCarrier) {
PanasonicData other_carrier = FRAME_56;
other_carrier.carrier_frequency = 36000;
EXPECT_EQ(FRAME_56, other_carrier);
PanasonicData other_address_2 = FRAME_56;
other_address_2.address_2 = 0x13;
EXPECT_NE(FRAME_56, other_address_2);
EXPECT_NE(FRAME_48, FRAME_56);
}
} // namespace esphome::remote_base::testing
@@ -116,8 +116,8 @@ on_onkyori:
on_panasonic:
then:
- logger.log:
format: "on_panasonic: %u %lu"
args: ["x.address", "long(x.command)"]
format: "on_panasonic: %u %u %u %lu"
args: ["x.nbits", "x.address", "x.address_2", "long(x.command)"]
on_pioneer:
then:
- logger.log:
@@ -21,6 +21,13 @@ binary_sensor:
address: 0x00
command: 0x0D
extension: 0x03
- platform: remote_receiver
name: Panasonic 56 bit Remote Input
panasonic:
nbits: 56
address: 0x4004
address_2: 0x12
command: 0x100BCBD
- platform: remote_receiver
name: Hob2Hood Light On
hob2hood:
@@ -103,6 +103,15 @@ button:
remote_transmitter.transmit_panasonic:
address: 0x4004
command: 0x1000BCD
- platform: template
name: Panasonic 56 bit
on_press:
remote_transmitter.transmit_panasonic:
nbits: 56
address: 0x4004
address_2: 0x12
command: !lambda return 0x1000BCD;
carrier_frequency: 36kHz
- platform: template
name: Pioneer
on_press:
@@ -0,0 +1,49 @@
"""A Panasonic address_2 is only sent with a 56-bit code."""
from __future__ import annotations
import pytest
from esphome.automation import ACTION_REGISTRY
from esphome.components.remote_base import (
CONF_ADDRESS_2,
PANASONIC_SCHEMA,
_panasonic_address_2_needs_56_bits,
)
import esphome.config_validation as cv
from esphome.const import CONF_NBITS
from esphome.core import Lambda
BASE = {"address": 0x4004, "command": 0x0100BCBD}
def test_address_2_needs_56_bits() -> None:
config = PANASONIC_SCHEMA({**BASE, CONF_NBITS: 56, CONF_ADDRESS_2: 0x12})
assert _panasonic_address_2_needs_56_bits(config)[CONF_ADDRESS_2] == 0x12
with pytest.raises(cv.Invalid, match="only sent with nbits: 56"):
_panasonic_address_2_needs_56_bits(
PANASONIC_SCHEMA({**BASE, CONF_NBITS: 48, CONF_ADDRESS_2: 0x12})
)
def test_zero_address_2_is_fine_with_48_bits() -> None:
config = PANASONIC_SCHEMA({**BASE, CONF_ADDRESS_2: 0})
assert _panasonic_address_2_needs_56_bits(config)[CONF_NBITS] == 48
def test_lambda_address_2_is_not_checked_here() -> None:
config = {**BASE, CONF_NBITS: 48, CONF_ADDRESS_2: Lambda("return 0x12;")}
assert isinstance(
_panasonic_address_2_needs_56_bits(config)[CONF_ADDRESS_2], Lambda
)
def test_transmit_action_checks_address_2() -> None:
schema = ACTION_REGISTRY["remote_transmitter.transmit_panasonic"].raw_schema
assert schema({**BASE, CONF_NBITS: 56, CONF_ADDRESS_2: 0x12})[CONF_NBITS] == 56
assert isinstance(
schema({**BASE, CONF_ADDRESS_2: Lambda("return 0x12;")})[CONF_ADDRESS_2],
Lambda,
)
with pytest.raises(cv.Invalid, match="only sent with nbits: 56"):
schema({**BASE, CONF_ADDRESS_2: 0x12})