From 71a3a81f5eb5ddf49f7c0394ee42404718c76fd4 Mon Sep 17 00:00:00 2001 From: David Woodhouse Date: Tue, 8 Aug 2023 20:48:32 +0100 Subject: [PATCH] First import --- pool.yaml | 554 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 554 insertions(+) create mode 100644 pool.yaml diff --git a/pool.yaml b/pool.yaml new file mode 100644 index 0000000..8963e4f --- /dev/null +++ b/pool.yaml @@ -0,0 +1,554 @@ +# +# Olimex ESP32-Gateway. +# +# GPIO13: 1Wire sensors for solar in/out temperatures +# GPIO14: 12v relay for bypass valve: high to close (pump via roof) +# GPIO16: 5v relay for 1Wire sensors' power (to reset them) + +esphome: + name: pool + +esp32: + board: esp32-gateway + framework: + type: arduino + +# Enable logging +logger: + level: DEBUG + +ota: + password: !secret ota_upgrade_pw + +external_components: + - source: github://nrandell/dallasng + - source: + type: local + path: ../git/esphome_syslog/components + components: [syslog] + +#wifi: +# ssid: !secret wifi_ssid +# password: !secret wifi_pw +# +# # Enable fallback hotspot (captive portal) in case wifi connection fails +# ap: +# ssid: "Pool Fallback Hotspot" +# password: "JWWHJb38UzxY" +# +# manual_ip: !include poolup.yaml +# +#captive_portal: + +syslog: + ip_address: !secret syslog_ip + +script: + - id: bounce_1w_power + then: + - switch.turn_off: power_1w + - delay: 1s + - switch.turn_on: power_1w + + - id: control_valve + then: + lambda: |- + if (id(solar_out_fails) || id(solar_in_fails)) + return; + + auto outtemp = id(solar_out).state; + auto intemp = id(solar_in).state; + + if (isnan(outtemp) || isnan(intemp)) + return; + + // We consumed this reading pair. + //id(solar_out_fails) = id(solar_in_fails) = 1; + + if (outtemp == intemp) + return; + + boolean want_state = outtemp > intemp; + if (id(valve_output).state == want_state) { + ESP_LOGD("control_valve", "Leaving valve %s", want_state ? "ON" : "OFF"); + return; + } + + static time_t last_change = 0; + time_t now = ::time(NULL); + + if (now < last_change + 300) { + ESP_LOGD("control_valve", "Too soon to turn %s (%d seconds)", want_state ? "ON" : "OFF", now - last_change); + return; + } + + last_change = now; + id(valve_output).toggle(); + +globals: + - id: solar_in_fails # Number of minutes without a reading. + type: int + restore_value: no + initial_value: '1' + + - id: solar_out_fails + type: int + restore_value: no + initial_value: '1' + +time: + - platform: sntp + id: sntp_time + servers: !secret ntp_servers + on_time: + # Turn on BLE client every 30 minutes for 2 minutes + # (or until it turns itself off after a successful reading) + - seconds: 0 + minutes: /10 + then: + - switch.turn_on: ble_switch + - delay: 2min + - switch.turn_off: ble_switch + - seconds: 0 + minutes: /1 + then: + - if: + condition: + lambda: |- + if (!id(sntp_time).now().is_valid()) { + // Don't count failures while it's turned off anyway. + return false; + } + if (id(power_1w).state) { + // Bump the failure count unconditionally. A successful reading will zero it. + id(solar_in_fails)++; + id(solar_out_fails)++; + } + // Failing for 5 consecutive readings (one per minute)? + return id(solar_in_fails) > 5 || id(solar_out_fails) > 5; + then: + # Turn them off and on again. + script.execute: bounce_1w_power + +ethernet: + type: LAN8720 + mdc_pin: GPIO23 + mdio_pin: GPIO18 + clk_mode: GPIO17_OUT + phy_addr: 0 + manual_ip: !include poolip.yaml + +#network: +# enable_ipv6: true + +mqtt: + broker: !secret mqtt_server + port: 1884 + discovery_prefix: ${mqtt_prefix}/homeassistant + log_topic: ${mqtt_prefix}/logs + username: "pool" + password: !secret pool_mqtt_pw + id: mqtt_client + on_connect: + then: + - mqtt.publish_json: + topic: domoticz/in + payload: |- + root["command"] = "udevice"; + root["idx"] = 512; + root["nvalue"] = id(valve_output).state ? 1 : 0; + + on_json_message: + - topic: domoticz/out + then: + - lambda: |- + int idx = x["idx"]; + int nvalue = x["nvalue"].as(); + + // ESP_LOGD("on_json_message", x["name"]); + switch (idx) { + case 512: /* Solar valve switch */ + if (nvalue) + id(valve_output).turn_on(); + else + id(valve_output).turn_off(); + } + + - topic: glow/BCDDC2C24DB0/SENSOR/electricitymeter + then: + - lambda: |- + auto cumulative = x["electricitymeter"]["energy"]["import"]["cumulative"]; + int kwh, watts; + if (cumulative) { + // { "command" : "udevice", "idx" : 29, "svalue" : $cumulative } + kwh = (cumulative.as() * 1000.0f) + 0.1f; + id(mqtt_client).publish_json("domoticz/in", [=](JsonObject root) { + root["command"] = "udevice"; + root["idx"] = 29; + root["svalue"] = std::to_string(kwh); + }); + } + auto power = x["electricitymeter"]["power"]["value"]; + if (power) { + watts = (power.as() * 1000.0f) + 0.1f; + // { "command" : "udevice", "idx" : 25, "svalue" : $power } + id(mqtt_client).publish_json("domoticz/in", [=](JsonObject root) { + root["command"] = "udevice"; + root["idx"] = 24; + root["svalue"] = std::to_string(watts); + }); + } + if (cumulative && power) { + id(mqtt_client).publish_json("domoticz/in", [=](JsonObject root) { + root["command"] = "udevice"; + root["idx"] = 514; + root["svalue"] = std::to_string(watts) + ";" + std::to_string(kwh); + }); + } + +dallasng: + - pin: GPIO13 + update_interval: 60s + +esp32_ble_tracker: #### Stop the active scan #### + scan_parameters: + active: false + continuous: true + on_ble_advertise: + - mac_address: + - C0:00:00:01:67:d7 + then: + - lambda: |- + ESP_LOGD("ble_adv", "New BLE device"); + ESP_LOGD("ble_adv", " address: %s", x.address_str().c_str()); + ESP_LOGD("ble_adv", " name: %s", x.get_name().c_str()); + ESP_LOGD("ble_adv", " Advertised service UUIDs:"); + for (auto uuid : x.get_service_uuids()) { + ESP_LOGD("ble_adv", " - %s", uuid.to_string().c_str()); + } + ESP_LOGD("ble_adv", " Advertised service data:"); + for (auto data : x.get_service_datas()) { + ESP_LOGD("ble_adv", " - %s: (length %i)", data.uuid.to_string().c_str(), data.data.size()); + } + ESP_LOGD("ble_adv", " Advertised manufacturer data:"); + for (auto data : x.get_manufacturer_datas()) { + ESP_LOGD("ble_adv", " - %s: (length %i)", data.uuid.to_string().c_str(), data.data.size()); + } + on_scan_end: + - lambda: |- + if (id(ble_yc01_ble_connected).state) { + ESP_LOGD("ble_yc01", "Scan complete, triggering update"); + id (ble_yc01_rssi).update(); + id (ble_yc01_sensor).update(); + } + +button: + - platform: template + name: "Start Scan" + on_press: + - esp32_ble_tracker.start_scan: + + - platform: template + name: "Stop Scan" + on_press: + - esp32_ble_tracker.stop_scan: + +light: + - platform: binary + name: "Green LED" + id: green_led + output: led_output + +output: + - id: led_output + platform: gpio + pin: GPIO33 + +binary_sensor: +- platform: status + name: "ESP32 Status" +- platform: template + id: ble_yc01_ble_connected + icon: mdi:bluetooth-connect + name: "BLE Connected" + +###################################################### +## ## +## To initiate to connection with the BLE device ## +## ## +###################################################### + +ble_client: + - mac_address: C0:00:00:01:67:d7 #Use the MAC address of your BLE device + id: ble_yc01 + on_connect: #### Actions to perform when connecting to the BLE device #### + then: + - lambda: |- + ESP_LOGD("ble_client_lambda", "Connected to BLE-YC01"); + id(ble_yc01_ble_connected).publish_state(true); + + on_disconnect: + then: + - lambda: |- + ESP_LOGD("ble_client", "Disconnected from BLE-YC01"); + id(ble_yc01_ble_connected).publish_state(false); + +###################################################### +## ## +## Sensors associated with the BLE device ## +## ## +###################################################### + +sensor: #### Template sensor as their values are publish from a lambda or the BLE client #### + - platform: dallasng + address: 0xd4030497940a0e28 + name: "Solar in" + id: solar_in + on_value: + then: + lambda: |- + id(solar_in_fails) = 0; + id(mqtt_client).publish_json("domoticz/in", [=](JsonObject root) { + root["command"] = "udevice"; + root["idx"] = 507; + root["svalue"] = std::to_string(x); + }); + id(control_valve)->execute(); + + - platform: dallasng + address: 0xa0031397941af528 + name: "Solar out" + id: solar_out + on_value: + then: + - lambda: |- + id(solar_out_fails) = 0; + id(mqtt_client).publish_json("domoticz/in", [=](JsonObject root) { + root["command"] = "udevice"; + root["idx"] = 508; + root["svalue"] = std::to_string(x); + }); + id(control_valve)->execute(); + +# - platform: dallasng +# address: 0xc603156332b8ff28 +# name: "Temp 3" +# id: temp3 + +# - platform: dallasng +# address: 0x040115636a80ff28 +# name: "Temp 4" +# id: temp4 + + - platform: template + name: "BLE-YC01 EC" + id: ble_yc01_ec_sensor + unit_of_measurement: "µS/cm" + accuracy_decimals: 0 + state_class: measurement + icon: mdi:water-opacity + + - platform: template + name: "BLE-YC01 TDS" + id: ble_yc01_tds_sensor + unit_of_measurement: "ppm" + accuracy_decimals: 0 + state_class: measurement + icon: mdi:water-opacity + + - platform: template + name: "BLE-YC01 Temperature" + id: ble_yc01_temperature_sensor + unit_of_measurement: "°C" + accuracy_decimals: 2 + state_class: measurement + device_class: temperature + on_value: + then: + lambda: |- + id(mqtt_client).publish_json("domoticz/in", [=](JsonObject root) { + root["command"] = "udevice"; + root["idx"] = 510; + root["svalue"] = std::to_string(x); + }); + + - platform: template + name: "BLE-YC01 ORP" + id: ble_yc01_orp_sensor + unit_of_measurement: "mV" + accuracy_decimals: 0 + state_class: measurement + device_class: voltage + + - platform: template + name: "BLE-YC01 pH" + id: ble_yc01_ph_sensor + unit_of_measurement: "pH" + accuracy_decimals: 2 + state_class: measurement + icon: mdi:ph + on_value: + then: + lambda: |- + id(mqtt_client).publish_json("domoticz/in", [=](JsonObject root) { + root["command"] = "udevice"; + root["idx"] = 511; + root["svalue"] = std::to_string(x); + }); + + - platform: template + name: "BLE-YC01 battery" + id: ble_yc01_battery + unit_of_measurement: "%" + accuracy_decimals: 0 + state_class: measurement + device_class: battery + icon: mdi:battery + + - platform: template + name: "BLE-YC01 CL" + id: ble_yc01_cloro + unit_of_measurement: "ppm" + accuracy_decimals: 1 + state_class: measurement + icon: mdi:water-opacity + on_value: + then: + lambda: |- + id(mqtt_client).publish_json("domoticz/in", [=](JsonObject root) { + root["command"] = "udevice"; + root["idx"] = 509; + root["svalue"] = std::to_string(x); + }); + + - platform: ble_client + type: rssi + id: ble_yc01_rssi + ble_client_id: ble_yc01 + update_interval: never + name: "BLE-YC01 RSSI" + + - platform: ble_client #### Sensor required to manage values coming from the BLE device #### + ble_client_id: ble_yc01 + type: characteristic + id: ble_yc01_sensor + update_interval: never + internal: true + service_uuid: FF01 + characteristic_uuid: FF02 + #### Lambda to decode values and push to the associated sensors #### + lambda: |- + + if (x.size() == 0) return NAN; + + std::string rawmsg; + for (int i = 0; i < x.size(); i++) { + char buf[7]; + snprintf(buf, 7, " 0x%02x", (unsigned char) x[i]); + rawmsg = rawmsg + buf; + } + ESP_LOGD("ble_client.receive", "raw value received with %d bytes: [%s]", x.size(), rawmsg.c_str()); // #### Useful for debugging #### + + // ### DECODING ### + uint8_t tmp = 0; + uint8_t hibit = 0; + uint8_t lobit = 0; + uint8_t hibit1 = 0; + uint8_t lobit1 = 0; + auto message = x; + + for (int i = x.size() -1 ; i > 0; i--) { + tmp=message[i]; + hibit1=(tmp&0x55)<<1; + lobit1=(tmp&0xAA)>>1; + tmp=message[i-1]; + hibit=(tmp&0x55)<<1; + lobit=(tmp&0xAA)>>1; + + message[i]=~(hibit1|lobit); + message[i-1]=~(hibit|lobit1); + + } + + rawmsg = ""; + for (int i = 0; i < message.size(); i++) { + char buf[7]; + snprintf(buf, 7, " 0x%02x", (unsigned char) message[i]); + rawmsg = rawmsg + buf; + } + ESP_LOGD("ble_client.receive", "value received with %d bytes: [%s]", message.size(), rawmsg.c_str()); // #### For debug #### + + // #### Extraction of individual values #### + auto temp = ((message[13]<<8) + message[14]); + auto ph = ((message[3]<<8) + message[4]); + auto orp = ((message[20]<<8) + message[21]); + auto battery = ((message[15]<<8) + message[16]); + auto ec = ((message[5]<<8) + message[6]); + auto tds = ((message[7]<<8) + message[8]); + auto cloro = ((message[11]<<8) + message[12]); + + // #### Sensors updated with new values + id(ble_yc01_temperature_sensor).publish_state(temp/10.0); + id(ble_yc01_ph_sensor).publish_state(ph/100.0); + id(ble_yc01_orp_sensor).publish_state(orp); + id(ble_yc01_battery).publish_state(battery/31.9); + id(ble_yc01_ec_sensor).publish_state(ec); + id(ble_yc01_tds_sensor).publish_state(tds); + if (cloro == 65535) + id(ble_yc01_cloro).publish_state(NAN); + else + id(ble_yc01_cloro).publish_state(cloro/10.0); + + // Once we have a single reading, turn off until the next attempt + id(ble_switch).turn_off(); + return NAN; // this sensor isn't actually used other than to hook into raw value and publish to template sensors + + +switch: #### To switch on and off the communication with the BLE device #### + - platform: ble_client + id: ble_switch + ble_client_id: ble_yc01 + name: "Enable BLE-YC01" + restore_mode: ALWAYS_ON + on_turn_on: + - esp32_ble_tracker.start_scan: + continuous: true + on_turn_off: + - esp32_ble_tracker.stop_scan: + + + - platform: gpio + id: valve_output + name: "Valve switch" + pin: GPIO14 + restore_mode: ALWAYS_ON + on_turn_on: + then: + - light.turn_on: green_led + - mqtt.publish_json: + topic: domoticz/in + payload: |- + root["command"] = "udevice"; + root["idx"] = 512; + root["nvalue"] = 1; + on_turn_off: + then: + - light.turn_off: green_led + - mqtt.publish_json: + topic: domoticz/in + payload: |- + root["command"] = "udevice"; + root["idx"] = 512; + root["nvalue"] = 0; + + - platform: gpio + id: power_1w + name: "Power 1Wire" + pin: + number: GPIO16 + inverted: true + restore_mode: ALWAYS_ON + on_turn_on: + - lambda: |- + id(solar_in_fails) = 0; + id(solar_out_fails) = 0; -- 2.49.0