hey, thanks for the feedback.
didn’t mean for it to be a train wreck.
i have an espHome switch that controls my domestic warm water circulation pump.
when it’s on, warm water gets pumped through the pipes.
when It’s been on for at least 5-6minutes the the past 10 minutes im pretty sure I’ll get warm water off the tap furthest away from the warm water heater.
at the closest water tap that may only be 1-2minutes.
the switches I have close to the water tap allow for a visualization of an 8 speed fan that I’d like to use to visualize how “warm” the warm water will likely be if I turn it on right now.
to calculate this, my esphome switch has a circular buffer of the last 20 times its been toggled on or off:
globals:
- id: switch_state_changes
type: std::vector<std::pair<int, bool>>
initial_value: 'std::vector<std::pair<int, bool>>()'
script:
- id: record_switch_state_change
then:
- lambda: |-
// Record a switch state change (on/off) with the current timestamp.
auto now = id(ha_time).now().timestamp;
bool state = id(relay).state;
// Ensure the buffer doesn't exceed 20 entries.
if (id(switch_state_changes).size() >= 20) {
id(switch_state_changes).erase(id(switch_state_changes).begin());
}
id(switch_state_changes).push_back(std::make_pair(now, state));
id(visualization_sensor).update();
switch:
- platform: gpio
name: "${friendly_name}"
pin: GPIO12
id: relay
on_turn_on:
then:
- script.execute: record_switch_state_change
on_turn_off:
then:
- script.execute: record_switch_state_change
now to calculate how Long the pump has been running with in the las lookback_window
I cycle through the buffer, and add up the amount of time the switch has been in the “on” state within that lookback window:
sensor:
- platform: template
id: visualization_sensor
disabled_by_default: true
lambda: |-
long current_time = id(ha_time).now().timestamp;
long duration_on = 0;
long lookback_window = 600;
long last_change_time = 0;
bool was_on = false;
// Iterate through all state changes to find the relevant ones.
for (auto &change : id(switch_state_changes)) {
long change_time = change.first;
bool is_on = change.second;
//check if change is within lookback window
if(change_time < (current_time - lookback_window)){
last_change_time = current_time - lookback_window;
was_on = is_on;
}else{
if(!is_on && was_on){
duration_on += (change_time - last_change_time);
}
if(is_on != was_on){
last_change_time = change_time;
was_on = is_on;
}
}
}
float percent_on = (static_cast<float>(duration_on) / static_cast<float>(lookback_window));
ESP_LOGD("custom", "duration_on: %i", duration_on);
ESP_LOGD("custom", "as percentage %f", percent_on*100);
return (percent_on * 8); // To return the visualization integer.
as you can see this is currently hardcoded as a loopback window of 600 seconds (10minutes) and is published as a sensor.
to display at different values at different taps and avoid having multiple sensors that only exist to be forwarded as visualization, I’d rather create a service
api:
services:
- service: percent_on_in_window
variables:
window: int
then:
- script.execute: <lambda from above that takes window parameter and returns percent_on>
that I’d call with a script in home assistant, take the result value and assign it to the visualization entity
maybe there’s a better way to do it. currently I just have a single calculation for the tap furthest away from the hot water heater that updates all displays to the same value. it gets updated via espsensor on_value:
then homeassistant.service: number.set_value
on all visualization entities.
but thats because I don’t know how to return a value from an api: service
call