HPCL + Android-homestatus-Display

Programmierung der HomeMatic CCU mittels contronics homeputer CL

Moderator: Co-Administratoren

Antworten
Familienvater
Beiträge: 7151
Registriert: 31.12.2006, 15:18
System: Alternative CCU (auf Basis OCCU)
Wohnort: Rhein-Main
Danksagung erhalten: 34 Mal

HPCL + Android-homestatus-Display

Beitrag von Familienvater » 03.09.2014, 00:46

Moin,

weil ja im Bug-Forum über das Easy-Display gesprochen wird (welches ich nicht getestet habe):
Ich habe mir die homestatus-App für Android gekauft, und mal damit gespielt, weil mir am HM-StatusDisplay einfach diverse Möglichkeiten fehlen.
HomeStatus ist ganz nett, wahrscheinlich ähnlich wie das Easy-Display, um es aber mit HPCL und der CCU zu nutzen, muss man sich ganz schön verrenken (meine Meinung).

Da sich das homeStatus-Display ALLES aus Systemvariablen der CCU zieht, die einem bestimmten Namensschema entsprechen müssen, habe ich mir schnell was parametrierbares in VBA zusammengedengelt, was mir ein HM-Script erstellt und in die Zwischanablage kopiert, das dann die Systemvariablen auf der CCU per Skript-Testen-Fenster erzeugt (ich war schon immer ein fauler Hund ;-), aber das erzeugen der Systemvariablen ist eigentlich nur ein mal notwendig)

Das in meinen Augen größte Problem ist, das aus HPCL Systemvariablen gesetzt werden müssen, und im optimalfall das Display nur bei Bedarf "getriggert" wird, um sich die Änderungen abzuholen (Das Display kann auch "pollen" und einfach alle x-Sekunden alle Systemvariablen aus der CCU auslesen, und dann ggf. die Darstellung aktualisieren)
Systemvariablen setzen bedeutet aber, das "verzögerungen" im Programmablauf auftreten, und das ist etwas, was ich überhaupt nicht mag. Um das zu umgehen, habe ich soetwas wie ein Makro erstellt, was bis zu 10 Worker-Threads (übertrieben, aber trifft es) anspricht, die das Setzen der Systemvariablen asyncron übernehmen und das Display triggern, damit laufen meine Makros nach wie vor absolut syncron ab.
Wie mache ich das mit den Worker-Threads:
Noch mal kurz der Unterschied zwischen den Aufrufen für Makros:
AUFRUFEN: das aufgerufene Makro wird syncron erledigt, solange keine verzögernden Befehle genutzt werden, im optimalfall kann man sich das so vorstellen, wie wenn die Befehle im aufrufenden Makro selbst stehen würden.
STARTE: die Ausführung des gestarteten Makros wird erst begonnen, wenn zum aktuellen Zeitpunkt "nichts mehr" zu tun ist, weil alles syncron abgearbeitet wurde, oder weil in syncron ablaufenden Makros eine Verzögerung auftritt (warten, Systemvariablen, getsite, Sprung zu einer Marke OBERHALB im Makro,...)

Ich habe ein zentrales Makro (dieses wird AUFGERUFEN), in diesem Makro werden ein paar Variablen vom aufrufenden Makro gesetzt, und das zentrale Makro "verteilt" Round-Robin auf 10 weitere Makros die erhaltenen Daten, und STARTET dann den Worker-Thread. Der kann jetzt asyncron arbeiten, und die Systemvariable setzen, wenn das passiert ist, triggert der Worker Thread dann noch das Display (ich habe es erst mit getsite gemacht, aber das dauert mir zu lange, dann mache ich es lieber Shell-wget, das Ergebnis vom wget interessiert mich eh nicht).

