diff --git a/.gitignore b/.gitignore index d8b4157aef93a9ac611766077b969c9f87628941..d8d70fd0416e991747f956c796f092a133d6dd8d 100644 --- a/.gitignore +++ b/.gitignore @@ -2,4 +2,4 @@ # This is an example and may include too much for your use-case. # You can modify this file to suit your needs. /.esphome/ -/secrets.yaml +**/secrets.yaml diff --git a/README.md b/README.md index b732f9c54ab07b39f6718f9fe8673bc039240ddf..acae40ede7ffee488705eb87ac0c5b066716977b 100644 --- a/README.md +++ b/README.md @@ -16,4 +16,4 @@ Home Grow(n) Monitoring * MLX90614-DCI IR Temperature Sensor (3.3V, I2C) ## Configuration -Copy `secrets.yaml.dist` to `secrets.yaml` and adjust to your preferences. +Copy `packages/secrets.yaml.dist` to `packages/secrets.yaml` and adjust to your preferences. diff --git a/growbox.yaml b/growbox.yaml index bfb2d0c2299852b52d04f39c69551a4179244b50..91bd3947770c10072f267a6c165a72da99fa82ed 100644 --- a/growbox.yaml +++ b/growbox.yaml @@ -2,90 +2,9 @@ substitutions: devicename: "growbox" comment: "Grow Tent Monitor" -esphome: - includes: - - include/yieryi_3178.h - packages: - device_base: !include base.yaml - -esp32: - board: esp32dev - -time: - - platform: sntp - id: sntp_time - timezone: ${timezone} - update_interval: 1h - -prometheus: - include_internal: true - relabel: - rssi: - id: rssi - name: RSSI - device_uptime: - id: uptime - name: Uptime - # temperature: - # id: temperature - # name: Temperature - # humidity: - # id: humidity - # name: Humidity - # pressure: - # id: pressure - # name: Pressure - -i2c: - sda: 21 - scl: 22 - -uart: - id: uart_rs485 - tx_pin: 19 # TXD - rx_pin: 18 # RXD - baud_rate: 9600 - -sensor: -# - platform: bme280 -# address: 0x76 -# update_interval: 5s -# temperature: -# id: temperature -# name: "Air: Temperature" -# accuracy_decimals: 2 -# humidity: -# id: humidity -# name: "Air: Humidity" -# accuracy_decimals: 2 -# pressure: -# id: pressure -# name: "Air: Pressure" -# accuracy_decimals: 2 - - platform: custom - lambda: |- - auto yieryi_3178 = new Yieryi3178(id(uart_rs485), 5000); - App.register_component(yieryi_3178); - return {yieryi_3178->ec, yieryi_3178->ph, yieryi_3178->rh, yieryi_3178->temp, yieryi_3178->bat}; - sensors: - - id: water_ec - name: "Water: EC" - unit_of_measurement: "mS" - accuracy_decimals: 3 - - id: water_ph - name: "Water: pH" - unit_of_measurement: "pH" - accuracy_decimals: 2 - - id: water_rh - name: "Water: RH" - unit_of_measurement: "%" - accuracy_decimals: 0 - - id: water_temp - name: "Water: Temperature" - unit_of_measurement: "°C" - accuracy_decimals: 1 - - id: water_bat - name: "Water: Battery" - unit_of_measurement: "%" - accuracy_decimals: 1 + base: !include packages/base.yaml + esp32: !include packages/esp32.yaml + time: !include packages/time.yaml + bme280: !include packages/bme280.yaml + yieryi2178: !include packages/yieryi3178.yaml diff --git a/base.yaml b/packages/base.yaml similarity index 81% rename from base.yaml rename to packages/base.yaml index a381935b7cdcf4fc85794c747db2e379414869bc..4529a40cc6a520be004e1fe9d33cd3feff04c281 100644 --- a/base.yaml +++ b/packages/base.yaml @@ -13,6 +13,7 @@ esphome: substitutions: domain: !secret domain timezone: !secret timezone + bme280_script: "dummy" preferences: flash_write_interval: 0s @@ -43,6 +44,16 @@ web_server: # local: true # broken: https://github.com/esphome/issues/issues/3720 # include_internal: true +prometheus: + include_internal: true + relabel: + rssi: + id: rssi + name: RSSI + device_uptime: + id: uptime + name: Uptime + sensor: - platform: wifi_signal id: rssi @@ -77,3 +88,14 @@ text_sensor: button: - platform: restart name: "Device Restart" + +http_request: + id: http_request_data + useragent: esphome/${devicename} + timeout: 10s + +script: + - id: dummy + then: + - lambda: |- + return; diff --git a/packages/bme280.yaml b/packages/bme280.yaml new file mode 100644 index 0000000000000000000000000000000000000000..049972794336bf262902c36782ed7caf226b661f --- /dev/null +++ b/packages/bme280.yaml @@ -0,0 +1,36 @@ +sensor: +- platform: bme280 + address: 0x76 + update_interval: 5s + temperature: + id: bme280_temperature + name: "Air: Temperature" + accuracy_decimals: 2 + on_value: + then: + - lambda: |- + id(${bme280_script}).execute(); + humidity: + id: bme280_humidity + name: "Air: Humidity" + accuracy_decimals: 2 + on_value: + then: + - lambda: |- + id(${bme280_script}).execute(); + pressure: + id: bme280_pressure + name: "Air: Pressure" + accuracy_decimals: 2 + +prometheus: + relabel: + bme280_temperature: + id: temperature + name: Temperature + bme280_humidity: + id: humidity + name: Humidity + bme280_pressure: + id: pressure + name: Pressure diff --git a/packages/ds18b20.yaml b/packages/ds18b20.yaml new file mode 100644 index 0000000000000000000000000000000000000000..8e91ff62ec08c66ef3fa260e43e40ee02cd6c172 --- /dev/null +++ b/packages/ds18b20.yaml @@ -0,0 +1,10 @@ +dallas: + - pin: GPIO23 + update_interval: 5s + +sensor: +- platform: dallas + index: 0 + id: temperature_water + name: "Water: Temperature" + accuracy_decimals: 2 diff --git a/packages/esp32.yaml b/packages/esp32.yaml new file mode 100644 index 0000000000000000000000000000000000000000..4277e277924b0347e5a143304d5f431a001d3ce6 --- /dev/null +++ b/packages/esp32.yaml @@ -0,0 +1,6 @@ +esp32: + board: esp32dev + +i2c: + sda: 21 + scl: 22 diff --git a/packages/fan.yaml b/packages/fan.yaml new file mode 100644 index 0000000000000000000000000000000000000000..27ceff79a645d18beea4d73369c079bcd803c3ad --- /dev/null +++ b/packages/fan.yaml @@ -0,0 +1,127 @@ +prometheus: + relabel: + propbox_fan: + id: fan + name: Fan + +sensor: + - platform: template + name: "temperature_max" + lambda: return atoi(id(temperature_max).state.c_str()); + internal: true + - platform: template + name: "temperature_hysteresis" + lambda: return atoi(id(temperature_hysteresis).state.c_str()); + internal: true + - platform: template + name: "temperature_min" + lambda: return atoi(id(temperature_min).state.c_str()); + internal: true + - platform: template + name: "humidity_max" + lambda: return atoi(id(humidity_max).state.c_str()); + internal: true + - platform: template + name: "humidity_hysteresis" + lambda: return atoi(id(humidity_hysteresis).state.c_str()); + internal: true + - platform: template + name: "humidity_min" + lambda: return atoi(id(humidity_min).state.c_str()); + internal: true + +select: + - platform: template + id: temperature_max + name: "Fan: Temperature On" + options: ["21", "22", "23", "24", "25", "26", "27", "28", "29", "30"] + initial_option: "27" + optimistic: true + restore_value: true + - platform: template + id: temperature_hysteresis + name: "Fan: Temperature Hysteresis" + options: ["1", "2", "3", "4", "5"] + initial_option: "1" + optimistic: true + restore_value: true + - platform: template + id: temperature_min + name: "Fan: Temperature Off" + options: ["19", "20", "21", "22", "23", "24", "25"] + initial_option: "21" + optimistic: true + restore_value: true + - platform: template + id: humidity_max + name: "Fan: Humidity On" + options: ["40", "45", "50", "55", "60", "65", "70", "75", "80"] + initial_option: "70" + optimistic: true + restore_value: true + - platform: template + id: humidity_hysteresis + name: "Fan: Humidity Hysteresis" + options: ["1", "2", "3", "4", "5", "10", "15"] + initial_option: "5" + optimistic: true + restore_value: true + - platform: template + id: humidity_min + name: "Fan: Humidity Off" + options: ["20", "25", "30", "35", "40", "45", "50"] + initial_option: "40" + optimistic: true + restore_value: true + +switch: + - platform: template + id: propbox_fan + name: "Status: Fan" + optimistic: true + turn_on_action: + - http_request.post: http://${hostname_fan}/switch/power/turn_on + turn_off_action: + - http_request.post: http://${hostname_fan}/switch/power/turn_off + +interval: + - interval: 1min + then: + - script.execute: fan_state + + +script: + - id: fan_control + then: + - lambda: |- + // switch fan on when temperature/humidity is above max, unless humidity/temperature is below min + if ( + !id(propbox_fan).state && + ((id(bme280_temperature).state >= atoi(id(temperature_max).state.c_str()) && id(bme280_humidity).state >= atoi(id(humidity_min).state.c_str())) || + ( id(bme280_humidity).state >= atoi(id(humidity_max).state.c_str()) && id(bme280_temperature).state >= atoi(id(temperature_min).state.c_str()))) + ) { + id(propbox_fan).turn_on(); + } + + // switch fan off when temperature and humidity are below max minus hysteresis + else if ( + id(propbox_fan).state && + id(bme280_temperature).state <= atoi(id(temperature_max).state.c_str()) - atoi(id(temperature_hysteresis).state.c_str()) && + id(bme280_humidity).state <= atoi(id(humidity_max).state.c_str()) - atoi(id(humidity_hysteresis).state.c_str()) + ) { + id(propbox_fan).turn_off(); + } + - id: fan_state + then: + - lambda: |- + HTTPClient http; + bool state = false; + http.begin("http://${hostname_fan}/switch/power"); + if (http.GET() == 200) { + DynamicJsonDocument doc(200); + deserializeJson(doc, http.getString()); + state = (doc["state"] == "ON") ? true : false; + } + id(propbox_fan).publish_state(state); + http.end(); + \ No newline at end of file diff --git a/packages/light.yaml b/packages/light.yaml new file mode 100644 index 0000000000000000000000000000000000000000..a0204a0bb8d863e3aeb6413505b46a2d6937d159 --- /dev/null +++ b/packages/light.yaml @@ -0,0 +1,61 @@ +substitutions: + # Schedules + #schedule_grow_on: "0 0 3 * * *" + #schedule_grow_off: "0 0 21 * * *" + #schedule_bloom_on: "0 0 6 * * *" + #schedule_bloom_off: "0 0 18 * * *" + schedule_grow_on: "5,15,25,35,45,55 * * * * *" + schedule_grow_off: "0,10,20,30,40,50 * * * * *" + schedule_bloom_on: "0 * * * * *" + schedule_bloom_off: "30 * * * * *" + +prometheus: + relabel: + propbox_light: + id: light + name: Light + +sensor: + - platform: template + name: "schedule" + lambda: return id(schedule).active_index(); + internal: true + +select: + - platform: template + id: schedule + name: "Light: Schedule" + options: ["Disabled", "Grow", "Bloom"] + initial_option: "Disabled" + optimistic: true + restore_value: true + +switch: + - platform: template + id: propbox_light + name: "Status: Light" + optimistic: true + turn_on_action: + - http_request.post: http://${hostname_light}/switch/power/turn_on + turn_off_action: + - http_request.post: http://${hostname_light}/switch/power/turn_off + +interval: + - interval: 1min + then: + - script.execute: light_state + +script: + - id: light_state + then: + - lambda: |- + HTTPClient http; + bool state = false; + http.begin("http://${hostname_light}/switch/power"); + if (http.GET() == 200) { + DynamicJsonDocument doc(200); + deserializeJson(doc, http.getString()); + state = (doc["state"] == "ON") ? true : false; + } + id(propbox_light).publish_state(state); + http.end(); diff --git a/include/nous_a1t.yaml b/packages/nous_a1t.yaml similarity index 91% rename from include/nous_a1t.yaml rename to packages/nous_a1t.yaml index ec9ad7796ab85f4995a58a0fc9be85ce317a2f81..d1743b657445734b486079b3f06ec808795acbf3 100644 --- a/include/nous_a1t.yaml +++ b/packages/nous_a1t.yaml @@ -1,12 +1,10 @@ +substitutions: + current_res: "0.00280" # Higher value gives lower watt readout + voltage_div: "775" # Lower value gives lower voltage readout + esp8266: board: esp8285 -time: - - platform: sntp - id: sntp_time - timezone: ${timezone} - update_interval: 1h - light: - platform: status_led id: led_status diff --git a/secrets.yaml.dist b/packages/secrets.yaml.dist similarity index 100% rename from secrets.yaml.dist rename to packages/secrets.yaml.dist diff --git a/packages/time.yaml b/packages/time.yaml new file mode 100644 index 0000000000000000000000000000000000000000..a4f830e546884d7cd5adea5519ab96fb324f17d8 --- /dev/null +++ b/packages/time.yaml @@ -0,0 +1,5 @@ +time: + - platform: sntp + id: sntp_time + timezone: ${timezone} + update_interval: 1h diff --git a/packages/yieryi3178.yaml b/packages/yieryi3178.yaml new file mode 100644 index 0000000000000000000000000000000000000000..f5f0cd04d1c90ac0de54c8f560d12032451bc39d --- /dev/null +++ b/packages/yieryi3178.yaml @@ -0,0 +1,55 @@ +esphome: + includes: + - include/yieryi_3178.h + +uart: + id: uart_rs485 + tx_pin: 19 # TXD + rx_pin: 18 # RXD + baud_rate: 9600 + +sensor: +- platform: custom + lambda: |- + auto yieryi3178 = new Yieryi3178(id(uart_rs485), 5000); + App.register_component(yieryi3178); + return {yieryi3178->ec, yieryi3178->ph, yieryi3178->rh, yieryi3178->temp, yieryi3178->bat}; + sensors: + - id: yieryi3178_ec + name: "Water: EC" + unit_of_measurement: "mS" + accuracy_decimals: 3 + - id: yieryi3178_ph + name: "Water: pH" + unit_of_measurement: "pH" + accuracy_decimals: 2 + - id: yieryi3178_humidity + name: "Water: Humidity" + unit_of_measurement: "%" + accuracy_decimals: 0 + - id: yieryi3178_temperature + name: "Water: Temperature" + unit_of_measurement: "°C" + accuracy_decimals: 1 + - id: yieryi3178_battery + name: "Water: Battery Level" + unit_of_measurement: "%" + accuracy_decimals: 1 + +prometheus: + relabel: + yieryi3178_ec: + id: water_ec + name: "EC" + yieryi3178_ph: + id: water_ph + name: "pH" + yieryi3178_humidity: + id: water_humidity + name: "Humidity (EC/pH Monitor)" + yieryi3178_temperature: + id: water_temperature + name: "Water Temperature" + yieryi3178_battery: + id: water_battery + name: "Battery Level" diff --git a/propbox-fan.yaml b/propbox-fan.yaml index 65c1d7a36f5bea000935a060160d3467f3650607..3d8cc132a7dbb1355627a94ce1df195f2d5db4b5 100644 --- a/propbox-fan.yaml +++ b/propbox-fan.yaml @@ -1,14 +1,8 @@ substitutions: devicename: "propbox-fan" comment: "Propagator Fan Control" - - current_res: "0.00280" # Higher value gives lower watt readout - voltage_div: "775" # Lower value gives lower voltage readout packages: - device_base: !include base.yaml - -prometheus: - include_internal: true - -<<: !include include/nous_a1t.yaml + base: !include packages/base.yaml + time: !include packages/time.yaml + nous_a1t: !include packages/nous_a1t.yaml diff --git a/propbox-light.yaml b/propbox-light.yaml index 121a904da7cb2c9b9a74c9b91e1ee9a79333897c..fd76f53793d898aa498c67d1509dadbefc4021de 100644 --- a/propbox-light.yaml +++ b/propbox-light.yaml @@ -1,14 +1,8 @@ substitutions: devicename: "propbox-light" comment: "Propagator Light Control" - - current_res: "0.00280" # Higher value gives lower watt readout - voltage_div: "775" # Lower value gives lower voltage readout packages: - device_base: !include base.yaml - -prometheus: - include_internal: true - -<<: !include include/nous_a1t.yaml + base: !include packages/base.yaml + time: !include packages/time.yaml + nous_a1t: !include packages/nous_a1t.yaml diff --git a/propbox.yaml b/propbox.yaml index a90679ad0ffd435b7919420f3a73c646a4e1f611..2db9a5252d0ba3c89ef3917dfa00ec7bec4fd866 100644 --- a/propbox.yaml +++ b/propbox.yaml @@ -1,203 +1,22 @@ substitutions: devicename: "propbox" - comment: "Propagator Monitor" + comment: "Propagator Controller" # Propbox Nodes hostname_light: "propbox-light.${domain}" hostname_fan: "propbox-fan.${domain}" - # Schedules - #schedule_grow_on: "0 0 3 * * *" - #schedule_grow_off: "0 0 21 * * *" - #schedule_bloom_on: "0 0 6 * * *" - #schedule_bloom_off: "0 0 18 * * *" - schedule_grow_on: "5,15,25,35,45,55 * * * * *" - schedule_grow_off: "0,10,20,30,40,50 * * * * *" - schedule_bloom_on: "0 * * * * *" - schedule_bloom_off: "30 * * * * *" + # Overrides + bme280_script: "fan_control" packages: - device_base: !include base.yaml + base: !include packages/base.yaml + esp32: !include packages/esp32.yaml + bme280: !include packages/bme280.yaml + ds18b20: !include packages/ds18b20.yaml + fan: !include packages/fan.yaml + light: !include packages/light.yaml -esp32: - board: esp32dev - -i2c: - sda: 21 - scl: 22 - -dallas: - - pin: GPIO23 - update_interval: 5s - -http_request: - id: http_request_data - useragent: esphome/${devicename} - timeout: 10s - -prometheus: - include_internal: true - relabel: - rssi: - id: rssi - name: RSSI - device_uptime: - id: uptime - name: Uptime - propbox_light: - id: light - name: Light - propbox_fan: - id: fan - name: Fan - temperature: - id: temperature - name: Temperature - humidity: - id: humidity - name: Humidity - pressure: - id: pressure - name: Pressure - temperature_water: - id: temperature_water - name: "Temperature Water" - -sensor: - - platform: bme280 - address: 0x76 - update_interval: 5s - temperature: - id: temperature - name: "Sensor: Temperature" - accuracy_decimals: 2 - on_value: - then: - - script.execute: fan_control - humidity: - id: humidity - name: "Sensor: Humidity" - accuracy_decimals: 2 - on_value: - then: - - script.execute: fan_control - pressure: - id: pressure - name: "Sensor: Pressure" - accuracy_decimals: 2 - - - platform: dallas - index: 0 - id: temperature_water - name: "Sensor: Water Temperature" - accuracy_decimals: 2 - - - platform: template - name: "schedule" - lambda: return id(schedule).active_index(); - internal: true - - platform: template - name: "temperature_max" - lambda: return atoi(id(temperature_max).state.c_str()); - internal: true - - platform: template - name: "temperature_hysteresis" - lambda: return atoi(id(temperature_hysteresis).state.c_str()); - internal: true - - platform: template - name: "temperature_min" - lambda: return atoi(id(temperature_min).state.c_str()); - internal: true - - platform: template - name: "humidity_max" - lambda: return atoi(id(humidity_max).state.c_str()); - internal: true - - platform: template - name: "humidity_hysteresis" - lambda: return atoi(id(humidity_hysteresis).state.c_str()); - internal: true - - platform: template - name: "humidity_min" - lambda: return atoi(id(humidity_min).state.c_str()); - internal: true - -select: - - platform: template - id: schedule - name: "Light: Schedule" - options: ["Disabled", "Grow", "Bloom"] - initial_option: "Disabled" - optimistic: true - restore_value: true - - platform: template - id: temperature_max - name: "Fan: Temperature On" - options: ["21", "22", "23", "24", "25", "26", "27", "28", "29", "30"] - initial_option: "27" - optimistic: true - restore_value: true - - platform: template - id: temperature_hysteresis - name: "Fan: Temperature Hysteresis" - options: ["1", "2", "3", "4", "5"] - initial_option: "1" - optimistic: true - restore_value: true - - platform: template - id: temperature_min - name: "Fan: Temperature Off" - options: ["19", "20", "21", "22", "23", "24", "25"] - initial_option: "21" - optimistic: true - restore_value: true - - platform: template - id: humidity_max - name: "Fan: Humidity On" - options: ["40", "45", "50", "55", "60", "65", "70", "75", "80"] - initial_option: "70" - optimistic: true - restore_value: true - - platform: template - id: humidity_hysteresis - name: "Fan: Humidity Hysteresis" - options: ["1", "2", "3", "4", "5", "10", "15"] - initial_option: "5" - optimistic: true - restore_value: true - - platform: template - id: humidity_min - name: "Fan: Humidity Off" - options: ["20", "25", "30", "35", "40", "45", "50"] - initial_option: "40" - optimistic: true - restore_value: true - -switch: - - platform: template - id: propbox_light - name: "Status: Light" - optimistic: true - turn_on_action: - - http_request.post: http://${hostname_light}/switch/power/turn_on - turn_off_action: - - http_request.post: http://${hostname_light}/switch/power/turn_off - - platform: template - id: propbox_fan - name: "Status: Fan" - optimistic: true - turn_on_action: - - http_request.post: http://${hostname_fan}/switch/power/turn_on - turn_off_action: - - http_request.post: http://${hostname_fan}/switch/power/turn_off - -interval: - - interval: 1min - then: - - script.execute: propbox_light_state - - interval: 1min - then: - - script.execute: propbox_fan_state - time: - platform: sntp id: sntp_time @@ -229,51 +48,3 @@ time: id(propbox_light).turn_off(); } -script: - - id: fan_control - then: - - lambda: |- - // switch fan on when temperature/humidity is above max, unless humidity/temperature is below min - if ( - !id(propbox_fan).state && - ((id(temperature).state >= atoi(id(temperature_max).state.c_str()) && id(humidity).state >= atoi(id(humidity_min).state.c_str())) || - ( id(humidity).state >= atoi(id(humidity_max).state.c_str()) && id(temperature).state >= atoi(id(temperature_min).state.c_str()))) - ) { - id(propbox_fan).turn_on(); - } - - // switch fan off when temperature and humidity are below max minus hysteresis - else if ( - id(propbox_fan).state && - id(temperature).state <= atoi(id(temperature_max).state.c_str()) - atoi(id(temperature_hysteresis).state.c_str()) && - id(humidity).state <= atoi(id(humidity_max).state.c_str()) - atoi(id(humidity_hysteresis).state.c_str()) - ) { - id(propbox_fan).turn_off(); - } - - id: propbox_light_state - then: - - lambda: |- - HTTPClient http; - bool state = false; - http.begin("http://${hostname_light}/switch/power"); - if (http.GET() == 200) { - DynamicJsonDocument doc(200); - deserializeJson(doc, http.getString()); - state = (doc["state"] == "ON") ? true : false; - } - id(propbox_light).publish_state(state); - http.end(); - - id: propbox_fan_state - then: - - lambda: |- - HTTPClient http; - bool state = false; - http.begin("http://${hostname_fan}/switch/power"); - if (http.GET() == 200) { - DynamicJsonDocument doc(200); - deserializeJson(doc, http.getString()); - state = (doc["state"] == "ON") ? true : false; - } - id(propbox_fan).publish_state(state); - http.end(); - \ No newline at end of file