1
0
mirror of https://github.com/esphome/esphome.git synced 2025-10-21 19:23:45 +01:00
This commit is contained in:
J. Nick Koston
2025-10-15 19:57:29 -10:00
parent a999349fa5
commit f75f11b550
3 changed files with 148 additions and 12 deletions

View File

@@ -453,7 +453,7 @@ async def skip_initial_filter_to_code(config, filter_id):
return cg.new_Pvariable(filter_id, config)
@FILTER_REGISTRY.register("min", MinFilter, MIN_SCHEMA)
@FILTER_REGISTRY.register("min", Filter, MIN_SCHEMA)
async def min_filter_to_code(config, filter_id):
window_size: int = config[CONF_WINDOW_SIZE]
send_every: int = config[CONF_SEND_EVERY]
@@ -463,10 +463,11 @@ async def min_filter_to_code(config, filter_id):
# Saves 99.98% memory for large windows (e.g., 20KB → 4 bytes for window_size=5000)
if window_size == send_every:
# Use streaming filter - O(1) memory instead of O(n)
rhs = cg.new_Pvariable(StreamingMinFilter, window_size, send_first_at)
return cg.Pvariable(filter_id, rhs)
rhs = StreamingMinFilter.new(window_size, send_first_at)
return cg.Pvariable(filter_id, rhs, StreamingMinFilter)
# Use sliding window filter - maintains ring buffer
return cg.new_Pvariable(filter_id, window_size, send_every, send_first_at)
rhs = MinFilter.new(window_size, send_every, send_first_at)
return cg.Pvariable(filter_id, rhs, MinFilter)
MAX_SCHEMA = cv.All(
@@ -481,7 +482,7 @@ MAX_SCHEMA = cv.All(
)
@FILTER_REGISTRY.register("max", MaxFilter, MAX_SCHEMA)
@FILTER_REGISTRY.register("max", Filter, MAX_SCHEMA)
async def max_filter_to_code(config, filter_id):
window_size: int = config[CONF_WINDOW_SIZE]
send_every: int = config[CONF_SEND_EVERY]
@@ -489,9 +490,10 @@ async def max_filter_to_code(config, filter_id):
# Optimization: Use streaming filter for batch windows (window_size == send_every)
if window_size == send_every:
rhs = cg.new_Pvariable(StreamingMaxFilter, window_size, send_first_at)
return cg.Pvariable(filter_id, rhs)
return cg.new_Pvariable(filter_id, window_size, send_every, send_first_at)
rhs = StreamingMaxFilter.new(window_size, send_first_at)
return cg.Pvariable(filter_id, rhs, StreamingMaxFilter)
rhs = MaxFilter.new(window_size, send_every, send_first_at)
return cg.Pvariable(filter_id, rhs, MaxFilter)
SLIDING_AVERAGE_SCHEMA = cv.All(
@@ -508,7 +510,7 @@ SLIDING_AVERAGE_SCHEMA = cv.All(
@FILTER_REGISTRY.register(
"sliding_window_moving_average",
SlidingWindowMovingAverageFilter,
Filter,
SLIDING_AVERAGE_SCHEMA,
)
async def sliding_window_moving_average_filter_to_code(config, filter_id):
@@ -518,9 +520,10 @@ async def sliding_window_moving_average_filter_to_code(config, filter_id):
# Optimization: Use streaming filter for batch windows (window_size == send_every)
if window_size == send_every:
rhs = cg.new_Pvariable(StreamingMovingAverageFilter, window_size, send_first_at)
return cg.Pvariable(filter_id, rhs)
return cg.new_Pvariable(filter_id, window_size, send_every, send_first_at)
rhs = StreamingMovingAverageFilter.new(window_size, send_first_at)
return cg.Pvariable(filter_id, rhs, StreamingMovingAverageFilter)
rhs = SlidingWindowMovingAverageFilter.new(window_size, send_every, send_first_at)
return cg.Pvariable(filter_id, rhs, SlidingWindowMovingAverageFilter)
EXPONENTIAL_AVERAGE_SCHEMA = cv.All(

View File

@@ -0,0 +1,58 @@
esphome:
name: test-batch-window-filters
host:
api:
batch_delay: 0ms # Disable batching to receive all state updates
logger:
level: DEBUG
# Template sensor that we'll use to publish values
sensor:
- platform: template
name: "Source Sensor"
id: source_sensor
accuracy_decimals: 2
# Batch window filters (window_size == send_every) - use streaming filters
- platform: copy
source_id: source_sensor
name: "Min Sensor"
id: min_sensor
filters:
- min:
window_size: 5
send_every: 5
send_first_at: 1
- platform: copy
source_id: source_sensor
name: "Max Sensor"
id: max_sensor
filters:
- max:
window_size: 5
send_every: 5
send_first_at: 1
- platform: copy
source_id: source_sensor
name: "Moving Avg Sensor"
id: moving_avg_sensor
filters:
- sliding_window_moving_average:
window_size: 5
send_every: 5
send_first_at: 1
# Button to trigger publishing test values
button:
- platform: template
name: "Publish Values Button"
id: publish_button
on_press:
- lambda: |-
// Publish 10 values: 1.0, 2.0, ..., 10.0
for (int i = 1; i <= 10; i++) {
id(source_sensor).publish_state(float(i));
}

View File

@@ -0,0 +1,75 @@
esphome:
name: test-sliding-window-filters
host:
api:
batch_delay: 0ms # Disable batching to receive all state updates
logger:
level: DEBUG
# Template sensor that we'll use to publish values
sensor:
- platform: template
name: "Source Sensor"
id: source_sensor
accuracy_decimals: 2
# ACTUAL sliding window filters (window_size != send_every) - use ring buffers
# Window of 5, send every 2 values
- platform: copy
source_id: source_sensor
name: "Sliding Min Sensor"
id: sliding_min_sensor
filters:
- min:
window_size: 5
send_every: 2
send_first_at: 1
- platform: copy
source_id: source_sensor
name: "Sliding Max Sensor"
id: sliding_max_sensor
filters:
- max:
window_size: 5
send_every: 2
send_first_at: 1
- platform: copy
source_id: source_sensor
name: "Sliding Median Sensor"
id: sliding_median_sensor
filters:
- median:
window_size: 5
send_every: 2
send_first_at: 1
- platform: copy
source_id: source_sensor
name: "Sliding Moving Avg Sensor"
id: sliding_moving_avg_sensor
filters:
- sliding_window_moving_average:
window_size: 5
send_every: 2
send_first_at: 1
# Button to trigger publishing test values
button:
- platform: template
name: "Publish Values Button"
id: publish_button
on_press:
- lambda: |-
// Publish 10 values: 1.0, 2.0, ..., 10.0
// With window_size=5, send_every=2, send_first_at=1:
// - Output at position 1: window=[1], min=1, max=1, median=1, avg=1
// - Output at position 3: window=[1,2,3], min=1, max=3, median=2, avg=2
// - Output at position 5: window=[1,2,3,4,5], min=1, max=5, median=3, avg=3
// - Output at position 7: window=[3,4,5,6,7], min=3, max=7, median=5, avg=5
// - Output at position 9: window=[5,6,7,8,9], min=5, max=9, median=7, avg=7
for (int i = 1; i <= 10; i++) {
id(source_sensor).publish_state(float(i));
}