NEWS
Klärung von AdpaterEntwicklungs Fragen
-
Wie schon in einem anderen Thread geschrieben bin ich gerade dabei mich in das Thema Adapter einzulesen, nun habe ich mal damit begonnen mein "Seitenwechsel-Script" in einen Adapter zu portiern.
Das was ich umsetzen will klappt und funktioniert auch, aber wenn ich mir den Code anschaue kriege ich schier das ko.... nur leider ist mein Wissen hier begrenzt, die lernbereitschaft ist aber da!
Ich muss einige vorher angelegte Objekte abfragen und mache das derzeit mit der adapter.getState Funktion inkl. dem Callback.Das bläst mir den Code aber unnötig auf, da ich jedes mal den error abfange was ich persönlich jetzt nicht machen würde (gerne lasse ich mich hier belehren). Als Beispiel habe ich mal eine der Funktionen hier gepostet wo in den Zeilen 3, 9, 17, 36, 43 eben genau dieses getState auftaucht.
Ich wollte mal nun die wirklichen Programmierer/Entwickler fragen ob es da nicht eine hübschere / professionellere Methode gibt das umzusetzen.
Meistens möchte ich nicht nur dass es funktioniert sondern es sollte auch schön aussehen.
function switchToHomeView() { timerTout = setTimeout(function () { adapter.getState('switchTimer', (err, state) => { if (!state || state.val === null) { adapter.log.error('Error by getting Value switchTimer'); } else { let timer = parseInt(state.val, 10); if (timer > 1) { adapter.getState('lockViewActive', (err, state) => { if (!state || state.val === null) { adapter.log.error('Error bei getting Value of lockViewActive'); } else { if(state.val === true){ //Timeout prüfen? if(timerTout) clearTimeout(timerTout); adapter.setState('switchTimer', 0); adapter.getState('actualLockView', (err, state) => { if (!state || state.val === null) { adapter.log.error('Error bei getting Value of actualLockView'); } else { if(state.val != newState.split('/').pop()){ switchToViewImmediate(project+'/'+state.val); } } }); } else { adapter.setState('switchTimer',timer - 1); switchToHomeView(); } } }); } else{ adapter.setState('switchTimer', 0); adapter.getForeignState('vis.0.control.instance', (err, state) => { if (!state || state.val === null) { adapter.log.error('Error bei getting Value of vis.0.control.instance'); } else { if(state.val == 'undefined') adapter.setForeignState('vis.0.control.instance', 'FFFFFFFF'); } }); adapter.getState('actualHomeView', (err, state) => { if (!state || state.val === null) { adapter.log.error('Error bei getting Value of actualHomeView'); } else { adapter.log.info(project + '/' + state.val) adapter.setForeignState('vis.0.control.data', project + '/' + state.val); adapter.setForeignState('vis.0.control.command', 'changeView'); } }); } } }); }, 1000); }
Merci schonmal!
-
@Peoples Die Lösung heisst async/await mit Promises.
Damit kannst du dann z.B. schreiben (sorry, bin TypeScript Fan, kann sein dass das nicht perfektes JavaScript ist):
try { const switchTimer = await adapter.getStateAsync('switchTimer'); if (switchTimer.val === null) { throw new Error('Error by getting Value switchTimer'); } const timer = parseInt(state.val, 10); if (timer > 1) { const lockViewActive = await adapter.getStateAsync('lockViewActive'); /* ... und so weiter */ } else { /* der andere Fall... */ } } catch (error) { /* Fehler behandeln... */ }
Mehr Infos findest du z.B. hier: https://javascript.info/async-await
-
@Peoples Und noch ein grundsätzlicher Tipp: schau dir mal "gute" Adapter an, wie die geschrieben sind.
"Gut" kannst du selber definieren, aber ich glaube, du hast da ein gutes Bauchgefühl:
wenn ich mir den Code anschaue kriege ich schier das ko....
Wenn du bereit bist etwas mehr zu lernen, empfehle ich dir sehr, den Adapter gleich in TypeScript zu schreiben - das ist wirklich keine Magie!
Und verwende unbedingt den Adapter Creator: https://github.com/ioBroker/create-adapter
npx @iobroker/create-adapter [options]
-
@UncleSam sagte in Suche Alternative zu adapter.getState mit Callback:
@Peoples Und noch ein grundsätzlicher Tipp: schau dir mal "gute" Adapter an, wie die geschrieben sind.
"Gut" kannst du selber definieren, aber ich glaube, du hast da ein gutes Bauchgefühl:
wenn ich mir den Code anschaue kriege ich schier das ko....
Wenn du bereit bist etwas mehr zu lernen, empfehle ich dir sehr, den Adapter gleich in TypeScript zu schreiben - das ist wirklich keine Magie!
Und verwende unbedingt den Adapter Creator: https://github.com/ioBroker/create-adapter
npx @iobroker/create-adapter [options]
Den Adapter-Creater habe ich benutzt und versuche eben nun erstmal alle Funktionen meines Scripts zum Laufen zu kriegen, das nächste Ziel ist dann den Code aufzuhübschen.
Wenn ich mich dann noch nicht gefragt habe warum ich das angefangen habe, werde ich mir das Thema TypeScript auch noch anschauen.Aber für Laien ist das im Ganzen schon ein sehr forderndes Thema
-
@Peoples sagte in Suche Alternative zu adapter.getState mit Callback:
Aber für Laien ist das im Ganzen schon ein sehr forderndes Thema
Absolut. Ich wünsche dir auf jeden Fall viel Erfolg. Und du weisst ja: im Forum wirst du immer geholfen!
-
@Peoples sagte in Suche Alternative zu adapter.getState mit Callback:
werde ich mir das Thema TypeScript auch noch anschauen.
==> https://forum.iobroker.net/topic/36493/einsteiger-cursus-demonstration-von-typescript
@Peoples sagte in Suche Alternative zu adapter.getState mit Callback:
aber wenn ich mir den Code anschaue kriege ich schier das ko....
Das kann ich dir bezüglich
async/await
noch ans Herz legen:
https://gist.github.com/AlCalzone/d14b854b69ce5e8a03718336cc650a95# -
Tausend Dank schonmal!
Ich habe das Ganze jetzt wie folgt geändert:
async function switchToHomeView() { try { const switchTimer = await adapter.getStateAsync('switchTimer'); const lockViewActive = await adapter.getStateAsync('lockViewActive'); const actualLockView = await adapter.getStateAsync('actualLockView'); const actualHomeView = await adapter.getStateAsync('actualHomeView'); const visInstance = await adapter.getForeignStateAsync('vis.0.control.instance'); timerTout = setTimeout(function () { let timer = parseInt(switchTimer.val, 10) if(timer > 1){ if(lockViewActive.val === true){ if(timerTout) clearTimeout(timerTout); adapter.setState('switchTimer', 0); //Woher kommt newState if(actualLockView.val != newState.split('/').pop()){ switchToViewImmediate(project+'/'+state.val); } } else { adapter.setState('switchTimer',timer - 1); switchToHomeView(); } } else { adapter.setState('switchTimer', 0); if(visInstance.val == undefined) adapter.setForeignState('vis.0.control.instance', 'FFFFFFFF'); adapter.setForeignState('vis.0.control.data', project + '/' + actualHomeView.val); adapter.setForeignState('vis.0.control.command', 'changeView'); } }, 1000); } catch (error) { adapter.log.error(error); } }
Rein optisch gefällt mir das nun schon sehr viel besser, ABER funktionieren tut das nun irgendwie nicht mehr richtig.
Der Timer läuft nicht mehr im Sekundentakt sondern im 2 Sekundentakt, man sieht zwar in den Objekten dass er sekündlich getriggert wird aber die Wertänderung ist nur alle 2.
Wie kann ich das beheben ausser dass ich das Timeout von 1000 auf 500 setze?
-
@Peoples ich denke du solltest den timeout vor die await Statements setzen. Der rennt dann schon einmal los während noch auf die Werte gewartet wird.
A.
-
@Asgothian
Die Idee hatte ich auch schon dann klappt das mit dem await nicht mehr, da das Timeout keine async function istEdit
Nach längerm suchen und probieren habe ich es dann doch hinbekommen einfach das Timeout auch als async:async function switchToHomeView() { try { const switchTimer = await adapter.getStateAsync('switchTimer'); const lockViewActive = await adapter.getStateAsync('lockViewActive'); const actualLockView = await adapter.getStateAsync('actualLockView'); const actualHomeView = await adapter.getStateAsync('actualHomeView'); const visInstance = await adapter.getForeignStateAsync('vis.0.control.instance'); timerTout = await setTimeout(async function () { let timer = parseInt(switchTimer.val, 10) if(timer > 1){ if(lockViewActive.val === true){ if(timerTout) clearTimeout(timerTout); await adapter.setStateAsync('switchTimer', 0); //Woher kommt newState if(actualLockView.val != newState.split('/').pop()){ switchToViewImmediate(project+'/'+state.val); } } else { await adapter.setStateAsync('switchTimer',timer - 1); switchToHomeView(); } } else { await adapter.setStateAsync('switchTimer', 0); if(visInstance.val == undefined) await adapter.setForeignStateAsync('vis.0.control.instance', 'FFFFFFFF'); await adapter.setForeignStateAsync('vis.0.control.data', project + '/' + actualHomeView.val); await adapter.setForeignStateAsync('vis.0.control.command', 'changeView'); } }, 1000); } catch (error) { adapter.log.error(error); } }
-
Ich habe jetzt mal den Titel geändert, um ein weiteres Problemchen hier mit einfügen zu können ohne einen neuen Thread auf zu machen
``
Jetzt bleibt aber noch eine Frage:
Ich erstelle nach folgendem Muster meine Datenpunkte:adapter.setObjectNotExistsAsync('actualHomeView', { type: 'state', common: { name: 'View what is set as Home', type: 'string', role: 'indicator', read: true, write: true, }, native: {}, });
wenn ich diese jetzt mit deleteState lösche möchte passiert garnichts. Ich habe auch schon Beispiel mit einer Schleife gefunden und probiert aber auch ohne Erfolg.
Wie schaffe ich es bspw. den Ordner Demoview 2 inkl. aller darin enthaltenen Datenpunkte zu löschen?viewswitch.0 - Datenpunkt - Datenpunkt |_ Views (Unterordner) |_ Demoview 1(Unterordner) - Datenpunkt 1 - Datenpunkt 2 - Datenpunkt 3 - Datenpunkt 4 |_ Demoview 2(Unterordner) - Datenpunkt 1 - Datenpunkt 2 - Datenpunkt 3 - Datenpunkt 4 |_ Demoview 3(Unterordner) - Datenpunkt 1 - Datenpunkt 2 - Datenpunkt 3 - Datenpunkt 4 - Datenpunkt 1 - Datenpunkt 2 - Datenpunkt 3 - Datenpunkt 4
-
@Peoples sagte in Klärung von AdpaterEntwicklungs Fragen:
Wie schaffe ich es bspw. den Ordner Demoview 2 inkl. aller darin enthaltenen Datenpunkte zu löschen?
Wie hast du es denn versucht (Code)? Ist Demoview 2 ein Channel oder Folder-Objekt oder nur ein Teil der ID?
-
@AlCalzone
Ich habe es versucht wie in diesem gefundenen Forumsthread: https://forum.iobroker.net/topic/948/adapter-alle-objekte-states-eines-adapters-l%C3%B6schen/4dann kannst du es mit```` adapter.getChannels(function (err, channels) { for(var d = 0; d < channels.length, d++) { adapter.deleteChannel(channels[d]._id); }
Aber bei mir passiert garnichts, nichtmal ein Fehler wird ausgegeben. Meine Objektstrucktur sieht so aus:
Hintergrund ist folgender, wenn der Adapter gestartet wird, werden über die entsprechende vis-views.json alle vorhandenen Views eingelesen und als "Ordner" mit 4 States angelegt.
Nun beobachte ich mit watchfile die Datei und bei einer Änderung möchte ich eine Funktion aufrufen die prüft ob neue Views dazu gekommen sind und diese dann ergänzt oder nicht mehr vorhandene löscht.
-
@Peoples
deleteChannel
,deleteDevice
etc. gehen davon aus, dass du dich an die vorgesehene Strukturdevice.channel.state
hältst. Das ist hier nicht der Fall - du hast nur States ohne übergeordnete Device- und Channel-Objekte.
Das erkennst du daran, dass nebenDemoView
in der Typ-Spalte nichtchannel
steht.Du musst also die zu löschenden States einzeln löschen, sprich
- mit getStates die States auflisten
- diese durchgehen und diejenigen mit deleteState löschen, die weg sollen.
-
@AlCalzone
Wäre es besser Channel zu benutzen?
Mir ist ehrlich gesagt nicht ganz klar was der Unterschied ist.
Und wenn ich die states lösche wie kriege ich die Ordner dann weg -
@Peoples
Die "Ordner" sind nur virtuell, sobald alle States mit diesem ID-Abschnitt weg sind, sind die auch weg.Streng genommen sollte keiner dieser "Ordner" ohne ein zugehöriges Objekt existieren. Objekte definieren unter anderem den Sinn dieses "Ordners" und Anzeigenamen, etc.. Die Objekttypen sind definiert unter https://www.iobroker.net/#en/documentation/dev/objectsschema.md?objecttypes
Die "klassische" Struktur, die wohl noch aus der Homematic-Welt stammt, ist
device.channel.state
, wobei eindevice
ein physisches Gerät beschreibt,channel
eine Teilfunktion (z.B. Licht) undstates
einzelne Zustände dieser Teilfunktion.
Abweichen davon gibt es auch noch den Objekttypfolder
, der zum Gruppieren von States oder weiteren Ordnern dient, die nicht 1:1 in diese Struktur passen. Wäre für dich vermutlich der richtige Anwendungsfall, kann aber dennoch nicht mitdeleteChannel
gelöscht werden. Hättest du die zugehörigen Objekte angelegt, müsstest du sie neben den States perdeleteObject
löschen.@apollon77 noch was hinzuzufügen?
-
@AlCalzone
Also ich hab jetzt zwar ewig gebraucht aber wenn ich es so mache:adapter.getStates('Views.DemoView' + '.*', async function (err, states) { for (const idS in states) { adapter.delObject(idS, function(err){ // adapter.deleteState(idS, function(err){ // if (err) { // adapter.log.error('cannot delete state : ' + idS + ' Error: ' + err); // } // }); }); } });
ist der Ordner samt Inhalt weg, die Frage die ich mir stelle ist nur ob es wirklich notwendig ist deleteState zu nutzen da mit dem nur delObject auch alles weg ist.
-
die logik in iobroker ist
ein objekt hat 0 bis viele states
ein state gehört immer zu einem objektwenn du delState benutzt ist die objektdefinition immer noch da, auch wenn du das im objektbaum nicht mehr siehst. dort wird ein objekt nur angezeigt wenn es mindestens einen state hat.
wenn du dann das objekt bspw neu mit einem anderen typ erzeugen willst, dann funktioniert das nicht. es gibt aberr auch keine fehlermeldung.bei der Erzeugung von objekten/states muss man 2 Sachen unterscheiden, die im objektbaum
nicht gleich ersichtlich sind.1.) es gibt devices und channels, die eigene states haben
bspw "meinadapter.0.devicename" oder "meinadapter.0.channelname"
darunter kann es dann ein oder mehrere datenpunkte geben
bspw "meinadapter.0.devicename.datenpunkt1" oder "meinadapter.0.devicename.datenpunkt2"
Der Datenpunkt heißt in der internen Verwaltung genau so.
channel und devices müssen separat gelöscht werden- es gibt nur datenpunkte
bspw "meinadapter.0.datenpunk1" oder "meinadapter.0.datenpunkt2"
bspw "meinadapter.0.datenpunk1.datenpunkt11" oder "meinadapter.0.datenpunkt2.datenpunkt21"
Der Datenpunkt heißt in der internen Verwaltung genau so.
Wenn alle Unterdatenpunkte gelöscht sind, sind auch die "Ordner" weg
Das was du als Ordner im Objektbaum siehst, wird nur in der Visualisierung so dargestellt, dass jeder Punkt ab Instanzbezeichnung zu einem eigenen Ordner interpretiert wird. Der Ordner existiert zumindest bei 2) so nirgends.
- es gibt nur datenpunkte
-
@OliverIO sagte in Klärung von AdpaterEntwicklungs Fragen:
ein objekt hat 0 bis viele states
Das stimmt so nicht ganz.
Ein Objekt hat 0 oder 1 direkt zugeordneten State, kann aber im Fall von Device/Channel/Folder untergeordnete Objekte haben, die ihre eigenen States besitzen.@OliverIO sagte in Klärung von AdpaterEntwicklungs Fragen:
dort wird ein objekt nur angezeigt wenn es mindestens einen state hat.
Stimmt auch nicht:
Alle 3 dieser Objekte haben keinen State.Ich denke du wirfst hier Objekte und die Baumstruktur im Admin durcheinander.
-
@AlCalzone
hm, hab ich so unter 1) geschrieben.
in iobroker gibt es doch keine folder, nur devices und channels.
die ordner/folder werden nur in der darstellung so angezeigt.
ein eigenständigen eintrag für ordern/folder gibt es nicht (ausser channel und devices) -
@OliverIO sagte in Klärung von AdpaterEntwicklungs Fragen:
ein eigenständigen eintrag für ordern/folder gibt es nicht
Doch! https://www.iobroker.net/#en/documentation/dev/objectsschema.md?objecttypes --> letzter Eintrag "folder"
Hatte ich oben übrigens schon geschrieben.