├── 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 |
--------------------------------------------------------------------------------