mirror of
https://github.com/esphome/esphome.git
synced 2026-10-18 04:02:54 +00:00
[sensor][number] Add an in_range integration test with a fixed truth table
This commit is contained in:
@@ -0,0 +1,158 @@
|
||||
esphome:
|
||||
name: test-in-range-conditions
|
||||
|
||||
host:
|
||||
|
||||
api:
|
||||
actions:
|
||||
- action: run_cases
|
||||
then:
|
||||
- script.execute:
|
||||
id: check
|
||||
label: neg
|
||||
value: !lambda "return -10.0f;"
|
||||
- script.wait: check
|
||||
- script.execute:
|
||||
id: check
|
||||
label: low
|
||||
value: !lambda "return 0.0f;"
|
||||
- script.wait: check
|
||||
- script.execute:
|
||||
id: check
|
||||
label: mid
|
||||
value: !lambda "return 5.0f;"
|
||||
- script.wait: check
|
||||
- script.execute:
|
||||
id: check
|
||||
label: high
|
||||
value: !lambda "return 10.0f;"
|
||||
- script.wait: check
|
||||
- script.execute:
|
||||
id: check
|
||||
label: over
|
||||
value: !lambda "return 20.0f;"
|
||||
- script.wait: check
|
||||
- script.execute:
|
||||
id: check
|
||||
label: nan
|
||||
value: !lambda "return NAN;"
|
||||
- script.wait: check
|
||||
- script.execute:
|
||||
id: check
|
||||
label: neginf
|
||||
value: !lambda "return -INFINITY;"
|
||||
- script.wait: check
|
||||
- script.execute:
|
||||
id: check
|
||||
label: posinf
|
||||
value: !lambda "return INFINITY;"
|
||||
- script.wait: check
|
||||
- logger.log: "INRANGE done"
|
||||
|
||||
logger:
|
||||
level: DEBUG
|
||||
|
||||
sensor:
|
||||
- platform: template
|
||||
id: range_sensor
|
||||
name: Range Sensor
|
||||
|
||||
number:
|
||||
- platform: template
|
||||
id: range_number
|
||||
name: Range Number
|
||||
optimistic: true
|
||||
min_value: -100
|
||||
max_value: 100
|
||||
step: 0.5
|
||||
|
||||
script:
|
||||
- id: check
|
||||
parameters:
|
||||
label: string
|
||||
value: float
|
||||
then:
|
||||
# Set the states directly so NaN and infinities reach the conditions unchanged
|
||||
- lambda: |-
|
||||
id(range_sensor).publish_state(value);
|
||||
id(range_number).publish_state(value);
|
||||
- if:
|
||||
condition:
|
||||
sensor.in_range:
|
||||
id: range_sensor
|
||||
above: 0
|
||||
then:
|
||||
- logger.log:
|
||||
format: "INRANGE %s sensor_above 1"
|
||||
args: [label.c_str()]
|
||||
else:
|
||||
- logger.log:
|
||||
format: "INRANGE %s sensor_above 0"
|
||||
args: [label.c_str()]
|
||||
- if:
|
||||
condition:
|
||||
sensor.in_range:
|
||||
id: range_sensor
|
||||
below: 10
|
||||
then:
|
||||
- logger.log:
|
||||
format: "INRANGE %s sensor_below 1"
|
||||
args: [label.c_str()]
|
||||
else:
|
||||
- logger.log:
|
||||
format: "INRANGE %s sensor_below 0"
|
||||
args: [label.c_str()]
|
||||
- if:
|
||||
condition:
|
||||
sensor.in_range:
|
||||
id: range_sensor
|
||||
above: 0
|
||||
below: 10
|
||||
then:
|
||||
- logger.log:
|
||||
format: "INRANGE %s sensor_both 1"
|
||||
args: [label.c_str()]
|
||||
else:
|
||||
- logger.log:
|
||||
format: "INRANGE %s sensor_both 0"
|
||||
args: [label.c_str()]
|
||||
- if:
|
||||
condition:
|
||||
number.in_range:
|
||||
id: range_number
|
||||
above: 0
|
||||
then:
|
||||
- logger.log:
|
||||
format: "INRANGE %s number_above 1"
|
||||
args: [label.c_str()]
|
||||
else:
|
||||
- logger.log:
|
||||
format: "INRANGE %s number_above 0"
|
||||
args: [label.c_str()]
|
||||
- if:
|
||||
condition:
|
||||
number.in_range:
|
||||
id: range_number
|
||||
below: 10
|
||||
then:
|
||||
- logger.log:
|
||||
format: "INRANGE %s number_below 1"
|
||||
args: [label.c_str()]
|
||||
else:
|
||||
- logger.log:
|
||||
format: "INRANGE %s number_below 0"
|
||||
args: [label.c_str()]
|
||||
- if:
|
||||
condition:
|
||||
number.in_range:
|
||||
id: range_number
|
||||
above: 0
|
||||
below: 10
|
||||
then:
|
||||
- logger.log:
|
||||
format: "INRANGE %s number_both 1"
|
||||
args: [label.c_str()]
|
||||
else:
|
||||
- logger.log:
|
||||
format: "INRANGE %s number_both 0"
|
||||
args: [label.c_str()]
|
||||
@@ -0,0 +1,62 @@
|
||||
"""Test sensor.in_range and number.in_range against a fixed truth table."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import asyncio
|
||||
import re
|
||||
|
||||
import pytest
|
||||
|
||||
from .types import APIClientConnectedFactory, RunCompiledFunction
|
||||
|
||||
# Bounds in the fixture: above 0, below 10, both inclusive. A NaN state is never in range.
|
||||
# Columns: above only, below only, both bounds.
|
||||
EXPECTED = {
|
||||
"neg": (0, 1, 0), # -10
|
||||
"low": (1, 1, 1), # 0, the lower bound
|
||||
"mid": (1, 1, 1), # 5
|
||||
"high": (1, 1, 1), # 10, the upper bound
|
||||
"over": (1, 0, 0), # 20
|
||||
"nan": (0, 0, 0),
|
||||
"neginf": (0, 1, 0),
|
||||
"posinf": (1, 0, 0),
|
||||
}
|
||||
CONDITIONS = ("above", "below", "both")
|
||||
RESULT_RE = re.compile(r"INRANGE (\w+) (sensor|number)_(above|below|both) ([01])")
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_in_range_conditions(
|
||||
yaml_config: str,
|
||||
run_compiled: RunCompiledFunction,
|
||||
api_client_connected: APIClientConnectedFactory,
|
||||
) -> None:
|
||||
"""Each in_range condition gives the same result for sensors and numbers."""
|
||||
loop = asyncio.get_running_loop()
|
||||
results: dict[tuple[str, str, str], int] = {}
|
||||
done = loop.create_future()
|
||||
|
||||
def check_output(line: str) -> None:
|
||||
if match := RESULT_RE.search(line):
|
||||
label, entity, condition, value = match.groups()
|
||||
results[(label, entity, condition)] = int(value)
|
||||
elif "INRANGE done" in line and not done.done():
|
||||
done.set_result(True)
|
||||
|
||||
async with (
|
||||
run_compiled(yaml_config, line_callback=check_output),
|
||||
api_client_connected() as client,
|
||||
):
|
||||
_, services = await client.list_entities_services()
|
||||
service = next((s for s in services if s.name == "run_cases"), None)
|
||||
assert service is not None, "run_cases action not found"
|
||||
await client.execute_service(service, {})
|
||||
await asyncio.wait_for(done, timeout=10.0)
|
||||
|
||||
expected = {
|
||||
(label, entity, condition): row[index]
|
||||
for label, row in EXPECTED.items()
|
||||
for entity in ("sensor", "number")
|
||||
for index, condition in enumerate(CONDITIONS)
|
||||
}
|
||||
assert results == expected
|
||||
Reference in New Issue
Block a user