les beaux jours arrivants, il va falloir remettre en route la régulation de la piscine.
du coup j'ai repris le scripts d'aleph0 viewtopic.php?f=9&t=3795 et je l'ai modifié car j'utilise les sorties de la raspberry qui est installée dans le local de la piscine équipée d'une carte 8 relais.
voici le scripts bibliothèque :
Code : Tout sélectionner
--Bibliothèque de fonctions lua pour scripts domoticz
function round(num, dec)
if num == 0 then
return 0
else
local mult = 10^(dec or 0)
return math.floor(num * mult + 0.5) / mult
end
end
function lastupdate(obj,isvar)
-- retourne le nombre de secondes écoulées depuis la dernière mise à jour d'un capteur ou variable
-- isvar : true si obj est une variable ; false ou optionel sinon
isvar=isvar or false
if isvar then
s=uservariables_lastupdate[obj]
else
s=otherdevices_lastupdate[obj]
end
s_year = string.sub(s, 1, 4)
s_month = string.sub(s, 6, 7)
s_day = string.sub(s, 9, 10)
s_hour = string.sub(s, 12, 13)
s_minutes = string.sub(s, 15, 16)
s_seconds = string.sub(s, 18, 19)
t2 = os.time{year=s_year, month=s_month, day=s_day, hour=s_hour, min=s_minutes, sec=s_seconds}
return os.difftime (os.time(), t2)
end
function Notification(texte)
-- Envoi "texte" en notification
print(texte)
commandArray[#commandArray+1] = {['SendNotification']="Domoticz#"..texte..'#0'}
end
function RefreshDev(device)
-- Recopie la valeur actuelle dans un capteur pour qu'il ne passe pas en rouge
commandArray[#commandArray+1] = {['UpdateDevice'] = otherdevices_idx[device]..'|0|'..tostring(otherdevices_svalues[device])}
end
function RefreshSP(device)
-- Recopie la valeur actuelle dans un setpoint pour qu'il ne passe pas en rouge
commandArray[#commandArray+1] = {['SetSetPoint:'..otherdevices_idx[device]] = tostring(otherdevices_svalues[device])}
end
function UpdateSw(device,commande)
--Update un switch Domoticz
commandArray[#commandArray+1] = {[device] = commande}
end
function UpdateVar(variable,valeur)
--Update une variable Domoticz
commandArray[#commandArray+1] = {['Variable:'..variable] = tostring(valeur)}
end
function UpdateDev(device,nvalue,svalues)
--Met à jour un device numérique Domoticz
commandArray[#commandArray+1] = {['UpdateDevice'] = otherdevices_idx[device]..'|'..tostring(nvalue)..'|'..tostring(svalues)}
end
function UpdateCpt(device,incr)
-- Met à jour un counter
-- device : nom du compteur
-- incr : Quantité à ajouter au compteur
local Cpt
Cpt = otherdevices_svalues[device]
Cpt=Cpt or 0 -- Utile pour les compteurs nouvellement crées
commandArray[#commandArray+1] = {['UpdateDevice'] = tostring(otherdevices_idx[device]).."|0|"..tostring(Cpt+incr)}
end
function UpdateP1(device,HC,conso,prod,freq)
-- Met à jour le P1 smart counter
-- device : nom du compteur
-- HC : "On" si heures creuses, "Off" si heures pleines
-- conso : consommation instantannée
-- prod : production instantannée
-- freq : frequence d'exécution du script (en minutes)
local u1, u2, p1, p2
local dbg=0
u1, u2, p1, p2 = string.match(otherdevices_svalues[device],"(.-);(.-);(.-);(.-);.*")
-- Utile pour les compteurs nouvellement crées
u1=u1 or 0
u2=u2 or 0
p1=p1 or 0
p2=p2 or 0
if dbg == 1 then
print("Mise à jour de "..tostring(device))
print("u1 "..tostring(u1))
print("u2 "..tostring(u2))
print("p1 "..tostring(p1))
print("p2 "..tostring(p2))
print("conso "..tostring(conso))
print("prod "..tostring(prod))
end
if otherdevices[HC] == 'Off' then
-- svalue=USAGE1;USAGE2;RETURN1;RETURN2;CONS;PROD
commandArray[#commandArray+1] = {['UpdateDevice'] = otherdevices_idx[device]..'|0|'..tostring(u1+conso*freq/60)..';'..tostring(u2)..';'..tostring(p1+prod*freq/60)..';'..tostring(p2)..';'..tostring(conso)..';'..tostring(prod)}
else
commandArray[#commandArray+1] = {['UpdateDevice'] = otherdevices_idx[device]..'|0|'..tostring(u1)..';'..tostring(u2+conso*freq/60)..';'..tostring(p1)..';'..tostring(p2+prod*freq/60)..';'..tostring(conso)..';'..tostring(prod)}
end
end
function ConsoDev(switch,ConsoMarche,ConsoVeille)
-- Retourne la consommation d'un appareil commandé
-- selon qu'il est en marche ou en veille
if otherdevices[switch] == 'On' then
return ConsoMarche
else
return ConsoVeille
end
end
function os.capture(cmd, raw)
local f = assert(io.popen(cmd, 'r'))
local s = assert(f:read('*a'))
f:close()
if raw then return s end
s = string.gsub(s, '^%s+', '')
s = string.gsub(s, '%s+$', '')
s = string.gsub(s, '[\n\r]+', ' ')
return s
end
function IsAlive(IP)
if os.capture("ping -qnc 1 -w1 "..IP.." >/dev/null && echo On || echo Off")=="On" then
return true
else
return false
end
end
function UpdateRmt(credentials,ipp,local_dev,remote_idx)
--[[mise à jour du même capteur sur un domoticz distant
credentials : login:pw sur le domoticz distant
ipp : ip:port du domoticz distant
local_dev : nom du capteur local à envoyer
remote_idx : index du capteur distant à mettre à jour
]]--
cmd = "http://"..credentials.."@"..ipp.."/json.htm?type=command¶m=udevice&idx="..tostring(remote_idx).."&nvalue=0&svalue="..otherdevices_svalues[local_dev]
--print(cmd)
os.execute('curl "'..cmd..'" &')
end
Code : Tout sélectionner
--[[ Script de gestion de la piscine
Fonctions :
1. Gestion de la chloration
1.1 En mode Off
On ne fait rien ; si la pompe chlore était en marche au moment du basculement, on l'arrête
1.2 En mode manuel
Sur appuie du bouton "injection chlore", on actionne la pompe à chlore
pour une durée correspondant au dosage demandé par la consigne manuelle
Si la filtration est arrếtée, on la redémarre le temps de l'injection et du mélange
1.2 En mode automatique :
La chloration automatique est demandée par le script de filtration. Il
s'assure donc lui-même que le filtration est démarrée depuis suffisament
longtemps pour que les mesures de pH et de redox soient significative de
l'eau de la piscine et non pas de l'eau des tuyaux ou du filtre
Dans le cas où l'utilisateur demande une chloration par le bouton
alors qu'on est filtration arrếtée et en mode auto... on utilise un temps
de pré-filtration plus long pour avoir le temps d'avoir des mesures pas trop fausses
1.3 Suivi du chlore injecté et de la quantité restante dans le bidon
On incrémente le compteur de chlore injecté
et on décrémente le device de chlore restant pour pouvoir avertir quand il est
presque vide et ne pas essayer d'injecter plus de chlore qu'il n'en reste dans
le bidon
2. Filtrage des mesures reçues par les capteurs pH et ORP
- On prends d'abord une moyenne sur 10 mesures (5min) pour filtrer les trop
grosses fluctuations
- On ne prends en comptes les mesures que quand la filtration marche, et
5 minutes après le début de celle-ci pour ne pas tenir compte des mesures
sur l'eau des tuyaux et du filtre
3. Gestion du débit bas de la pompe de filtration (pas utilisé dans mon cas)
Si le débit de la pompe de filtration est plus bas que la normale, il y a un problème.
On définit 2 seuils :
- L'un pour alerter que les filtres ou pré-filtres sont encrassés et qu'il est temps
de les nettoyer
- L'autre pour arrêter la pompe si le débit est très faible (désamorçage,
pompe coincée, etc...)
4. Gestion de la filtration
4.1 Calculer la durée et la période optimale de filtration
5.2 Démarrer / arrêter la filtration
Si température eau > 12°C, selon planning calculé au dessus
Sinon, marche si température air <1°C, arrêt si >2°C
6. Estime le bilan thermique de la piscine en fonction des apports solaire et des
dépertitions par convection (pas utilisé dans mon cas)
--]]
-- Paramètres communs
local dbPath = "/home/pi/domoticz/domoticz.db"
local dev_TAir = "Température Extérieure" -- Température extérieure
local dev_TEau = "Température piscine" -- Température eau piscine
local dev_Status = "Status Piscine" -- Capteur texte de log des évènements
-- Paramètres pour fonction 1. Filtration
local var_PPStart = "PpePiscStart" -- Variable contenant l'heure de départ de la filtration
local var_PPDuree = "PpePiscDuree" -- Variable contenant la durée de la filtration
local idx_DebitFl = 208 -- Index du débimètre de filtration, utilisé pour déduire la durée de filtration effective
local dev_DureeFl = "Temps de filtration" -- Capteur pour logger la durée de filtration calculée
local dev_PpeFl = "Filtration piscine" -- Interrupteur de commande de la pompe
local dev_ModeFl = "Mode filtration piscine" -- Selecteur du mode de la pompe (Manu/Auto HC/AutoHP)
--local dev_PpeFl_Q = "Débit filtration piscine"
local dev_cptFl = "Consommation Piscine" -- Compteur de consommation électrique piscine
local dev_HC = "Heures creuses EDF" -- Indicateur d'heure creuses EdF
local dev_FiltT ="Filtration temporisée" --Bouton de demande de la filtration temporisée
local dev_DureeFlManu="Durée de filtration manu (min)" -- Durée voulue
-- Paramètre pour chloration automatique
local dev_Melange = "Mélange en cours" -- Dummy switch pour indiquer au script filtration de ne pas arrếter la pompe
local dev_ModeCl = "Mode chloration piscine" -- Sélecteur de mode de Chloration
local dev_InjCl = "Injection Chlore" -- Bouton de demande d'injection de chlore
local dev_PpeCl = "Pompe Chlore" -- Commande de la pompe à Chlore
local dev_CptCl = "Consommation Chlore" -- Totaliseur de chlore injecté
local dev_FreeCl = "Chlore libre" -- Estimateur de Chlore libre
local dev_SPCl = "Dose de Chlore (ml)" -- Consigne pour injection manuelle
local dev_SPORP = "Consigne redox (mV)" -- Consigne pour injection automatique
local dev_SPBidonCl = "Capacité bidon chlore (ml)" -- Contenance du nouveau bidon de chlore
local dev_QBidonCl = "Chlore restant" -- Quantité de chlore restante dans le bidon
local dev_ChtBidonCl= "Changement bidon chlore" -- Bouton de changement du bidon de chlore
local dev_ORPt = "Tension sonde Redox" -- Valeur de la tension de la sonde Redox
local dev_ORPi = "ORP" -- ORP instantanné
local dev_ORPm = "Redox moyen" -- redox moyen sur 5 min
local Cal_QCl = 0.186 -- débit pompe doseuse en ml/s
local var_PpeClOld = "Etat pompe chlore"
local var_PpeClTime = "Heure Cht état pompe chlore"
local var_PpeClTodo = "Reste a faire pompe chlore"
local var_LastORP = "Last_ORP"
local var_IntORP = "Integrale ORP"
local var_KpRedox = "Kp redox"
local var_ORPvetal = "Valeur étalon Redox" -- Valeur étalon de l'échantillon Redox
local var_ORPvlue = "Valeur lue Redox" -- Valeur de la tension de l'échantillon Redox
-- Paramètres pour régulation pH
local dev_ModeAc = "Mode régulation pH" -- Sélecteur de mode de régulation pH
local dev_InjAc = "Injection Acide" -- Bouton de demande d'injection d'acide
local dev_PpeAc = "Pompe Acide" -- Commande de la pompe d'Acide
local dev_CptAc = "Consommation Acide" -- Totaliseur de chlore injecté
local dev_SPAc = "Dose Acide (ml)" -- Consigne pour injection manuelle
local dev_SPpH = "Consigne pH" -- Consigne pour injection automatique
local dev_SPBidonAc = "Capacité bidon acide (ml)" -- Contenance du nouveau bidon de acide
local dev_QBidonAc = "Acide restant" -- Quantité d'acide restante dans le bidon
local dev_ChtBidonAc = "Changement bidon acide" -- Bouton de changement du bidon de acide
local dev_pHt = "Tension sonde pH" -- Valeur de la tension de la sonde pH
local dev_pHi = "pH" -- pH instantanné
local dev_pHm = "pH moyen" -- pH moyen sur 5 min
local Cal_QAc = 0.186 -- débit pompe doseuse en ml/s
local var_PpeAcOld = "Etat pompe acide"
local var_PpeAcTime = "Heure Cht état pompe acide"
local var_PpeAcTodo = "Reste a faire pompe acide"
local var_KpAcide = "Kp acide"
local var_pHetal1 = "Valeur étalon 1 pH" -- Valeur étalon de l'échantillon n°1
local var_pHetal2 = "Valeur étalon 2 pH" -- Valeur étalon de l'échantillon n°2
local var_pHvetal1 = "Valeur lue étalon 1 pH" -- Valeur de la tension de l'échantillon n°1
local var_pHvetal2 = "Valeur lue étalon 2 pH" -- Valeur de la tension de l'échantillon n°2
local dev_PpeDos ="Relais marche" --1=pompe dosage en marche
local dev_ClAc ="Relais chlore" --0=pompe chlore ; 1=pompe acide
-- Réglages
local cst_PpeFl_Qn=14 -- Débit nominal de la pompe de filtration en m3/h
local FiltrPreInjection=60 -- durée en secondes de démarrage de la filtration avant l'injection Chlore
local FiltrPostInjection=1500 -- durée en secondes du mélange nécessaire après une injection chlore
local seuil_PpeFl_Q_LL=20 -- seuil d'arrêt de la pompe sur débit bas en l/min desactivé en attendant de trouver qqch de fiable
local seuil_PpeFl_Q_L =80 -- seuil d'alerte sur débit bas pompe en l/min
local cst_pctCl=9.6 -- % de chlore dans la javel
local cst_Pisc_V=55 -- volume du bassin, en m3
local cst_PpeFl_W =750 -- Puissance électrique de la pompe piscine
local cst_Adj =0.25 -- Coefficient à ajuster en + ou en - pour filtrer plus ou moins longtemps
-- -------------------------------
-- Bibliothèque de fonctions
-- -------------------------------
local debug=1
dofile('/home/pi/domoticz/scripts/lua/bibliotheque.lua')
-- Functions specific to this script
function RaspInj(Ppe,LocalDev,Pause,Duree)
-- Demande à la raspberry d'injecter du chlore pour une Duree après un délai de Pause
-- Ppe = "Cl" ou "Ac"
-- LocalDev= Bouton local de commande de la pompe
-- Pause = Durée de la pause avant l'injection
-- Duree = Durée de l'injection
print ("Temps de pause avant démarrage : "..tostring(Pause).."sec")
if Ppe=="Cl" then
print ("Durée d'injection de Chlore : "..tostring(Duree).."sec")
UpdateSw(LocalDev,'On AFTER '..tostring(Pause))
elseif Ppe=="Ac" then
print ("Durée d'injection d'Acide : "..tostring(Duree).."sec")
UpdateSw(LocalDev,'On AFTER '..tostring(Pause))
else
print("Erreur RaspInj : Pompe inconnue. Doit être Cl ou Ac")
end
-- La pompe est normalement arrêtée par la raspberry et son statut mis à jour
-- On double ici la commande d'arrêt au cas ou
UpdateSw(LocalDev,'Off AFTER '..tostring(Pause+Duree))
end
function ChgBidon(dev_ChtBidon,dev_QBidon,dev_SPBidon)
--[[Gestion du changement de bidon
dev_ChtBidon = swicth indiquant un changement de bidon
dev_QBidon = Device stockant la quantité restante dans le bidon
dev_SPBidon = Setpoint contenant la nouvelle quantité dans le bidon
--]]
if devicechanged[dev_ChtBidon] and otherdevices[dev_ChtBidon]=="On" then
UpdateSw(dev_ChtBidon,"Off")
UpdateDev(dev_QBidon,0,otherdevices_svalues[dev_SPBidon])
end
end
function MaJInjection(dev_Ppe,dev_Cpt,dev_QBidon,var_PpeOld,var_PpeTime,var_PpeTodo,Cal_Q,contenu,wrn_QBidon)
--[[ Calcul de la quantité injecté
- Les commandes de pompes sont passées par des longpulses ou par le bouton
- Le statut du bouton est mis à jour par la raspberry
- En cas de passage de off à on, on stocke l'heure dans une variable (nb de sec depuis minuit)
- En cas de passage de on à off, on calcule la durée de marche et on met à jour les compteurs avec ça
dev_Ppe = Switch de commande de la pompe d'injection
dev_Cpt = Compteur de suivi de la quantité injectée
dev_QBidon=Device de quantité restante
var_PpeOld = État précédent de la pompe d'injection
var_PpeTime= Heure d'enclenchement de la pompe d'injection
var_PpeTodo= Durée d'injection restant à faire
cal_Q = calibration de la pompe d'injection
contenu = texte spécifiant le contenu du bidon
--]]
local val_PpeOld, val_PpeTime, Q, val_QBidon, Duree_effective
if devicechanged[dev_Ppe] then
val_PpeOld=uservariables[var_PpeOld]
if otherdevices[dev_Ppe]=="On" and val_PpeOld=="Off" then
-- Passage de Off à On
print("Démarrage "..dev_Ppe)
UpdateVar(var_PpeOld,"On")
time = os.date("*t")
UpdateVar(var_PpeTime,3600*time.hour+60*time.min+time.sec)
end
if otherdevices[dev_Ppe]=="Off" and val_PpeOld=="On" then
-- Passage de On à Off
print("Arrêt "..dev_Ppe)
UpdateVar(var_PpeOld,"Off")
time = os.date("*t")
val_PpeTime=uservariables[var_PpeTime]
Duree_effective=3600*time.hour+60*time.min+time.sec-val_PpeTime
Q=round(Duree_effective*Cal_Q,0)
print("Quantitée réellement injectée :"..tostring(Q).." ml")
-- Mise à jour du compteur de consommation de produit
UpdateCpt(dev_Cpt,Q)
-- Mise à jour de la quantité restante de chlore
val_QBidon=otherdevices_svalues[dev_QBidon]-Q
UpdateDev(dev_QBidon,0,val_QBidon)
--Vérification quantité restante dans le bidon
if val_QBidon<=wrn_QBidon then
Notification('Il reste seulement '..val_QBidon..' ml de '..contenu..' : Remplir le bidon rapidement')
end
--Mise à jour de la variable "reste à faire"
UpdateVar(var_PpeTodo,math.max(0,uservariables[var_PpeTodo]-Duree_effective))
end
end
end
function ChkQBidon(Q,dev_QBidon,contenu)
-- Vérifie que la quantité Q se trouve bien dans le bidon dev_QBidon
-- et renvoi le min de Q ou de la quantité restante
local val_QBidon
local min_QBidon=300 -- on ne pompe pas en dessous de 300ml dans le bidon (desamorçage)
val_QBidon=tonumber(otherdevices_svalues[dev_QBidon])
if Q > val_QBidon-min_QBidon then
print("Erreur : On demande a injecter "..Q.."ml de "..contenu.." alors qu'il reste seulement "..val_QBidon.."ml dans le bidon" )
print("Quantité à injecter réduite en conséquence")
Notification('Bidon de '..contenu..' vide : Le remplir immédiatement')
end
return math.min(Q,val_QBidon-min_QBidon)
end
function CalcFreeCl(pH,ORP)
-- Calcule le chlore libre en fonction du pH et du redox de l'eau
pH=tonumber(pH)
ORP=tonumber(ORP)
return round(10^((ORP-715-50*(7-pH))/(300+50*(7-pH))),2)
end
function CalcFiltration()
--[[ Calcule
- la durée optimale de filtration en fonction de la Température
de l'eau, du débit de la pompe et du volume du bassin
- L'heure de début de la filtration selon qu'on veut filtrer en heure creuses ou pleines
Utilise les variables globales suivantes
dev_Teau : device du thermomètre d'eau
dev_Mode : le sélecteur de mode de filtration
cst_Qpompe : débit nominal de la pompe en m3/h
cst_Vbassin : Volume du bassin en m3
var_PPStart : Nom de la variable qui va stocker l'heure de départ de la filtration
var_PPDuree : Nom de la vatiable qui va stocker la durée de filtration
--]]
local val_TEau, MidiSol, DebutFiltration, dureeFiltration, HDebFilt, HFinFiltr
print("Script Piscine : Calcul de la filtration")
val_TEau=os.capture('sqlite3 '..dbPath..' "select temp_avg from temperature_calendar where devicerowid='..otherdevices_idx[dev_TEau]..' order by date desc limit 1"')
val_TEau=round(val_TEau,1)
print("Température moyenne hier : "..val_TEau.." °C")
dureeFiltration=round(60*math.max(0,cst_Adj*cst_Pisc_V/cst_PpeFl_Qn*((val_TEau))-10),0) -- Résultat en minutes
--dureeFiltration=round(60*math.max(10/60,0.65*(val_TEau-10)),0) -- Résultat en minutes ; durée min=10min
print("Durée de filtration : "..dureeFiltration.." min")
if otherdevices[dev_ModeFl] == "Auto HC" then
-- Mode Auto Heures Creuses
-- On filtre jusqu'à 7h00 si possible (=420 min), et on déborde si nécessaire
DebutFiltration=math.max(10,420-dureeFiltration)
else
-- Mode Auto Heures Pleines ou Manu
--On filtre en journée, centré sur le midi solaire
MidiSol=round((timeofday['SunriseInMinutes']+timeofday['SunsetInMinutes'])/2,0) -- en minutes
DebutFiltration=round(MidiSol-dureeFiltration/2,0) -- en minutes
-- On termine au coucher du soleil pour avoir le temps de remonter le taux de chlore
-- après les grosses chaleurs
--DebutFiltration=timeofday['SunsetInMinutes']-dureeFiltration
end
HDebFilt=os.date("%H:%M", DebutFiltration*60-3600)
HFinFiltr=os.date("%H:%M", (DebutFiltration+dureeFiltration)*60-3600)
print("Filtration de "..HDebFilt.." à "..HFinFiltr.." soit "..dureeFiltration.." min")
print("Début de filtration : "..HDebFilt)
UpdateVar(var_PPStart,HDebFilt)
UpdateVar(var_PPDuree,dureeFiltration)
UpdateDev(dev_Status,0,"------ Nouvelle journée ------")
UpdateDev(dev_Status,0,"Filtration de "..HDebFilt.." à "..HFinFiltr)
end
function ChkFiltration()
--[[Gestion de la filtration (Mode de la filtration Manu / Auto HC / Auto HP)
Start / Stop de la pompe
Demande de chloration automatique
Demande d'acidification automatique
--]]
local Min_last_injection=23*60+30 -- Heure maxi de la denière injection en minute
local Min_last_stop=23*60+45 -- Heure maxi pour arrêter la filtration en minute
print("Script Piscine : Gestion de la filtration")
PpeDuree=tonumber(uservariables[var_PPDuree])
val_ModeFl=otherdevices[dev_ModeFl]
val_PpeFl=otherdevices[dev_PpeFl]
val_FlagCl=otherdevices[dev_Melange]
if (val_ModeFl ~= "Manu") then
print("Température de la piscine : "..otherdevices_svalues[dev_TEau].." °C")
--val_TEau = tonumber(string.match(otherdevices_svalues[dev_TEau],"(%d+.%d*);.*"))
--val_TEau = tonumber(string.match(otherdevices_svalues[dev_TEau],"(.-);.*"))
val_TEau=tonumber(otherdevices_svalues[dev_TEau])
if val_TEau >= 12 or time.wday==1 then
-- La témpérature d'eau est >=12°C : on utilise la temporisation
-- le dimanche, on by-pass la température. couplée à une durée de filtration mini non nulle, ça
-- permet de forcer le démarrage de la pompe 1x/semaine en hiver pour éviter les grippages
MinDuJour=60*time.hour+time.min
PpeStart=tonumber(string.sub(uservariables[var_PPStart],1,2))*60+tonumber(string.sub(uservariables[var_PPStart],4,5))
-- Calcul de la durée de fonctionnement de la pompe aujourd'hui
-- Basé sur le débit de la pompe
today = os.date("%Y-%m-%d")
--PpeFait=os.capture('sqlite3 '..dbPath..' "select sum(percentage) from percentage where devicerowid='..idx_DebitFl..' and Date(date)=\''..today..'\'"')*5/cst_DebitFlNom
PpeFait=os.capture('sqlite3 '..dbPath..' "select sum(value3) from multimeter where devicerowid='..otherdevices_idx[dev_cptFl]..' and Date(date)=\''..today..'\'"')*5/cst_PpeFl_W
print("Déjà effectué : "..PpeFait.." minutes sur "..PpeDuree)
if (MinDuJour >= PpeStart and PpeFait < PpeDuree and val_PpeFl == "Off") then
print("Filtration piscine : Démarrage par timer")
UpdateSw(dev_PpeFl,'On')
UpdateDev(dev_Status,0,"Démarrage par timer")
elseif val_PpeFl == "On" and ((val_FlagCl == "Off" and PpeFait >= PpeDuree) or MinDuJour>=Min_last_stop) then
-- Hint : On n'arrête pas la pompe si une chloration est en cours
print("Filtration piscine : Arrêt par timer")
--[[Après l'arrêt de la pompe, la chute de pression permet à un peu d'acide et de chlore de rejoindre
la canalisation principale et risque d'endomager les sondes (elasticité des tuyaux d'injection). C'est
pour cette raison qu'on rallume la filtration 1 minutes après l'arrêt pour évacuer vers la piscine
ce petit surplus de produit
]]--
UpdateSw(dev_PpeFl,'Off')
UpdateSw(dev_PpeFl,'On AFTER 2 MINUTE')
UpdateSw(dev_PpeFl,'Off AFTER 3 MINUTES')
UpdateDev(dev_Status,0,"Arrêt par timer")
end
--[[ Gestion de l'acidification automatique :
Si on est en acidification automatique, une seule acidification à 30min après la filtration
!! Prendre garde que les tests pour acide et chlore soient mutuellement exclusif
de manière à ne pas positionner les 2 switches en même temps
--]]
if otherdevices[dev_ModeAc]== "Auto" and val_PpeFl=="On" and val_FlagCl=="Off"
and PpeFait >= 25 and PpeFait <=PpeDuree/2 then
print("Demande d'acidification automatique pendant filtration en automatique")
UpdateSw(dev_InjAc,'On')
DdeInjAc=true
end
--[[ Gestion de la chloration automatique :
Si on est en chloration automatique, et que la filtration a marché 1h
et qu'il reste encore 1/2h de filtration avant la fin
--]]
if otherdevices[dev_ModeCl]== "Auto" and val_PpeFl=="On" and val_FlagCl=="Off" and DdeInjAc~=true
and PpeFait > PpeDuree/2 and (PpeDuree-PpeFait) >30 and MinDuJour<=Min_last_injection then
print("Demande de chloration automatique pendant filtration en automatique")
UpdateSw(dev_InjCl,'On')
end
elseif gst_HorsGel then
--[[ La température de l'eau est <12°C : on utilise la température
Démarrage si température d'air <= 1
Arrêt si température d'air >= 1.5 et température eau >= 0.5
--]]
val_Tair = tonumber(otherdevices_svalues[dev_TAir])
if (val_Tair <= 1) and val_PpeFl == "Off" then
-- Filtration On
print("Filtration piscine : Démarrage par température")
UpdateDev(dev_Status,0,"Démarrage par température")
UpdateSw(dev_PpeFl,'On')
elseif val_Tair >= 1.5 and val_TEau>=0.5 and val_PpeFl == "On" and val_FlagCl == "Off" then
-- Filtration Off
print("Filtration piscine : Arrêt par température")
UpdateDev(dev_Status,0,"Arrêt par température")
UpdateSw(dev_PpeFl,'Off')
end
end -- filtration timer/temperature
else
print("Mode manu, pas de changement")
if otherdevices[dev_ModeCl]== "Auto" and val_FlagCl=="Off" and val_PpeFl=="On" then
print("Demande de chloration automatique pendant filtration en manuel")
UpdateSw(dev_InjCl,'On')
end
end -- end mode auto/manu
-- Mise à jour du capteur loggant la durée de filtration
UpdateDev(dev_DureeFl,0,round(PpeDuree/60,2))
end
function BilanThermique()
-- Calcule le bilan thermique de la piscine
local dev_BThPisc = "Bilan Th piscine" -- Virtual custom sensor pour bilan puissance piscine
local dev_sunAlt = "Sun Altitude"
local var_ASP = "ApportSolairePiscine" -- variable contenant l'incidence solaires, en W/m², estimés par le script script_time_SolarSensor.lua
local dev_TEauEst = "Température piscine estimée"
local Sbache = 20 -- m²
local Sparoi = 20 -- m²
local h = 10.5 -- W/m²K, coefficient de convection
local Ksol = 1.2 -- W/m²K, conductance thermique du sol
local Tsol = 15 -- °C, température du sol sous la piscine, à 10cm de profondeur
local eps = 0.95 -- emissivité de la bache
local eff = 0.8 -- %, proportion de la bache touchant l'eau directement
local val_TEau, val_Tair, val_sunAlt, val_ASP, Deperditions,NewTEau
local TEau_estimee=0
local lcldbg=0
-- val_TEau = tonumber(string.match(otherdevices_svalues[dev_TEau],"(%d+.%d*);.*"))
--val_TEau = tonumber(string.match(otherdevices_svalues[dev_TEau],"(.-);.*"))
val_TEau=tonumber(otherdevices_svalues[dev_TEau])
if val_TEau >= -10 and val_TEau <= 45 then
-- On ne calcule le bilan thermique que si la température d'eau est réaliste...
-- Car on reçoit parfois des 74.9°C :(
-- Il manque les pertes par évaporation et par rayonnement en hivers...
val_Tair = otherdevices_svalues[dev_TAir]
val_sunAlt=otherdevices[dev_sunAlt]
val_ASP = uservariables[var_ASP]*eps*eff*Sbache*math.sin(math.rad(val_sunAlt))
Deperditions = round((val_TEau-val_Tair)*((Sbache+Sparoi)*h)+(val_TEau-Tsol)*Sbache*Ksol,0)
UpdateDev(dev_BThPisc,0,val_ASP-Deperditions)
--Workaround pour le thermomètre de piscine cassé -> calcul d'une estimation sur la base du bilan thermique
val_TEauEst = tonumber(string.match(otherdevices_svalues[dev_TEauEst],"(.-);.*"))
-- val_TEauEst=val_TEau
NewTEau=round(val_TEauEst+(val_ASP-Deperditions)/4128/24/1000*5*60,3)
if lcldbg==1 then
print("TEau "..val_TEauEst)
print("TAir "..val_Tair)
print("ASP "..val_ASP)
print("Perte "..Deperditions)
print("Nouvelle température d'eau "..NewTEau)
end
UpdateDev(dev_TEauEst,0,tostring(NewTEau)..";13;0")
if TEau_estimee==1 then
-- NewTEau=18.8
UpdateDev(dev_TEau,0,tostring(NewTEau)..";13;0")
end
else
print("Température d'eau incorrecte ; bilan thermique non calculé")
end
end
function ChkFlow()
--[[
******************************************************************
Gestion du débit bas de la pompe de filtration
******************************************************************
Le débit est remonté toutes les minutes. La première minute est
donc potentiellement à 0 et la deuxième pas forcément exacte
On ne prends donc une action d'à partir de la deuxième minute de filtration
pour laisser le temps au débit d'être mesuré correctement
--]]
if debug==2 then
print("État filtration : "..otherdevices[dev_PpeFl])
print("Lastupdate : "..lastupdate(dev_PpeFl).." s")
print("Débit filtration : "..otherdevices[dev_PpeFl_Q].." l/min")
end
if devicechanged[dev_PpeFl_Q] and otherdevices[dev_PpeFl] == "On"
and lastupdate(dev_PpeFl)>120 then
val_PpeQ=tonumber(otherdevices[dev_PpeFl_Q])
if val_PpeQ<=seuil_PpeFl_Q_LL then
Notification("Débit pompe piscine trop bas ("..val_PpeQ.." l/min)")
-- print("débit pompe parti en couille : "..val_PpeQ.." l/min")
-- On passe la filtration en manu et on arrête la pompe (pour éviter que la filtration auto ne la redémarre !
-- Penser à interdire la chloration dans ce cas là : Hint : ne chlorer que si le débit est suffisant
-- UpdateSw(dev_ModeFl,'Set Level 10')
UpdateSw(dev_PpeCl,'Off')
UpdateSw(dev_PpeFl,'Off AFTER 5')
elseif val_PpeQ<=seuil_PpeFl_Q_L then
time = os.date("*t")
-- On n'alerte pour ça que le samedi à 14h car l'action n'est pas urgente
-- Todo : Encore faut-il qu'on pompe à cette heure là, mais c'est probable
if time.wday==7 and time.hour==14 and time.min==00 then
Notification('Débit pompe piscine bas : Filtres encrassés')
end
end
end
end
function CalcAvg()
--[[****************************************************************************
Calcul du pH et ORP moyen sur une minute pour la régulation
********************************************************************************
Uniquement quand la filtration fonctionne depuis plus de 5min
et que l'injection est arrêtée car elle perturbe la mesure
--]]
if otherdevices[dev_PpeFl]=="On" and lastupdate(dev_PpeFl)>=300
and otherdevices[dev_PpeCl]=="Off" and otherdevices[dev_PpeAc]=="Off" then
if (devicechanged[dev_pHt]) then --moyenne glissante sur 15min (30 valeurs)
UpdateDev(dev_pHm,0,(29*tonumber(otherdevices_svalues[dev_pHm])+otherdevices_svalues[dev_pHi])/30)
end
if (devicechanged[dev_ORPt]) then
-- Calcul de la moyenne glissante sur 15 min (30 valeurs) pour affichage et action P de la régulation
val_ORPi=otherdevices[dev_ORPi]
val_ORPm=(29*tonumber(otherdevices[dev_ORPm])+tonumber(val_ORPi))/30
UpdateDev(dev_ORPm,0,val_ORPm)
-- Calcul de la moyenne glissante sur 60min (120 valeurs) pour action D de la régulation ORP
UpdateVar(var_LastORP,(119*uservariables[var_LastORP]+val_ORPi)/120)
--[[ Calcul de l'intégrale ORPm-SPORP pour l'action I de la régulation ORP
val_SPORP=otherdevices_svalues[dev_SPORP]
UpdateVar(var_IntORP,uservariables[var_IntORP]+(val_SPORP-val_ORPm)/30) --]]
end
end
end
function CalcClAuto()
-- La chloration auto est uniquement déclenchée pendant la filtration.
local PID_Kp=9.7 -- ml de Chlore par mV de redox voulu...
local PID_Ki=0 -- Paramètre integrale
local PID_Kd=10 -- Paramètre dérivée
local PID_dB=0 -- bande morte : 10mV
val_SPORP=otherdevices_svalues[dev_SPORP] -- lecture de la consigne redox
val_ORP=otherdevices[dev_ORPm] -- lecture de la valeur redox actuelle (moyenne 5min)
val_LastORP=uservariables[var_LastORP] -- lecture de la valeur redox précédente (il y a 30min)
val_pH=tonumber(otherdevices[dev_pHm])
if math.abs(val_SPORP-val_ORP)>PID_dB and (val_LastORP-val_ORP) >= -2 and val_pH<7.5 then
val_IntORP=uservariables[var_IntORP] -- lecture de l'erreur intégrale
--Calcul de la quantité de chlore à injecter
Cl_actuel=CalcFreeCl(val_pH,val_ORP)
Cl_voulu=CalcFreeCl(val_pH,val_SPORP)
--QCl_P=PID_Kp*100*(Cl_voulu - Cl_actuel)/cst_pctCl*cst_volPisc
QCl_P=PID_Kp*100*(Cl_voulu - Cl_actuel)/cst_pctCl*cst_Pisc_V
QCl_I=PID_Ki*val_IntORP
QCl_D=PID_Kd*(val_LastORP-val_ORP)
QCl=round(math.max(0,QCl_P+QCl_I+QCl_D),0)
else
if math.abs(val_SPORP-val_ORP)<=PID_dB then
print("ORP dans la bande morte : aucune action à prendre")
end
if (val_LastORP-val_ORP) <= -1 then
print("ORP croissante : aucune action à prendre")
end
if val_pH>=7.5 then
print("pH >=7.5 : Régulation automatique non pertinente")
end
QCl=0
end
if debug==1 then
print("Consigne..............: "..tostring(val_SPORP).." mV")
print("Mesure actuelle.......: "..tostring(round(val_ORP,0)).." mV")
print("Mesure il y a 60 min..: "..tostring(round(val_LastORP,0)).." mV")
print("Action proportionnelle: "..tostring(round(QCl_P or 0,0)).." ml")
print("Action intégrale......: "..tostring(round(QCl_I or 0,0)).." ml")
print("Action dérivée........: "..tostring(round(QCl_D or 0,0)).." ml")
print("Action totale.........: "..tostring(QCl).." ml")
print ("Chlore actuel : "..tostring(Cl_actuel))
print ("Chlore voulu : "..tostring(Cl_voulu))
end
return QCl
end
function CalcAcAuto()
local val_SPpH, val_pH, QAc
local PID_Kp=1500 -- 150 ml pour 0.1 de pH
val_SPpH=otherdevices_svalues[dev_SPpH]
val_pH=tonumber(otherdevices[dev_pHm])
QAc_P=PID_Kp*(val_pH-val_SPpH)
QAc=round(math.max(0,QAc_P),0)
if debug==1 then
print("Consigne..............: "..tostring(val_SPpH))
print("Mesure actuelle.......: "..tostring(round(val_pH,2)))
print("Action proportionnelle: "..tostring(round(QAc_P or 0,0)).." ml")
print("Action totale.........: "..tostring(QAc).." ml")
end
return QAc
end
function DoInjection(Q,Cal_Q,dev_Ppe,produit,var_PpeTodo)
--[[ gère l'injection d'une quantité Q de produit avec la pompe dev_Ppe et le coefficient
de calibration Cal_Q et met à jour la variable var_PpeTodo
Variable globale :
dev_PpeFl, la pompe de filtration
dev_Melange, le flag de mélange en cours
FiltrPreInjection
FiltrPostInjection
--]]
local FiltrationSup,Ppe_Duree,Pr
if Q >= 5 then
-- On ne prends une action que si la consigne d'injection suffisante
-- Side effect : Régler la consigne à 0 permet de désactiver l'injection facilement
print("Script Injection "..produit.." piscine : Injection de "..Q.."ml de "..produit)
if otherdevices[dev_PpeFl]=='Off' then
-- On demande d'injecter du chlore alors que la filtration est arrếtée
-- Il faut donc :
-- * démarrer la filtration un peu avant la chloration (~60s)
-- * Injecter le Chlore (Q/Cal_Q secondes)
-- * Maintenir la filtration ~30min après l'injection pour répartir le chlore dans tout le bassin
FiltrationSup=Q+FiltrPreInjection+FiltrPostInjection
print("La filtration est arrêtée, on la redémarre pour une durée de "..tostring(round(FiltrationSup/60,0)).." minutes")
UpdateSw(dev_PpeFl,'On FOR '..tostring(round(FiltrationSup/60,0)))
UpdateDev(dev_Status,0,"Démarrage pour injection pendant "..tostring(round(FiltrationSup/60,0)).." minutes")
else
FiltrPreInjection=0
FiltrationSup=Q+FiltrPostInjection
end
-- Mise en marche de la pompe doseuse pour une durée correspondante à la quantité de Chlore à injecter
Ppe_Duree=round(Q/Cal_Q,0)
if produit=="chlore" then Pr="Cl" else Pr="Ac"
end
RaspInj(Pr,dev_Ppe,FiltrPreInjection,Ppe_Duree)
--Positionnement de la variable "reste à faire pompe"
--pendant l'injection
UpdateVar(var_PpeTodo,Ppe_Duree)
-- Mise à jour du switch "Mélange en cours" pour éviter que le script
-- de filtration n'arrête la Pompe pendant la chloration ou le temps de mélange
-- ou ne redemande une nouvelle chloration pendant le temps de mélange
UpdateSw(dev_Melange,'On FOR '..tostring(round(FiltrationSup/60,0)))
UpdateDev(dev_Status,0,"Injection de "..Q.."ml de "..produit)
else
print("Script Injection "..produit.." piscine: La consigne ("..Q.." ml) est trop faible : Pas d'injection")
end
end
function ManagePumps()
--[[Gère les pompes peristaltiques
Elles ne doivent pas être commandées directement,
InjCl, InjCl en production
PpeCl_On, PpeAc_On et Ppe_Off en tests
Qui gèrent la commutation Cl/Ac et la mise en marche des pompes
]]--
-- local dev_PpeDos="Relais marche" --1=pompe dosage en marche
-- local dev_ClAc="Relais chlore" --0=pompe chlore ; 1=pompe acide
-- Action (uniquement pour debug et/ou amorçage des pompes)
--Mise à jour des switches sans actions
if devicechanged[dev_PpeAc]=="On" then
UpdateSw(dev_PpeDos,"On")
UpdateSw(dev_ClAc,"On")
elseif devicechanged[dev_PpeCl]=="On" then
UpdateSw(dev_PpeDos,"On")
UpdateSw(dev_ClAc,"Off")
elseif devicechanged[dev_PpeAc]=="Off" or devicechanged[dev_PpeCl]=="Off" then
UpdateSw(dev_PpeDos,"Off")
UpdateSw(dev_ClAc,"Off")
end
--end
end
function CalcAvgKp(mesure,conso)
--[[fonction pour recalculer le Kp moyen sur une semaine pour le pH et l'ORP
mesure : device contenant la mesure
conso : device counter contenant la quantité injectée]]--
local OneWeekAgo,Kp,sql
local dbg=0
OneWeekAgo=os.date("%Y-%m-%d",os.time()-7*24*3600)
--OneWeekAgo=os.date("%Y-%m-%d",os.time()-1*24*3600)
sql= "select avg(value/(Percentage_Max-Percentage_Min))"
sql=sql.." from percentage_calendar,meter_calendar"
sql=sql.." where percentage_calendar.devicerowid="..otherdevices_idx[mesure]
sql=sql.." and meter_calendar.devicerowid="..otherdevices_idx[conso]
sql=sql.." and meter_calendar.date=percentage_calendar.date"
sql=sql.." and meter_calendar.date>='"..OneWeekAgo.."'"
if dbg==1 then
print("CalcAvgKp")
print(OneWeekAgo)
print(sql)
end
Kp=os.capture('sqlite3 '..dbPath..' "'..sql..'"')
print("Kp moyen hebdo pour "..mesure.." : "..tostring(Kp))
return Kp
end
function CalcORP()
--[[fonction pour le calcul de la valeur de l'ORP en tenant compte de la valeur étalon]]--
local val_VORP
val_ORPt = tonumber(otherdevices_svalues[dev_ORPt])
val_ORPvetal = uservariables[var_ORPvetal]
val_ORPvlue = uservariables[var_ORPvlue]
if devicechanged[dev_ORPt] then
val_VORP = round(((val_ORPt*val_ORPvetal)/val_ORPvlue),0)
if val_VORP >2000 then
val_VORP = 2000
print ("Erreur de mesure sur la sonde de redox")
elseif val_VORP <-2000 then
val_VORP = -2000
print ("erreur de mesure sur la sonde de redox")
end
UpdateDev(dev_ORPi,0,val_VORP)
end
end
function CalcpH()
--[[fonction pour le calcul de la valeur du pH en fonction de 2 valeurs pour l'étalonnage de la sonde pH]]--
local val_VpH
val_pHt = tonumber(otherdevices_svalues[dev_pHt])
val_pHetal1 = uservariables[var_pHetal1]
val_pHetal2 = uservariables[var_pHetal2]
val_pHvetal1 = uservariables[var_pHvetal1]
val_pHvetal2 = uservariables[var_pHvetal2]
if devicechanged[dev_pHt] then
val_VpH = round(val_pHetal1+(val_pHetal2-val_pHetal1)*((val_pHt-val_pHvetal1)/(val_pHvetal2-val_pHvetal1)),1)
if val_VpH >14 then
val_VpH=14
print ("Valeur erronnée du pH")
elseif val_VpH<0 then
val_VpH=0
print ("Valeur erronnée du pH ")
end
UpdateDev(dev_pHi,0,val_VpH)
end
end
-- -------------------------------
-- Fin bibliothèque de fonctions
-- -------------------------------
commandArray = {}
if devicechanged == nil then -- partie "Time"
time = os.date("*t")
--Calcul de la durée optimale de filtration et de l'heure de démarrage
if time.hour==0 and time.min ==1 then
--if (((time.min-1) % 5)== 0) then
CalcFiltration()
UpdateVar(var_KpRedox,round(CalcAvgKp(dev_ORPm,dev_CptCl),0))
UpdateVar(var_KpAcide,round(CalcAvgKp(dev_pHm, dev_CptAc),0))
end
-- MàJ du calcul de la pompe de filtration
if (((time.min-2) % 15)== 0) then ChkFiltration() end
-- Mise à jour du compteur de consommation de la piscine toutes les 5min
if (((time.min-2) % 5)== 0) then
UpdateP1(dev_cptFl,dev_HC,ConsoDev(dev_PpeFl,cst_PpeFl_W,0),0,5)
end
--[[ Mise à jour des capteurs et consignes toutes les heures
simple recopie de la valeur pour qu'ils ne passent pas en rouge... --]]
if time.min==5 then
RefreshDev(dev_CptCl)
RefreshDev(dev_QBidonCl)
RefreshDev(dev_CptAc)
RefreshDev(dev_QBidonAc)
RefreshDev(dev_FreeCl)
RefreshDev(dev_pHm)
RefreshDev(dev_ORPm)
RefreshSP(dev_SPBidonCl)
RefreshSP(dev_SPORP)
RefreshSP(dev_SPCl)
RefreshSP(dev_SPBidonAc)
RefreshSP(dev_SPpH)
RefreshSP(dev_SPAc)
RefreshSP(dev_DureeFlManu)
end
--[[ Bilan thermique mis à jour toutes les 5 minutes décalée d'1 minute pour
être sûr que les variables calculées par le script Solar sont bien à jour--]]
--if (((time.min - 1)% 5)==0) then BilanThermique() end
--[[ Calcul du chlore libre, seulement pendant la filtration
Toutes les minutes et 5 minutes après le démarrage de la pompe de filtraton--]]
if otherdevices[dev_PpeFl]=="On" and lastupdate(dev_PpeFl)>=300 then
UpdateDev(dev_FreeCl,0,CalcFreeCl(otherdevices[dev_pHm],otherdevices[dev_ORPm]))
end
-- Fin partie "Time"
else -- partie "Device"
-- 0. Gestion de la filtration temporisée
if devicechanged[dev_FiltT]=="On" and otherdevices[dev_PpeFl]=="Off" and otherdevices[dev_ModeFl]=="Manu" then
dureeFiltration=tostring(round(60*tonumber(otherdevices[dev_DureeFlManu])))
print("Piscine : "..dureeFiltration.." s")
UpdateSw(dev_PpeFl,"On FOR "..dureeFiltration.." SECONDS")
end
-- 1. Gestion de la chloration
if devicechanged[dev_InjCl]=="On" and otherdevices[dev_Melange] == "Off" then
UpdateSw(dev_InjCl,'Off')
-- 2. Détermination de la dose à injecter
if otherdevices[dev_ModeCl]== "Manu" then
print("Chloration demandée en mode manu")
QCl=round(otherdevices_svalues[dev_SPCl],0) -- Quantité de Chlore, en ml
elseif otherdevices[dev_ModeCl]== "Auto" then
QCl=CalcClAuto()
else
print("Chloration demandée en mode Off : Ignoré")
QCl=0
end
-- 3. Vérification qu'on a bien la quantité demandée dans le bidon
QCl=ChkQBidon(QCl,dev_QBidonCl,"chlore")
-- 4. Injection du chlore et asservissement à la filtration
DoInjection(QCl,Cal_QCl,dev_PpeCl,"chlore",var_PpeClTodo)
end
-- 5. Gestion de l'injection d'acide
if devicechanged[dev_InjAc]=="On" and otherdevices[dev_Melange] == "Off" then
UpdateSw(dev_InjAc,'Off')
-- 6. Détermination de la dose à injecter
if otherdevices[dev_ModeAc]== "Manu" then
print("Acidification demandée en mode manu")
QAc=round(otherdevices_svalues[dev_SPAc],0) -- Quantité de Chlore, en ml
elseif otherdevices[dev_ModeAc]== "Auto" then
QAc=CalcAcAuto()
else
print("Acidification demandée en mode Off : Ignoré")
QAc=0
end
-- 7. Vérification qu'on a bien la quantité demandée dans le bidon
QAc=ChkQBidon(QAc,dev_QBidonAc,"acide")
-- 8. Injection du chlore et asservissement à la filtration
DoInjection(QAc,Cal_QAc,dev_PpeAc,"acide",var_PpeAcTodo)
end
-- 9. Calcul de la quantité de Chlore injecté
MaJInjection(dev_PpeCl,dev_CptCl,dev_QBidonCl,var_PpeClOld,var_PpeClTime,var_PpeClTodo,Cal_QCl,"Chlore",1500)
MaJInjection(dev_PpeAc,dev_CptAc,dev_QBidonAc,var_PpeAcOld,var_PpeAcTime,var_PpeAcTodo,Cal_QAc,"Acide",500)
-- 10. Gestion du changement de bidon de chlore ou acide
ChgBidon(dev_ChtBidonCl,dev_QBidonCl,dev_SPBidonCl)
ChgBidon(dev_ChtBidonAc,dev_QBidonAc,dev_SPBidonAc)
-- 11. Calcul du pH et ORP moyen
CalcAvg()
-- 12. Calcul de l'ORP en fonction des étalons
CalcORP()
-- 13. Calcul du pH en fonction des étalons
CalcpH()
--14. Vérification du débit de la pompe
--ChkFlow()
--15. Gestion des switches de pompes peristaltiques
ManagePumps()
end --Fin partie Device
return commandArray

les interrupteurs :

les mesures :

pour le branchement des 2 pompes j'ai gardé le meme principe
par contre il faudra que je fasse quelques modif car avec imperihome les thermostat ne vont que jusqu'à 100 et de 0.5 à 0.5 donc surement faire un calcul pour faire du x10 ou /10 pour ce ce soit pratique avec imperihome