Pour ceux que ça intéresse, j'ai enfin trouvé du temps pour me pencher sur le problème et avec l'aide de Gemini j'ai enfin un script qui tourne avec une version récente de python !
Code : Tout sélectionner
#!/usr/bin/python
# domo2vito
# JS MARTIN
# version 2.11 - 07032017
# Version 2.11 - 20022024 (Version corrigée pour un python plus récent)
import telnetlib
import sys
import os
import time
import requests
from requests.auth import HTTPBasicAuth
import json
############# Parameters #################################
#~~~~~~~~~~ Parameters Domoticz ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
domoticz_ip='ip'
domoticz_port='port'
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# Commands viTalk
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
#autorized commands
commands = ["mode","saving","party","party_soll_temp","outdoor_temp","raum_soll_temp","raum_ist_temp","k_ist_temp","ww_soll_temp","ww_ist_temp","k_abgas_temp","power","errors","runtime","red_raum_soll_temp"]
#write only commands
write_only_commands = ["mode","saving","party","party_soll_temp","ww_soll_temp","red_raum_soll_temp","raum_soll_temp"]
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# IDX de Domoticz
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# remplacer XXX par votre idx de votre widget
dummy_idx={'mode':82, 'saving': 83, 'party':84, 'party_soll_temp':85, 'outdoor_temp':86, 'raum_soll_temp':101, 'raum_ist_temp':88, 'k_ist_temp':89, 'ww_soll_temp':99, 'ww_ist_temp':91, 'k_abgas_temp':92, 'power':96, 'error_log':94, 'problem':95, 'delta-rt':340, 'gas':341, 'red_raum_soll_temp':337, 'holiday_temp':338, 'raum_soll_temp_W':339}
user_idx={'runtime-boiler':37} # please create a user variable [integer] (overall boiler time) - see https://www.domoticz.com/wiki/User_variables
# 0 mode : dummy selector switch OFF/Water/Heating (Boiler [FR:chaudiere] stand-by/water only/water+heating) {read/write}
# 1 saving : dummy switch ON/OFF (Boiler eco ON/OFF) {read/write}
# 2 party : dummy switch ON/OFF (party temporary manual control) - note : party ON does not work (vitalk bug ?) {read/write}
# 3 party_soll_temp : dummy thermostat setpoint (party temperature) {write only}
# 4 outdoor_temp : dummy temp (outdoor temperature) {read only}
# 5 raum_soll_temp : dummy temp (heating [FR:consigne] temperature ) {read only} - note : without vitotronic, you could use thermostat setpoint
# 6 raum_ist_temp : dummy temp (heating current [FR:actuelle] temperature) {read only}
# 7 k_ist_temp : dummy temp (boiler current temperature) {read only}
# 8 ww_soll_temp : dummy thermostat setpoint (hot water setpoint temperature) {write only}
# 9 ww_ist_temp : dummy temp (hot water current temperature) {read only}
#10 k_abgas_temp : dummy temp (exhaust gas [FR:gaz evacue'] temperature) {read only}
#11 power : dummy percentage (% boiler power) {read only}
#12 error_log : dummy alert (show the two last log codes) {read only}
# problem : dummy switch (set switch to On if there is a internal boiler problem)
#13 runtime : custom sensor [seconds] (Operating time of boiler since last script call) {read only}
# gas : dummy incremental counter [gas] (Estimation of Gas consumtion)
#14 red_raum_soll_temp: dummy thermostat setpoint (reduced temperature) {write only}
# holiday_temp : dummy thermostat setpoint (holiday temperature)
# raum_soll_temp_W : dummy thermostat setpoint (normal temperature) {write only} - note : does not work with vitotrol 300
# |_ for raum_soll_temp_W, you need to put the bolean Use_normal_temperature_setpoint to True
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# Mode debug
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# if debug = True => verbose
debug=True
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# Variable
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
#Debit max gas (need to be adjust to synchronize estimated and real gas consumption)
Deb=0.1099 #0.12 = trop fort 1400 l / 0.09 = trop faible 390 l - 1min
# 0.136 - 3min
# Activated normal temperature setpoint
Use_normal_temperature_setpoint=True
#######################################################################
###### viTalk_connect() : connect to viTalk localhost:83
#######################################################################
def viTalk_connect():
global debug
retry=3
while retry!=0:
try:
tn = telnetlib.Telnet("localhost", 83)
if debug:
print ("Connected to viTalk telnet !")
return tn
except:
print ("Connection ERROR - I try to restart viTalk deamon")
#I try to restart viTalk
cmd = 'sudo systemctl start vitalk.service'
os.system(cmd)
time.sleep(10)
retry-=1
else:
tn.read_until(b"$", 5)
if retry==0:
tn.close()
print ("Connection ERROR - I could not restart viTalk deamon")
sys.exit()
######
#######################################################################
###### viTalk_read(cmd) : read a value
#######################################################################
def viTalk_read(cmd):
global debug
global tn
retry=3
value=""
while retry!=0:
tn.read_until(b"$", 5)
tn.write(b"g "+cmd+"\n")
value_bytes = tn.read_until(b"$", 5) # Lire les donnes en bytes
value = value_bytes.decode().strip("\n$") # Dcoder en str et supprimer les caractres
if value!="" and value!='NULL':
break
retry-=1
if retry==0:
print ("Connection ERROR - I try to restart viTalk deamon")
os.system('sudo systemctl start vitalk.service')
#tn.close()
print(cmd+": "+value+" error")
sys.exit()
value = value.strip("\n$")
if debug:
print ("get "+cmd+" : answer = "+value)
return value
######
#######################################################################
###### viTalk_check() : test viTalk and restart it if needed
#######################################################################
def viTalk_check():
global debug
global tn
retry=3
value=""
while retry!=0:
tn.read_until(b"$", 5)
tn.write(b"g saving \n")
value_bytes = tn.read_until(b"$", 5) # Lire les donnes en bytes
value = value_bytes.decode().strip("\n$") # Dcoder en str et supprimer les caractres
if value!="" and value!='NULL':
if debug:
print ("Check viTalk data OK ! (no NULL data received)")
break
retry-=1
if retry==0:
print ("Connection ERROR - I try to restart viTalk deamon")
os.system('sudo systemctl start vitalk.service')
sys.exit()
######
#######################################################################
###### viTalk_set(cmd,val) : unsecured write
#######################################################################
def viTalk_set(cmd,val):
global debug
global tn
if cmd!="mode":
tn.write(b"s "+cmd+" "+val+"\n")
else: # vitalk does not manage mode 3 and 4 => raw set directly
tn.write(b"rs 2323 "+val+"\n")
tn.read_until(b"$", 5).strip("\n$")
if debug: print("set "+cmd+" "+val)
######
#######################################################################
###### viTalk_write(cmd,val) : secured write (I check if value is
###### really updated)
#######################################################################
def viTalk_write(cmd,val):
global debug
global tn
retry=3
value=""
while retry!=0:
viTalk_set(cmd,val)
tn.read_until(b"$", 5)
time.sleep(1)
value=viTalk_read(cmd)
if val==value:
break
retry-=1
if retry==0:
tn.close()
print(cmd+" "+val+" : error")
sys.exit()
if debug:
print ("check last command set "+cmd+" "+val+" : OK")
#########
#######################################################################
###### check_command(cmd) : is it an authorized command ?
#######################################################################
def check_command(cmd):
global commands
global debug
try:
#check position of cmd in commands list
commands.index(cmd)
except:
print ("command not found")
return False
else:
if debug:
print ("this command "+cmd+" is known")
return True
####
#######################################################################
###### check_set_command(cmd) : is it an authorized write command ?
#######################################################################
def check_set_command(cmd):
global commands
global debug
try:
#check position of cmd in write only commands list
write_only_commands.index(cmd)
except:
print ("set command not found (I can't write with this command)")
return False
else:
if debug:
print ("this command "+cmd+" is known for writing")
return True
####
#######################################################################
###### get_setpoint_temp(idx) : get temperature from domoticz setpoint
#######################################################################
def get_setpoint_temp(idx):
r = requests.get('http://'+domoticz_ip+':'+domoticz_port+'/json.htm?type=command¶m=getdevices&rid='+str(idx))
status=r.status_code
if status == 200:
r=r.json()
result={}
return r['result'][0]['Data']
else:
print ("Get setpoint temp error - IDX:"+str(idx))
####
########################################################################
###### get_state(idx) : get dummy state from domoticz
########################################################################
def get_state(idx):
r = requests.get('http://'+domoticz_ip+':'+domoticz_port+'/json.htm?type=command¶m=getdevices&rid='+str(idx))
status=r.status_code
if status == 200:
r=r.json()
result={}
return r['result'][0]['Data']
else:
print ("Get state error - IDX:"+str(idx)+" not found ?")
####
########################################################################
###### get_last_runtime(idx) : get last runtime value from domoticz
########################################################################
def get_last_runtime(idx):
r = requests.get('http://'+domoticz_ip+':'+domoticz_port+'/json.htm?type=command¶m=getuservariable&idx='+str(idx))
status=r.status_code
if status == 200:
r=r.json()
result={}
return r['result'][0]['Value']
else:
print ("Get state error - IDX:"+str(idx))
####
#-----------------------------------### START ###--------------------------------------------#
# arguments in command line
nb_arg=len(sys.argv)-1
if nb_arg==0:
print ("HELP : ")
print ("domo2vito update : update all domoticz values")
print ("domo2vito command : get command (e.g. domo2vito mode <=> get mode)")
print ("domo2vito command value : set command value (e.g. domo2vito mode 1 <=> set mode 1)")
print ("--> supported commands :")
for command in commands:
print (" - "+command)
if nb_arg==1 and sys.argv[1]!="update":
if check_command(sys.argv[1]):
if debug:
print ("get "+sys.argv[1])
tn=viTalk_connect()
viTalk_read(sys.argv[1])
tn.close()
if nb_arg==1 and sys.argv[1]=="update":
tn=viTalk_connect()
viTalk_check()
values=[]
i=0
tn.read_until(b"$", 5)
for command in commands:
tn.write(b"g " + command.encode() + b"\n")
value_bytes = tn.read_until(b"$", 5)
if value_bytes is not None:
value = value_bytes.decode().strip("\n$")
print(str(i) + ") command : get " + command + " => " + value)
else:
print(f"Error receiving data for command {command}")
values.append(value) # Assuming you still want to append even if None
i += 1
if debug: print ("+Now I check if I need to update some values into Viessmann boiler or into Domoticz...")
mcur=time.localtime().tm_min # to update setpoint one time every hour - get minute of current hour
if mcur>40 and mcur<46: # Heartbeat - update setpoint in Domoticz one time per hour just to avoid "lost widget status"
setpoint_heartbeat=True
else:
setpoint_heartbeat=False
# party_soll_temp : get setpoint party_soll_temp and update boiler if needed
temp=int(float(get_setpoint_temp(dummy_idx['party_soll_temp'])))
if temp!=int(float(values[3])): #Viessmann ignore decimales
if temp<10: temp=10 # party mini temp
if temp>30: temp=30 # party maxi temp
if debug: print ("3) party setpoint has changed : I update Viessmann boiler with party temperature = "+str(temp))
viTalk_set("party_soll_temp",str(temp))
else:
if setpoint_heartbeat:
req='http://'+domoticz_ip+':'+domoticz_port+'/json.htm?type=command¶m=udevice&idx='+str(dummy_idx['party_soll_temp'])+'&nvalue=0&svalue='+str(temp)
requests.get(req)
# get setpoint ww_soll_temp and update boiler if needed
temp=int(float(get_setpoint_temp(dummy_idx['ww_soll_temp'])))
if temp!=int(float(values[8])): #Viessmann ignore decimales
if temp<20: temp=20 # hot water mini temp
if temp>70: temp=70 # hot water maxi temp
if debug: print ("8) hot water setpoint has changed : I update Viessmann boiler with hot water temperature = "+str(temp))
viTalk_set("ww_soll_temp",str(temp))
else:
if setpoint_heartbeat:
req='http://'+domoticz_ip+':'+domoticz_port+'/json.htm?type=command¶m=udevice&idx='+str(dummy_idx['ww_soll_temp'])+'&nvalue=0&svalue='+str(temp)
requests.get(req)
# holiday mode : emulated* holiday mode *I put boiler in permanent reduce mode with a custom holiday temp
if int(values[0])==3: # mode 'permanent reduce'
if debug: print ("Holiday mode : I check if reduced temp. = domoticz holiday setpoint temp.")
temp=int(float(get_setpoint_temp(dummy_idx['holiday_temp'])))
if temp!=int(float(values[14])):
if temp<10: temp=10 # holidays mini temp
if temp>30: temp=30 # holidays maxi temp
viTalk_set("red_raum_soll_temp",str(temp)) # Reduced temp = domoticz holiday temp
if debug: print ("- I update reduced temperature with domoticz holiday setpoint temperature")
else:
# get setpoint red_raum_soll_temp and update boiler if needed
if debug: print ("NOT Holiday mode : I check if reduced temp. = domoticz reduced setpoint temp.")
temp=int(float(get_setpoint_temp(dummy_idx['red_raum_soll_temp'])))
if temp!=int(float(values[14])): #Viessmann ignore decimales
if temp<10: temp=10 # reduced mini temp
if temp>30: temp=30 # reduced maxi temp
if debug: print ("14) Reduced temp setpoint has changed : I update Viessmann boiler with reduced temperature = "+str(temp))
viTalk_set("red_raum_soll_temp",str(temp))
if Use_normal_temperature_setpoint:
if debug: print ("Use_normal_temperature_setpoint => activated")
temp=int(float(get_setpoint_temp(dummy_idx['raum_soll_temp_W'])))
if temp!=int(float(values[5])): #Viessmann ignore decimales
if temp<10: temp=10 # reduced mini temp
if temp>30: temp=30 # reduced maxi temp
if debug: print ("15) Normal temp setpoint has changed : I update Viessmann boiler with normal temperature = "+str(temp))
viTalk_set("raum_soll_temp",str(temp))
if setpoint_heartbeat:
temp=int(float(get_setpoint_temp(dummy_idx['holiday_temp'])))
if temp<10: temp=10 # holidays mini temp
if temp>30: temp=30 # holidays maxi temp
req='http://'+domoticz_ip+':'+domoticz_port+'/json.htm?type=command¶m=udevice&idx='+str(dummy_idx['holiday_temp'])+'&nvalue=0&svalue='+str(temp)
requests.get(req)
temp=int(float(get_setpoint_temp(dummy_idx['red_raum_soll_temp'])))
if temp<10: temp=10 # reduced mini temp
if temp>30: temp=30 # reduced maxi temp
req='http://'+domoticz_ip+':'+domoticz_port+'/json.htm?type=command¶m=udevice&idx='+str(dummy_idx['red_raum_soll_temp'])+'&nvalue=0&svalue='+str(temp)
requests.get(req)
if Use_normal_temperature_setpoint:
temp=int(float(get_setpoint_temp(dummy_idx['raum_soll_temp_W'])))
if temp<10: temp=10 # normal mini temp
if temp>30: temp=30 # normal maxi temp
req='http://'+domoticz_ip+':'+domoticz_port+'/json.htm?type=command¶m=udevice&idx='+str(dummy_idx['raum_soll_temp_W'])+'&nvalue=0&svalue='+str(temp)
requests.get(req)
tn.close()
# mode (selector level : 0=OFF 10=water 20=heating 30=holidays 40=always On)
previous_value=get_state(dummy_idx['mode'])
if previous_value=="Off": previous_value="Set Level: 0 %"
if int(previous_value[11:-1])!=int(values[0])*10:
if debug: print ("0) Value MODE has changed : I update domoticz with mode = "+values[0])
req='http://'+domoticz_ip+':'+domoticz_port+'/json.htm?type=command¶m=switchlight&idx='+str(dummy_idx['mode'])+'&switchcmd=Set%20Level&level='+str(int(values[0])*10)
requests.get(req)
# saving : dummy switch ON/OFF (Boiler eco ON/OFF) : if a user modify boiler or vitotrol, update Domoticz
previous_value=get_state(dummy_idx['saving'])
if (previous_value=="On" and values[1]=="0") or (previous_value=="Off" and values[1]=="1"):
if debug: print ("1) Value SAVING has changed : I update domoticz with saving = "+values[1])
if values[1]=="0":
req='http://'+domoticz_ip+':'+domoticz_port+'/json.htm?type=command¶m=switchlight&idx='+str(dummy_idx['saving'])+'&switchcmd=Off'
else:
req='http://'+domoticz_ip+':'+domoticz_port+'/json.htm?type=command¶m=switchlight&idx='+str(dummy_idx['saving'])+'&switchcmd=On'
requests.get(req)
# party : dummy switch ON/OFF (party temporary manual control) : if a user modify boiler or vitotrol, update Domoticz
previous_value=get_state(dummy_idx['party'])
if (previous_value=="On" and values[2]=="0") or (previous_value=="Off" and values[2]=="1"):
if debug: print ("2) Value PARTY has changed : I update domoticz with party = "+values[2])
if values[2]=="0":
req='http://'+domoticz_ip+':'+domoticz_port+'/json.htm?type=command¶m=switchlight&idx='+str(dummy_idx['party'])+'&switchcmd=Off'
else:
req='http://'+domoticz_ip+':'+domoticz_port+'/json.htm?type=command¶m=switchlight&idx='+str(dummy_idx['party'])+'&switchcmd=On'
requests.get(req)
# outdoor_temp : dummy temp (outdoor temperature)
req='http://'+domoticz_ip+':'+domoticz_port+'/json.htm?type=command¶m=udevice&idx='+str(dummy_idx['outdoor_temp'])+'&nvalue=0&svalue='+values[4]
requests.get(req)
# raum_soll_temp : dummy temp (heating setpoint [FR:consigne] temperature) - note : without vitotronic, use thermostat setpoint
req='http://'+domoticz_ip+':'+domoticz_port+'/json.htm?type=command¶m=udevice&idx='+str(dummy_idx['raum_soll_temp'])+'&nvalue=0&svalue='+values[5]
requests.get(req)
# raum_ist_temp : dummy temp (heating current [FR:actuelle] temperature)
req='http://'+domoticz_ip+':'+domoticz_port+'/json.htm?type=command¶m=udevice&idx='+str(dummy_idx['raum_ist_temp'])+'&nvalue=0&svalue='+values[6]
requests.get(req)
# k_ist_temp : dummy temp (boiler current temperature)
req='http://'+domoticz_ip+':'+domoticz_port+'/json.htm?type=command¶m=udevice&idx='+str(dummy_idx['k_ist_temp'])+'&nvalue=0&svalue='+values[7]
requests.get(req)
# ww_ist_temp : dummy temp (hot water current temperature)
req='http://'+domoticz_ip+':'+domoticz_port+'/json.htm?type=command¶m=udevice&idx='+str(dummy_idx['ww_ist_temp'])+'&nvalue=0&svalue='+values[9]
requests.get(req)
# k_abgas_temp : dummy temp (exhaust gas [FR:gaz evacue'] temperature)
req='http://'+domoticz_ip+':'+domoticz_port+'/json.htm?type=command¶m=udevice&idx='+str(dummy_idx['k_abgas_temp'])+'&nvalue=0&svalue='+values[10]
requests.get(req)
# power : dummy percentage (% boiler power)
previous_power_value=get_state(dummy_idx['power'])[:-1] # on supprime le % a la fin de la chaine
req='http://'+domoticz_ip+':'+domoticz_port+'/json.htm?type=command¶m=udevice&idx='+str(dummy_idx['power'])+'&nvalue=0&svalue='+values[11]
requests.get(req)
# Get previous boiler runtime value from Domoticz database (user variable)
last_rt=int(get_last_runtime(user_idx['runtime-boiler']))
delta_rt=int(values[13])-last_rt # delta-runtime
# update boiler-runtime since last update
req='http://'+domoticz_ip+':'+domoticz_port+'/json.htm?type=command¶m=udevice&idx='+str(dummy_idx['delta-rt'])+'&nvalue=0&svalue='+str(delta_rt)
requests.get(req)
# Set overall boiler runtime into Domoticz database (user variable)
req='http://'+domoticz_ip+':'+domoticz_port+'/json.htm?type=command¶m=updateuservariable&idx='+str(user_idx['runtime-boiler'])+'&vname=runtime-boiler&vtype=0&vvalue='+values[13]
requests.get(req)
if debug: print ("+Now I calculate the gas consumed since last script call:")
# gas : gas estimation
power_average=(float(previous_power_value)+float(values[11]))/2
gas_used=delta_rt*Deb*power_average/100
if debug:
print (" -Viessmann boiler runtime since last script call (sec) = "+str(delta_rt))
print (" -Viessmann boiler power average (%) = "+ str(power_average))
print (" -Estimated gas used (liter) = "+str(gas_used))
req='http://'+domoticz_ip+':'+domoticz_port+'/json.htm?type=command¶m=udevice&idx='+str(dummy_idx['gas'])+'&nvalue=0&svalue='+str(gas_used)
requests.get(req)
if debug: print ("+Now I check boiler error...")
# error_log : dummy alert (show the two last log codes)
# problem : dummy switch (set switch to On if there is a internal boiler problem)
#values[12]="123,255,235,244,244,244,244,244,244,244 ;"
V_error=values[12][0:3]
V_previous_error=values[12][4:7]
V_error_hexa=str(hex(int(V_error)))[2:4] # hexa convertion
V_previous_error_hexa=str(hex(int(V_previous_error)))[2:4] # hexa convertion
V_previous_error_state=get_state(str(dummy_idx['error_log']))[0:2]
V_previous_problem_state=get_state(str(dummy_idx['problem']))
if V_error=="000":
if debug: print (" -no internal error : all it is OK"+" (previous code="+V_previous_error_hexa+")")
if V_previous_error_state!="OK": # I update error_log alert only if state is modified
req='http://'+domoticz_ip+':'+domoticz_port+'/json.htm?type=command¶m=udevice&idx='+str(dummy_idx['error_log'])+'&nvalue=1&svalue='+'OK (previous='+V_previous_error_hexa+')'
requests.get(req)
if V_previous_problem_state=="On": # I update problem switch only if state is modified
if debug: print (" -state of boiler error status changed (no problem): I update the Domotics boiler problem switch")
req='http://'+domoticz_ip+':'+domoticz_port+'/json.htm?type=command¶m=switchlight&idx='+str(dummy_idx['problem'])+'&switchcmd=Off'
requests.get(req)
else:
if debug: print (" -there is an internal error : code="+V_error_hexa+" ( previous ="+V_previous_error_hexa+")")
if V_previous_error_state!=V_error_hexa: # I update error_log alert only if state is modified
req='http://'+domoticz_ip+':'+domoticz_port+'/json.htm?type=command¶m=udevice&idx='+str(dummy_idx['error_log'])+'&nvalue=4&svalue='+V_error_hexa+' error (previous='+V_previous_error_hexa+')'
requests.get(req)
if V_previous_problem_state=="Off": # I update problem switch only if state is modified
if debug: print (" -state of boiler error status changed (problem): I update the Domotics boiler problem switch")
req='http://'+domoticz_ip+':'+domoticz_port+'/json.htm?type=command¶m=switchlight&idx='+str(dummy_idx['problem'])+'&switchcmd=On'
requests.get(req)
if nb_arg==2:
if check_set_command(sys.argv[1]):
print ("set "+sys.argv[1]+" "+sys.argv[2])
tn=viTalk_connect()
viTalk_write(sys.argv[1],sys.argv[2])
tn.close()