Jetzt habe ich mal den ganz großen Wurf gemacht und mein JSON-Skript soweit erweitert, das es auch beliebige, komplexere, valide JSON-Ausgaben von Web-Seiten / APIs auswerten kann. Und zwar vollautomatisch ohne Anpassungen im Code, mit auszählen von Textstellen oder ähnlichem. Der einzige Nachteil ist, das dadurch die anzulegenden Systemvariablen durch den im JSON vorgegebenen Namen bestimmt sind.
Da die Probleme mit system.exec und intensiver String-Verarbeitung und begrenzten Variablennamen mit einer aktuellen Firmware längst Geschichte sind, ist es nun möglich sowas ohne Sonderlösungen in der Skriptsprache der CCU abzubilden.
Was macht das Skript?
API eines externen Servers abfragen, den JSON-Output decodieren und in vorhandene Systemvariablen schreiben
benötigte Fähigkeiten:
Grundkenntnisse in der WebUI-Programmierung (Anlegen und Ändern von Programmen, Einfügen und Anpassen von Skripten)
Anlegen von Systemvariablen
Skripte unter "Skript testen" laufen lassen und die Ausgaben verstehen und intepretieren
grundlegendes Logik-Verständnis / Abstraktionsvermögen um die beschriebenen Lösungen ggf. auf eigene Bedürfnisse anzupassen
Systemgrenzen: Arrays funktionieren, Unter-Objekte funktionieren, Objekte geschachtelt in Arrays funktionieren, ebenso Arrays in Gruppen. Allerdings funktioniert nur eine Verschachtelungsebene sicher. Mehrfach geschachtelte Objekte werden u. U. nicht vollständig gelesen.
Wenn die in JSON genutzten Zeichen zur Strukturierung ":,[]{}" in den Nutzdaten vorkommen werden sie durch Leerzeichen ersetzt (einstellbar durch anpassen der Variable "durch"). Unterstriche '_' werden durch Bindestriche '-' ersetzt. Um Arrays getrennt von Objekten zu behandeln ersetze ich in Arrays das Komma ',' durch Backslash '\'. Die ggf in den Nutzdaten vorhandenen \ werden zu / umgeschrieben. Das kann ggf. durch Änderung der Variablen TrennerArray und TrennerArrayErsatz angepasst werden, wenn man unbedingt das \ in den Daten benötigt.
Damit es keine Kollision mit vorhandenen Systemvariablen (SV) gibt, wird dem SV-Namen ein Präfix vorangestellt. Der Präfix kann nach eigenen Wünschen und Bedürfnissen angepasst werden. Nur dieses Präfix und die URL der abzurufenden Web-Adresse sind im Skript zu ändern. Das sind diese Stellen:
Code: Alles auswählen
string url = "http://IP/";
string prefix = "JSON_";
Code: Alles auswählen
string CUXD_Device = "CUxD.CUX2801001:1";
Code: Alles auswählen
! optional kann man Texte am Anfang und Ende abschneiden
string Start_JSON =''; ! 'Hier den Start des eigentlichen JSON-Codes eingeben'
string Ende_JSON =''; ! 'Hier den Ende des eigentlichen JSON-Codes eingeben'
Das Skript enthält Testdaten. D.h. wenn man das Skript unverändert in "Skript testen" laufen lässt, erhält man auf jeden Fall eine Ausgabe. Die Testdaten (Bereich zwischen !Testdaten und ! Ende Testdaten) sollte man nach dem ersten Testlauf rauslöschen, damit das Skript bei Fehlern in der Datenübermittlung richtig reagieren kann.
Für die Werte, die man aus dem JSON benötigt, muss man eine Systemvariable mit dem passenden Namen anlegen: bestehend aus Präfix+JSON-Schlüsselname. Bei komplexeren JSON-Daten mit Unter-Objekten und Arrays setzen sich die SV-Namen dann aus Objektname+JSON-Schlüssel+Arrayname+fortlaufender Nummer zusammen.
Am besten Testen unter Programme / Zentralenverknüpfungen / Skript testen.
Dann erhält man eine Ausgabe, an der man sehen kann, wie die Systemvariablen heißen müssen und welche Werte da abgelegt werden. Wenn die SV existiert, dann wird im Erfolgsfall === SV xxx written === ausgegeben.
Wenn es da läuft in ein Programm einbinden.
Hier der Skript-Code:
Code: Alles auswählen
!JSON universell auslesen
!V1.11 MichaelN https://homematic-forum.de/forum/viewtopic.php?f=18&t=68762
!GPL-3.0-or-later
string url = "http://IP/";
url = "wget --no-check-certificate --timeout=2 -O - '"#url#"'";
string prefix = "JSON_";
string CUXD_Device = ""; ! falls man CUxD nutzt, Device eintragen z.B. "CUxD.CUX2801001:1"
! optional kann man Texte am Anfang und Ende abschneiden
string Start_JSON =''; ! 'Hier den Start des eigentlichen JSON-Codes eingeben'
string Ende_JSON =''; ! 'Hier den Ende des eigentlichen JSON-Codes eingeben'
! Trenner kann notfalls angepasst werden, wenn es mit den Nutzdaten kollidiert
string TrennerArray = " \ "; TrennerArray = TrennerArray.Trim();
string TrennerArrayErsatz = "/";
! festlegen welche Zeichen in den Nutzdaten durch welches Zeichen ersetzt werden
string ersetzen = ":,[]{}";
string durch = " ";
string rueck;
string error;
object SV;
string SVName;
var Wert;
!Testdaten rauslöschen für produktiven Betrieb
rueck = '{
"Vorname": "Ronald",
"Nachname": "Reagan",
"Geburtsdatum": "1911-02-06",
"Nationalitaet": "US-amerikanisch",
"Partei":
{
"Name": "Republican Party",
"Synonyme": ["Republikaner","Grand Old Party", "GOP"],
"Hauptsitz": "Washington/D.C.",
"Gründungsdatum": "1854-03-20",
"Gründungsort": "Ripon"
},
"Amt": "US-Präsident",
"Hobbys": ["Reiten", "Golfen", "Lesen"],
"verheiratet": true,
"Kinder": [
{
"Vorname": "Michael",
"Nachname": "Reagan" },
{
"Vorname": "Patti",
"Nachname": "Davis" },
{
"Vorname": "Ron",
"Nachname": "Reagan" }]
}
';
! Ende Testdaten
!- Abruf der Daten
if ( !CUXD_Device ) {
! system.exec
system.Exec(url , &rueck, &error);
} else {
! CUxD
dom.GetObject(CUXD_Device#".CMD_SETS").State(url);
dom.GetObject(CUXD_Device#".CMD_QUERY_RET").State(1);
rueck = dom.GetObject(CUXD_Device#".CMD_RETS").State();
}
if (!rueck== "")
{
WriteLine("=== HTTP-Request OK === "#rueck);
if ( Start_JSON.Length() > 0 ) {
! JSON am Anfang kürzen
rueck = rueck.Substr(rueck.Find(Start_JSON));
}
if ( Ende_JSON.Length() > 0 ) {
! JSON am Ende kürzen
rueck = rueck.Substr(0,rueck.Find(Ende_JSON));
}
rueck = rueck.ToLatin(); ! wegen deutschen Umlauten
rueck = rueck.Trim(" {}[]");
rueck = rueck.Replace('\n','');
rueck = rueck.Replace('\r','');
rueck = rueck.Replace('_','-');
rueck = rueck.Replace(TrennerArray,TrennerArrayErsatz);
if (rueck.Substr(0,1) == "[" ) { rueck = "data:"#rueck; }
WriteLine("=== nach Replace === '"#rueck#"'");
! Array und Payload aufbereiten
integer pos1 = 0; integer posG = 0;
integer pos2 = rueck.Length();
string temp = ""; string temp2;
string teil;
boolean Array = false; boolean Payload = false;
while ( pos1 < pos2 )
{
teil = rueck.Substr(pos1,1);
if ( teil == "[")
{! Array gefunden
Array = true;
}
if ( (teil == "," ) && Array )
{! Komma gegen Schraegstrich tauschen
teil = TrennerArray;
}
if ( teil == "]")
{! Array Ende
Array = false;
}
if ( teil == '"' ) {
if ( Payload )
{! Payload Ende
Payload = false;
} else {
! Payload Start
Payload = true;
}
}
if ( ( ersetzen.Contains(teil) ) && Payload )
{! verbotene Zeichen austauschen
teil = durch;
}
temp = temp#teil;
pos1 = pos1 + 1;
}
rueck = temp;
rueck = rueck.Replace('"','');
WriteLine("=== nach Aufbereitung === '"#rueck#"'");
WriteLine("=== Output V1.11 ===");
string Part; string SVNameArray;
string Gruppe = ""; string GruppeNeu;
integer G1; integer G2;
integer ArrayZaehler = 0;
boolean ArrayMitGruppe = false;
foreach (Part, rueck.Split(",") ){
if (Part.Contains(":"))
{
if (Part.Contains("{"))
{ ! Unter-Objekt gefunden
!WriteLine("Z96 Part='"#Part#"'"); ! *** DEBUG
if (Part.Contains("[")) {
! enthält Arrray
GruppeNeu = Part.StrValueByIndex(":", 0);
GruppeNeu = GruppeNeu.Trim() #"_";
Part = Part.Substr(GruppeNeu.Length()+2).Trim("{ ");
Gruppe = Gruppe # GruppeNeu; Gruppe = Gruppe.Replace("__","_");
!WriteLine("Gruppe alt:"#Gruppe); ! DEBUG ***
!WriteLine("Inhalt alt:"#Part); ! DEBUG ***
} else {
! enthält kein Array, aber möglicherweise geschachtelte Objekte
temp = Part.Replace("{",";");
G2 = web.webGetValueListCount(temp);
G1=0;
while (G1 < (G2-1)) {
GruppeNeu = web.webGetValueFromList(temp,(G1)).Trim(" :");
if ( (GruppeNeu == "_") || (GruppeNeu == "") ) {
ArrayZaehler = ArrayZaehler + 1;
GruppeNeu = ArrayZaehler.ToString(0).Trim() # "_";
}
Gruppe = Gruppe # GruppeNeu # "_";
G1 = G1 + 1;
}
if ( Gruppe == "_" ) {
ArrayZaehler = ArrayZaehler + 1;
Gruppe = ArrayZaehler.ToString(0).Trim() # "_";
}
Gruppe = Gruppe.Replace("__","_");
Part = web.webGetValueFromList(temp,(G2-1));
!WriteLine("Gruppe neu:"#Gruppe); ! *** DEBUG
!WriteLine("Inhalt neu:"#Part); ! **** DEBUG
}
!WriteLine("=== GRUPPE-START: "#Gruppe);
}
if (Part.Contains(TrennerArray)) {
! Array gefunden
!WriteLine("=== ARRAY auflösen:"#Part);
ArrayMitGruppe = false;
Part = Part.Replace("[","");
Part = Part.Replace("]","");
pos1 = 0; posG = 0;
SVName = Gruppe;
if (Part.Contains(":"))
{
SVName = Part.StrValueByIndex(":", 0);
Part = Part.Substr(SVName.Length()+1).Trim();
SVName = SVName.Trim("[{ ");
!WriteLine("*** Z365 ARRAY aufgelöst in:"#SVName#"==="#Part#"==="); ! DEBUG ***
}
foreach (temp,Part.Split(TrennerArray))
{
!WriteLine("Z352 temp="#temp); ! DEBUG ***
if ( temp.Contains(":")) {
! Array enthält eine Gruppe
SVName = temp.StrValueByIndex(":", 0).Trim("{ ");
temp2 = temp;
temp = temp.StrValueByIndex(":", 1).Trim(" []");
SVNameArray = SVName#posG.ToString(0).Trim();
if ( temp.Contains("{")) {
! weitere Gruppe gefunden
ArrayMitGruppe = true;
Gruppe = Gruppe # SVName # "_";
temp = temp.Trim(" {"); ! temp.StrValueByIndex("{", 1).Trim();
SVName = temp;
temp = temp2.StrValueByIndex(":", 2).Trim(" []");
SVNameArray = SVName#posG.ToString(0).Trim();
!WriteLine("Z363 Gruppe="#Gruppe#" SVNameAray="#SVNameArray#" temp="#temp); ! DEBUG ***
}
if ( temp.Contains("}")) { posG = posG + 1; }
!WriteLine("Z367 Gruppe="#Gruppe#" SVNameAray="#SVNameArray#" temp="#temp); ! DEBUG ***
WriteLine (prefix # Gruppe # SVNameArray #" = '"# temp.Trim(" {[]}") #"'");
SV = dom.GetObject(ID_SYSTEM_VARIABLES).Get((prefix # Gruppe # SVNameArray));
if (SV) {
SV.State(temp.Trim(" {[]}"));
WriteLine("=== SV "#prefix # Gruppe # SVNameArray #" written ===");
}
} else {
! keine Gruppe
SVNameArray = SVName#pos1.ToString(0).Trim();
WriteLine (prefix # Gruppe # SVNameArray #" = '"# temp.Trim(" {[]}") #"'");
SV = dom.GetObject(ID_SYSTEM_VARIABLES).Get((prefix # Gruppe # SVNameArray));
if (SV) {
SV.State(temp.Trim(" {[]}"));
WriteLine("=== SV "#prefix # Gruppe # SVNameArray #" written ===");
}
pos1 = pos1 + 1;
}
if ( (temp.Contains("}")) && (ArrayMitGruppe) )
{ ! Unter-Objekt zu Ende
! Gruppe um letzten Eintrag kürzen
temp2 = Gruppe.Replace("_",";");
G2 = web.webGetValueListCount(temp2);
!WriteLine("Z365 GRUPPE: "#Gruppe#" ==="); ! DEBUG ***
Gruppe = ""; G1=0;
while (G1 < (G2-2)) {
Gruppe = Gruppe # web.webGetValueFromList(temp2,(G1)) # "_";
!WriteLine("Z368 "#G1#"/"#G2#":"#Gruppe); ! *** DEBUG
G1 = G1 + 1;
}
!WriteLine("=== Z374 GRUPPE-ENDE: "#Gruppe#" ===");
ArrayMitGruppe = false;
}
}
if ( posG > 0 ) {
!WriteLine("Z413 Part="#Part); ! *** DEBUG
! Gruppe um letzten Eintrag kürzen
temp2 = Gruppe.Replace("_",";");
G2 = web.webGetValueListCount(temp2);
!WriteLine("=== GRUPPE-ENDE (array): "#Gruppe#"***");
Gruppe = ""; G1=0;
while (G1 < (G2-2)) {
Gruppe = Gruppe # web.webGetValueFromList(temp2,(G1)) # "_";
!WriteLine("Z422 "#G1#"/"#G2#":"#Gruppe); ! *** DEBUG
G1 = G1 + 1;
}
}
! ARRAY ENDE
} else {
! ohne Array
! WriteLine("Z154 Part='"#Part#"'"); ! *** DEBUG
Part = Part.Replace(":",";");
SVName = Gruppe#web.webGetValueFromList(Part,0).Trim();
Wert = web.webGetValueFromList(Part,1).RTrim("}").Trim(" {[]}");
WriteLine (prefix # SVName #" = '"# Wert #"'");
SV = dom.GetObject(ID_SYSTEM_VARIABLES).Get((prefix # SVName));
if (SV) {
SV.State(Wert);
WriteLine("=== SV "#prefix # SVName #" written ===");
}
if (Part.Contains("}"))
{ ! Unter-Objekt zu Ende
! *** Gruppe muss um so viele Einträge gekürzt werden, wie } vorhanden sind in Part ***
Part = Part.Replace(" ","");
! WriteLine("Z199 Part='"#Part#"'"); ! *** DEBUG
while ( Part.EndsWith("}") ) {
! Gruppe um letzten Eintrag kürzen
temp = Gruppe.Replace("_",";");
G2 = web.webGetValueListCount(temp);
Gruppe = ""; G1=0;
while (G1 < (G2-2)) {
Gruppe = Gruppe # web.webGetValueFromList(temp,(G1)) # "_";
! WriteLine("=== "#G1#":"#Gruppe); *** DEBUG
G1 = G1 + 1;
}
Part = Part.Substr(0,(Part.Length()-1)) ;
! WriteLine("Z212 Part="#Part); ! *** DEBUG
} ! Ende while
!WriteLine("=== GRUPPE-ENDE: "#Gruppe#" ===");
}
}
}
}
} else {
WriteLine("Error at HTTP Request");
}
Aus diesen Testdaten (Quelle Wikipedia)
Code: Alles auswählen
{
"Vorname": "Ronald",
"Nachname": "Reagan",
"Geburtsdatum": "1911-02-06",
"Nationalitaet": "US-amerikanisch",
"Partei":
{
"Name": "Republican Party",
"Synonyme": ["Republikaner", "Grand Old Party", "GOP"],
"Hauptsitz": "Washington/D.C.",
"Gründungsdatum": "1854-03-20",
"Gründungsort": "Ripon"
},
"Amt": "US-Präsident",
"Hobbys": ["Reiten", "Golfen", "Lesen"],
"verheiratet": true,
"Kinder": [
{
"Vorname": "Michael",
"Nachname": "Reagan" },
{
"Vorname": "Patti",
"Nachname": "Davis" },
{
"Vorname": "Ron",
"Nachname": "Reagon" }]
}
Code: Alles auswählen
nach Replace: ' Vorname: Ronald, Nachname: Reagan, Geburtsdatum: 1911-02-06, Nationalitaet: US-amerikanisch, Partei: { Name: Republican Party, Synonyme: [Republikaner, Grand Old Party, GOP], Hauptsitz: Washington/D.C., Gründungsdatum: 1854-03-20, Gründungsort: Ripon }, Amt: US-Präsident, Hobbys: [Reiten, Golfen, Lesen], verheiratet: true, Kinder: [ { Vorname: Michael, Nachname: Reagan }, { Vorname: Patti, Nachname: Davis }, { Vorname: Ron, Nachname: Reagon }]}'
nach Array-Aufbereitung: ' Vorname: Ronald, Nachname: Reagan, Geburtsdatum: 1911-02-06, Nationalitaet: US-amerikanisch, Partei: { Name: Republican Party, Synonyme: [Republikaner\ Grand Old Party\ GOP], Hauptsitz: Washington/D.C., Gründungsdatum: 1854-03-20, Gründungsort: Ripon }, Amt: US-Präsident, Hobbys: [Reiten\ Golfen\ Lesen], verheiratet: true, Kinder: [ { Vorname: Michael\ Nachname: Reagan }\ { Vorname: Patti\ Nachname: Davis }\ { Vorname: Ron\ Nachname: Reagon }]}'
=== Output ===
JSON_Vorname = 'Ronald'
JSON_Nachname = 'Reagan'
JSON_Geburtsdatum = '1911-02-06'
JSON_Nationalitaet = 'US-amerikanisch'
=== GRUPPE-START: Partei_
JSON_Partei_Name = 'Republican Party'
=== ARRAY auflösen: Synonyme: [Republikaner\ Grand Old Party\ GOP]
JSON_Partei_Synonyme0 = 'Republikaner'
JSON_Partei_Synonyme1 = 'Grand Old Party'
JSON_Partei_Synonyme2 = 'GOP'
JSON_Partei_Hauptsitz = 'Washington/D.C.'
JSON_Partei_Gründungsdatum = '1854-03-20'
JSON_Partei_Gründungsort = 'Ripon'
=== GRUPPE-ENDE: Partei_***
JSON_Amt = 'US-Präsident'
=== ARRAY auflösen: Hobbys: [Reiten\ Golfen\ Lesen]
JSON_Hobbys0 = 'Reiten'
JSON_Hobbys1 = 'Golfen'
JSON_Hobbys2 = 'Lesen'
JSON_verheiratet = 'true'
=== GRUPPE-START: Kinder_
=== ARRAY auflösen:[ { Vorname: Michael\ Nachname: Reagan }\ { Vorname: Patti\ Nachname: Davis }\ { Vorname: Ron\ Nachname: Reagon }]}
JSON_Kinder_Vorname0 = 'Michael'
JSON_Kinder_Nachname0 = 'Reagan'
JSON_Kinder_Vorname1 = 'Patti'
JSON_Kinder_Nachname1 = 'Davis'
JSON_Kinder_Vorname2 = 'Ron'
JSON_Kinder_Nachname2 = 'Reagon'
=== GRUPPE-ENDE: Kinder_***
Braucht man den zweiten Wert des Array Hobbys, dann legt man eine SV JSON_Hobbys1 an. Achtung: Arrays werde bei 0 startend nummeriert.
Benötigt man den Wert Name aus dem Partei-Objekt, dann lautet der SV-Name JSON_Partei_Name.