nixos-configuration/hosts/iron/services/esphome/devices/water-bottle.yaml
2025-04-07 23:22:56 +02:00

238 lines
6 KiB
YAML

substitutions:
tolerance: "10"
minimum_sip: "20"
default_bottle_tare: "198"
esphome:
name: "water-bottle"
friendly_name: "Water bottle"
esp32:
board: lolin_s2_mini
variant: ESP32S2
framework:
type: esp-idf
sdkconfig_options:
CONFIG_ESP_CONSOLE_USB_CDC: y
logger:
hardware_uart: USB_CDC
api:
encryption:
key: !secret apikey_water_bottle
on_client_connected:
- light.turn_on:
id: pixels
effect: "scan"
red: 0%
green: 25%
blue: 0%
- delay: 2s
- light.turn_off:
id: pixels
ota:
- platform: esphome
password: !secret otapass_water_bottle
wifi:
ssid: !secret wifi_ssid_bw
password: !secret wifi_password_bw
domain: .iot.bw.jalr.de
enable_on_boot: True
fast_connect: True
power_save_mode: none
output_power: 10
globals:
- id: volume_when_removed
type: int
restore_value: no
initial_value: "NAN"
- id: bottle_returned
type: bool
restore_value: no
initial_value: "false"
sensor:
- platform: wifi_signal
name: "WiFi Signal Sensor"
update_interval: 60s
- platform: hx711
internal: true
id: scale
dout_pin: GPIO16
clk_pin: GPIO17
gain: 128
update_interval: 0.1s
unit_of_measurement: g
filters:
- quantile:
window_size: 10
send_every: 5
send_first_at: 5
quantile: .9
- calibrate_linear:
- 197155 -> 0
- 246676 -> 50
- platform: copy
name: "absolute volume"
id: volume_absolute
icon: "mdi:water"
source_id: scale
filters:
- lambda: "return x - id(bottle_tare).state;"
- clamp:
min_value: 0
ignore_out_of_range: true
- quantile:
window_size: 5
send_every: 5
send_first_at: 5
quantile: .9
on_value:
then:
- if:
condition:
lambda: |-
if (id(bottle_returned)) {
id(bottle_returned) = false;
return true;
}
return false;
then:
- lambda: |-
ESP_LOGI("main", "Bottle returned, waiting to settle");
ESP_LOGI("main", "Volume absolute: %f", id(volume_absolute).state);
- delay: 3s
- lambda: |-
ESP_LOGI("main", "Bottle settled");
ESP_LOGI("main", "Volume when removed: %d", id(volume_when_removed));
ESP_LOGI("main", "Volume absolute: %f", id(volume_absolute).state);
if (id(volume_when_removed) > id(volume_absolute).state + ${minimum_sip}) {
ESP_LOGI("main", "Volume total before: %f", id(volume_total).state);
auto call = id(volume_total).make_call();
call.set_value(id(volume_when_removed) - id(volume_absolute).state + id(volume_total).state);
call.perform();
ESP_LOGI("main", "Volume total now: %f", id(volume_total).state);
}
- platform: template
name: "Water consumption rate"
id: "volume_total_derivative"
unit_of_measurement: "ml/h"
icon: "mdi:water-check"
lambda: |-
static float last_value = 0;
static float last_time = 0;
float time = (float) millis();
if (last_time == 0){
last_value = id(volume_total).state;
last_time = time;
return {};
}
float change = ( ( id(volume_total).state - last_value ) / ( time - last_time ) ) *1000*60*60;
last_value = id(volume_total).state;
last_time = time;
return change;
filters:
- sliding_window_moving_average:
window_size: 3
send_every: 2
- or:
- delta: 0.01
- heartbeat: 120minutes
- throttle: 30s
number:
- platform: template
name: "Bottle tare"
id: bottle_tare
icon: mdi:weight-gram
optimistic: true
initial_value: "${default_bottle_tare}"
unit_of_measurement: "g"
step: 1
min_value: 0
max_value: 1000
- platform: template
name: "total volume"
id: volume_total
optimistic: true
icon: "mdi:water"
unit_of_measurement: "ml"
step: 1
min_value: 0
max_value: 10000
binary_sensor:
- platform: template
name: "Bottle present"
id: bottle_present
lambda: |-
if (id(scale).state > id(bottle_tare).state - ${tolerance}) {
return true;
} else {
return false;
}
on_release:
then:
- lambda: |-
id(volume_when_removed) = id(volume_absolute).state;
ESP_LOGI("main", "Volume absolute: %f ml", id(volume_absolute).state);
ESP_LOGI("main", "Bottle removed with %d ml", id(volume_when_removed));
- light.turn_on:
id: pixels
effect: "normal_pulse"
red: 75%
green: 0%
blue: 0%
on_press:
then:
- globals.set:
id: bottle_returned
value: "true"
- if:
condition:
lambda: "return id(volume_absolute).state > 250;"
then:
- light.turn_on:
id: pixels
effect: "normal_pulse"
red: 0%
green: 50%
blue: 50%
else:
- light.turn_on:
id: pixels
effect: "normal_pulse"
red: 50%
green: 25%
blue: 0%
- delay: 2s
- light.turn_off:
id: pixels
e131:
method: multicast
light:
- platform: esp32_rmt_led_strip
id: pixels
rgb_order: GRB
pin: GPIO12
num_leds: 8
rmt_channel: 0
chipset: ws2812
effects:
- addressable_scan:
name: scan
move_interval: 20ms
scan_width: 1
- pulse:
name: slow_pulse
transition_length: 500ms
update_interval: 10s
- pulse:
name: normal_pulse
min_brightness: 20%