Und da sind wir bei einer Sache, die beim HomeStaus-Display in meinen Augen doof ist:
Der Weg zurück vom Display zu HPCL geht praktisch nur mit den 50 virtuellen BidCos-RF-Tastern, das Display selbst hat keinerlei "Intelligenz", für jede Reaktion auf einen Knopfdruck auf dem Display muss per virtuellem Taster ein Makro in HPCL auslösen, Systemvariablen setzen, und das Display wieder triggern.
Die Zeit, die dabei vergeht, bezeichne ich mal als lag (engl. für Nachhinken, Verzögerung). Und bis auf einen Tastendruck etwas passiert und die Rückmeldung auf dem Display da ist, dauert es eine gefühlte Ewigkeit (ich schätze ~5 Sekunden). Schalten geht deutlich schneller, das ist max. 1-2 Sekunden Verzögerung.

Wie vom Hersteller der App auch angedacht kann man es eher für die "Visualisierung" nutzen, für die Interaktion mit dem Bediener ist es für ungeduldige weniger geeignet, es hat aber "witzige" Features, wie das Text-To-Speech, die ich so noch nicht hatte (ich habe z.B. keinen MP3-HM-Funkgong, und da müsste man auch die Ansagen selber einsprechen oder die MP3s im Vorfeld irgendwie erstellen).

Programmieraufwand:
Ich würde sagen, für Interaktion mit dem Benutzer und flexiblen Menüs ist es sehr viel Aufwand, nur für die Visualisierung taugt es aber, und mit ein bisschen strukturierten Makros zur Unterstützung bleibt es übersichtlich. Es mag sein, das sich bei der Nutzung von HM-Script die Reaktionszeit verbessern ließe, aber sicher bin ich mir da auch nicht.
Ich mag meine CCU1 nicht gegen die Wand fahren, indem ich das Display (oder auch mehrere) auf einen Refresh-Intervall von 1 Sekunde stelle, weil das Display dann wahrscheinlich 83000x pro Tag unnötig die CCU nervt, weil sich eh nichts getan hat, bei 10 Sekunden Refresh platzt die CCU1 wahrscheinlich nicht, auch wenn immer noch 8000 unnötige Anfragen vom Display gestellt werden, aber die Reaktionszeit wäre mir zu lang, deswegen meine Lösung mit dem getriggerten Refresh und meinen Worker-Threads.
Und ganz wichtig:
Ich habe noch keine (sinnvolle) Möglichkeit gefunden, das ganze so von "aussen" parametrierbar zu machen, das nicht dauerndes Neukompilieren/Übertragen/Starten notwendig ist, weil irgendwo dann doch eine Systemvariable falsch gesetzt wurde (Klar, ich könnte in meinem HPCL-Projekt nochmal 100te neue Zeichenkettenvariablen anlegen, und die als Vorgaben nutzen, dann kann man die von aussen setzen oder auch per Ini-File "einlesen"). Man kann zwar z.B. mit der WebUI-HQ die Darstellung der Buttons relativ schnell testen, aber für Interaktion braucht es dann doch die Änderungen im HPCL-Projekt.

Wenn ich was Präsentationsfähiges habe, mache ich mal ein paar Screenshots vom Tablet-Bildschirm, zur Zeit sind das eher Machbarkeitsstudien, wie sich das so darstellt. Und dabei merke ich immer wieder, das HPCL ein paar ganz wichtige Dinge fehlen, mir vor allem so etwas wie Arrays, wo ich mit einem Index in einer Variablen EINFACH auf ein Array von Werten zugreifen kann, und am besten soetwas schönes wie Listen, die man mit for ... each durchiterieren kann :-)

Neugierde geweckt?
Hier mal das Zentrale Makro, was auf die Worker-Threads verteilt:

Code: Alles auswählen

//! ============================================================
//! OBJEKT dm_SetAction
//! ============================================================
//! OBJEKT-TYP              : Makro
//! BEZEICHNUNG             : dm_SetAction
//! STARTWERT               :
//! ------------------------------------------------------------
//! AUSFÜHRUNGSINTERVALL    : nein
//! AUSFÜHRUNG BEI EINGABE  : ja
//! AUSFÜHRUNG BEI EMPFANG  : nein
//! AUSFÜHRUNG BEI ÄNDERUNG : nein
//! ------------------------------------------------------------
//! KONF.:ALLV=0
//!
//! ============================================================
//! VARIABLENDEFINITIONEN
//! ============================================================
//! NAME                TYP                 STARTWERT
//! ------------------------------------------------------------
//! lngRoundRobin       Zahl                0
//! strButton           Zeichen
//! strSysVarName       Zeichen
//! strValue            Zeichen
//! lngDisplay          Zahl                0
//! lngDebug            Zahl                1
//! lngActive           Zahl                1
//! lngTriggerMethod    Zahl                0

