mirror of
https://github.com/esphome/esphome.git
synced 2026-09-28 15:30:21 +00:00
[light] Shrink LightState by 12 bytes per light (#19711)
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
import esphome.codegen as cg
|
||||
from esphome.components.light import generate_gamma_table
|
||||
from esphome.components.light import gamma_table_initializer
|
||||
from tests.testing_helpers import ComponentManifestOverride
|
||||
|
||||
|
||||
@@ -12,15 +12,13 @@ def override_manifest(manifest: ComponentManifestOverride) -> None:
|
||||
async def to_code(config):
|
||||
await original_to_code(config)
|
||||
cg.add_define("USE_LIGHT_GAMMA_LUT")
|
||||
# Use the light component's own generate_gamma_table() so the
|
||||
# Use the light component's own gamma_table_initializer() so the
|
||||
# benchmark stays in sync with any formula changes.
|
||||
forward = generate_gamma_table(2.8)
|
||||
values = ", ".join(f"0x{int(v):04X}" for v in forward)
|
||||
# Use extern-visible (non-static) array so the benchmark .cpp
|
||||
# can reference it via extern declaration.
|
||||
# Extern-visible (non-static) so the benchmark .cpp can reference it.
|
||||
cg.add_global(
|
||||
cg.RawStatement(
|
||||
f"extern const uint16_t bench_gamma_2_8_fwd[256] PROGMEM = {{{values}}};"
|
||||
"extern const esphome::light::GammaTable bench_gamma_2_8 PROGMEM = "
|
||||
f"{gamma_table_initializer(2.8)};"
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
@@ -4,8 +4,8 @@
|
||||
#include "esphome/components/light/light_state.h"
|
||||
|
||||
// Gamma 2.8 forward LUT generated by the light component's Python codegen
|
||||
// (see tests/benchmarks/components/light/__init__.py which calls generate_gamma_table())
|
||||
extern const uint16_t bench_gamma_2_8_fwd[256];
|
||||
// (see tests/benchmarks/components/light/__init__.py which calls gamma_table_initializer())
|
||||
extern const esphome::light::GammaTable bench_gamma_2_8;
|
||||
|
||||
namespace esphome::benchmarks {
|
||||
|
||||
@@ -37,8 +37,7 @@ static void setup_rgbww_light(BenchLightOutput &output, TestLightState &light) {
|
||||
output.traits_.set_max_mireds(500.0f);
|
||||
light.configure("test_light");
|
||||
light.set_default_transition_length(0);
|
||||
light.set_gamma_correct(2.8f);
|
||||
light.set_gamma_table(bench_gamma_2_8_fwd);
|
||||
light.set_gamma_table(&bench_gamma_2_8);
|
||||
}
|
||||
|
||||
// --- LightCall::perform() with instant RGB color change (Home Assistant API path) ---
|
||||
|
||||
@@ -0,0 +1,37 @@
|
||||
"""Limits and flash layout that let LightState stay small."""
|
||||
|
||||
import pytest
|
||||
|
||||
from esphome import config_validation as cv
|
||||
from esphome.components.light.effects import (
|
||||
MAX_EFFECTS,
|
||||
MONOCHROMATIC_EFFECTS,
|
||||
validate_effects,
|
||||
)
|
||||
|
||||
|
||||
def _effects(count: int) -> list[dict[str, dict[str, str]]]:
|
||||
return [{"pulse": {"name": f"Pulse {i}"}} for i in range(count)]
|
||||
|
||||
|
||||
def test_rejects_more_effects_than_the_index_holds() -> None:
|
||||
with pytest.raises(cv.Invalid, match=f"at most {MAX_EFFECTS} effects"):
|
||||
validate_effects(MONOCHROMATIC_EFFECTS)(_effects(MAX_EFFECTS + 1))
|
||||
|
||||
|
||||
def test_accepts_a_normal_effect_list() -> None:
|
||||
assert len(validate_effects(MONOCHROMATIC_EFFECTS)(_effects(3))) == 3
|
||||
|
||||
|
||||
def test_gamma_table_initializer_holds_the_lut_then_gamma_times_100() -> None:
|
||||
from esphome.components.light import gamma_table_initializer, generate_gamma_table
|
||||
|
||||
init = gamma_table_initializer(2.8)
|
||||
lut = ", ".join(f"0x{int(v):04X}" for v in generate_gamma_table(2.8))
|
||||
assert init == f"{{{{{lut}}}, 280}}"
|
||||
|
||||
|
||||
def test_gamma_table_initializer_saturates_gamma_that_overflows_uint16() -> None:
|
||||
from esphome.components.light import gamma_table_initializer
|
||||
|
||||
assert gamma_table_initializer(1000.0).endswith(", 65535}")
|
||||
@@ -8,6 +8,8 @@ def override_manifest(manifest: ComponentManifestOverride) -> None:
|
||||
cg.add_define("USE_WEBSERVER")
|
||||
# api_connection.cpp reports the port whenever USE_WEBSERVER is set
|
||||
cg.add_define("USE_WEBSERVER_PORT", 80)
|
||||
# The gamma table and its stored gamma are behind this define
|
||||
cg.add_define("USE_LIGHT_GAMMA_LUT")
|
||||
|
||||
manifest.to_code = to_code_testing
|
||||
manifest.dependencies = manifest.dependencies + ["json"]
|
||||
|
||||
@@ -254,6 +254,7 @@ light:
|
||||
state: INITIAL
|
||||
brightness: INITIAL
|
||||
- platform: monochromatic
|
||||
flash_transition_length: 100ms
|
||||
id: test_monochromatic_light
|
||||
name: Monochromatic Light
|
||||
output: test_ledc_1
|
||||
|
||||
@@ -0,0 +1,68 @@
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include "esphome/components/light/light_effect.h"
|
||||
#include "esphome/components/light/light_output.h"
|
||||
#include "esphome/components/light/light_state.h"
|
||||
|
||||
namespace esphome::light::testing {
|
||||
|
||||
namespace {
|
||||
|
||||
class BrightnessOutput : public LightOutput {
|
||||
public:
|
||||
LightTraits get_traits() override {
|
||||
LightTraits traits;
|
||||
traits.set_supported_color_modes({ColorMode::BRIGHTNESS});
|
||||
return traits;
|
||||
}
|
||||
void write_state(LightState *state) override {}
|
||||
};
|
||||
|
||||
class NoopEffect : public LightEffect {
|
||||
public:
|
||||
using LightEffect::LightEffect;
|
||||
void apply() override {}
|
||||
};
|
||||
|
||||
// start_effect_() is where the uint32_t index is narrowed to the stored uint16_t.
|
||||
class TestableLightState : public LightState {
|
||||
public:
|
||||
using LightState::LightState;
|
||||
using LightState::start_effect_;
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
// add_effects() is public, so an external component can exceed the codegen cap on effect count;
|
||||
// an index the uint16_t can't hold must be ignored rather than wrap onto another effect.
|
||||
TEST(LightStateEffect, IndexAboveUint16IsIgnoredAndKeepsTheActiveEffect) {
|
||||
BrightnessOutput output;
|
||||
TestableLightState state(&output);
|
||||
NoopEffect effect("Noop");
|
||||
state.add_effects({&effect});
|
||||
|
||||
state.start_effect_(1);
|
||||
ASSERT_EQ(state.get_current_effect_index(), 1u);
|
||||
|
||||
state.start_effect_(0x10000u); // unchecked narrowing wraps this to 0, which stops the effect
|
||||
EXPECT_EQ(state.get_current_effect_index(), 1u);
|
||||
}
|
||||
|
||||
// get_gamma_correct() reads the gamma codegen stores after the lookup table, rounded to two decimals.
|
||||
TEST(LightStateGamma, ReadsTheGammaStoredWithTheTable) {
|
||||
static constexpr GammaTable TABLE{{}, 280};
|
||||
BrightnessOutput output;
|
||||
LightState state(&output);
|
||||
state.set_gamma_table(&TABLE);
|
||||
EXPECT_FLOAT_EQ(state.get_gamma_correct(), 2.8f);
|
||||
EXPECT_EQ(state.get_gamma_table(), TABLE.lut);
|
||||
}
|
||||
|
||||
TEST(LightStateGamma, IsZeroWithoutATable) {
|
||||
BrightnessOutput output;
|
||||
LightState state(&output);
|
||||
EXPECT_FLOAT_EQ(state.get_gamma_correct(), 0.0f);
|
||||
EXPECT_EQ(state.get_gamma_table(), nullptr);
|
||||
}
|
||||
|
||||
} // namespace esphome::light::testing
|
||||
Reference in New Issue
Block a user