238 lines
6 KiB
YAML
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%
|