// Von Aussen wird 
// lngDisplay auf 1 ... x gesetzt (auf jeden Fall notwendig, damit der Worker-Thread weis, welches Display getriggert werden muss)
// strButton, alternativ direkt strSysVarName gesetzt
// strValue ist der Wert, der in die Systemvariable geschrieben werden soll

// Das ganze kann man auch einfach abschalten, wenn es hintendran knallen sollte...
wenn lngActive <> 1 dann
  strButton := ""
  strValue := ""
  strSysVarName := ""
  lngDisplay := 0
  verlassen
endewenn

// Display-Set-Actions per Round-Robin auf 10 Tasks verteilen, weil SysVar-Setzen und Display-Triggern Zeit verbraucht.
lngRoundRobin := lngRoundRobin + 1

wenn lngRoundRobin < 0 oder lngRoundRobin > 9 dann
  lngRoundRobin := 0
endewenn

wenn lngRoundRobin = 9 dann
  wenn strSysVarName = "" dann
    dm_SetAction9.strButton := strButton
    strButton := ""
  sonst
    dm_SetAction9.strButton := ""
    dm_SetAction9.strSysVarName := strSysVarName
    strSysVarName := ""
  endewenn

  dm_SetAction9.strValue := strValue
  strValue := ""
  dm_SetAction9.lngDisplay := lngDisplay
  lngDisplay := 0
  starte dm_SetAction9
  verlassen
endewenn

// Diese Blöcke für lngRoundRobin = 8 ... 1, wobei halt dann in dm_SetAction8...1 geschrieben wird

wenn lngRoundRobin = 1 dann
  wenn strSysVarName = "" dann
    dm_SetAction1.strButton := strButton
    strButton := ""
  sonst
    dm_SetAction1.strButton := ""
    dm_SetAction1.strSysVarName := strSysVarName
    strSysVarName := ""
  endewenn
  dm_SetAction1.strButton := strButton
  strButton := ""
  dm_SetAction1.strValue := strValue
  strValue := ""
  dm_SetAction1.lngDisplay := lngDisplay
  lngDisplay := 0
  starte dm_SetAction1
  verlassen
endewenn

//wenn lngRoundRobin = 0 dann
wenn strSysVarName = "" dann
  dm_SetAction0.strButton := strButton
  strButton := ""
sonst
  dm_SetAction0.strButton := ""
  dm_SetAction0.strSysVarName := strSysVarName
  strSysVarName := ""
endewenn
dm_SetAction0.strValue := strValue
strValue := ""
dm_SetAction0.lngDisplay := lngDisplay
lngDisplay := 0
starte dm_SetAction0
verlassen
//endewenn
Hier Beispielshaft ein Worker-Thread:

Code: Alles auswählen

//! ============================================================
//! OBJEKT dm_SetAction0
//! ============================================================
//! OBJEKT-TYP              : Makro
//! BEZEICHNUNG             : dm_SetAction0
//! STARTWERT               :
//! ------------------------------------------------------------
//! AUSFÜHRUNGSINTERVALL    : nein
//! AUSFÜHRUNG BEI EINGABE  : ja
//! AUSFÜHRUNG BEI EMPFANG  : nein
//! AUSFÜHRUNG BEI ÄNDERUNG : nein
//! ------------------------------------------------------------
//! KONF.:ALLV=0
//!
//! ============================================================
//! VARIABLENDEFINITIONEN
//! ============================================================
//! NAME                TYP                 STARTWERT
//! ------------------------------------------------------------
//! strButton           Zeichen
//! strValue            Zeichen
//! lngDisplay          Zahl                0
//! strSysVarName       Zeichen
//! strURL              Zeichen
//! strReturn           Zeichen

