Omdat de communicatie met de Duco box via de modbus niet echt wilde lukken, ben ik toch maar aan de slag gegaan via een andere optie.
Volgens de website van leverancier Duco is er een eigen academie, waarin les gevolgd kan worden voor de installateurs.
En de installateurs hebben natuurlijk ook gereedschap nodig, en de software daarvoor was/is te vinden op de website van Duco.
Met behulp van zoekmachines en het lezen van diverse forums vond ik dat de benodigde kabel, voor de koppeling tussen de Box en de PC zelf was te maken.
Na wachten op de bestelling uit China had ik eindelijk de onderdelen binnen.
en de kabel
Nog even aan elkaar solderen en klaar. Alleen was er wat onduidelijkheid wat ik op het web vond. Maar uiteindelijk gelukt.
Eerst in de PC aansluiten via een usb kabel. En via apparaten beheer zag ik:
Dus het juiste Serial device gevonden. Yess. (COM4)
Daarna kon ik met de tool van Duco gegevens van de Ducobox uitlezen.
Vervolgstap is natuurlijk de gegevens invoeren in Domoticz.
![]() |
|
Deze tekst is nog in ontwikkeling en nog niet helemaal klaar. | |
Als eerste hebben we de serial drivers nodig voor de USB-RS232 kabel.
(Ik installeer het op een RPI waarop ook opentherm staat.)
pi@opentherm:~ $ sudo apt-get install python-serial
Daarna maak ik een script aan waarmee ik het start:
pi@opentherm:~ $ nano duco.sh
En daar plk ik in de tekst:
"#!/bin/bash
cd ~
python duco.py 2>&1 | tee output.log"
Opslaan met Control X en gevolgd door Y.
Maak het uitvoerbaar met het commando:
pi@opentherm:~ $ chmod 777 duco.sh
Daarna het script in Python begin ik met:
pi@opentherm:~ $ nano duco.sh
"
#!/usr/bin/env python
import time
import serial
import urllib2
import base64
import json
from decimal import *
def ducocommand(command, verbose = 0):
ser.write(command)
reply = ser.readline().split('\r')
time.sleep(0.1)
if verbose > 0:
print reply
if reply[-1].strip() is not '>':
print "Reply was \"%s\", instead of \">\"" %reply[-1].strip()
return False
else:
return reply
ser = serial.Serial(
port='/dev/ttyUSB0',
baudrate = 115200,
parity=serial.PARITY_NONE,
stopbits=serial.STOPBITS_ONE,
bytesize=serial.EIGHTBITS,
timeout=1
)
# 1 = Box Vent: (status) en (Fanspeed) Sens: idx 2x
# 2 = schakelaar Badkemer 1e etage Vent: (status) Sens: (temp) idx 2320(temp)
# 3 = schakelaar Woonkamer Vent: (Status) en (Sens req) Sens: (CO2) en (Temp) idx 2309(CO2) 2321(temp)
# 4 = schakelaar Badkamer 2e etage Vent; (status) idx 600
# node struct: nodeid, type, temp, humidity, co2, temp domo idx, humidity domo idx, co2 domo idx
nodetypes = {
'BOX': 'Duco Box', # toerental en stand
'SWITCH': 'schakelaar', #geen
'UC': 'bedieningsschakelaar', # temperatuur
'UCCO2': 'bedieningsschakelaar met CO2 sensor', # CO2 en temperatuur
'UCRH': 'bedieningsschakelaar met luchtvochtigheidsensor',
'VLV': 'regelklep',
'VLVCO2': 'regelklep met CO2 sensor',
'VLVRH': 'regelklep met luchtvochtigheidsensor',
'CLIMA': 'ventilatierooster',
'UCBAT': 'bedieningsschakelaar RF met batterij' #geen
}
switchstates = { 'On': 1, 'Off': 0 }
domoticz = "http://192.168.xxx.xxx:8080/"
domoticzusername = "xxxxxxxxxxx"
domoticzpassword = "xxxxxxxxxxx"
base64string = base64.encodestring('%s:%s' % (domoticzusername, domoticzpassword)).replace('\n', '')
co2nodesdomoticz = {3: 2309}
rhnodesdomoticz = {2: 329, 4: 330}
switchnodesdomoticz = {2: 334, 3: 335, 4:336}
debietnodesdomoticz = {1: 2322} # Nu de Fan in RPM is 2322, de % waarde is 2310
tempnodesdomoticz = {3:2328, 2: 2327}
co2nodes = []
rhnodes = []
tempnodes = []
switchnodes = []
debietnodes = []
line = ''
commandreply = ducocommand(b'network\r')
start = commandreply.index(' --- start list ---')
end = commandreply.index(' --- end list ---')
result = commandreply[start+1:end]
# print result # geeft het resultaat van de bevraging aan de box
for x in result:
#print x
try:
nodeid = int(x.split('|')[0])
except Exception:
continue
# Type (Kolom 2, betret: Box, schakelaar, klep etc)
nodetype = x.split('|')[2].strip()
#print nodetype
#print "Node %i is een %s" % (nodeid, nodetypes[nodetype])
if nodetype.find('CO2') >= 0 and not nodeid in co2nodes:
co2nodes.append(nodeid);
#print "-co2- %i" % nodeid
# UC is bedieningsschakelaar, UCBAT als, maar met batterij, maar deze heeft geen bi-communicatie
if (nodetype.find('UC') >= 0 and not nodetype.find('UCBAT') >= 0 ) and not nodeid in tempnodes:
tempnodes.append(nodeid);
#print "-temperatuur- %i" % nodeid
# Todo: get switch state and only send changes
if nodetype.find('SWITCH') >= 0 and not nodeid in switchnodes:
oldswitchstate = 0
switchnodes.append(nodeid);
#print "switch %i" % nodeid
switchstate = x.split('|')[19].split(':')[0].strip()
#print "\tstatus %s" % (switchstate)
if switchstate.find('OFF') >= 0 or switchstate.find('FADE') >= 0:
switchstate = 0
else:
switchstate = 1
# /json.htm?type=devices&rid=IDX
try:
url = domoticz + "json.htm?type=devices&rid=" + str(switchnodesdomoticz[nodeid])
request = urllib2.Request(url)
request.add_header("Authorization", "Basic %s" % base64string)
result = urllib2.urlopen(request)
switchjson = json.loads(result.read())
oldswitchstate = switchstates[switchjson["result"][0]["Status"]]
except urllib2.URLError:
print "Getting switch state for %i went wrong" % nodeid
#print "switch %i %i" % (oldswitchstate, switchstate)
# only update on change
if oldswitchstate != switchstate:
# /json.htm?type=command¶m=udevice&idx=IDX&nvalue=?
try:
url = domoticz + "json.htm?type=command¶m=udevice&idx=" + str(switchnodesdomoticz[nodeid]) + "&nvalue=" + str(switchstate)
print (url , "switch")
request = urllib2.Request(url)
request.add_header("Authorization", "Basic %s" % base64string)
result = urllib2.urlopen(request)
except urllib2.URLError:
print "url went wrong"
# Weergave van de BOX, het % op 10, of Fanspeed op pos 15
if nodetype.find('VLV') >= 0 or nodetype.find('CLIMA') >= 0 or nodetype.find('BOX') >= 0:
debietnodes.append(nodeid);
#print "Klep/rooster %i" % nodeid
debiet = int(x.split('|')[10].split(':')[0].strip())
#debiet = int(x.split('|')[15].split(':')[0].strip())
#print "\tdebiet %i" % (debiet)
# /json.htm?type=command¶m=udevice&idx=IDX&nvalue=0&svalue=PERCENTAGE
try:
url = domoticz + "json.htm?type=command¶m=udevice&idx=" + str(debietnodesdomoticz[nodeid]) + "&nvalue=0&svalue=" + str(debiet)
print url , "percentage or fan-speed"
request = urllib2.Request(url)
request.add_header("Authorization", "Basic %s" % base64string)
result = urllib2.urlopen(request)
except urllib2.URLError:
print "url went wrong"
i = 0
while i < len(co2nodes):
# CO2 verwerking
commandreply = ducocommand(b'nodeparaget %i 74\r' % co2nodes[i])
# print commandreply
co2ppm = int(commandreply[2].split('--> ')[1])
#print "Node %i meet %i CO2 ppm" % (co2nodes[i], co2ppm)
if co2ppm < 50:
continue
# /json.htm?type=command¶m=udevice&idx=IDX&nvalue=PPM
try:
url = domoticz + "json.htm?type=command¶m=udevice&idx=" + str(co2nodesdomoticz[co2nodes[i]]) + "&nvalue=" + str(co2ppm)
print url , "CO2"
request = urllib2.Request(url)
request.add_header("Authorization", "Basic %s" % base64string)
result = urllib2.urlopen(request)
except urllib2.URLError:
print "url went wrong"
i += 1
# Temperatuur verwerking
i = 0
while i < len(tempnodes):
commandreply = ducocommand(b'nodeparaget %i 73\r' % tempnodes[i])
#print commandreply
tempc = int(commandreply[2].split('--> ')[1])
tempcd = float(tempc) / 10.0 # temp in graden met .0 decimaal
# print "Node %i meet %f temperatuur in graden, orgineel %i " % (tempnodes[i], tempcd, tempc)
# /json.htm?type=command¶m=udevice&idx=IDX&nvalue=PPM
try:
url = domoticz + "json.htm?type=command¶m=udevice&idx=" + str(tempnodesdomoticz[tempnodes[i]]) + "&nvalue=0&svalue=" + str(tempcd)
print url , "Temperatuur"
request = urllib2.Request(url)
request.add_header("Authorization", "Basic %s" % base64string)
result = urllib2.urlopen(request)
except urllib2.URLError:
print "url went wrong"
i += 1
# luchtvochtigheid verwerking
i = 0
while i < len(rhnodes):
commandreply = ducocommand(b'nodeparaget %i 75\r' % rhnodes[i])
#print commandreply
rh = float(commandreply[2].split('--> ')[1]) / 100.0
#print "Node %i meet %.2f procent luchtvochtigheid" % (rhnodes[i], rh)
if rh < 1:
continue
if rh > 25 and rh < 60:
humstat = 1
if rh >= 60:
humstat = 3
if rh <= 25:
humstat = 2
# /json.htm?type=command¶m=udevice&idx=IDX&nvalue=HUM&svalue=HUM_STAT
try:
url = domoticz + "json.htm?type=command¶m=udevice&idx=" + str(rhnodesdomoticz[rhnodes[i]]) + "&nvalue=" + str(rh) + "&svalue=" + str(humstat)
print url , "Luchtvochtigheid"
request = urllib2.Request(url)
request.add_header("Authorization", "Basic %s" % base64string)
result = urllib2.urlopen(request)
except urllib2.URLError:
print "url went wrong"
i += 1
print " "
"
Dan uiteindelijk automatisch elke 5 minuten laten draaien, middels een crontab.
Dit doe ik door het commando:
pi@opentherm:~ $ crontab -e
Ik kies dan voor de editor nano (2) en voeg dan onderaan, na de laatste regel, dus geen # er voor:
"*/5 * * * * /home/pi/duco.sh"
Afsluiten weer met Control-X Y
Met heel veel dank voor het script van Koen Kooi in GitHub
Ik heb het wel wat moeten aanpassen aan mijn eigen DucoBox en situatie.
Wat wel tegen viel was dat de thermometer niet echt goed lijkt te zijn.
Websites met daarin bruikbare informatie.
In mijn poging het project goed af te ronden heb ik mijn informatie van diverse websites gehaald. Omdat ik die informatie niet wil verliezen en ook de niet gebruikte kennis wil delen, heb ik de gebruikte links opgesomd.
- www.duco.eu/duco-academy
- www.duco.eu/download-network-control-tool
- http://arnemauer.nl/ducobox-gateway/
- https://gathering.tweakers.net/forum/list_messages/1724875
- https://github.com/SteinHeselmans/DucoBox
- https://gathering.tweakers.net/forum/list_messages/1881029