├── LICENSE ├── README.md ├── configuration.yaml ├── custom_components └── atlas_scientific │ ├── __init__.py │ ├── example.yaml │ ├── manifest.json │ └── sensor.py ├── help └── media │ ├── Raspipool_Firefox.gif │ ├── raspipool_ORP_FC_pH.png │ ├── raspipool_bypass.png │ ├── raspipool_chem.png │ ├── raspipool_history.png │ ├── raspipool_main.png │ ├── raspipool_main2.png │ ├── raspipool_notifications.png │ ├── raspipool_overview.png │ ├── raspipool_relay_connections.png │ ├── raspipool_relay_connections_v3.png │ └── raspipool_sensors_connections.png ├── packages └── raspipool │ ├── bleach.yaml │ ├── exterior.yaml │ ├── fc.yaml │ ├── flow.yaml │ ├── muriatic.yaml │ ├── notifications.yaml │ ├── orp.yaml │ ├── ph.yaml │ ├── pump.yaml │ ├── recorder.yaml │ ├── settings.yaml │ ├── switches.yaml │ ├── system.yaml │ ├── temp.yaml │ └── themes.yaml └── ui-lovelace.yaml /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 segalion 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # raspipool 2 | **Swimming-Pool Automation Systen with Raspberry Pi + Home Assistant** 3 | 4 | 5 | 6 | ## Overview: 7 | 8 | A cost-effective, easy-to-build, easy-to-use "Swimming-Pool Automation System" with top functions to automate, control and monitorize (from web) small-medium size swimming pools. 9 | 10 | - Automatic filter control (fixed and dual-speed pumps, 1 or 2 daily cycles) based on temperature. 11 | - 3 sensors: 12 | - water temperature (one-wire [DS18B20 waterprof](https://aliexpress.com/item/32968031204.html)) 13 | - [ph](https://www.atlas-scientific.com/product_pages/circuits/ezo_ph.html) and [orp](https://www.atlas-scientific.com/product_pages/circuits/ezo_orp.html) with [EZO circuits](https://www.atlas-scientific.com/product_pages/components/ezo-carrier-board.html). A custom UART sensor for HA has been developed. 14 | - and minimun of [4 relays](https://aliexpress.com/item/32961638909.html) [or 6](https://aliexpress.com/item/32997012084.html) controlling : 15 | - pump on/off and pump speed (high/low) 16 | - muriatic acid injection (to regulate pH) and bleach injection (to mantain sanitization level) 17 | 18 | System is intended to monitoring and automagically control most important functions and notify to mobile all possible events. 19 | 20 | ## Build system: 21 | 22 | Follow instructions in wiki [howto build a bypass to connect sensors to the pool](https://github.com/segalion/raspipool/wiki/Bypass-for-sensors), [howto connect sensors to the raspberry pi](https://github.com/segalion/raspipool/wiki/Sensors-connection-(DS18B20,-and-EZO-pH-and-ORP)) and [howto connect relays between pumps and raspberry pi](https://github.com/segalion/raspipool/wiki/Connection-of-relays-for-pump-control) 23 | 24 | ## Install 25 | 0. Install [hassbian](https://www.home-assistant.io/docs/installation/hassbian/installation/) in a raspberry pi (3 or 4), and give wifi connection. (If advanced user, you can instead install [raspbian](https://myhydropi.com/raspberry-pi-initial-setup) + [Home Assistant with this prefered method](https://www.home-assistant.io/docs/installation/raspberry-pi/)) 26 | 1. Copy 'custom_components', 'packages' folders (with all paths and contents) and 'ui-lovelace.yaml' frontend file in homeassistant conf_dir ( i.e. /home/homeassistant/.homeassistant/ ). 27 | 2. Modify your 'configuration.yaml' (including ' packages: !include_dir_named packages', disabling automations, scripts and groups, discovery and lovelace in yaml mode) as example in code 28 | 3. Create/modify proper 'secrets.yaml' for apis (latitude/longitude, pushbullet api, openweathermap api, etc). 29 | 30 | 31 | ## TODO: 32 | - Correction of FC-ORP based on CYA (actually only linear correction) 33 | - SWC – Salt Water Chlorinator (instead of bleach injections) 34 | - Control Variable Speed Motor based on 3 digital inputs (0 to 7 speeds) 35 | - Integrate [mega-io board](https://www.sequentmicrosystems.com/megaio.html) (relays and ACD with i2c control) instead of actual gpio-relay HATs 36 | 37 | ## Optional 38 | For pumps <= 1.5 HP, a external sensor to measure power consumption and [safe motor](https://en.wikipedia.org/wiki/Magnetic_starter) (based on sonoff POW) 39 | 40 | Thanks to Hidromaster, Piscidoc, and all DIY enthusiasts from [hablemosdepisicnas](http://www.hablemosdepiscinas.com/foro/viewtopic.php?f=11&t=3906) and [TFP](https://www.troublefreepool.com/threads/raspipool-pool-automation-system-with-raspberry-pi-home-assistant.188410/) forums. 41 | -------------------------------------------------------------------------------- /configuration.yaml: -------------------------------------------------------------------------------- 1 | homeassistant: 2 | name: RaspiPool 3 | # Location required to calculate the time the sun rises and sets 4 | latitude: !secret latitude 5 | longitude: !secret longitude 6 | unit_system: metric 7 | customize: !include customize.yaml 8 | packages: !include_dir_named packages 9 | default_config: 10 | # Disable Discover some devices automatically 11 | # discovery: 12 | tts: 13 | - platform: google_translate 14 | logger: 15 | default: critical 16 | logs: 17 | homeassistant.components.sensor.atlas_scientific: debug 18 | homeassistant.components.switch.command_line: debug 19 | 20 | group: !include groups.yaml 21 | # Disable automation 22 | # automation: !include automations.yaml 23 | script: !include scripts.yaml 24 | lovelace: 25 | mode: yaml 26 | -------------------------------------------------------------------------------- /custom_components/atlas_scientific/__init__.py: -------------------------------------------------------------------------------- 1 | """Atlas Scientific""" 2 | import logging 3 | 4 | # The domain of your component. Should be equal to the name of your component. 5 | DOMAIN = "atlas_scientific" 6 | _LOGGER = logging.getLogger(__name__) 7 | 8 | def setup(hass, config): 9 | """Setup the service example component.""" 10 | def compensate_temperature(call): 11 | """My first service.""" 12 | _LOGGER.info('Compensate temperature to ...', call.data) 13 | # _LOGGER.info('Compensate temperature to ...', call.data.get('temperature')) 14 | 15 | # Register our service with Home Assistant. 16 | hass.services.register(DOMAIN, 'compensate_temp', compensate_temperature) 17 | 18 | # Return boolean to indicate that initialization was successfully. 19 | return True 20 | -------------------------------------------------------------------------------- /custom_components/atlas_scientific/example.yaml: -------------------------------------------------------------------------------- 1 | sensor: 2 | - platform: atlas_scientific 3 | port: /dev/ttyAMA0 4 | # port: /dev/ttyUSB0 5 | scan_interval: 300 6 | 7 | -------------------------------------------------------------------------------- /custom_components/atlas_scientific/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "domain": "atlas_scientific", 3 | "name": "Atlas Scientific", 4 | "documentation": "https://github.com/home-assistant/example-custom-config/tree/master/custom_components/example_sensor/", 5 | "dependencies": [], 6 | "codeowners": [], 7 | "requirements": ["pyserial"] 8 | } 9 | -------------------------------------------------------------------------------- /custom_components/atlas_scientific/sensor.py: -------------------------------------------------------------------------------- 1 | # Under MIT licence 2 | # Release 0.1 (06/08/2019) by segalion at gmail 3 | # ORP & pH tested. DO & EC from datasheets, so possible errors like ORP/OR 4 | 5 | 6 | import logging 7 | import serial 8 | from homeassistant.helpers.entity import Entity 9 | from homeassistant.const import ( 10 | CONF_NAME, CONF_UNIT_OF_MEASUREMENT, CONF_PORT) 11 | from homeassistant.components.sensor import PLATFORM_SCHEMA 12 | import voluptuous as vol 13 | import homeassistant.helpers.config_validation as cv 14 | #from homeassistant.const import TEMP_CELSIUS 15 | 16 | _LOGGER = logging.getLogger(__name__) 17 | CONF_OFFSET = 'offset' 18 | # Validation of the user's configuration 19 | PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({ 20 | vol.Required(CONF_PORT): cv.string, 21 | vol.Optional(CONF_NAME, default='ezo'): cv.string, 22 | vol.Optional(CONF_OFFSET, default=0.0): vol.Coerce(float) 23 | }) 24 | 25 | def setup_platform(hass, config, add_devices, discovery_info=None): 26 | """Setup the sensor platform.""" 27 | add_devices([AtlasSensor( 28 | name=config.get(CONF_NAME), 29 | port=config.get(CONF_PORT), 30 | offset=config.get(CONF_OFFSET) 31 | )]) 32 | 33 | class AtlasSensor(Entity): 34 | """Representation of a Sensor.""" 35 | 36 | def __init__(self, name, port, offset): 37 | """Initialize the sensor.""" 38 | self.ser = serial.Serial(port, 9600, timeout=3, write_timeout=3) 39 | _LOGGER.info("Serial for Atlas EZO @%s = %s" % (port,self.ser)) 40 | self._state = None 41 | self._name = name 42 | self._offset = offset 43 | ezos = {"ph": ['ph', 'pH', 'mdi:alpha-h-circle'], 44 | "orp": ['orp', 'mV', 'mdi:alpha-r-circle'], 45 | "or": ['orp', 'mV', 'mdi:alpha-r-circle'], 46 | "do": ['dissolved_oxygen','mV', 'mdi:alpha-x-circle'], 47 | "d.o.": ['dissolved_oxygen','mV', 'mdi:alpha-x-circle'], 48 | "ec": ['conductivity', "EC", 'mdi:alpha-c-circle']} 49 | # Reset buffer 50 | self._read("") 51 | # Get Status 52 | status = self._read("Status") 53 | # Set response ON 54 | ok = self._read("*OK,1") 55 | ok += self._read("RESPONSE,1") 56 | # Set continuos mode OFF 57 | c = self._read("C,0") 58 | # Get kind of EZO 59 | for i in range(5): 60 | ezo = self._read("I") 61 | if ezo is not None: 62 | ezo = ezo.lower().split(',') 63 | if len(ezo)>2 and ezo[1] in ezos: 64 | self._ezo_dev = ezos[ezo[1]][0] 65 | self._ezo_uom = ezos[ezo[1]][1] 66 | self._ezo_icon = ezos[ezo[1]][2] 67 | self._name += ("_" + self._ezo_dev) 68 | break 69 | _LOGGER.info("Atlas EZO '%s' detected [Status=%s, Ok=%s, c=%s]", ezo,status,ok,c) 70 | 71 | @property 72 | def name(self): 73 | """Return the name of the sensor.""" 74 | # return "Atlas Scientific" 75 | return self._name 76 | 77 | @property 78 | def device_class(self): 79 | """Return the device class of the sensor.""" 80 | return self._ezo_dev 81 | 82 | @property 83 | def icon(self): 84 | """Return the icon of the sensor.""" 85 | return self._ezo_icon 86 | 87 | @property 88 | def state(self): 89 | """Return the state of the sensor.""" 90 | return self._state 91 | 92 | @property 93 | def unit_of_measurement(self): 94 | """Return the unit of measurement.""" 95 | return self._ezo_uom 96 | 97 | def _read(self,command="R",terminator="\r*OK\r"): 98 | self.ser.write((command + "\r").encode()) 99 | line = "" 100 | for i in range(50): 101 | line += self.ser.read().decode() 102 | if ( line[0]=="*" and line[-1]=="\r") or terminator in line: break 103 | return line.replace(terminator,"") 104 | 105 | def update(self): 106 | """Fetch new state data for the sensor. 107 | """ 108 | try: 109 | r = self._read() 110 | self._state = float(r) + self._offset 111 | _LOGGER.debug("update state = '%s'" % self._state) 112 | except: 113 | _LOGGER.info("readed '%s'" % r) 114 | return 115 | 116 | def __del__(self): 117 | """close the sensor.""" 118 | self.ser.close() 119 | -------------------------------------------------------------------------------- /help/media/Raspipool_Firefox.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/segalion/raspipool/1834e490b5a977efe0c389213b8ce8b69f3fa70f/help/media/Raspipool_Firefox.gif -------------------------------------------------------------------------------- /help/media/raspipool_ORP_FC_pH.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/segalion/raspipool/1834e490b5a977efe0c389213b8ce8b69f3fa70f/help/media/raspipool_ORP_FC_pH.png -------------------------------------------------------------------------------- /help/media/raspipool_bypass.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/segalion/raspipool/1834e490b5a977efe0c389213b8ce8b69f3fa70f/help/media/raspipool_bypass.png -------------------------------------------------------------------------------- /help/media/raspipool_chem.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/segalion/raspipool/1834e490b5a977efe0c389213b8ce8b69f3fa70f/help/media/raspipool_chem.png -------------------------------------------------------------------------------- /help/media/raspipool_history.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/segalion/raspipool/1834e490b5a977efe0c389213b8ce8b69f3fa70f/help/media/raspipool_history.png -------------------------------------------------------------------------------- /help/media/raspipool_main.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/segalion/raspipool/1834e490b5a977efe0c389213b8ce8b69f3fa70f/help/media/raspipool_main.png -------------------------------------------------------------------------------- /help/media/raspipool_main2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/segalion/raspipool/1834e490b5a977efe0c389213b8ce8b69f3fa70f/help/media/raspipool_main2.png -------------------------------------------------------------------------------- /help/media/raspipool_notifications.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/segalion/raspipool/1834e490b5a977efe0c389213b8ce8b69f3fa70f/help/media/raspipool_notifications.png -------------------------------------------------------------------------------- /help/media/raspipool_overview.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/segalion/raspipool/1834e490b5a977efe0c389213b8ce8b69f3fa70f/help/media/raspipool_overview.png -------------------------------------------------------------------------------- /help/media/raspipool_relay_connections.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/segalion/raspipool/1834e490b5a977efe0c389213b8ce8b69f3fa70f/help/media/raspipool_relay_connections.png -------------------------------------------------------------------------------- /help/media/raspipool_relay_connections_v3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/segalion/raspipool/1834e490b5a977efe0c389213b8ce8b69f3fa70f/help/media/raspipool_relay_connections_v3.png -------------------------------------------------------------------------------- /help/media/raspipool_sensors_connections.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/segalion/raspipool/1834e490b5a977efe0c389213b8ce8b69f3fa70f/help/media/raspipool_sensors_connections.png -------------------------------------------------------------------------------- /packages/raspipool/bleach.yaml: -------------------------------------------------------------------------------- 1 | sensor: 2 | - platform: template 3 | sensors: 4 | bleach_tank: 5 | friendly_name: "Bleach tank" 6 | value_template: "{{ states('input_number.bleach_tank')|round(2)}}" 7 | 8 | - platform: history_stats 9 | name: bleach_on_last_hour 10 | entity_id: switch.orp 11 | state: 'on' 12 | type: time 13 | end: '{{now().replace(minute=0).replace(second=0)}}' 14 | duration: 01:00:00 15 | 16 | - platform: history_stats 17 | name: bleach_on_last_48h 18 | entity_id: switch.orp 19 | state: 'on' 20 | type: time 21 | end: '{{now()}}' 22 | duration: 48:00:00 23 | 24 | input_number: 25 | bleach_tank: 26 | min: 0 27 | max: 50 28 | step: 0.1 29 | unit_of_measurement: 'l' 30 | icon: mdi:blood-bag 31 | bleach_concentration: 32 | min: 1 33 | max: 15 34 | step: .25 35 | unit_of_measurement: '%' 36 | icon: mdi:water-percent 37 | mode: box 38 | bleach_speed: 39 | min: 10 40 | max: 100 41 | step: 1 42 | unit_of_measurement: 'ml/min' 43 | icon: mdi:needle 44 | notify_bleach_high: 45 | min: 0 46 | max: 15 47 | step: 0.1 48 | unit_of_measurement: 'l/48h' 49 | notify_bleach_tank: 50 | min: 0 51 | max: 5 52 | step: 0.5 53 | unit_of_measurement: 'l' 54 | icon: mdi:flask-empty-outline 55 | bleach_inject: 56 | min: 0 57 | max: 20000 58 | step: 5 59 | unit_of_measurement: 'ml' 60 | icon: mdi:beaker 61 | mode: box 62 | 63 | 64 | automation: 65 | - alias: bleach_every_hour 66 | id: '1554367517425' 67 | initial_state: true 68 | trigger: 69 | - platform: time_pattern 70 | minutes: 1 71 | condition: [] 72 | action: 73 | - service: input_number.set_value 74 | data_template: 75 | entity_id: input_number.bleach_tank 76 | value: " {{ ( states('input_number.bleach_tank')|float - ( states('sensor.bleach_on_last_hour')|float * states('input_number.bleach_speed')|int * 6 / 100 ) ) | round(2) }} " 77 | 78 | - alias: bleach_tank_low 79 | id: '1559049845420' 80 | initial_state: true 81 | trigger: 82 | platform: template 83 | value_template: "{% if states('input_number.notify_bleach_tank')|int> 0 and states('sensor.bleach_tank')|float < states('input_number.notify_bleach_tank')|float %}true{% endif %}" 84 | condition: [] 85 | action: 86 | - data_template: 87 | message: "@{{now().strftime('%H:%M')}}[{{now().day}}/{{now().month}}]" 88 | title: "Bleach tank low ({{states('input_number.bleach_tank')|float}} liters )!!!" 89 | service: notify.pushbullet 90 | 91 | - alias: bleach_high 92 | id: '1559049844490' 93 | initial_state: true 94 | trigger: 95 | platform: template 96 | value_template: "{% if states('input_number.notify_bleach_high')|int> 0 and states('sensor.bleach_on_last_48h')|float > (states('input_number.notify_bleach_high')|float *100 ) / (states('input_number.bleach_speed')|float *6) %}true{% endif %}" 97 | condition: [] 98 | action: 99 | - entity_id: switch.orp 100 | service: switch.turn_off 101 | - entity_id: input_boolean.lock_bleach 102 | service: input_boolean.turn_on 103 | - data_template: 104 | message: "{{now().strftime('%H:%M')}}[{{now().day}}/{{now().month}}]" 105 | title: "Bleach inject blocked ({{states('input_number.notify_bleach_high')}} in 48 hours)!!!" 106 | service: notify.pushbullet 107 | 108 | - alias: bleach_set 109 | id: '1559149839459' 110 | initial_state: true 111 | trigger: [] 112 | condition: [] 113 | action: 114 | - service: input_number.set_value 115 | data_template: 116 | entity_id: input_number.bleach_inject 117 | value: " {{ ( states('input_number.capacity')|float * ( states('input_number.fc_target')|float - states('sensor.e_fc')|float ) * (100 / states('input_number.bleach_concentration')|float ), 0)|max |round(0)|int }} " 118 | 119 | - alias: bleach_inject 120 | id: '1559149426435' 121 | initial_state: true 122 | trigger: [] 123 | condition: [] 124 | action: 125 | - condition: state 126 | entity_id: input_boolean.lock_bleach 127 | state: 'off' 128 | - condition: numeric_state 129 | entity_id: input_number.bleach_inject 130 | value_template: "{{ states('input_number.bleach_inject')|float / states('input_number.capacity')|float }}" 131 | above: 10.0 132 | below: 200.0 133 | - entity_id: switch.orp 134 | service: switch.turn_on 135 | - data_template: 136 | message: "{{now().strftime('%H:%M')}}[{{now().day}}/{{now().month}}]" 137 | title: "Bleach injection: ({{states('input_number.bleach_inject')}} ml)!!!" 138 | service: notify.pushbullet 139 | - delay: 00:00:{{ [0 , 60 * states('input_number.bleach_inject')|float/ states('input_number.bleach_speed')|float ]|max|int }} 140 | - entity_id: switch.orp 141 | service: switch.turn_off 142 | - data_template: 143 | message: "{{now().strftime('%H:%M')}}[{{now().day}}/{{now().month}}]" 144 | title: "Bleach injection end!!!" 145 | service: notify.pushbullet 146 | 147 | -------------------------------------------------------------------------------- /packages/raspipool/exterior.yaml: -------------------------------------------------------------------------------- 1 | sensor: 2 | # Weather prediction 3 | - platform: openweathermap 4 | api_key: !secret openweathermap_api_key 5 | name: Exterior 6 | language: es 7 | monitored_conditions: 8 | - weather 9 | - temperature 10 | - wind_speed 11 | - humidity 12 | - pressure 13 | - clouds 14 | - rain 15 | scan_interval: 600 16 | 17 | - platform: time_date 18 | display_options: 19 | - 'time' 20 | -------------------------------------------------------------------------------- /packages/raspipool/fc.yaml: -------------------------------------------------------------------------------- 1 | sensor: 2 | # Estimated sensor of effective Free Chlorine 3 | - platform: template 4 | sensors: 5 | e_fc: 6 | value_template: "{{ ( 0.23 * (1 + states('input_number.e_fc_adjust')|float / 100 ) * ( 14 - states('sensor.ph_mean')|float) ** ( 1 / (400 - states('sensor.orp_mean')|float) ) * ( states('sensor.ph_mean')|float -4.1) ** ( ( states('sensor.orp_mean')|float - 516)/145) + 10.0 ** ( (states('sensor.orp_mean')|float + states('sensor.ph_mean')|float * 70 -1282) / 40 ) ) |round(1) }}" 7 | unit_of_measurement: ppm 8 | # icon_template: mdi:chemical-weapon 9 | icon_template: mdi:react 10 | friendly_name: "Estimated FC" 11 | 12 | input_number: 13 | e_fc_adjust: 14 | min: -100 15 | max: 100 16 | unit_of_measurement: '%' 17 | icon: mdi:react 18 | mode: box 19 | fc_target: 20 | min: 0 21 | max: 30 22 | step: 0.1 23 | mode: box 24 | unit_of_measurement: 'ppm' 25 | icon: mdi:react 26 | 27 | 28 | automation: 29 | - alias: fc_ok 30 | id: '1559638825701' 31 | initial_state: true 32 | trigger: 33 | platform: numeric_state 34 | entity_id: sensor.e_fc 35 | above: 0.60 36 | below: 5.00 37 | for: 38 | minutes: 1 39 | condition: [] 40 | action: 41 | - service: frontend.set_theme 42 | data: 43 | name: default 44 | - alias: fc_high 45 | id: '1559638825702' 46 | initial_state: true 47 | trigger: 48 | platform: numeric_state 49 | entity_id: sensor.e_fc 50 | above: 5.01 51 | for: 52 | minutes: 1 53 | condition: [] 54 | action: 55 | - service: frontend.set_theme 56 | data: 57 | name: danger 58 | 59 | 60 | - alias: fc_low 61 | id: '1559638825702' 62 | initial_state: true 63 | trigger: 64 | platform: numeric_state 65 | entity_id: sensor.e_fc 66 | below: 0.59 67 | for: 68 | minutes: 1 69 | condition: [] 70 | action: 71 | - service: frontend.set_theme 72 | data: 73 | name: algae 74 | 75 | 76 | - alias: fc_target 77 | id: '1559146873657' 78 | initial_state: true 79 | trigger: 80 | - platform: state 81 | entity_id: input_number.fc_target 82 | condition: [] 83 | action: 84 | - service: automation.trigger 85 | entity_id: automation.bleach_set 86 | 87 | -------------------------------------------------------------------------------- /packages/raspipool/flow.yaml: -------------------------------------------------------------------------------- 1 | sensor: 2 | - platform: template 3 | sensors: 4 | pump_state: 5 | friendly_name: "Pump State" 6 | value_template: >- 7 | {% if is_state('switch.pump', 'on') %} 8 | {% if is_state('switch.turbo', 'on') %} 9 | high 10 | {% else %} 11 | low 12 | {% endif %} 13 | {% else %} 14 | None 15 | {% endif %} 16 | 17 | remain_cycles: 18 | friendly_name: "Remain cycles" 19 | value_template: >- 20 | {% set nextcycle = strptime(states('sensor.cycle_pool'),'%H:%M').hour + strptime(states('sensor.cycle_pool'),'%H:%M').minute|int/60 %} 21 | {{(states('input_number.notify_cycles')|float - (states('sensor.cummulated_flow_low')|float + states('sensor.cummulated_flow_high')|float * 5) / (state_attr('input_datetime.recirculation','hour')+ state_attr('input_datetime.recirculation','minute')|float/60) )|round(1)}} 22 | unit_of_measurement: '' 23 | entity_id: 24 | - input_number.notify_cycles 25 | - sensor.cummulated_flow_low 26 | - sensor.cummulated_flow_high 27 | 28 | - platform: history_stats 29 | name: cummulated_flow_low 30 | entity_id: sensor.pump_state 31 | state: 'low' 32 | type: time 33 | start: "{{ state_attr('input_datetime.cummulated_flow_start','timestamp') }}" 34 | end: "{{ now() }}" 35 | 36 | - platform: history_stats 37 | name: cummulated_flow_high 38 | entity_id: sensor.pump_state 39 | state: 'high' 40 | type: time 41 | start: "{{ state_attr('input_datetime.cummulated_flow_start','timestamp') }}" 42 | end: "{{ now() }}" 43 | 44 | input_datetime: 45 | cummulated_flow_start: 46 | name: Cummulated Flow Start 47 | has_date: true 48 | has_time: true 49 | icon: mdi:timer 50 | 51 | input_number: 52 | notify_cycles: 53 | name: Notify cycles flow 54 | icon: mdi:timer 55 | min: 0 56 | max: 100 57 | step: 5 58 | 59 | automation: 60 | - alias: notify_cummulated_flow 61 | id: '1559049859380' 62 | initial_state: true 63 | trigger: 64 | - platform: numeric_state 65 | entity_id: sensor.remain_cycles 66 | below: 1 67 | condition: 68 | - condition: numeric_state 69 | entity_id: input_number.notify_cycles 70 | above: 0 71 | action: 72 | - data_template: 73 | message: "in low speed: {{state_attr('sensor.cummulated_flow_low','value')}}{{'\n'}}in high speed: {{state_attr('sensor.cummulated_flow_high','value')}}{{'\n'}}@{{now().strftime('%H:%M')}}[{{now().day}}/{{now().month}}]" 74 | title: "Flow: {{states('input_number.notify_cycles')|int}} cycles reached!!!" 75 | service: notify.pushbullet 76 | 77 | - alias: reset_cummulated_flow_start 78 | id: '1559293506609' 79 | trigger: [] 80 | condition: [] 81 | action: 82 | - service: input_datetime.set_datetime 83 | data_template: 84 | entity_id: input_datetime.cummulated_flow_start 85 | date: "{{now().timestamp()|timestamp_custom('%Y-%m-%d', True)}}" 86 | time: "{{now().timestamp()|timestamp_custom('%H:%M:%S', True)}}" 87 | 88 | 89 | -------------------------------------------------------------------------------- /packages/raspipool/muriatic.yaml: -------------------------------------------------------------------------------- 1 | sensor: 2 | - platform: template 3 | sensors: 4 | muriatic_tank: 5 | friendly_name: "Muriatic tank" 6 | value_template: "{{ states('input_number.muriatic_tank')|round(2)}}" 7 | 8 | - platform: history_stats 9 | name: muriatic_on_last_hour 10 | entity_id: switch.ph 11 | state: 'on' 12 | type: time 13 | end: '{{now().replace(minute=0).replace(second=0)}}' 14 | duration: 01:00:00 15 | 16 | - platform: history_stats 17 | name: muriatic_on_last_48h 18 | entity_id: switch.ph 19 | state: 'on' 20 | type: time 21 | end: '{{now()}}' 22 | duration: 48:00:00 23 | 24 | 25 | input_number: 26 | muriatic_tank: 27 | min: 0 28 | max: 30 29 | step: 0.1 30 | unit_of_measurement: 'l' 31 | icon: mdi:blood-bag 32 | muriatic_concentration: 33 | min: 5 34 | max: 30 35 | step: .1 36 | unit_of_measurement: '%' 37 | icon: mdi:water-percent 38 | mode: box 39 | muriatic_speed: 40 | min: 10 41 | max: 100 42 | step: 1 43 | unit_of_measurement: 'ml/min' 44 | icon: mdi:needle 45 | notify_muriatic_high: 46 | min: 0 47 | max: 5 48 | step: 0.1 49 | unit_of_measurement: 'l/48h' 50 | notify_muriatic_tank: 51 | min: 0 52 | max: 5 53 | step: 0.5 54 | unit_of_measurement: 'l' 55 | icon: mdi:flask-empty 56 | muriatic_inject: 57 | min: 0 58 | max: 10000 59 | step: 5 60 | unit_of_measurement: 'ml' 61 | icon: mdi:beaker 62 | mode: box 63 | 64 | automation: 65 | - alias: muriatic_every_hour 66 | id: '1554167717425' 67 | initial_state: true 68 | trigger: 69 | - platform: time_pattern 70 | minutes: 1 71 | condition: [] 72 | action: 73 | - service: input_number.set_value 74 | data_template: 75 | entity_id: input_number.muriatic_tank 76 | value: " {{ ( states('input_number.muriatic_tank')|float - ( states('sensor.muriatic_on_last_hour')|float * states('input_number.muriatic_speed')|int * 6 / 100 ) ) | round(2) }} " 77 | 78 | - alias: muriatic_tank_low 79 | id: '1559049845419' 80 | initial_state: true 81 | trigger: 82 | platform: template 83 | value_template: "{% if states('input_number.notify_muriatic_tank')|int> 0 and states('sensor.muriatic_tank')|float < states('input_number.notify_muriatic_tank')|float %}true{% endif %}" 84 | condition: [] 85 | action: 86 | - data_template: 87 | message: "@{{now().strftime('%H:%M')}}[{{now().day}}/{{now().month}}]" 88 | title: "Muriatic tank low ({{states('input_number.muriatic_tank')|float}} liters )!!!" 89 | service: notify.pushbullet 90 | 91 | - alias: muriatic_high 92 | id: '1559049844489' 93 | initial_state: true 94 | trigger: 95 | platform: template 96 | value_template: "{% if states('input_number.notify_muriatic_high')|int> 0 and states('sensor.muriatic_on_last_48h')|float > (states('input_number.notify_muriatic_high')|float*100 ) / (states('input_number.muriatic_speed')|float *6) %}true{% endif %}" 97 | condition: [] 98 | action: 99 | - entity_id: switch.ph 100 | service: switch.turn_off 101 | - entity_id: input_boolean.lock_muriatic 102 | service: input_boolean.turn_on 103 | - data_template: 104 | message: "{{now().strftime('%H:%M')}}[{{now().day}}/{{now().month}}]" 105 | title: "Muriatic inject blocked ({{states('input_number.notify_muriatic_high')}} in 48 hours)!!!" 106 | service: notify.pushbullet 107 | 108 | - alias: muriatic_set 109 | id: '1559049844459' 110 | initial_state: true 111 | trigger: [] 112 | condition: [] 113 | action: 114 | - service: input_number.set_value 115 | data_template: 116 | entity_id: input_number.muriatic_inject 117 | value: " {{ ( states('input_number.capacity')|float * ( 10 ** (8 - states('input_number.ph_target')|float ) - 10 ** (8 - states('sensor.ph_mean')|float ) ) * (126.19 / states('input_number.muriatic_concentration')|float ),0 )|max |round(0)|int }} " 118 | 119 | - alias: muriatic_inject 120 | id: '1559949826459' 121 | initial_state: true 122 | trigger: [] 123 | condition: [] 124 | action: 125 | - condition: state 126 | entity_id: input_boolean.lock_muriatic 127 | state: 'off' 128 | - condition: numeric_state 129 | entity_id: input_number.muriatic_inject 130 | value_template: "{{ states('input_number.muriatic_inject')|float / states('input_number.capacity')|float }}" 131 | above: 5.0 132 | below: 100.0 133 | - entity_id: switch.ph 134 | service: switch.turn_on 135 | - data_template: 136 | message: "{{now().strftime('%H:%M')}}[{{now().day}}/{{now().month}}]" 137 | title: "Muriatic injection: ({{states('input_number.muriatic_inject')}} ml)!!!" 138 | service: notify.pushbullet 139 | - delay: 00:00:{{ [0 , 60 * states('input_number.muriatic_inject')|float/ states('input_number.muriatic_speed')|float ]|max|int }} 140 | - entity_id: switch.ph 141 | service: switch.turn_off 142 | - data_template: 143 | message: "{{now().strftime('%H:%M')}}[{{now().day}}/{{now().month}}]" 144 | title: "Muriatic injection end!!!" 145 | service: notify.pushbullet 146 | 147 | -------------------------------------------------------------------------------- /packages/raspipool/notifications.yaml: -------------------------------------------------------------------------------- 1 | notify: 2 | - platform: pushbullet 3 | api_key: !secret pushbullet_api_key 4 | name: pushbullet 5 | 6 | input_boolean: 7 | notify_seasson: 8 | name: Notify seasson changes 9 | icon: mdi:comment-alert 10 | 11 | 12 | automation: 13 | 14 | - alias: winter_session 15 | id: '1559049956784' 16 | initial_state: true 17 | trigger: 18 | - platform: numeric_state 19 | entity_id: input_number.cycle 20 | below: 240 21 | for: 22 | hours: 121 23 | condition: 24 | - condition: state 25 | entity_id: input_boolean.notify_seasson 26 | state: 'on' 27 | action: 28 | - data_template: 29 | message: "@{{now().strftime('%H:%M')}}[{{now().day}}/{{now().month}}]" 30 | title: 'Winter is comming!!!' 31 | service: notify.pushbullet 32 | 33 | - alias: summer_session 34 | id: '1559049956785' 35 | initial_state: true 36 | trigger: 37 | - platform: numeric_state 38 | entity_id: input_number.cycle 39 | above: 240 40 | for: 41 | hours: 121 42 | condition: 43 | - condition: state 44 | entity_id: input_boolean.notify_seasson 45 | state: 'on' 46 | action: 47 | - data_template: 48 | message: "@{{now().strftime('%H:%M')}}[{{now().day}}/{{now().month}}]" 49 | title: "Summer is here!!!" 50 | service: notify.pushbullet 51 | 52 | -------------------------------------------------------------------------------- /packages/raspipool/orp.yaml: -------------------------------------------------------------------------------- 1 | sensor: 2 | - platform: statistics 3 | name: orp 4 | entity_id: sensor.ezo_orp 5 | max_age: 6 | minutes: 10 7 | precision: 0 8 | 9 | - platform: atlas_scientific 10 | port: /dev/ttyUSB0 11 | offset: 78 12 | scan_interval: 31536000 13 | 14 | automation: 15 | - alias: orp_start 16 | id: '1559639825764' 17 | initial_state: true 18 | trigger: 19 | - platform: state 20 | entity_id: switch.orp 21 | to: 'on' 22 | condition: 23 | - condition: state 24 | entity_id: switch.pump 25 | state: 'off' 26 | action: 27 | - service: switch.turn_off 28 | entity_id: switch.orp 29 | 30 | - alias: orp_bad 31 | id: '1559149493756' 32 | initial_state: true 33 | trigger: 34 | - platform: template 35 | value_template: '{{state_attr("sensor.orp_mean", "standard_deviation") > 20 }}' 36 | condition: [] 37 | action: 38 | - service: notify.pushbullet 39 | data_template: 40 | message: 'Between {{state_attr("sensor.orp_mean", "min_value")}} and {{state_attr("sensor.orp_mean", "max_value")}}{{- "\n\n" -}}@{{now().strftime("%H:%M")}}[{{now().day}}/{{now().month}}]' 41 | title: 'ORP probe bad (calibrate?) !!!' 42 | - service: automation.turn_off 43 | entity_id: automation.orp_bad 44 | 45 | 46 | -------------------------------------------------------------------------------- /packages/raspipool/ph.yaml: -------------------------------------------------------------------------------- 1 | sensor: 2 | - platform: atlas_scientific 3 | # port: /dev/ttyUSB0 4 | port: /dev/ttyAMA0 5 | # scan_interval: 60 6 | scan_interval: 31536000 7 | # scan_interval: 10 8 | 9 | - platform: statistics 10 | name: ph 11 | entity_id: sensor.ezo_ph 12 | max_age: 13 | minutes: 10 14 | # precision: 3 15 | 16 | input_number: 17 | ph_target: 18 | min: 6.5 19 | max: 8.5 20 | step: 0.01 21 | unit_of_measurement: 'pH' 22 | icon: mdi:alpha-h-circle-outline 23 | mode: box 24 | 25 | automation: 26 | - alias: ph_start 27 | id: '1559669855764' 28 | initial_state: true 29 | trigger: 30 | - platform: state 31 | entity_id: switch.ph 32 | to: 'on' 33 | condition: 34 | - condition: state 35 | entity_id: switch.pump 36 | state: 'off' 37 | action: 38 | - service: switch.turn_off 39 | entity_id: switch.ph 40 | 41 | - alias: ph_high 42 | id: '1559049859759' 43 | initial_state: true 44 | trigger: 45 | - platform: numeric_state 46 | entity_id: sensor.ph_mean 47 | above: 7.5 48 | for: 49 | hours: 48 50 | condition: [] 51 | action: 52 | - data_template: 53 | message: '{{trigger.to_state.attributes.friendly_name}}: {{trigger.to_state.state}}{{- 54 | "\n\n" -}}@{{now().strftime("%H:%M")}}[{{now().day}}/{{now().month}}]' 55 | title: 'pH high ({{trigger.to_state.state}})!!!' 56 | service: notify.pushbullet 57 | 58 | - alias: ph_low 59 | id: '1559049839760' 60 | initial_state: true 61 | trigger: 62 | - platform: numeric_state 63 | entity_id: sensor.ph_mean 64 | below: 6.9 65 | for: 66 | hours: 48 67 | condition: [] 68 | action: 69 | - data_template: 70 | message: '{{trigger.to_state.attributes.friendly_name}}: {{trigger.to_state.state}}{{- 71 | "\n\n" -}}@{{now().strftime("%H:%M")}}[{{now().day}}/{{now().month}}]' 72 | title: 'pH low ({{trigger.to_state.state}})!!!' 73 | service: notify.pushbullet 74 | 75 | - alias: ph_bad 76 | id: '1559049893756' 77 | initial_state: true 78 | trigger: 79 | - platform: template 80 | value_template: '{{state_attr("sensor.ph_mean", "standard_deviation") > 0.1}}' 81 | condition: [] 82 | action: 83 | - service: notify.pushbullet 84 | data_template: 85 | message: '{{trigger.to_state.attributes.friendly_name}}: {{trigger.to_state.state}}{{- 86 | "\n\n" -}}@{{now().strftime("%H:%M")}}[{{now().day}}/{{now().month}}]' 87 | title: 'pH probe bad (calibrate?) !!!' 88 | - service: automation.turn_off 89 | entity_id: automation.ph_bad 90 | 91 | - alias: ph_target 92 | id: '1559146893656' 93 | initial_state: true 94 | trigger: 95 | - platform: state 96 | entity_id: input_number.ph_target 97 | condition: [] 98 | action: 99 | - service: automation.trigger 100 | entity_id: automation.muriatic_set 101 | 102 | -------------------------------------------------------------------------------- /packages/raspipool/pump.yaml: -------------------------------------------------------------------------------- 1 | sensor: 2 | - platform: template 3 | sensors: 4 | cycle_pool: 5 | friendly_name: "Next Cycle pump" 6 | value_template: >- 7 | {% if is_state('sensor.pool_temp','unknown') %} 8 | {% set temp = states('sensor.pool_water_temperature')|float %} 9 | {%- else -%} 10 | {% set temp = states('sensor.pool_temp')|float %} 11 | {%- endif -%} 12 | {{ ( state_attr('input_datetime.recirculation','timestamp')|float * 13 | ( 1 - is_state('input_boolean.turbo','on')|int * states('input_number.turbo')|float * 2 / 300 ) * 14 | ( temp / [1, ( 9 - temp / 2)]|max ) / 15 | ( states('input_number.quality')|float + 6 ) )|round(0) | timestamp_custom('%H:%M', false) }} 16 | entity_id: 17 | - sensor.pool_temp 18 | - sensor.pool_water_temperature 19 | - input_number.cycle 20 | - input_number.quality 21 | icon_template: >- 22 | {% if states('input_number.cycle')|int < 0 %} 23 | mdi:water-off 24 | {% elif states('input_number.cycle')|int < 240 %} 25 | mdi:weather-snowy 26 | {% elif states('input_number.cycle')|int >= 1440 %} 27 | mdi:numeric-{{ (states('input_number.cycle')|int / 1440)|int }}-box-multiple-outline 28 | {% else %} 29 | mdi:weather-sunny 30 | {% endif %} 31 | 32 | 33 | input_number: 34 | out_of_order: 35 | min: -1 36 | max: 10 37 | unit_of_measurement: 'days' 38 | icon: mdi:engine-off 39 | cycle: 40 | min: -10 41 | max: 14400 42 | unit_of_measurement: 'minutes' 43 | mode: box 44 | second_cycle: 45 | min: 0 46 | max: 50 47 | step: 5 48 | unit_of_measurement: '%' 49 | icon: mdi:replay 50 | pump_on_for: 51 | min: 0 52 | max: 10 53 | step: 0.25 54 | unit_of_measurement: 'hours' 55 | icon: mdi:engine 56 | 57 | 58 | automation: 59 | - alias: cycle_start 60 | id: '1559137717424' 61 | initial_state: true 62 | trigger: 63 | - platform: template 64 | value_template: "{{ states.sensor.time.state == (states.input_datetime.cycle_start.attributes.timestamp 65 | | int | timestamp_custom('%H:%M', False)) }}" 66 | condition: [] 67 | action: 68 | - service: input_number.set_value 69 | data_template: 70 | entity_id: input_number.out_of_order 71 | value: "{{ states('input_number.out_of_order')|int - 1 }}" 72 | - condition: numeric_state 73 | entity_id: input_number.out_of_order 74 | below: 0 75 | - service: input_number.set_value 76 | data_template: 77 | entity_id: input_number.cycle 78 | value: "{% set lmins = states('input_number.cycle')|int %} {%- if lmins < 79 | 0 -%} {{ lmins + 1 }} {%- else -%} {% set mins = strptime(states('sensor.cycle_pool'),'%H:%M').hour 80 | * 60 + strptime(states('sensor.cycle_pool'),'%H:%M').minute %} {%- if 81 | lmins < 240 -%} {{ mins + lmins }} {%- elif lmins < 1440 -%} {{ mins }} {%- else -%} {{ lmins }} {%- endif -%} 82 | {%- endif -%}" 83 | - condition: numeric_state 84 | entity_id: input_number.cycle 85 | above: 239 86 | - service: automation.trigger 87 | entity_id: automation.cycle_pump_start 88 | - condition: numeric_state 89 | entity_id: input_number.cycle 90 | below: 1440 91 | - delay: 00:00:10 92 | - service: automation.trigger 93 | entity_id: automation.cycle_turbo_start 94 | - condition: numeric_state 95 | entity_id: input_number.second_cycle 96 | above: 0 97 | - delay: 00:{{720 + (states('input_number.cycle')|int * (100 - states('input_number.second_cycle')|int)/200)|int }}:00 98 | 99 | - alias: cycle_pump_start 100 | id: '1559293900608' 101 | trigger: [] 102 | condition: [] 103 | action: 104 | - entity_id: switch.pump 105 | service: switch.turn_on 106 | - entity_id: automation.ph_bad 107 | service: automation.turn_on 108 | - entity_id: automation.orp_bad 109 | service: automation.turn_on 110 | - delay: 00:15:00 111 | - service: automation.trigger 112 | entity_id: automation.bleach_set 113 | - service: automation.trigger 114 | entity_id: automation.bleach_inject 115 | - delay: 00:05:00 116 | - service: automation.trigger 117 | entity_id: automation.muriatic_set 118 | - service: automation.trigger 119 | entity_id: automation.muriatic_inject 120 | - delay: 00:{{ [1440 , (states('input_number.cycle')|int * (1 - states('input_number.second_cycle')|int/100))|int]|min }}:00 121 | - entity_id: switch.pump 122 | service: switch.turn_off 123 | - condition: numeric_state 124 | entity_id: input_number.cycle 125 | above: 1439 126 | - service: input_number.set_value 127 | data_template: 128 | entity_id: input_number.cycle 129 | value: "{{ states('input_number.cycle')|int - 1440 }}" 130 | 131 | - alias: cycle_turbo_start 132 | id: '1559223936600' 133 | trigger: [] 134 | condition: [] 135 | action: 136 | - condition: state 137 | entity_id: input_boolean.turbo 138 | state: 'on' 139 | - condition: numeric_state 140 | entity_id: input_number.turbo 141 | above: '0' 142 | - entity_id: switch.turbo 143 | service: switch.turn_off 144 | - delay: 00:{{ (states('input_number.cycle')|int * (1 - states('input_number.second_cycle')|int/100) * (1 - states('input_number.turbo')|int /100) /2 )|int }}:00 145 | - entity_id: switch.turbo 146 | service: switch.turn_on 147 | - delay: 00:{{ (states('input_number.cycle')|int * (1 - states('input_number.second_cycle')|int/100) * states('input_number.turbo')|int /100 )|int }}:00 148 | - entity_id: switch.turbo 149 | service: switch.turn_off 150 | initial_state: true 151 | 152 | - alias: pump_start 153 | id: '1559049859754' 154 | initial_state: true 155 | trigger: 156 | - platform: state 157 | entity_id: switch.pump 158 | to: 'on' 159 | condition: 160 | - condition: state 161 | entity_id: input_boolean.turbo 162 | state: 'on' 163 | - condition: state 164 | entity_id: switch.turbo 165 | state: 'off' 166 | action: 167 | - service: switch.turn_on 168 | entity_id: switch.turbo 169 | - delay: 00:00:02 170 | - service: switch.turn_off 171 | entity_id: switch.turbo 172 | 173 | - alias: pump_stop 174 | id: '1559449855764' 175 | initial_state: true 176 | trigger: 177 | - platform: state 178 | entity_id: switch.pump 179 | to: 'off' 180 | condition: [] 181 | action: 182 | - service: switch.turn_off 183 | entity_id: switch.ph 184 | - service: switch.turn_off 185 | entity_id: switch.orp 186 | 187 | - alias: every_2_min 188 | id: '1554168816425' 189 | initial_state: true 190 | trigger: 191 | - platform: time_pattern 192 | minutes: '/2' 193 | condition: 194 | - condition: state 195 | entity_id: switch.pump 196 | state: 'on' 197 | for: 198 | minutes: 5 199 | action: 200 | - service: homeassistant.update_entity 201 | entity_id: sensor.ezo_ph 202 | - service: homeassistant.update_entity 203 | entity_id: sensor.pool_water_temperature 204 | - service: homeassistant.update_entity 205 | entity_id: sensor.ezo_orp 206 | 207 | 208 | - alias: pump_on_for 209 | id: '1549429855765' 210 | initial_state: true 211 | trigger: 212 | - platform: numeric_state 213 | entity_id: input_number.pump_on_for 214 | above: '0' 215 | condition: [] 216 | action: 217 | - service: switch.turn_on 218 | entity_id: switch.pump 219 | - delay: "{{states('input_number.pump_on_for')|int}}:{{((states('input_number.pump_on_for')|float % 1)*60)|int}}:0" 220 | - service: switch.turn_off 221 | entity_id: switch.pump 222 | - service: input_number.set_value 223 | data: 224 | entity_id: input_number.pump_on_for 225 | value: '0' 226 | -------------------------------------------------------------------------------- /packages/raspipool/recorder.yaml: -------------------------------------------------------------------------------- 1 | 2 | recorder: 3 | purge_keep_days: 10 4 | include: 5 | entities: 6 | - sensor.pump_state 7 | - sensor.pool_temp_mean 8 | - sensor.orp_mean 9 | - sensor.ph_mean 10 | - sensor.pool_water_temperature 11 | - sensor.ezo_ph 12 | - sensor.ezo_orp 13 | - sensor.remain_cycles 14 | - input_number.muriatic_tank 15 | - input_number.bleach_tank 16 | - sensor.exterior_condition 17 | - sensor.exterior_temperature 18 | - switch.ph 19 | - switch.orp 20 | - sensor.e_fc 21 | 22 | -------------------------------------------------------------------------------- /packages/raspipool/settings.yaml: -------------------------------------------------------------------------------- 1 | input_datetime: 2 | cycle_start: 3 | name: Pump cycle start time 4 | has_date: false 5 | has_time: true 6 | recirculation: 7 | name: Recirculation cycle 8 | has_time: true 9 | has_date: false 10 | icon: mdi:timer 11 | 12 | 13 | input_number: 14 | capacity: 15 | name: Pool capacity (m3) 16 | min: 5 17 | max: 100 18 | unit_of_measurement: m3 19 | icon: mdi:water-pump 20 | quality: 21 | name: Pool quality 22 | min: 0 23 | max: 9 24 | unit_of_measurement: stars 25 | icon: mdi:star 26 | turbo: 27 | name: '% in high speed' 28 | min: 0 29 | max: 100 30 | step: 5 31 | unit_of_measurement: '%' 32 | icon: mdi:circle-slice-2 33 | 34 | 35 | input_boolean: 36 | turbo: 37 | name: Dual-speed Pump 38 | icon: mdi:play-speed 39 | 40 | factory_started: 41 | name: Factory config started 42 | icon: mdi:auto-fix 43 | 44 | 45 | automation: 46 | - alias: factory_started 47 | id: '1559149839000' 48 | initial_state: true 49 | trigger: 50 | platform: homeassistant 51 | event: start 52 | condition: 53 | condition: state 54 | entity_id: input_boolean.factory_started 55 | state: 'off' 56 | action: 57 | - service: input_boolean.turn_on 58 | data: 59 | entity_id: input_boolean.factory_started 60 | - service: input_number.set_value 61 | data_template: 62 | entity_id: input_number.capacity 63 | value: 50 64 | - service: input_number.set_value 65 | data_template: 66 | entity_id: input_number.quality 67 | value: 4 68 | - service: input_number.set_value 69 | data_template: 70 | entity_id: input_number.bleach_tank 71 | value: 20 72 | - service: input_number.set_value 73 | data_template: 74 | entity_id: input_number.bleach_concentration 75 | value: 5 76 | - service: input_number.set_value 77 | data_template: 78 | entity_id: input_number.bleach_speed 79 | value: 25 80 | - service: input_number.set_value 81 | data_template: 82 | entity_id: input_number.fc_target 83 | value: 1.5 84 | - service: input_number.set_value 85 | data_template: 86 | entity_id: input_number.e_fc_adjust 87 | value: 0 88 | - service: input_number.set_value 89 | data_template: 90 | entity_id: input_number.ph_target 91 | value: 7.2 92 | - service: input_number.set_value 93 | data_template: 94 | entity_id: input_number.muriatic_tank 95 | value: 10 96 | - service: input_number.set_value 97 | data_template: 98 | entity_id: input_number.muriatic_concentration 99 | value: 20 100 | - service: input_number.set_value 101 | data_template: 102 | entity_id: input_number.muriatic_speed 103 | value: 25 104 | 105 | 106 | 107 | -------------------------------------------------------------------------------- /packages/raspipool/switches.yaml: -------------------------------------------------------------------------------- 1 | # Part of raspipool project @ github (by segalion at gmail) 2 | 3 | switch: 4 | - platform: rpi_gpio 5 | ports: 6 | # gpio_in_BCM_number: name 7 | # example=> pin#35 = 'gpio.24' = rpi24 = BCM_19 => 19: pump 8 | # execute 'gpio readall' to see all gpios an theis states in console 9 | 19: Pump 10 | 26: Turbo 11 | 20: ph 12 | 21: orp 13 | invert_logic: true 14 | 15 | input_boolean: 16 | lock_bleach: 17 | initial: off 18 | icon: mdi:lock 19 | lock_muriatic: 20 | initial: off 21 | icon: mdi:lock 22 | 23 | 24 | binary_sensor: 25 | - platform: template 26 | sensors: 27 | bleach: 28 | friendly_name: "Bleach injection" 29 | entity_id: 30 | - switch.orp 31 | - input_boolean.lock_bleach 32 | device_class: plug 33 | value_template: "{{ is_state('switch.orp', 'on') }}" 34 | icon_template: >- 35 | {% if is_state('input_boolean.lock_bleach', 'on') %} 36 | mdi:lock 37 | {% endif %} 38 | muriatic: 39 | friendly_name: "Muriatic injection" 40 | entity_id: 41 | - switch.ph 42 | - input_boolean.lock_muriatic 43 | device_class: plug 44 | value_template: "{{ is_state('switch.ph', 'on') }}" 45 | icon_template: >- 46 | {% if is_state('input_boolean.lock_muriatic', 'on') %} 47 | mdi:lock 48 | {% endif %} 49 | 50 | -------------------------------------------------------------------------------- /packages/raspipool/system.yaml: -------------------------------------------------------------------------------- 1 | #https://www.home-assistant.io/components/sensor.command_line/ 2 | sensor: 3 | - platform: command_line 4 | name: RPI Temperature 5 | command: "cat /sys/class/thermal/thermal_zone0/temp" 6 | unit_of_measurement: "°C" 7 | value_template: '{{ value | multiply(0.001) | round(1) }}' 8 | - platform: systemmonitor 9 | resources: 10 | - type: disk_use_percent 11 | arg: / 12 | - type: last_boot 13 | -------------------------------------------------------------------------------- /packages/raspipool/temp.yaml: -------------------------------------------------------------------------------- 1 | sensor: 2 | - platform: template 3 | sensors: 4 | pool_temp: 5 | friendly_name: "Pool Temp." 6 | entity_id: sensor.pool_water_temperature 7 | value_template: "{{ states('sensor.pool_water_temperature')|float|round(1) }}" 8 | # icon_template: mdi:coolant-temperature 9 | icon_template: mdi:oil-temperature 10 | unit_of_measurement: 'ºC' 11 | 12 | - platform: onewire 13 | names: 14 | # 28-011564d449ff: Pool water 15 | 28-031504ae5eff: Pool water 16 | scan_interval: 31536000 17 | 18 | automation: 19 | - alias: temp_low 20 | id: '1559049959765' 21 | initial_state: true 22 | trigger: 23 | - platform: numeric_state 24 | entity_id: sensor.pool_water_temperature 25 | below: 7 26 | for: 27 | hours: 48 28 | condition: [] 29 | action: 30 | - data_template: 31 | message: '{{trigger.to_state.attributes.friendly_name}}: {{trigger.to_state.state}}{{- 32 | "\n\n" -}}@{{now().strftime("%H:%M")}}[{{now().day}}/{{now().month}}]' 33 | title: 'temperature low ({{trigger.to_state.state}})!!!' 34 | service: notify.pushbullet 35 | 36 | - alias: temp_high 37 | id: '1559049959765' 38 | initial_state: true 39 | trigger: 40 | - platform: numeric_state 41 | entity_id: sensor.pool_water_temperature 42 | above: 32 43 | for: 44 | hours: 48 45 | condition: [] 46 | action: 47 | - data_template: 48 | message: '{{trigger.to_state.attributes.friendly_name}}: {{trigger.to_state.state}}{{- 49 | "\n\n" -}}@{{now().strftime("%H:%M")}}[{{now().day}}/{{now().month}}]' 50 | title: 'temperature high ({{trigger.to_state.state}})!!!' 51 | service: notify.pushbullet 52 | 53 | -------------------------------------------------------------------------------- /packages/raspipool/themes.yaml: -------------------------------------------------------------------------------- 1 | frontend: 2 | themes: 3 | danger: 4 | primary-color: red 5 | maintenance: 6 | primary-color: grey 7 | algae: 8 | primary-color: green 9 | 10 | -------------------------------------------------------------------------------- /ui-lovelace.yaml: -------------------------------------------------------------------------------- 1 | title: Raspipool 2 | views: 3 | - badges: 4 | - sensor.exterior_temperature 5 | - sensor.e_fc 6 | - sensor.cycle_pool 7 | - sensor.exterior_condition 8 | title: Pool 9 | panel: false 10 | icon: 'mdi:pool' 11 | cards: 12 | - type: vertical-stack 13 | cards: 14 | - type: horizontal-stack 15 | cards: 16 | - max: 35 17 | min: 22 18 | theme: Backend-selected 19 | entity: sensor.pool_water_temperature 20 | severity: 21 | green: 27 22 | red: 33 23 | yellow: 31 24 | name: temp 25 | type: gauge 26 | - max: 8 27 | min: 6.5 28 | theme: Backend-selected 29 | entity: sensor.ph_mean 30 | severity: 31 | green: 6.95 32 | red: 7.75 33 | yellow: 7.35 34 | name: ph 35 | type: gauge 36 | - max: 1999 37 | min: -1999 38 | theme: Backend-selected 39 | entity: sensor.orp_mean 40 | severity: 41 | green: 650 42 | red: 800 43 | yellow: 600 44 | name: orp 45 | type: gauge 46 | - show_name: false 47 | entities: 48 | - entity: sensor.pool_water_temperature 49 | hold_action: 50 | action: call-service 51 | service: homeassistant.update_entity 52 | service_data: 53 | entity_id: sensor.pool_water_temperature 54 | - entity: sensor.ezo_ph 55 | hold_action: 56 | action: call-service 57 | service: homeassistant.update_entity 58 | service_data: 59 | entity_id: sensor.ezo_ph 60 | - entity: sensor.ezo_orp 61 | hold_action: 62 | action: call-service 63 | service: homeassistant.update_entity 64 | service_data: 65 | entity_id: sensor.ezo_orp 66 | type: glance 67 | show_state: true 68 | - title: prueba 69 | type: horizontal-stack 70 | cards: 71 | - max: 20 72 | min: 0 73 | theme: default 74 | entity: sensor.remain_cycles 75 | severity: 76 | green: 15 77 | red: 5 78 | yellow: 10 79 | name: remain cycles 80 | type: gauge 81 | - max: 10 82 | min: 0 83 | theme: default 84 | entity: sensor.muriatic_tank 85 | severity: 86 | green: 5 87 | red: 1 88 | yellow: 3 89 | type: gauge 90 | - max: 15 91 | min: 0 92 | theme: default 93 | entity: sensor.bleach_tank 94 | severity: 95 | green: 8 96 | red: 0 97 | yellow: 3 98 | type: gauge 99 | - show_name: true 100 | entities: 101 | - entity: switch.pump 102 | hold_action: 103 | action: toggle 104 | - entity: switch.turbo 105 | hold_action: 106 | action: toggle 107 | - entity: binary_sensor.muriatic 108 | hold_action: 109 | action: call-service 110 | service: switch.toggle 111 | service_data: 112 | entity_id: switch.ph 113 | tap_action: 114 | action: navigate 115 | navigation_path: /lovelace/2 116 | - entity: binary_sensor.bleach 117 | hold_action: 118 | action: call-service 119 | service: switch.toggle 120 | service_data: 121 | entity_id: switch.orp 122 | tap_action: 123 | action: navigate 124 | navigation_path: /lovelace/2 125 | type: glance 126 | show_state: false 127 | show_icon: true 128 | - badges: [] 129 | title: History 130 | panel: true 131 | icon: 'mdi:chart-line' 132 | cards: 133 | - type: horizontal-stack 134 | cards: 135 | - title: last 3 hours 136 | entities: 137 | - entity: sensor.pump_state 138 | - entity: switch.orp 139 | name: Bleach inj. 140 | - entity: switch.ph 141 | name: Muriatic inj. 142 | - entity: sensor.ph_mean 143 | name: pH 144 | - entity: sensor.orp_mean 145 | name: ORP 146 | - entity: sensor.pool_water_temperature 147 | name: water temp. 148 | type: history-graph 149 | hours_to_show: 3 150 | - title: last 3 days 151 | entities: 152 | - entity: sensor.pump_state 153 | - entity: switch.orp 154 | name: Bleach inj. 155 | - entity: switch.ph 156 | name: Muriatic inj. 157 | - entity: sensor.ph_mean 158 | name: pH 159 | - entity: sensor.orp_mean 160 | name: ORP 161 | - entity: sensor.pool_water_temperature 162 | name: water temp. 163 | type: history-graph 164 | hours_to_show: 72 165 | - badges: [] 166 | title: Chemical 167 | panel: true 168 | icon: 'mdi:flask' 169 | cards: 170 | - type: horizontal-stack 171 | cards: 172 | - entities: 173 | - label: Muriatic ( - pH ) 174 | type: section 175 | - entity: input_number.muriatic_tank 176 | name: Tank level 177 | - entity: input_number.muriatic_concentration 178 | name: Concentration 179 | - entity: input_number.muriatic_speed 180 | name: Speed injection 181 | - entity: switch.ph 182 | name: Pump injection 183 | secondary_info: last-changed 184 | - entity: input_boolean.lock_muriatic 185 | name: Lock Pump 186 | type: entities 187 | show_header_toggle: false 188 | - entities: 189 | - label: Bleach ( + ORP ) 190 | type: section 191 | - entity: input_number.bleach_tank 192 | name: Tank level 193 | - entity: input_number.bleach_concentration 194 | name: Concentration 195 | - entity: input_number.bleach_speed 196 | name: Speed injection 197 | - entity: switch.orp 198 | name: Pump injection 199 | secondary_info: last-changed 200 | - entity: input_boolean.lock_bleach 201 | name: Lock Pump 202 | type: entities 203 | show_header_toggle: false 204 | - badges: [] 205 | path: default_view 206 | icon: 'mdi:message-alert' 207 | title: Alerts 208 | cards: 209 | - title: Notifications 210 | entities: 211 | - entity: input_number.notify_cycles 212 | - entity: input_boolean.notify_seasson 213 | - entity: input_number.notify_muriatic_high 214 | - entity: input_number.notify_muriatic_tank 215 | - entity: input_number.notify_bleach_tank 216 | - entity: input_number.notify_bleach_high 217 | type: entities 218 | show_header_toggle: true 219 | - badges: [] 220 | title: Settings 221 | icon: 'mdi:tune' 222 | cards: 223 | - title: Pool Settings 224 | entities: 225 | - label: Pool 226 | type: section 227 | - entity: input_number.capacity 228 | name: Capacity 229 | - entity: input_number.quality 230 | name: Quality 231 | - entity: input_datetime.recirculation 232 | name: Turnover rate 233 | - entity: input_boolean.turbo 234 | - entity: input_number.turbo 235 | - label: Filter 236 | type: section 237 | - entity: input_datetime.cycle_start 238 | - entity: input_number.second_cycle 239 | - entity: sensor.cycle_pool 240 | - entity: input_number.cycle 241 | type: entities 242 | show_header_toggle: false 243 | theme: default 244 | - title: Chemical settings 245 | entities: 246 | - label: Free Chlorine (FC) 247 | type: section 248 | - entity: input_number.fc_target 249 | name: FC target 250 | - entity: sensor.e_fc 251 | name: FC actual 252 | - entity: input_number.bleach_inject 253 | name: Next bleach injection (ml) 254 | - entity: input_number.e_fc_adjust 255 | name: Adjust ±% FC-ORP 256 | - label: pH 257 | type: section 258 | - entity: input_number.ph_target 259 | name: pH target 260 | - entity: sensor.ph_mean 261 | name: pH actual 262 | - entity: input_number.muriatic_inject 263 | name: Next muriatic injection (ml) 264 | type: entities 265 | show_header_toggle: false 266 | - badges: 267 | - sun.sun 268 | - sensor.time 269 | - sensor.cycle_pool 270 | - input_number.out_of_order 271 | title: Advanced 272 | icon: 'mdi:settings' 273 | cards: 274 | - type: horizontal-stack 275 | cards: 276 | - icon_height: 64px 277 | type: entity-button 278 | tap_action: 279 | action: none 280 | entity: input_number.out_of_order 281 | show_name: true 282 | name: Maintenance 283 | show_icon: true 284 | hold_action: 285 | action: more-info 286 | - icon_height: 64px 287 | type: entity-button 288 | tap_action: 289 | action: more-info 290 | entity: input_datetime.cummulated_flow_start 291 | show_name: true 292 | name: Reset Cumm. Flow 293 | show_icon: true 294 | hold_action: 295 | action: call-service 296 | service: automation.trigger 297 | service_data: 298 | entity_id: automation.reset_cummulated_flow_start 299 | - icon_height: 64px 300 | type: entity-button 301 | tap_action: 302 | action: none 303 | entity: input_number.pump_on_for 304 | show_name: true 305 | name: Filter for ... 306 | show_icon: true 307 | hold_action: 308 | action: more-info 309 | - entities: 310 | - entity: input_datetime.cummulated_flow_start 311 | - entity: sensor.cummulated_flow_high 312 | - entity: sensor.cummulated_flow_low 313 | type: entities 314 | 315 | --------------------------------------------------------------------------------