wenn strSysVarName = "" dann
  strSysVarName := "hs_display" + lngDisplay + ":" + strButton
endewenn

wenn dm_SetAction.lngDebug > 0 dann
  // ins Syslog schreiben
  syslog.optNoWrite := 1
  syslog.strMessage := "dm_SetAction0: Start Set-Action fuer Display "+lngDisplay+", setze "+strSysVarName+" auf '" + strValue+ "'"
  aufrufen syslog
endewenn

// Zeitverbrauch...
setccusysvar(strSysVarName,strValue,settings.SysVarHost)

strSysVarName := ""
strValue := ""

// Eine RefreshUrl ist z.B. 192.168.1.123/refresh
// Man könnte auch direkt http://192.168.1.123:8080/refresh speichern
// dann kann nur wieder getsite nichts damit anfangen... Deswegen habe ich die getsite-Kompatible Form gespeichert,
// bei Nutzung von wget wird das entsprechend umgebaut.

wenn lngDisplay = 1 dann
  strUrl := settings.RefreshUrlDisp1
endewenn

wenn lngDisplay = 2 dann
  strUrl := settings.RefreshUrlDisp2
endewenn

wenn lngDisplay = 3 dann
  strUrl := settings.RefreshUrlDisp3
endewenn

wenn lngDisplay = 4 dann
  strUrl := settings.RefreshUrlDisp4
endewenn

wenn lngDisplay = 5 dann
  strUrl := settings.RefreshUrlDisp5
endewenn

wenn dm_SetAction.lngDebug > 0 dann
  // ins Syslog schreiben
  syslog.optNoWrite := 1
  syslog.strMessage := "dm_SetAction0: trigger Display "+lngDisplay+" mit URL '" + strURL + "'"
  aufrufen syslog
endewenn

wenn dm_SetAction.lngTriggerMethod = 1 dann
  // Zeitverbrauch...
  getsite(strURL, 8080, strverlassen)
  wenn dm_SetAction.lngDebug > 0 dann
    // ins Syslog schreiben
    syslog.optNoWrite := 1
    syslog.strMessage := "dm_SetAction0: Display "+lngDisplay+" wurde getriggert, Antwort: "+strverlassen
    aufrufen syslog
  endewenn
sonst
  strUrl := "http://" + getstrpar(strURL, 1, "/") + ":8080/" + getstrpar(strURL, 2, "/")
  // m_execute macht im Prinzip nichts anderes als Startprogramm
  m_execute.strCommand := "/usr/bin/wget -q -O /dev/nul "+strURL
  m_execute.lngNoSyslog := 1 - dm_SetAction.lngDebug
  aufrufen m_execute
endewenn

lngDisplay := 0
Und hier jetzt z.B. mal ein Aufruf aus einem Makro:

Code: Alles auswählen

//! ============================================================
//! OBJEKT dm_InitDisplay
//! ============================================================
//! OBJEKT-TYP              : Makro
//! BEZEICHNUNG             : dm_InitDisplay
//! STARTWERT               :
//! ------------------------------------------------------------
//! AUSFÜHRUNGSINTERVALL    : nein
//! AUSFÜHRUNG BEI EINGABE  : ja
//! AUSFÜHRUNG BEI EMPFANG  : nein
//! AUSFÜHRUNG BEI ÄNDERUNG : nein
//! ------------------------------------------------------------
//! KONF.:ALLV=0/MAV=0
//!
//! ============================================================
//! VARIABLENDEFINITIONEN
//! ============================================================
//! NAME                TYP                 STARTWERT
//! ------------------------------------------------------------

// Button Homestatus-Display einstellen
dm_SetAction.lngDisplay := 3
dm_SetAction.strButton := "B4" // z.B. A1, A2 oder D4
dm_SetAction.strValue := "{text:&nbsp;Uhrzeit<br>&nbsp;ansagen}"
dm_SetAction.strValue := dm_SetAction.strValue  + "{color:black}" 
dm_SetAction.strValue := dm_SetAction.strValue  + "{backgroundColor:grey}"
dm_SetAction.strValue := dm_SetAction.strValue  + "{img:clock_" + settings.PicSize + ".png}"
dm_SetAction.strValue := dm_SetAction.strValue  + "{action:BidCoS-RF:49,PRESS_SHORT}" 
dm_SetAction.strValue := dm_SetAction.strValue  + "{fontSize:"+settings.FontSize2Rows+"}" 

