Ankündigung

Einklappen
Keine Ankündigung bisher.

Anleitung: Widgeterstellung und zugehörige Logik (Bsp. Wärmepumpe mit web2com)

Einklappen
X
 
  • Filter
  • Zeit
  • Anzeigen
Alles löschen
neue Beiträge

    [callidomus] Anleitung: Widgeterstellung und zugehörige Logik (Bsp. Wärmepumpe mit web2com)

    Hallo erstmal!

    In diesem Thread versuch ich einmal mit Unterstützung von Waldemar (@mumpf) zu zeigen, wie wir uns die notwendige Logik zum Auslesen des web2com-Interfaces inkl. Der passenden Widgets basteln.
    Ziel war bzw. ist es gewesen eine Wärmepumpe inkl. web2com-Interface auszulesen und darzustellen.
    (in meinem Fall eine Ochsner GMSW – Erdwärme Sole)

    Das Ergebnis sieht am iPhone bzw. iPad so aus:

    2017-01-13 16.58.32.pngDatei 13.01.17, 17 11 18.jpeg

    Bzw. mit einem zweiten Widget:


    Was man grob dafür braucht:
    • eine Idee und vielleicht schon die Logik, die umgesetzt werden soll
    • Bilddaten in einer (aufgeräumten) SVG, in der man Werte/Texte ersetzen kann
    • ein __init__.py zum erzeugen des Widgets
    • für jeden darzustellenden Wert ein Item (falls man will: Ein komplexes Item mit allen Werten)
    • ein widget.js für die Darstellung der Item-Werte und evtl. zum setzen der class-Attribute der html-tags
    • ein widget.css für die eingentliche Darstellung (Font, Farbe etc.) der class-Attribute




    -------

    1. Schritt: Ausgangspunkt die Weboberfläche/Schnittstelle der Wärmepumpe

    Bildschirmfoto vom 2017-01-13 14-29-11.png

    Die rechts dargestellten Daten wollen wir möglichst alle auslesen.
    Und an der Wärmepumpe gibt es z.B. folgende Ansichten, die ich gerne auch in CD in der Übersicht hätte:
    Ochsner-AnzeigeWP.png


    2. Schritt: Parsen der Daten
    Wie komm ich an die Daten aus der web2com-Weboberfläche? Einmal im Baum (Mitte) einen Wert ausgewählt, lässt sich in Chrome über "Einstellungen"/"weitere Tools" die "Entwicklertools" öffnen.

    Chrome-Entwicklertools.png

    Und dort kann ein Element mit Rechtsklick -> Copy ->Copy as cURL ein Kommando bekommen, das ich mit cURL testen kann (z.B. Konsole unter Linux)(Curl ist ein Programm, das es ermöglicht, ohne Benutzerinteraktion Dateien von oder zu einem Server zu übertragen).

    Chrome-cURL.png

    Dann bekomm ich folgendes:
    Code:
    curl 'http://192.168.1.200/ws' -H 'Authorization: Basic VVNxxxxxMjM=' -H 'Origin: http://192.168.1.200' -H 'Accept-Encoding: gzip, deflate' -H 'Accept-Language: de-DE,de;q=0.8,en-US;q=0.6,en;q=0.4' -H 'User-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/54.0.2840.90 Safari/537.36' -H 'Content-Type: text/xml; charset=UTF-8' -H 'Accept: */*' -H 'Referer: http://192.168.1.200/RC7000.html?locale=de' -H 'Connection: keep-alive' -H 'SOAPAction: http://ws01.lom.ch/soap/getDP' --data-binary $'<?xml version="1.0" encoding="UTF-8"?><SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:ns="http://ws01.lom.ch/soap/">\n <SOAP-ENV:Body>\n   <ns:getDpRequest>\n    <ref>\n     <oid>/1/2/1/125/4</oid>\n     <prop/>\n    </ref>\n    <startIndex>0</startIndex>\n    <count>-1</count>\n   </ns:getDpRequest>\n\n </SOAP-ENV:Body>\n</SOAP-ENV:Envelope>' --compressed
    <oid> sind die Elemente im Menübaum. Also /1/2/1/125/4 entspricht:
    Code:
    /(1) EBusRoot/(2) 513200224/(1) WAERMEPUMPE/(125) Betriebsdaten/(4) 00:70 IST Temp.TQA
    Hier passe ich den "Autentifizierungsteil" noch an die realen Nutzerdaten und ein paar andere Dinge an:
    Code:
    curl 'http://192.168.1.200/ws' --user "USER:xxx" -H 'Content-Type: text/xml; charset=UTF-8' -H 'SOAPAction: http://ws01.lom.ch/soap/getDP' --data-binary $'<?xml version="1.0" encoding="UTF-8"?><SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:ns="http://ws01.lom.ch/soap/"><SOAP-ENV:Body><ns:getDpRequest><ref><oid>/1/2/1/125/4</oid><prop/></ref><startIndex>0</startIndex><count>1</count></ns:getDpRequest></SOAP-ENV:Body></SOAP-ENV:Envelope>'
    Damit bekomm ich auf der Konsole (in meinem Fall unter Linux) folgende Rückmeldung:
    Code:
    <?xml version="1.0" encoding="UTF-8"?>
    <SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:ns="http://ws01.lom.ch/soap/"><SOAP-ENV:Body><ns:getDpResponse><ref><oid>/(1) EBusRoot/(2) 513200224/(1) WAERMEPUMPE/(125) /(4) 00:70</oid><prop>-r--</prop></ref><dpCfg><index>4</index><name>00:70</name><prop>-r--</prop><desc>Scalar Var</desc><value>8.2</value><unit>°C</unit><type>13</type><step>0.1</step><minValue>0.0</minValue><maxValue>100.0</maxValue></dpCfg></ns:getDpResponse></SOAP-ENV:Body></SOAP-ENV:Envelope>
    Da steckt doch schon mal zw. <value> und </value> der gesuchte Wert für die zugehörige Adresse (<oid>).

    Im Fall eines Fehlers (z.B. wenn die Autentifizierung nicht passt) bekomm ich folgende Rückmeldung
    Code:
    <?xml version="1.0" encoding="UTF-8"?>
    <SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:ns="http://ws01.lom.ch/soap/"><SOAP-ENV:Body><SOAP-ENV:Fault><faultcode>SOAP-ENV:Client</faultcode><faultstring>HTTP Error: 401 Unauthorized</faultstring></SOAP-ENV:Fault></SOAP-ENV:Body></SOAP-ENV:Envelope>
    Da kann man doch den String <faultcode> oder <faultstring> gleich zur Auswertung mit auswerten



    3. Schritt: cURL Befehl in Python umsetzen
    Hier hab ich nun auch etwas grübeln müssen, was ich alles benötige um obigen cURL Befehl nun in einem Python-Skript unterzubringen.

    Letztendlich kam dabei folgendes raus:
    Code:
    import requests
    
    url='http://192.168.1.200/ws'
    username="USER"
    password="xxx"
    headers = {
        'Content-Type': 'text/xml; charset=UTF-8',
        'SOAPAction': 'http://ws01.lom.ch/soap/getDP',
    }
    
    def web2com_values(oid):
        data = '<?xml version="1.0" encoding="UTF-8"?><SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:ns="http://ws01.lom.ch/soap/"><SOAP-ENV:Body><ns:getDpRequest><ref><oid>' + oid + '</oid><prop/></ref><startIndex>0</startIndex><count>1</count></ns:getDpRequest></SOAP-ENV:Body></SOAP-ENV:Envelope>'
        resp = requests.post(url, auth=(username,password), headers=headers, data=data)
        if resp.text.find("faultstring") < 0:
            return resp.text[(resp.text.index("<value>")+len("<value>")):resp.text.index("</value>")]  
        else :
            return 0
    
    cd.WP.Waermepumpe.Betriebsdaten.Betriebsstunden = web2com_values('/1/2/1/125/7')
    Einfach den cURL-Befehl mit der Library "requests" in Python nachgebaut und gleich in eine Funktion mit Variablen gepackt, damit ich die Routine für alle Werte nutzen kann.



    4. Schritt: Notwendige Items in Callidomus (CD) anlegen und diese füllen
    Damit ich alle Werte in CD nutzen kann, müssen wir für die gewünschten Werte Items anlegen.
    Das funktioniert hier dann ganz gut mit dem gesamten Skript:
    Code:
    import requests
    
    url='http://192.168.1.200/ws'
    username="USER"
    password="123"
    headers = {
        'Content-Type': 'text/xml; charset=UTF-8',
        'SOAPAction': 'http://ws01.lom.ch/soap/getDP',
    }
    
    def web2com_values(oid):
        data = '<?xml version="1.0" encoding="UTF-8"?><SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:ns="http://ws01.lom.ch/soap/"><SOAP-ENV:Body><ns:getDpRequest><ref><oid>' + oid + '</oid><prop/></ref><startIndex>0</startIndex><count>1</count></ns:getDpRequest></SOAP-ENV:Body></SOAP-ENV:Envelope>'
        resp = requests.post(url, auth=(username,password), headers=headers, data=data)
        if resp.text.find("faultstring") < 0:
            return resp.text[(resp.text.index("<value>")+len("<value>")):resp.text.index("</value>")]  
        else :
            return 0
    
    cd.WP.Waermepumpe.Betriebsdaten.Betriebsstunden = web2com_values('/1/2/1/125/7')
    cd.WP.Waermepumpe.Betriebsdaten.IstTempTQA = web2com_values('/1/2/1/125/4')
    cd.WP.Waermepumpe.Betriebsdaten.IstTempTQE = web2com_values('/1/2/1/125/5')
    cd.WP.Waermepumpe.Betriebsdaten.IstTempTWV = web2com_values('/1/2/1/125/1')
    cd.WP.Waermepumpe.Betriebsdaten.IstTempTWR = web2com_values('/1/2/1/125/3')
    cd.WP.Waermepumpe.Betriebsdaten.Schaltzyklen = web2com_values('/1/2/1/125/6')
    cd.WP.Waermepumpe.Betriebsdaten.VolumenstromWaermenutzung = web2com_values('/1/2/1/125/8')
    cd.WP.Waermepumpe.Betriebsdaten.VolumenstromWaermequelle = web2com_values('/1/2/1/125/9')
    cd.WP.Waermepumpe.Betriebsdaten.VorlauftempSollwert = web2com_values('/1/2/1/125/2')
    cd.WP.Waermepumpe.Betriebsdaten.Leistung =  60/1000*cd.WP.Waermepumpe.Betriebsdaten.VolumenstromWaermequelle*abs(cd.WP.Waermepumpe.Betriebsdaten.IstTempTQA-cd.WP.Waermepumpe.Betriebsdaten.IstTempTQE)*1.163
    temp = web2com_values('/1/2/1/125/0')
    cd.WP.Waermepumpe.Betriebsdaten.StatusWaermeerzeuger = temp
    
    if int(temp) == 0:
        cd.WP.Waermepumpe.Betriebsdaten.Statustext = '0: Abgeschalten'
    elif int(temp) == 1:
        cd.WP.Waermepumpe.Betriebsdaten.Statustext = '1: Heizbetrieb'
    elif int(temp) == 2:
        cd.WP.Waermepumpe.Betriebsdaten.Statustext = '2: Vorlaufzeit Heizbetrieb'
    elif int(temp) == 3:
        cd.WP.Waermepumpe.Betriebsdaten.Statustext = '3: Extern gesperrt'
    elif int(temp) == 4:
        cd.WP.Waermepumpe.Betriebsdaten.Statustext = '4: Kühlbetrieb'
    elif int(temp) == 5:
        cd.WP.Waermepumpe.Betriebsdaten.Statustext = '5: Vorlaufzeit Kühlbetrieb'
    elif int(temp) == 6:
        cd.WP.Waermepumpe.Betriebsdaten.Statustext = '6: Vorlaufzeit Abtaubetrieb'
    elif int(temp) == 7:
        cd.WP.Waermepumpe.Betriebsdaten.Statustext = '7: Abtaubetrieb'
    elif int(temp) == 8:
        cd.WP.Waermepumpe.Betriebsdaten.Statustext = '8: Störung'
    elif int(temp) == 9:
        cd.WP.Waermepumpe.Betriebsdaten.Statustext = '9: Abtropfen'
    elif int(temp) == 10:
        cd.WP.Waermepumpe.Betriebsdaten.Statustext = '10: Abtausperrzeit'
    elif int(temp) == 11:
        cd.WP.Waermepumpe.Betriebsdaten.Statustext = '11: Abtau Vorheizung'
    elif int(temp) == 12:
        cd.WP.Waermepumpe.Betriebsdaten.Statustext = '12: Abtauen 1'
    elif int(temp) == 13:
        cd.WP.Waermepumpe.Betriebsdaten.Statustext = '13: Abtauen 2'
    elif int(temp) == 14:
        cd.WP.Waermepumpe.Betriebsdaten.Statustext = '14: Abtauen 3'
    elif int(temp) == 15:
        cd.WP.Waermepumpe.Betriebsdaten.Statustext = '15: Alarm'
    elif int(temp) == 16:
        cd.WP.Waermepumpe.Betriebsdaten.Statustext = '16: Störung - Wärmequelle zu kalt'
    elif int(temp) == 17:
        cd.WP.Waermepumpe.Betriebsdaten.Statustext = '17: Blockiert'
    elif int(temp) == 18:
        cd.WP.Waermepumpe.Betriebsdaten.Statustext = '18: Vorglühen'
    elif int(temp) == 19:
        cd.WP.Waermepumpe.Betriebsdaten.Statustext = '19: Eindüsung Ein'
    elif int(temp) == 20:
        cd.WP.Waermepumpe.Betriebsdaten.Statustext = '20: Eindüsung Aus'
    elif int(temp) == 22:
        cd.WP.Waermepumpe.Betriebsdaten.Statustext = '22: TWVmax Abschaltung'
    elif int(temp) == 23:
        cd.WP.Waermepumpe.Betriebsdaten.Statustext = '23: TWEmax Abschaltung'
    elif int(temp) == 24:
        cd.WP.Waermepumpe.Betriebsdaten.Statustext = '24: TWAmin Abschaltung'
    elif int(temp) == 25:
        cd.WP.Waermepumpe.Betriebsdaten.Statustext = '25: TKAmin Abschaltung'
    elif int(temp) == 26:
        cd.WP.Waermepumpe.Betriebsdaten.Statustext = '26: Bivalzenabschaltung'
    elif int(temp) == 27:
        cd.WP.Waermepumpe.Betriebsdaten.Statustext = '27: Warmwasser Ladesperre'
    elif int(temp) == 28:
        cd.WP.Waermepumpe.Betriebsdaten.Statustext = '28: Minimale Auszeit'
    elif int(temp) == 29:
        cd.WP.Waermepumpe.Betriebsdaten.Statustext = '29: Minimale Einzeit'
    elif int(temp) == 30:
        cd.WP.Waermepumpe.Betriebsdaten.Statustext = '30: Anheizen'
    elif int(temp) == 31:
        cd.WP.Waermepumpe.Betriebsdaten.Statustext = '31: Ausbrand'
    elif int(temp) == 32:
        cd.WP.Waermepumpe.Betriebsdaten.Statustext = '32: Pumpen-Nachlauf'
    elif int(temp) == 33:
        cd.WP.Waermepumpe.Betriebsdaten.Statustext = '33: Verzögerung Folge-WE'
    elif int(temp) == 34:
        cd.WP.Waermepumpe.Betriebsdaten.Statustext = '34: Betrieb Übertemperatur'
    elif int(temp) == 35:
        cd.WP.Waermepumpe.Betriebsdaten.Statustext = '35: Fülltüre geöffnet'
    elif int(temp) == 36:
        cd.WP.Waermepumpe.Betriebsdaten.Statustext = '36: Passivkühlung'
    elif int(temp) == 37:
        cd.WP.Waermepumpe.Betriebsdaten.Statustext = '37: Heizbetrieb angefordert'
    elif int(temp) == 38:
        cd.WP.Waermepumpe.Betriebsdaten.Statustext = '38: Kühlbetrieb angefordert'
    else:
        cd.WP.Waermepumpe.Betriebsdaten.Statustext = temp
    
    cd.WP.Fussbodenheizung.Betriebsdaten.Aussentemperatur = web2com_values('/1/2/5/119/1')
    cd.WP.Fussbodenheizung.Betriebsdaten.HeizkreisVorlauftemperatur = web2com_values('/1/2/5/119/5')
    cd.WP.Fussbodenheizung.Betriebsdaten.MittelwertAussentemperatur = web2com_values('/1/2/5/119/2')
    cd.WP.Fussbodenheizung.Betriebsdaten.Raumtemperatur = web2com_values('/1/2/5/119/3')
    cd.WP.Fussbodenheizung.Betriebsdaten.RelativeFeuchte = web2com_values('/1/2/5/119/7')
    cd.WP.Fussbodenheizung.Betriebsdaten.SollwertHeizkreisVorlauftemperatur = web2com_values('/1/2/5/119/6')
    cd.WP.Fussbodenheizung.Betriebsdaten.SollwertRaumtemperatur = web2com_values('/1/2/5/119/4')
    
    temp = web2com_values('/1/2/5/119/0')
    cd.WP.Fussbodenheizung.Betriebsdaten.StatusHeizkreis = temp
    
    if int(temp) == 0:
        cd.WP.Fussbodenheizung.Betriebsdaten.Statustext = '0: Abgeschaltet'
    elif int(temp) == 1:
        cd.WP.Fussbodenheizung.Betriebsdaten.Statustext = '1: Normal Heizbetrieb'
    elif int(temp) == 2:
        cd.WP.Fussbodenheizung.Betriebsdaten.Statustext = '2: Komfort Heizbetrieb'
    elif int(temp) == 3:
        cd.WP.Fussbodenheizung.Betriebsdaten.Statustext = '3: Spar Heizbetrieb'
    elif int(temp) == 4:
        cd.WP.Fussbodenheizung.Betriebsdaten.Statustext = '4: Frostschutzbetrieb'
    elif int(temp) == 5:
        cd.WP.Fussbodenheizung.Betriebsdaten.Statustext = '5: Zwangsabnahme'
    elif int(temp) == 6:
        cd.WP.Fussbodenheizung.Betriebsdaten.Statustext = '6: Zwangsdrosselung'
    elif int(temp) == 7:
        cd.WP.Fussbodenheizung.Betriebsdaten.Statustext = '7: Ferienbetrieb'
    elif int(temp) == 8:
        cd.WP.Fussbodenheizung.Betriebsdaten.Statustext = '8: Partybetrieb'
    elif int(temp) == 9:
        cd.WP.Fussbodenheizung.Betriebsdaten.Statustext = '9: Normal Kühlbetrieb'
    elif int(temp) == 10:
        cd.WP.Fussbodenheizung.Betriebsdaten.Statustext = '10: Komfort Kühlbetrieb'
    elif int(temp) == 11:
        cd.WP.Fussbodenheizung.Betriebsdaten.Statustext = '11: Spar Kühlbetrieb'
    elif int(temp) == 12:
        cd.WP.Fussbodenheizung.Betriebsdaten.Statustext = '12: Störung'
    elif int(temp) == 13:
        cd.WP.Fussbodenheizung.Betriebsdaten.Statustext = '13: Handbetrieb'
    elif int(temp) == 14:
        cd.WP.Fussbodenheizung.Betriebsdaten.Statustext = '14: Schutz Kühlbetrieb'
    elif int(temp) == 15:
        cd.WP.Fussbodenheizung.Betriebsdaten.Statustext = '15: Partybetrieb Kühlen'
    elif int(temp) == 16:
        cd.WP.Fussbodenheizung.Betriebsdaten.Statustext = '16: Austrocknung Aufheizen'
    elif int(temp) == 17:
        cd.WP.Fussbodenheizung.Betriebsdaten.Statustext = '17: Austrocknung Stationär'
    elif int(temp) == 18:
        cd.WP.Fussbodenheizung.Betriebsdaten.Statustext = '18: Austrocknung Auskühlen'
    elif int(temp) == 19:
        cd.WP.Fussbodenheizung.Betriebsdaten.Statustext = '19: Austrocknung Endphase'
    elif int(temp) == 20:
        cd.WP.Fussbodenheizung.Betriebsdaten.Statustext = '20: Nachtlüftung'
    elif int(temp) == 21:
        cd.WP.Fussbodenheizung.Betriebsdaten.Statustext = '21: Belüftung'
    elif int(temp) == 22:
        cd.WP.Fussbodenheizung.Betriebsdaten.Statustext = '22: Kühlbetrieb extern'
    elif int(temp) == 23:
        cd.WP.Fussbodenheizung.Betriebsdaten.Statustext = '23: Heizbetrieb extern'
    else :
        cd.WP.Fussbodenheizung.Betriebsdaten.Statustext = temp
    
    cd.WP.Waermeverteiler.Betriebsdaten.AnlageVorlauftemperatur = web2com_values('/1/2/8/122/2')
    cd.WP.Waermeverteiler.Betriebsdaten.HeizleistungHeizbetrieb = web2com_values('/1/2/8/122/4')
    cd.WP.Waermeverteiler.Betriebsdaten.HeizleistungWarmwasserbetrieb = web2com_values('/1/2/8/122/5')
    cd.WP.Waermeverteiler.Betriebsdaten.IstTempTPMPuffertemperatur = web2com_values('/1/2/8/122/1')
    cd.WP.Waermeverteiler.Betriebsdaten.IstTempTPOPuffertemperatur = web2com_values('/1/2/8/122/0')
    cd.WP.Waermeverteiler.Betriebsdaten.SollwertAnlagevorlauf = web2com_values('/1/2/8/122/3')
    
    temp = web2com_values('/1/2/8/122/6')
    cd.WP.Waermeverteiler.Betriebsdaten.StatusWaermemanager = temp
    
    if int(temp) == 0:
        cd.WP.Waermeverteiler.Betriebsdaten.Statustext = '0: Abgeschaltet'
    elif int(temp) == 1:
        cd.WP.Waermeverteiler.Betriebsdaten.Statustext = '1: Heizen'
    elif int(temp) == 2:
        cd.WP.Waermeverteiler.Betriebsdaten.Statustext = '2: Kühlen'
    elif int(temp) == 3:
        cd.WP.Waermeverteiler.Betriebsdaten.Statustext = '3: WWA'
    elif int(temp) == 4:
        cd.WP.Waermeverteiler.Betriebsdaten.Statustext = '4: PWA'
    elif int(temp) == 5:
        cd.WP.Waermeverteiler.Betriebsdaten.Statustext = '5: Heizen Kühlen'
    elif int(temp) == 6:
        cd.WP.Waermeverteiler.Betriebsdaten.Statustext = '6: Heizen WWA'
    elif int(temp) == 7:
        cd.WP.Waermeverteiler.Betriebsdaten.Statustext = '7: Heizen PWA'
    elif int(temp) == 8:
        cd.WP.Waermeverteiler.Betriebsdaten.Statustext = '8: Heizen Kühlen WWA'
    elif int(temp) == 9:
        cd.WP.Waermeverteiler.Betriebsdaten.Statustext = '9: Heizen Kühlen PWA'
    elif int(temp) == 10:
        cd.WP.Waermeverteiler.Betriebsdaten.Statustext = '10: Heizen WWA PWA'
    elif int(temp) == 11:
        cd.WP.Waermeverteiler.Betriebsdaten.Statustext = '11: Heizen Kühlen WWA PWA'
    elif int(temp) == 12:
        cd.WP.Waermeverteiler.Betriebsdaten.Statustext = '12: Kühlen WWA'
    elif int(temp) == 13:
        cd.WP.Waermeverteiler.Betriebsdaten.Statustext = '13: Kühlen PWA'
    elif int(temp) == 14:
        cd.WP.Waermeverteiler.Betriebsdaten.Statustext = '14: Kühlen WWA PWA'
    elif int(temp) == 15:
        cd.WP.Waermeverteiler.Betriebsdaten.Statustext = '15: WWA PWA'
    elif int(temp) == 16:
        cd.WP.Waermeverteiler.Betriebsdaten.Statustext = '16: Störung'
    else:
        cd.WP.Waermeverteiler.Betriebsdaten.Statustext = temp
    
    cd.WP.Warmwasserkreis.Betriebsdaten.IstTempTBWarmwasser = web2com_values('/1/2/7/121/1')
    cd.WP.Warmwasserkreis.Betriebsdaten.SollwertWarmwassertemperatur = web2com_values('/1/2/7/121/2')
    
    temp = web2com_values('/1/2/7/121/0')
    cd.WP.Warmwasserkreis.Betriebsdaten.StatusWarmwasser = temp
    
    if int(temp) == 0:
        cd.WP.Warmwasserkreis.Betriebsdaten.Statustext = '0: Abgeschaltet'
    elif int(temp) == 1:
        cd.WP.Warmwasserkreis.Betriebsdaten.Statustext = '1: Normal Ladebetrieb'
    elif int(temp) == 2:
        cd.WP.Warmwasserkreis.Betriebsdaten.Statustext = '2: Komfort Ladebetrieb'
    elif int(temp) == 3:
        cd.WP.Warmwasserkreis.Betriebsdaten.Statustext = '3: Zwangsdrosselung'
    elif int(temp) == 4:
        cd.WP.Warmwasserkreis.Betriebsdaten.Statustext = '4: Zwangsladung'
    elif int(temp) == 5:
        cd.WP.Warmwasserkreis.Betriebsdaten.Statustext = '5: Störung'
    elif int(temp) == 6:
        cd.WP.Warmwasserkreis.Betriebsdaten.Statustext = '6: WW Entnahme aktiv'
    elif int(temp) == 7:
        cd.WP.Warmwasserkreis.Betriebsdaten.Statustext = '7: Warnung'
    else:
        cd.WP.Warmwasserkreis.Betriebsdaten.Statustext = temp
    
    cd.WP.Zusatzheizung.Betriebsdaten.Betriebsstunden = web2com_values('/1/2/2/126/4')
    cd.WP.Zusatzheizung.Betriebsdaten.HeizenergieMWh = web2com_values('/1/2/2/126/6')
    cd.WP.Zusatzheizung.Betriebsdaten.HeizenergiekWh = web2com_values('/1/2/2/126/5')
    cd.WP.Zusatzheizung.Betriebsdaten.IstTempVWV = web2com_values('/1/2/2/126/1')
    cd.WP.Zusatzheizung.Betriebsdaten.Schaltzyklen = web2com_values('/1/2/2/126/3')
    cd.WP.Zusatzheizung.Betriebsdaten.VorlauftempSollwertAnforderung = web2com_values('/1/2/2/126/2')
    
    temp = web2com_values('/1/2/2/126/0')
    cd.WP.Zusatzheizung.Betriebsdaten.StatusWaermeerzeuger = temp
    
    if int(temp) == 0:
        cd.WP.Zusatzheizung.Betriebsdaten.Statustext = '0: Abgeschalten'
    elif int(temp) == 1:
        cd.WP.Zusatzheizung.Betriebsdaten.Statustext = '1: Heizbetrieb'
    elif int(temp) == 2:
        cd.WP.Zusatzheizung.Betriebsdaten.Statustext = '2: Vorlaufzeit Heizbetrieb'
    elif int(temp) == 3:
        cd.WP.Zusatzheizung.Betriebsdaten.Statustext = '3: Extern gesperrt'
    elif int(temp) == 4:
        cd.WP.Zusatzheizung.Betriebsdaten.Statustext = '4: Kühlbetrieb'
    elif int(temp) == 5:
        cd.WP.Zusatzheizung.Betriebsdaten.Statustext = '5: Vorlaufzeit Kühlbetrieb'
    elif int(temp) == 6:
        cd.WP.Zusatzheizung.Betriebsdaten.Statustext = '6: Vorlaufzeit Abtaubetrieb'
    elif int(temp) == 7:
        cd.WP.Zusatzheizung.Betriebsdaten.Statustext = '7: Abtaubetrieb'
    elif int(temp) == 8:
        cd.WP.Zusatzheizung.Betriebsdaten.Statustext = '8: Störung'
    elif int(temp) == 9:
        cd.WP.Zusatzheizung.Betriebsdaten.Statustext = '9: Abtropfen'
    elif int(temp) == 10:
        cd.WP.Zusatzheizung.Betriebsdaten.Statustext = '10: Abtausperrzeit'
    elif int(temp) == 11:
        cd.WP.Zusatzheizung.Betriebsdaten.Statustext = '11: Abtau Vorheizung'
    elif int(temp) == 12:
        cd.WP.Zusatzheizung.Betriebsdaten.Statustext = '12: Abtauen 1'
    elif int(temp) == 13:
        cd.WP.Zusatzheizung.Betriebsdaten.Statustext = '13: Abtauen 2'
    elif int(temp) == 14:
        cd.WP.Zusatzheizung.Betriebsdaten.Statustext = '14: Abtauen 3'
    elif int(temp) == 15:
        cd.WP.Zusatzheizung.Betriebsdaten.Statustext = '15: Alarm'
    elif int(temp) == 16:
        cd.WP.Zusatzheizung.Betriebsdaten.Statustext = '16: Störung'
    elif int(temp) == 17:
        cd.WP.Zusatzheizung.Betriebsdaten.Statustext = '17: Blockiert'
    elif int(temp) == 18:
        cd.WP.Zusatzheizung.Betriebsdaten.Statustext = '18: Vorglühen'
    elif int(temp) == 19:
        cd.WP.Zusatzheizung.Betriebsdaten.Statustext = '19: Eindüsung Ein'
    elif int(temp) == 20:
        cd.WP.Zusatzheizung.Betriebsdaten.Statustext = '20: Eindüsung Aus'
    elif int(temp) == 21:
        cd.WP.Zusatzheizung.Betriebsdaten.Statustext = '21: TWVmax Abschaltung'
    elif int(temp) == 22:
        cd.WP.Zusatzheizung.Betriebsdaten.Statustext = '22: TWVsoll Abschaltung'
    elif int(temp) == 23:
        cd.WP.Zusatzheizung.Betriebsdaten.Statustext = '23: TWEmax Abschaltung'
    elif int(temp) == 24:
        cd.WP.Zusatzheizung.Betriebsdaten.Statustext = '24: TWAmin Abschaltung'
    elif int(temp) == 25:
        cd.WP.Zusatzheizung.Betriebsdaten.Statustext = '25: TKAmin Abschaltung'
    elif int(temp) == 26:
        cd.WP.Zusatzheizung.Betriebsdaten.Statustext = '26: Bivalenzabschaltung'
    elif int(temp) == 27:
        cd.WP.Zusatzheizung.Betriebsdaten.Statustext = '27: Warmwasser Ladesperre'
    elif int(temp) == 28:
        cd.WP.Zusatzheizung.Betriebsdaten.Statustext = '28: Minimale Auszeit'
    elif int(temp) == 29:
        cd.WP.Zusatzheizung.Betriebsdaten.Statustext = '29: Minimale Einzeit'
    elif int(temp) == 30:
        cd.WP.Zusatzheizung.Betriebsdaten.Statustext = '30: Anheizen'
    elif int(temp) == 31:
        cd.WP.Zusatzheizung.Betriebsdaten.Statustext = '31: Ausbrand'
    elif int(temp) == 32:
        cd.WP.Zusatzheizung.Betriebsdaten.Statustext = '32: Pumpen-Nachlauf'
    elif int(temp) == 33:
        cd.WP.Zusatzheizung.Betriebsdaten.Statustext = '33: Verzögerung Folge-WE'
    elif int(temp) == 34:
        cd.WP.Zusatzheizung.Betriebsdaten.Statustext = '34: Betrieb Uebertemperatur'
    elif int(temp) == 35:
        cd.WP.Zusatzheizung.Betriebsdaten.Statustext = '35: Fülltüre geöffnet'
    elif int(temp) == 36:
        cd.WP.Zusatzheizung.Betriebsdaten.Statustext = '36: Passivkühlung'
    elif int(temp) == 37:
        cd.WP.Zusatzheizung.Betriebsdaten.Statustext = '37: Heizbetrieb angefordert'
    elif int(temp) == 38:
        cd.WP.Zusatzheizung.Betriebsdaten.Statustext = '38: Kühlbetrieb angefordert'
    else:
        cd.WP.Zusatzheizung.Betriebsdaten.Statustext = temp
    Dabei hab ich mir fleissig alle Einträge der Statustexte mit den Entwicklertools extrahiert und eingetragen.

    Die folgende Zeile ist vielleicht auch noch interessant:
    Code:
    cd.WP.Waermepumpe.Betriebsdaten.Leistung =  60/1000*cd.WP.Waermepumpe.Betriebsdaten.VolumenstromWaermequelle*abs(cd.WP.Waermepumpe.Betriebsdaten.IstTempTQA-cd.WP.Waermepumpe.Betriebsdaten.IstTempTQE)*1.163
    Hier berechne ich mir auf Grundlage des Volumenstroms (der Wasserfördermenge aus der WP in m³/h), der Temperaturdifferenz (Spreizung in Kelvin), der Dichte von Wasser (hier mit 1t/m³ angenommen) und der spezifischen Wärme von Wasser (in 1.163 Wh/(kg*K)) die Heizleistung meiner Wärmepumpe in Watt (W). Also im Detail:
    cd.WP.Waermepumpe.Betriebsdaten.VolumenstromWaerme quelle -> liegt in l/min vor -> also erstmal durch 1000, damit wir m³/min bekommen -> dann mal 60 für m³/h
    alle weiteren Werte bleiben und die Einheiten lassen sich wunderbar soweit wegkürzen, dass nur noch "Watt" übrig bleiben.

    Jetzt haben wir die Logik als Grundlage.
    Die nutze ich schon mal für die Detailanzeigen in CD:

    Detailansicht.png


    5. Schritt: Grafiken erstellen
    Wie oben gezeigt, möchte ich gerne die beiden Grafiken in die Visu integrieren:

    Ochsner-AnzeigeWP.png

    Dafür hab ich beide in LibreOffice Draw einfach nachgezeichnet und als SVG einzeln exportiert.

    Draw-Widgetanzeige.png

    Da kommt dann erstmal ganz viel Müll in der SVG mit an (Nur ein Auszug daraus):
    Code:
    <?xml version="1.0" encoding="UTF-8"?>
    <!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
    <svg version="1.2" width="103mm" height="51.12mm" viewBox="2900 6000 10300 5112" preserveAspectRatio="xMidYMid" fill-rule="evenodd" stroke-width="28.222" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" xmlns:ooo="http://xml.openoffice.org/svg/export" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:presentation="http://sun.com/xmlns/staroffice/presentation" xmlns:smil="http://www.w3.org/2001/SMIL20/" xmlns:anim="urn:oasis:names:tc:opendocument:xmlns:animation:1.0" xml:space="preserve">
     <defs class="ClipPathGroup">
      <clipPath id="presentation_clip_path" clipPathUnits="userSpaceOnUse">
       <rect x="2900" y="6000" width="10300" height="5112"/>
      </clipPath>
     </defs>
     <defs>
      <font id="EmbeddedFont_1" horiz-adv-x="2048">
       <font-face font-family="Liberation Sans embedded" units-per-em="2048" font-weight="normal" font-style="normal" ascent="1852" descent="423"/>
       <missing-glyph horiz-adv-x="2048" d="M 0,0 L 2047,0 2047,2047 0,2047 0,0 Z"/>
       <glyph unicode="°" horiz-adv-x="609" d="M 696,1145 C 696,1106 689,1069 674,1034 659,999 638,969 612,944 586,918 556,898 521,883 486,868 448,860 409,860 370,860 333,868 298,883 263,898 233,918 207,944 181,969 160,999 145,1034 130,1069 122,1106 122,1145 122,1185 130,1222 145,1257 160,1292 181,1322 207,1348 233,1373 263,1393 298,1408 333,1423 370,1430 409,1430 448,1430 486,1423 521,1408 556,1393 586,1373 612,1348 638,1322 659,1292 674,1257 689,1222 696,1185 696,1145 Z M 587,1145 C 587,1170 583,1193 574,1215 565,1237 552,1256 536,1273 520,1289 501,1302 480,1311 458,1320 434,1325 409,1325 384,1325 361,1320 340,1311 318,1302 299,1289 283,1273 267,1256 254,1237 245,1215 236,1193 231,1170 231,1145 231,1120 236,1097 245,1076 254,1054 267,1035 283,1019 299,1002 318,989 340,980 361,970 384,965 409,965 434,965 458,970 480,980 501,989 520,1002 536,1019 552,1035 565,1054 574,1076 583,1097 587,1120 587,1145 Z"/>
       <glyph unicode="t" horiz-adv-x="531" d="M 554,8 C 527,1 499,-5 471,-10 442,-14 409,-16 372,-16 228,-16 156,66 156,229 L 156,951 31,951 31,1082 163,1082 216,1324 336,1324 336,1082 536,1082 536,951 336,951 336,268 C 336,216 345,180 362,159 379,138 408,127 450,127 467,127 484,128 501,131 517,134 535,137 554,141 L 554,8 Z"/> .....
    Da muss man doch noch einiges drinn aufräumen, damit es am Ende wie folgt aussieht (Beipielhaft am Fussbodenheizungsteil):
    example.png
    Code:
    <svg width="300" height="168" viewBox="0 0 900 500">
    <text class="Fussbodenheizung" font-family="sans-serif" font-size="63px" x="485" y="329">-°C</text>
    <text class="Aussentemperatur" font-family="sans-serif" font-size="63px" x="5" y="159">-°C</text>
    <text class="Warmwasser" font-family="sans-serif" font-size="63px" x="705" y="192">-°C</text>
    <text class="Innentemp" font-family="sans-serif" font-size="63px" x="425" y="192">-°C</text>
    <text class="Status" font-family="sans-serif" font-size="63px"  x="200" y="492">...</text>
    
    <g fill="none" stroke="rgb(0,0,0)" stroke-width="20">
    <path d="M 290,130 L 520,10"/>
    <path d="M 350,400 L 350,100"/>
    <path d="M 300,400 L 1020,400"/>
    </g>
    
    <g fill="none" stroke="rgb(0,0,0)" stroke-width="10">
    <path d="M 390,250 L 760,250"/>
    <path d="M 440,290 L 470,290"/>
    <path d="M 390,370 L 760,370"/>
    <path d="M 759,250 C 759,250 759,250 760,250 763,250 766,250 770,252 773,254 775,256 777,260 779,263 780,266 780,270 780,273 779,276 777,280 775,283 773,285 770,287 766,289 763,290 760,290 759,290 759,290 759,290"/>
    <path d="M 759,330 C 759,330 759,330 760,330 763,330 766,330 770,332 773,334 775,336 777,340 779,343 780,346 780,350 780,353 779,356 777,360 775,363 773,365 770,367 766,369 763,370 760,370 759,370 759,370 759,370"/>
    <path d="M 441,330 C 440,330 440,330 440,330 436,330 433,329 430,327 427,325 424,323 423,320 421,316 420,313 420,310 420,306 421,303 423,300 424,296 427,294 430,292 433,290 436,290 440,290 440,290 440,290 441,290"/>
    <path d="M 690,290 L 760,290"/>
    <path d="M 440,330 L 470,330"/>
    <path d="M 690,330 L 760,330"/>
    </g>
    
    <g fill="none" stroke="rgb(0,0,0)" stroke-width="5">
    <path d="M 191,45 C 200,45 206,52 206,60 206,69 200,75 191,75 183,75 176,69 176,60 176,52 183,45 191,45 Z"/>
    <path d="M 196,45 L 206,25"/>
    <path d="M 206,55 L 226,45"/>
    <path d="M 206,65 L 226,75"/>
    <path d="M 196,75 L 206,95"/>
    <path d="M 186,75 L 176,95"/>
    <path d="M 94,29 C 97,26 100,23 103,21 106,20 109,18 112,17 115,16 117,14 121,14 124,14 127,14 130,14 133,14 136,16 139,17 142,19 144,23 147,24 150,26 153,28 156,28 159,29 162,31 165,32 167,33 171,33 174,34 177,36 179,38 181,41 182,44 182,47 181,50 179,53 175,54 172,56 169,58 166,60 163,62 160,63 157,62 154,64 152,66 149,67 147,70 146,74 142,75 139,77 136,79 133,80 130,81 127,81 124,81 121,80 118,78 116,78 113,77 110,76 106,75 104,73 101,71 99,67 96,65 92,63 90,62 87,60 84,59 81,58 79,55 77,52 77,49 77,46 77,43 79,40 82,38 84,36 88,35 90,33 L 93,31 95,29 94,29 Z"/>
    <path d="M 394,179 L 387,179 387,155 400,155 400,179 394,179 Z"/>
    <path d="M 394,176 C 403,176 409,183 409,191 409,200 403,206 394,206 386,206 379,200 379,191 379,183 386,176 394,176 Z"/>
    <path d="M 388,136 L 388,179"/>
    <path d="M 400,136 L 400,179"/>
    <path d="M 388,136 C 388,135 388,134 388,133 389,132 390,131 391,130 392,130 393,130 394,130 395,130 396,130 397,130 398,131 399,132 400,133 400,134 400,135 400,136 400,136 400,136 400,136"/>
    <path d="M 400,177 C 401,178 403,178 404,179 406,181 408,183 409,186 410,188 410,191 409,193 409,196 408,198 406,201 405,203 403,204 400,205 397,206 395,206 392,206 389,206 387,205 385,203 383,201 381,199 380,197 379,194 379,192 380,189 380,186 381,184 383,182 384,180 386,178 389,177"/>
    <path d="M 831,109 L 739,109 762,87 808,87 831,109 Z"/>
    <path d="M 761,109 L 740,130"/>
    <path d="M 782,109 L 768,130"/>
    <path d="M 831,130 L 810,109"/>
    <path d="M 803,130 L 789,109"/>
    <path d="M 785,88 L 785,67"/>
    <path d="M 785,67 C 785,60 792,60 792,60 L 813,60 820,60"/>
    </g>
    
    <g fill="rgb(0,0,0)" stroke="none">
    <path d="M 394,179 L 387,179 387,155 400,155 400,179 394,179 Z"/>
    <path d="M 394,176 C 403,176 409,183 409,191 409,200 403,206 394,206 386,206 379,200 379,191 379,183 386,176 394,176 Z M 379,176 L 379,176 Z M 410,207 L 410,207 Z"/>
    </g>
    </svg>
    --------

    Ab hier übergebe ich an Waldemar für die Erstellung der zugehörigen:
    • __init__.py
    • widget.js für die Darstellung der Item-Werte
    • widget.css für die eingentliche Darstellung (Font, Farbe etc.) der class-Attribute




    Grüße
    Thorsten
    Merken
    Merken
    Angehängte Dateien
    You do not have permission to view this gallery.
    This gallery has 2 photos.
    Zuletzt geändert von RoyalTS; 17.01.2017, 12:17.

    #2
    Hi allerseits,

    dann bin ich wohl dran. Ich hoffe, es ist übersichtlich genug:

    Zuerallererst: Ein Widget, dass nur Daten anzeigen soll, ist recht einfach zu machen. Wenn es um Interaktion gehen soll (also irgendwas clicken oder ziehen), dann kann es beliebig kompliziert werden, aber das ist nicht das Thema von diesem Thread.

    Die Hauptarbeit liegt in dem obigen SVG. Wenn man sich die ersten paar Zeilen anschaut:
    Code:
    <svg width="300" height="168" viewBox="0 0 900 500">
    <text class="Fussbodenheizung" font-family="sans-serif" font-size="63px" x="485" y="329">-°C</text>
    <text class="Aussentemperatur" font-family="sans-serif" font-size="63px" x="5" y="159">-°C</text>
    <text class="Warmwasser" font-family="sans-serif" font-size="63px" x="705" y="192">-°C</text>
    <text class="Innentemp" font-family="sans-serif" font-size="63px" x="425" y="192">-°C</text>
    <text class="Status" font-family="sans-serif" font-size="63px"  x="200" y="492">...</text>
    dann sieht man 2 Sachen:
    1. Die erste Zeile gibt mit width und height die Größe des Widgets in callidomus an, mit viewBox wird die callidomus-Größe auf ein abstraktes Koordinatensystem gemapped. Die Koordinatenangaben im SVG beziehen sich immer auf dieses Koordinatensystem.
    2. Die Platzhalter für die Ausgabe der Werte sind SVG-Text-Tags, die alle auch eine Klasse zugewiesen haben müssen, die sagt, was das für ein Text ist. Durch ihre x- und y-Koordinaten sind die Dinger schon im SVG an der richtigen Stelle.
    Wir brauchen jetzt also ein Stück Programmcode, der das SVG auf die Webseite der callidomus-Visu bringt und die Werte der Text-Tags (das sind die -°C hinten) mit Item-Werten aus callidomus austauscht und auch immer aktualisiert. Dann würde das in der Visu schon funktionieren. Ferner wollen wir das Widget ja auch noch in der GUI hinzufügen können, deswegen brauchen wir noch coding für den "Widget-Hinzufügen-Dialog".

    Das war es dann auch schon... dann machen wir das!

    Erstmal ein paar Vorbereitungen:
    Eigene Widgets gehören in das Verzeichnis
    Code:
    /data/callidomus/local/visu/widgets
    Da legen wir dann ein neues Unterverzeichnis an und erzeugen eine Widget-Verzeichnisstruktur:
    Code:
    cd /data/callidomus/local/visu/widgets
    md web2comheiz
    md web2comheiz/static
    nano web2comheiz/static/detail.svg
    ---- hier jetzt das svg aus Thorstens Beitrag reinkopieren ----
    ---- Taste Strg-o drücken ----
    ---- Taste Strg-x drücken ----
    So, jetzt haben wir schon mal das SVG an der richtigen Stelle.
    Für den "Widget-Hinzufügen-Dialog" und um das SVG auf die Visu-Seite zu bringen, brauchen wir ein python-Programm. Python deswegen, weil das von callidomus direkt aufgerufen wird und callidomus in python geschrieben ist. Zuerst kommt jetzt das kommentierte Programm, dann die Anweisung, wie man es ins System bekommt:

    Code:
    # Diese Imports muessen immer da sein
    import collections
    import widgets
    
    # Der Name der Widget-Klasse muss immer wie das Widget heißen, nur mit einem Großbuchstaben beginnen
    class Web2comheiz(widgets.Widget):
    
        # Als erstes muss man die API vom Widget beschreiben, also wie wird das Widget technisch aufgerufen
        api = {
            # Name des Widgets (derzeit nur Kleinbuchstaben und Zahlen)
            'name': 'web2comheiz',
            # Beschreibung des Widgets
            'desc': 'Heizungswidget für web2com Wärmepumpen',
            'cat': 'Value',
            # Beispielcode für den Aufruf
            # Das folgende MUSS syntaktisch korrekt sein, da es für den Visu- und GUI-Build genutzt wird!
            'examples': [
                # im folgenden muss der widgetname und alle Parameternamen korrekt sein, die Inhalte sind beliebig
                {'code': "web2comheiz(item='demo.temperature', fbh='demo.rtr.set',\
                    aussen='demo.rtr.mode', innen='demo.rtr.open', h2o='demo.rtr.set',\
                    name='web2comHeiz', desc='Heizung')"}
            ],
        }
    
        # Ab hier wird der Dialog fuer den Widget-Editor im GUI definiert
        def __init__(self):
            # Die folgende Zeile ist notwendig
            widgets.Widget.__init__(self)
    
            # Jedes Widget hat mindestens eine Item-Referenz, der Parameter item ist somit bereits definiert, wir geben dem Paramter
            # nur noch seinen Namen
            self.params['item']['label'] = 'Item Status'
    
            # Eine vollstaendige Item-Parameter-Definition mit Namen (label), Wertebereich (pattern), Argumentnotwendigkeit (required),
            # Clickaktion (onfocus) und Online-Hilfe (help). Daraus wird in der GUI ein Eingabefeld für ein Item.
            self.params['fbh'] = {'label': 'Item Fussbodenheizung', 'pattern': 'item', 'required': None,
                                  'onfocus': 'cg.acItems.focus(this);',
                                  'help': """ Temperatur der Fussbodenheizung in °C """}
    
            self.params['aussen'] = {'label': 'Item Aussentemperatur', 'pattern': 'item', 'required': None,
                                   'onfocus': 'cg.acItems.focus(this);',
                                   'help': """ Aussentemperatur in °C """}
    
            self.params['innen'] = {'label': 'Item Innentemperatur', 'pattern': 'item', 'required': None,
                                   'onfocus': 'cg.acItems.focus(this);',
                                   'help': """ Innentemperatur in °C """}
    
            self.params['h2o'] = {'label': 'Item Warmwassertemperatur', 'pattern': 'item', 'required': None,
                                   'onfocus': 'cg.acItems.focus(this);',
                                   'help': """ Warmwassertemperatur in °C """}
    
            # Hiermit kann man die Reihenfolge der Parameter auf der Eingabemaske bestimmen
            self.params.move_to_end('h2o', last=False)
            self.params.move_to_end('innen', last=False)
            self.params.move_to_end('aussen', last=False)
            self.params.move_to_end('fbh', last=False)
            self.params.move_to_end('item', last=False)
    
        # Das folgende Coding bringt das SVG auf die HTML-Seite der Visu und sagt der Visu auch, welche Items
        # dargestellt werden solen. Die Werte dieser Items aktualisiern sich dann auch automatisch.
        def __call__(self, **kwargs):
            # Notwendige Zeile
            self.include(__file__)
    
            # Defaultparameter, die vorbelegt werden
            kwargs.setdefault("size", "m")
            kwargs.setdefault("showstatus", 0)
    
            # Diese Items werden dargestellt und aktualisiert, deren Namen wurde in der oben definierten Maske eingegeben
            # und stehen in kwargs
            items = [kwargs['item'], kwargs['fbh'], kwargs['aussen'], kwargs['innen'], kwargs['h2o']]
    
            # Hier wird das Aussehen des Detail-Widgets bestimmt. Das ist das Widget für den Tablet- und Browser-Modus
            # Technisch wird hier das SVG geladen und auf die HTML-Seite gebracht. Ferner bekommt das SVG noch einen
            # Namen, damit man es spaeter finden kann
            kwargs['detail'] = self.load(
                "widgets/web2comheiz/detail.svg",
                {'class': 'web2comHeizDetail'})
    
            # Hier wird das Aussehen des Digest-Widgets bestimmt. Das ist das Widget für den Mobile-Modus (Handy)
            # Bei diesem Widget sieht der Handy-Modus genau so aus wie der Tablet-Modus, trotzdem bekommt das
            # Widget einen eigenen Namen
            kwargs['digest'] = self.load(
                "widgets/web2comheiz/detail.svg",
                {'class': 'web2comHeizDigest'})
    
            # Die oben definierten Items werden hier uebergeben, damit sie gelesen und aktualisiert werden koennen
            kwargs['attr'] = {'data-item': ' '.join(items)}
    
            # Und der Widget-Name wird noch uebergeben
            kwargs['widget'] = self.name
    
            # Hier wird technisch auf die HTML-Seite der Visu geschrieben
            print(self.frame(kwargs))
    Dieses Programm bekommt man folgendermaßen ins system:
    Code:
    nano __init__.py
    ---- Jetzt das obige Coding reinkopieren ----
    ---- Taste Strg-o ----
    ---- Taste Strg-x ----
    Mit den obigen Schritten hätten wir alles im Browser in der Visu und der Browser wüsste schon, welche Items aktualisiert werden sollen. Leider weiß der Browser noch nicht, wo er die aktualisierten Werte schreiben soll. Das macht man mit einem Javascript-Programm. Wir brauchen deswegen Javascript, weil dieses Programm im Browser auf dem Visu-Rechner läuft, nicht auf dem callidomus-Server. Und der Browser spricht nichts anderes als Javascript.

    Code:
    //Wichtig: hier muss der widgetname stehen
    cd.widgets.web2comheiz = {
    
        // Die folgende refresh-Funktion macht nichts, aber es gibt Fehler, wenn man sie rausloescht
        refresh: function (widgets) {
            //       widgets.each( function() {
            //           var widget = $(this);
            //       });
        },
    
        // Hier wird beschrieben, was aktualisiert werden soll
        // Die Funktion wird von der Visu aufgerufen und ihr wird gesagt
        // Hey widget, Dein item 'ABC' hat jetzt den val 5
        // Die Funktion dient dazu, diesen Wert an die richtige Stelle zu schreiben
        // Das Coding kann man noch stark komprimieren, aber mir ging es hier
        // eher darum, es nachvollziehbar zu machen
        update: function (widget, item, val) {
    
            // Erstmal holen wir uns alle Items, deren Positionen wir im SVG kennen und die wir somit aktualisiern wollen
            // Die Items haben wir im python an kwargs['attr'] uebergeben und holen sie uns hier zurueck
            var items = widget.attr('data-item').split(' ');
            // Jetzt wird jeder einzelne Item-Name rausgeholt
            var status = items[0];
            var fbh = items[1];
            var aussen = items[2];
            var innen = items[3];
            var h2o = items[4];
    
            // Es werden beide SVG, die wir im Python auf die Visu-Seite geschrieben haben, gesucht und gemerkt
            svg1 = d3.select(widget[0]).select('svg.web2comHeizDetail');
            svg2 = d3.select(widget[0]).select('svg.web2comHeizDigest');
    
            //Jetzt wird einfach fuer jedes Item geschaut, ob es den richtigen Namen hat und wenn ja, dann wird passend geaendert
            if (item == status) {
                // Beim Status-Item bekommt das SVG-Tag text mit dem Namen Staus den neuen Wert
                // Es wird immer in beiden SVG geaendert, das beide gleich behandelt werden
                svg1.select('text.Status').text(val);
                svg2.select('text.Status').text(val);
                return;
            } else if (item == fbh) {
                svg1.select('text.Fussbodenheizung').text(val.toFixed(1) + '°C');
                svg2.select('text.Fussbodenheizung').text(val.toFixed(1) + '°C');
                return;
            } else if (item == aussen) {
                svg1.select('text.Aussentemperatur').text(val.toFixed(1) + '°C');
                svg2.select('text.Aussentemperatur').text(val.toFixed(1) + '°C');
                return;
            } else if (item == innen) {
                svg1.select('text.Innentemp').text(val.toFixed(1) + '°C');
                svg2.select('text.Innentemp').text(val.toFixed(1) + '°C');
                return;
            } else if (item == h2o) {
                svg1.select('text.Warmwasser').text(val.toFixed(1) + '°C');
                svg2.select('text.Warmwasser').text(val.toFixed(1) + '°C');
                return;
            }
    
        }
    }
    Dieses Programm bekommt man folgendermaßen ins system:
    Code:
    nano widget.js
    ---- Jetzt das obige Coding reinkopieren ----
    ---- Taste Strg-o ----
    ---- Taste Strg-x ----
    So, das war es, ein widget.css brauchen wir in diesem Beispiel nicht, da die Formatierung schon im SVG geschehen ist.

    Jetzt muss man nur noch folgendes Kommando ausführen:
    Code:
    callidomus.gui build
    Anschließend kann man in der GUI das Widget zu einer Visuseite hinzufügen web2com.PNG

    Dann Visu generieren und das Widget genießen.



    Wichtig für neue Widgets: Derzeit können Widget-Namen nur aus Kleinbuchstaben und Zahlen bestehen, macht keine Grossbuchstaben rein, das funktioniert nicht.

    Gruß, Waldemar
    Zuletzt geändert von mumpf; 14.01.2017, 04:52.
    OpenKNX www.openknx.de

    Kommentar


      #3
      Hi,

      es kam noch die Frage, wie man einen Status im Gruppenkopf darstellt. Das geht relativ einfach. Ich habe im GUI auch noch ein Flag spendiert, über das man die Ausgabe im Gruppenkopf aktivieren/deaktivieren kann (wie Marcus das an vielen Stellen macht):

      web2com.PNG
      Was braucht man: Ein neues Feld "state" mit dem Text "Item Status", falls man nicht den Wert irgend eines schon vorhandenen Items im Kopf darstellen will.
      Das Flag "Status im Gruppenkopf anzeigen".

      Dann sind es nur Ergänzungen im obigen __init__.py, damit diese Sachen auf der Webseite auftauchen. Für die Statusausgabe selbst nutze ich das Value-Widget, das dann selbst für die Aktualisierung des Wertes sorgt. Deswegen muss man auch nichts am Javascript ändern:

      Code:
      # Diese Imports muessen immer da sein
      import collections
      import widgets
      
      # Der Name der Widget-Klasse muss immer wie das Widget heißen, nur mit einem Großbuchstaben beginnen
      class Web2comheiz(widgets.Widget):
      
          # Als erstes muss man die API vom Widget beschreiben, also wie wird das Widget technisch aufgerufen
          api = {
              # Name des Widgets (derzeit nur Kleinbuchstaben und Zahlen)
              'name': 'web2comheiz',
              # Beschreibung des Widgets
              'desc': 'Heizungswidget für web2com Wärmepumpen',
              'cat': 'Value',
              # Beispielcode für den Aufruf
              # Das folgende MUSS syntaktisch korrekt sein, da es für den Visu- und GUI-Build genutzt wird!
              'examples': [
                  # im folgenden muss der widgetname und alle Parameternamen korrekt sein, die Inhalte sind beliebig
                  {'code': "web2comheiz(item='demo.temperature', fbh='demo.rtr.set',\
                      aussen='demo.rtr.mode', innen='demo.rtr.open', h2o='demo.rtr.set',\
                      name='web2comHeiz', desc='Heizung')"},
      [COLOR=#FF0000]            {'code': "web2comheiz(item='demo.temperature', fbh='demo.rtr.set',\
                      aussen='demo.rtr.mode', innen='demo.rtr.open', h2o='demo.rtr.set',\
                      status='demo.rts.state', name='web2comHeiz', desc='Heizung', showstatus=1)"}[/COLOR]
              ],
          }
      
          # Ab hier wird der Dialog fuer den Widget-Editor im GUI definiert
          def __init__(self):
              # Die folgende Zeile ist notwendig
              widgets.Widget.__init__(self)
      
              # Jedes Widget hat mindestens eine Item-Referenz, der Parameter item ist somit bereits definiert, wir geben dem Paramter
              # nur noch seinen Namen
              self.params['item']['label'] = 'Item Status'
      
              # Eine vollstaendige Item-Parameter-Definition mit Namen (label), Wertebereich (pattern), Argumentnotwendigkeit (required),
              # Clickaktion (onfocus) und Online-Hilfe (help). Daraus wird in der GUI ein Eingabefeld für ein Item.
              self.params['fbh'] = {'label': 'Item Fussbodenheizung', 'pattern': 'item', 'required': None,
                                    'onfocus': 'cg.acItems.focus(this);',
                                    'help': """ Temperatur der Fussbodenheizung in °C """}
      
              self.params['aussen'] = {'label': 'Item Aussentemperatur', 'pattern': 'item', 'required': None,
                                     'onfocus': 'cg.acItems.focus(this);',
                                     'help': """ Aussentemperatur in °C """}
      
              self.params['innen'] = {'label': 'Item Innentemperatur', 'pattern': 'item', 'required': None,
                                     'onfocus': 'cg.acItems.focus(this);',
                                     'help': """ Innentemperatur in °C """}
      
              self.params['h2o'] = {'label': 'Item Warmwassertemperatur', 'pattern': 'item', 'required': None,
                                     'onfocus': 'cg.acItems.focus(this);',
                                     'help': """ Warmwassertemperatur in °C """}
      
      [COLOR=#FF0000]        self.params['state'] = {'label': 'Item Status', 'pattern': 'item', 
                                     'onfocus': 'cg.acItems.focus(this);',
                                     'help': """ Der Wert dieses Items wird in der Kopfzeile ausgegeben """}
      
              self.params['showstatus'] = {
                  'label': 'Status im Gruppenkopf anzeigen',
                  'type': 'checkbox',
                  'checked': None,
                  'help': """Wenn dieses Flag gesetzt ist, dann wird der Wert vom 'Status Item' in der Kopfzeile angezeigt.""",
              }[/COLOR]
      
      
              # Hiermit kann man die Reihenfolge der Parameter auf der Eingabemaske bestimmen
              self.params.move_to_end('h2o', last=False)
              self.params.move_to_end('innen', last=False)
              self.params.move_to_end('aussen', last=False)
              self.params.move_to_end('fbh', last=False)
              self.params.move_to_end('item', last=False)
      
          # Das folgende Coding bringt das SVG auf die HTML-Seite der Visu und sagt der Visu auch, welche Items
          # dargestellt werden solen. Die Werte dieser Items aktualisiern sich dann auch automatisch.
          def __call__(self, **kwargs):
              # Notwendige Zeile
              self.include(__file__)
      
              # Defaultparameter, die vorbelegt werden
              kwargs.setdefault("size", "m")
              kwargs.setdefault("showstatus", 0)
      
              # Diese Items werden dargestellt und aktualisiert, deren Namen wurde in der oben definierten Maske eingegeben 
              # und stehen in kwargs
              items = [kwargs['item'], kwargs['fbh'], kwargs['aussen'], kwargs['innen'], kwargs['h2o']]
      
      [COLOR=#FF0000]        # Statusausgabe in der Kopzeile der Gruppe
              # Hier soll exemplarisch eine kleine "Dynamisierung" gezeigt werden
              # Die Statusausgabe wird nur gemacht, wenn auch das entsprechende Flag angegeben ist und das Statusitem vorbelegt ist
              if int(kwargs['showstatus']) == 1 and 'state' in kwargs:
                  # Das Statusitem muss dann auch an Javascript gegeben werden
                  items.append(kwargs['state'])
                  # Das html für die Statusausgabe, nutzt das normale value-widget, deswegen ist kein extra javascript fuer ein Wertupdate noetig
                  showStatusHtml = '<span class="showState">' + self.widgets['value'].html(item=kwargs['state'], round=1) + '</span>'
                  # und der Laufzeit dieses html als Statusinfo übergeben 
                  kwargs['status'] = showStatusHtml[/COLOR]
      
              # Hier wird das Aussehen des Detail-Widgets bestimmt. Das ist das Widget für den Tablet- und Browser-Modus
              # Technisch wird hier das SVG geladen und auf die HTML-Seite gebracht. Ferner bekommt das SVG noch einen 
              # Namen, damit man es spaeter finden kann
              kwargs['detail'] = self.load(
                  "widgets/web2comheiz/detail.svg",
                  {'class': 'web2comHeizDetail'})
              # Hier wird das Aussehen des Digest-Widgets bestimmt. Das ist das Widget für den Mobile-Modus (Handy)
              # Bei diesem Widget sieht der Handy-Modus genau so aus wie der Tablet-Modus, trotzdem bekommt das 
              # Widget einen eigenen Namen
              kwargs['digest'] = self.load(
                  "widgets/web2comheiz/detail.svg",
                  {'class': 'web2comHeizDigest'})
              # Die oben definierten Items werden hier uebergeben, damit sie gelesen und aktualisiert werden koennen
              kwargs['attr'] = {'data-item': ' '.join(items)}
              # Und der Widget-Name wird noch uebergeben
              kwargs['widget'] = self.name
              # Hier wird technisch auf die HTML-Seite der Visu geschrieben
              print(self.frame(kwargs))
      Die Änderungen sind rot hervogehoben!

      Gruß, Waldemar






      OpenKNX www.openknx.de

      Kommentar


        #4
        Danke Waldemar!

        Macht total Spass neues zu basteln

        Bildschirmfoto vom 2017-01-16 16-01-57.png

        Kommentar


          #5
          Zitat von RoyalTS Beitrag anzeigen
          Macht total Spass neues zu basteln
          ... wer jetzt noch rumheult: "Solange 1-Wire nicht geht, benutze ich callidomus nicht!" - dem ist auch nicht mehr zu helfen.



          Kommentar


            #6
            ja sieht klasse aus!

            Kommentar


              #7
              Nabend,

              echt der Hammer, was Ihr da gebaut habt - so etwas hatte ich mir auch schon mal überlegt, doch den Aufwand gescheut. Ich denke ich muss mir da einen Teil für meine AlphaInnotec "borgen".


              Ciao,
              Fisch

              Kommentar


                #8
                Hi,
                vielleicht mal auf https://github.com/callidomus/widgets ablegen...
                Viee Grüsse
                Jürgen

                Kommentar


                  #9
                  @Jürgen: ich schau mal, dass ich die nächsten Tage ein Paket schnür' (inkl. Item-CSV, etc.)

                  Kommentar


                    #10
                    Hi Thorsten,

                    2 Sachen:
                    1. Du solltest noch einen griffigeren Namen für die beiden widgets finden. Da Du aber der Erfinder bist, ist es natürlich Dir überlassen
                    2. Bitten nicht in das widgets-git auch noch logiken und item-exporte... die Widgets sind komplett unabhängig vom Rest, können für jede Wärmepumpe oder sogar für Heizungen verwendet werden, sofern die angeforderten Messgrößen in Items verfügbar sind. In dieses git sollte nur das rein, was unter visu/widgets steht... Ansonsten wird es zu verwirrend.
                    Gruß, Waldemar
                    OpenKNX www.openknx.de

                    Kommentar


                      #11
                      Okok

                      Beide Vorschläge werden aufgenommen.

                      Kommentar


                        #12
                        Hi,
                        warum hast du bei FBH nur einen Wert gemacht? Ist das die Estrichtemperatur oder der Mittelwert VL/RL oder nur der VL Temperatur der FBH ist für mich nicht so klar?
                        VG
                        Jürgen

                        Kommentar


                          #13
                          Hi Jürgen,

                          das ist nur die "Heizkreis Vorlauftemperatur". Da hab ich mich an die Grafiken von Ochsner gehalten.

                          Grüße
                          Thorsten

                          Kommentar


                            #14
                            So, hab meine beiden Widgets nochmal überarbeitet.
                            Würde die aktuelle Version heute Abend daheim nochmal testen und dann auf Github ablegen.

                            Sollte sie jemand vorher testen wollen, einfach melden

                            Kommentar


                              #15
                              Hi Jürgen,

                              es ist der Wert eines Items - wie der Wert berechnet wird, hat nichts mit dem Widget zu tun. Mit der Anleitung oben sollte es aber auch kein Problem sein, weitere Werte im SVG zur Verfügung zu stellen.

                              Gruß, Waldemar
                              OpenKNX www.openknx.de

                              Kommentar

                              Lädt...
                              X