diff --git a/esphome/components/sensor/__init__.py b/esphome/components/sensor/__init__.py index ebbe0fbccc..03784ba76b 100644 --- a/esphome/components/sensor/__init__.py +++ b/esphome/components/sensor/__init__.py @@ -603,7 +603,7 @@ DELTA_SCHEMA = cv.Any( def _get_delta(value): if isinstance(value, str): assert value.endswith("%") - return 0.0, float(value[:-1]) + return 0.0, float(value[:-1]) / 100.0 return value, 0.0 diff --git a/tests/integration/fixtures/sensor_filters_delta.yaml b/tests/integration/fixtures/sensor_filters_delta.yaml index 19bd2d5ca4..2494a430da 100644 --- a/tests/integration/fixtures/sensor_filters_delta.yaml +++ b/tests/integration/fixtures/sensor_filters_delta.yaml @@ -28,6 +28,11 @@ sensor: id: source_sensor_4 accuracy_decimals: 1 + - platform: template + name: "Source Sensor 5" + id: source_sensor_5 + accuracy_decimals: 1 + - platform: copy source_id: source_sensor_1 name: "Filter Min" @@ -69,6 +74,13 @@ sensor: filters: - delta: 0 + - platform: copy + source_id: source_sensor_5 + name: "Filter Percentage" + id: filter_percentage + filters: + - delta: 50% + script: - id: test_filter_min then: @@ -154,6 +166,28 @@ script: id: source_sensor_4 state: 2.0 + - id: test_filter_percentage + then: + - sensor.template.publish: + id: source_sensor_5 + state: 100.0 + - delay: 20ms + - sensor.template.publish: + id: source_sensor_5 + state: 120.0 # Filtered out (delta=20, need >50) + - delay: 20ms + - sensor.template.publish: + id: source_sensor_5 + state: 160.0 # Passes (delta=60 > 50% of 100=50) + - delay: 20ms + - sensor.template.publish: + id: source_sensor_5 + state: 200.0 # Filtered out (delta=40, need >50% of 160=80) + - delay: 20ms + - sensor.template.publish: + id: source_sensor_5 + state: 250.0 # Passes (delta=90 > 80) + button: - platform: template name: "Test Filter Min" @@ -178,3 +212,9 @@ button: id: btn_filter_zero_delta on_press: - script.execute: test_filter_zero_delta + + - platform: template + name: "Test Filter Percentage" + id: btn_filter_percentage + on_press: + - script.execute: test_filter_percentage diff --git a/tests/integration/test_sensor_filters_delta.py b/tests/integration/test_sensor_filters_delta.py index c7a26bf9d1..9d0114e0c4 100644 --- a/tests/integration/test_sensor_filters_delta.py +++ b/tests/integration/test_sensor_filters_delta.py @@ -24,12 +24,14 @@ async def test_sensor_filters_delta( "filter_max": [], "filter_baseline_max": [], "filter_zero_delta": [], + "filter_percentage": [], } filter_min_done = loop.create_future() filter_max_done = loop.create_future() filter_baseline_max_done = loop.create_future() filter_zero_delta_done = loop.create_future() + filter_percentage_done = loop.create_future() def on_state(state: EntityState) -> None: if not isinstance(state, SensorState) or state.missing_state: @@ -66,6 +68,12 @@ async def test_sensor_filters_delta( and not filter_zero_delta_done.done() ): filter_zero_delta_done.set_result(True) + elif ( + sensor_name == "filter_percentage" + and len(sensor_values[sensor_name]) == 3 + and not filter_percentage_done.done() + ): + filter_percentage_done.set_result(True) async with ( run_compiled(yaml_config), @@ -80,6 +88,7 @@ async def test_sensor_filters_delta( "filter_max": "Filter Max", "filter_baseline_max": "Filter Baseline Max", "filter_zero_delta": "Filter Zero Delta", + "filter_percentage": "Filter Percentage", }, ) @@ -98,13 +107,14 @@ async def test_sensor_filters_delta( "Test Filter Max": "filter_max", "Test Filter Baseline Max": "filter_baseline_max", "Test Filter Zero Delta": "filter_zero_delta", + "Test Filter Percentage": "filter_percentage", } buttons = {} for entity in entities: if isinstance(entity, ButtonInfo) and entity.name in button_name_map: buttons[button_name_map[entity.name]] = entity.key - assert len(buttons) == 4, f"Expected 3 buttons, found {len(buttons)}" + assert len(buttons) == 5, f"Expected 5 buttons, found {len(buttons)}" # Test 1: Min sensor_values["filter_min"].clear() @@ -161,3 +171,18 @@ async def test_sensor_filters_delta( assert sensor_values["filter_zero_delta"] == pytest.approx(expected), ( f"Test 4 failed: expected {expected}, got {sensor_values['filter_zero_delta']}" ) + + # Test 5: Percentage (delta: 50%) + sensor_values["filter_percentage"].clear() + client.button_command(buttons["filter_percentage"]) + try: + await asyncio.wait_for(filter_percentage_done, timeout=2.0) + except TimeoutError: + pytest.fail( + f"Test 5 timed out. Values: {sensor_values['filter_percentage']}" + ) + + expected = [100.0, 160.0, 250.0] + assert sensor_values["filter_percentage"] == pytest.approx(expected), ( + f"Test 5 failed: expected {expected}, got {sensor_values['filter_percentage']}" + )