// sofort ausführen und erst nach Rückkehr weitermachen, im dm_SetAction wird ein neuer "Thread" gestartet
aufrufen dm_SetAction

// .....

// Sprache auf einem HomeStatus-Display ausgeben
dm_Say.lngDisplay := 3
dm_Say.strText := "Display wurde neu initialisiert."

// Das Makro dm_Say startet wiederum einen Workerthread per dm_SetAction, um die Say-Aktion ans Tablet zu schicken
aufrufen dm_Say
Wie man vielleicht merkt, habe ich schon versucht, das ganze von aussen parametrierbar zu machen, in dem ich die Icon-Größen in meinen Settings ändern kann, oder auch die FontSize. Aber wenn ich denke, das ich später mal mehrere Displays hätte, die nicht alle die selbe Auflösung haben, dann hätte ich an der Stelle UNHEIMLICH gerne so etwas wie Arrays. Mit Viel Mühe und Einsatz könnte man halt wieder die Worker-Threads intelligenter machen, damit die in Abhängigkeit von der Display-Nr dann irgendwelche Schlüsselworte in der übergebenen strValue-Zeichenkette ersetzen, und durch individuelle Werte für jedes Display ersetzen.
Was meine Lösung gar nicht berücksichtigt ist z.B. die Möglichkeit, mehrere Displays mit einer Systemvariablen zu steuern, und wenn man mehr als 10 Systemvariablen in kurzer Zeit setzen will, müsste man evtl auch die Anzahl der Workerthreads erhöhen, bei z.B. 24 Buttons (4x6) währe man mit 30 Workerthreads auf der sicheren Seite :-), falls beim erstmaligen Initialisieren des Displays alle 24 Buttons auf einmal gesetzt werden...

Viel Zeugs, gell?

Der Familienvater

Tobias78
Beiträge: 1464
Registriert: 27.06.2010, 01:01
Wohnort: Braunschweig
Hat sich bedankt: 4 Mal

Re: HPCL + Android-homestatus-Display

Beitrag von Tobias78 » 03.09.2014, 07:33

Hallo Familienvater,
Ich stelle fest, wir sind auf die gleichen Schwachstellen gestoßen und haben unterschiedliche "work-arounds" gefunden.

Zu dem lahmen "Setccusysvar":
Ich nutze für für alle 18+2 Variablen ein zentrales Makro, die jeweilige Systemvariable wird aber jeweils nur gesetzt, wenn sie auch geändert wurde. (Neu/alt Vergleich). Warum ist die Setccusysvar Funktion nur so elendig langsam in hpcl???

Zum Refresh:
Ich habe hierzu ein WebUi Makro (mit Cuxd) erstellt, welches ich über einen virtuellen Taster aus hpcl heraus triggern kann. Getsite braucht in hpcl ne Ewigkeit..."StarteProgramm" übrigens auch! Der refresh geht in meiner Lösung sehr schnell.

Neuer genereller Lösungsvorschlag:
Schön wäre es, wenn man im HomeStatus wählen könnte, ob CCU Systemvariablen oder HPCL Variablen ausgelesen werden sollen. Wir sollten die Jungs mal motivieren ;-)

Die Contronics Android App im Beta Stadium funktioniert übrigens bereits nahezu in Echtzeit! Mir gefällt momentan die Logic von HomeStatus für eine Anzeige aber wesentlich besser. Hier müssten die richtigen Leute sich zusammenfinden. Ideal wäre die Datenübertragung von Contronics über hpinst mit der Anzeige und sonstigen Features von HomeStatus zu kombinieren.

Gruß, Tobias.
--------------------------------------------
Im Einsatz und empfehlenswert:
RaspberryMatic,IO.Broker, Homeputer Studio; CuXD; PocketControl, HomeStatus, Robonect, Alexa, io.Broker
------------------------------------------

Familienvater
Beiträge: 7151
Registriert: 31.12.2006, 15:18
System: Alternative CCU (auf Basis OCCU)
Wohnort: Rhein-Main
Danksagung erhalten: 34 Mal

Re: HPCL + Android-homestatus-Display

Beitrag von Familienvater » 03.09.2014, 10:05

Moin Leidensgenosse,
Tobias78 hat geschrieben:Warum ist die Setccusysvar Funktion nur so elendig langsam in hpcl???
Weil die intern glaube ich nichts anderes als ein getsite nutzt...
Tobias78 hat geschrieben: Zum Refresh:
Ich habe hierzu ein WebUi Makro (mit Cuxd) erstellt, welches ich über einen virtuellen Taster aus hpcl heraus triggern kann. Getsite braucht in hpcl ne Ewigkeit..."StarteProgramm" übrigens auch! Der refresh geht in meiner Lösung sehr schnell.
Vielleicht teste ich das auch mal, cuxd habe ich eh drauf, aber damit gehen ja noch mehr virtuelle Taster flöten, um mehrere Displays triggern zu können...
Tobias78 hat geschrieben: Neuer genereller Lösungsvorschlag:
Schön wäre es, wenn man im HomeStatus wählen könnte, ob CCU Systemvariablen oder HPCL Variablen ausgelesen werden sollen. Wir sollten die Jungs mal motivieren ;-)
Man soll ja nie aufhören zu träumen, aber ich fürchte, ausser mir nutzt so gut wie niemand die HPCL-XML-RPC Schnittstelle...
Um Performant damit arbeiten zu können braucht man in meinen Augen den Variablenindex, und das abrufen der Variablenliste braucht bei mir inzwischen fast 60 Sekunden (die wird dann auf dem SQL-Server gecached, und alle meine Programme vergleichen beim Start, ob das Compile-Date des laufenden Projekts noch mit dem in der Datenbank übereinstimmt und ziehen dann die Liste vom Server, oder der erste requested eine neue)

Und die homestatus-Entwickler machen es sich ja auch einfach, und nutzen (zum Glück?) die XML-Api, um sich die Systemvariablen zu holen.
Man müsste also evtl. so etwas einfaches wie die XML-API für HPCL erfinden.

Ich habe die Contronics Beta-App versucht in Betrieb zu nehmen, hat aber bei mir nicht geklappt...

Der Familienvater

Benutzeravatar
Herbert_Testmann
Beiträge: 11062
Registriert: 17.01.2009, 11:30
Danksagung erhalten: 7 Mal

Re: HPCL + Android-homestatus-Display

Beitrag von Herbert_Testmann » 03.09.2014, 14:40

Hallo

ich habe ja von dem ganzen Schnittstellenkram keine Ahnung nich :)

Aber mal die Frage aus Sicht des Users ... Es gibt für iOS die PocketControl CL App. Diese holt sich die Daten von HomePuter CL und übergibt auch welche. Und zwar sehr schnell. Da sind nicht mehr Wartezeiten , als bei der gleichnamigen App als reine CCU Version.
Es muss also eine Schnittstelle geben, die in Echtzeit die CL Daten raus rückt.
---
Dieses Schreiben wurde maschinell erstellt und ist ohne Unterschrift gültig

Familienvater
Beiträge: 7151
Registriert: 31.12.2006, 15:18
System: Alternative CCU (auf Basis OCCU)
Wohnort: Rhein-Main
Danksagung erhalten: 34 Mal

Re: HPCL + Android-homestatus-Display

Beitrag von Familienvater » 03.09.2014, 15:25

Hi Herbert,

ich gehe davon aus, das es eine nicht öffentlich dokumentierte Schnittstelle gibt (ähnlich der VisuWin-Schnittstelle), mit der das realisiert wird.
Wenn ich die Lust und die Zeit hätte, könnte man das sicherlich mit Wireshark und gesundem Menschenverstand rausfinden, wie es funktioniert, aber ich habe andere Probleme :-)

Der Familienvater

Antworten

Zurück zu „homeputer CL“