FHEM Forum

FHEM => Frontends => Thema gestartet von: Master_Nick am 26 Oktober 2017, 13:07:28

Titel: Node-Red als Frontend
Beitrag von: Master_Nick am 26 Oktober 2017, 13:07:28
Moin  ;)

Es gibt hier noch gar keinen Bereich für Node-Red als Frontend - ja Node-Red ist natürlich auch mehr als das... aber ja durchaus auch ein Frontend.
Gibt es da gar keine Nachfrage nach oder keine Nutzer hier? :-)
Titel: Antw:Node-Red als Frontend
Beitrag von: rudolfkoenig am 26 Oktober 2017, 13:22:13
Vermutlich braucht man erst einen Entwickler, die Nutzer kommen dann schon freiwillig :)
Titel: Antw:Node-Red als Frontend
Beitrag von: Master_Nick am 26 Oktober 2017, 13:27:08
Ich schau mir das mal an - es agiert ja als MQTT Client. Somit sollte das relativ wenig Aufwand sein, da ja FHEM als Client schon vorhanden ist.

Oder ich werde mein blaues Wunder erleben :-)
Titel: Antw:Node-Red als Frontend
Beitrag von: Shojo am 26 Oktober 2017, 14:42:56
Da bin ich ja mal gespannt.  :)

Viellicht finde ich ja so doch den Weg zu MQTT.  ;D
Titel: Antw:Node-Red als Frontend
Beitrag von: hexenmeister am 26 Oktober 2017, 14:47:42
Durch eine Entkopplung über MQTT ist das noch überhaupt ein FHEM-Thema? Anders gefragt, was muss man denn in FHEM denn noch tun außer generischen MQTT-Bridges?
Titel: Antw:Node-Red als Frontend
Beitrag von: Master_Nick am 26 Oktober 2017, 14:50:14
Berechtigter Einwand... es hat wahrscheinlich weniger mit FHEM zu tun - aber ich habe halt FHEM und werde es versuchen in Node-Red als Oberfläche abzubilden für Handy und Tablet. Daher hat es dann halt wieder was mit FHEM zu tun... :-D

Aber wer weiß schon was da für Probleme aufkommen... :-D Scheint es ja noch gar nicht zu geben.
Titel: Antw:Node-Red als Frontend
Beitrag von: hexenmeister am 26 Oktober 2017, 15:00:11
Nicht falsch zu verstehen... Ich mag MQTT und auch NodeRed.
Mit MQTT verbinde ich mehrere FHEM-Instances miteinander (dafür habe ich sogar paar Erweiterungen zu dem MQTT-Modul beigesteuert) => funktioniert prima (nach mir besser und einfacher als FHEM2FHEM). :)
NodeRed nutze ich für Definitionen einiger Scenarien und für Alexa-Anbindung.
Habe auch schon versucht, IOBrocker als Oberfläche so anzubinden, bin aber mit dem IOBrocker (trotz einiger tollen Features) nicht so richtig warm geworden.
Daher werde ich Dein Vorhaben mit Interesse weiter verfolgen 8)
Titel: Antw:Node-Red als Frontend
Beitrag von: Shojo am 26 Oktober 2017, 15:02:29
Zitat von: hexenmeister am 26 Oktober 2017, 15:00:11
Habe auch schon versucht, IOBrocker als Oberfläche so anzubinden, bin aber mit dem IOBrocker (trotz einiger tollen Features) nicht so richtig warm geworden.
Daher werde ich Dein Vorhaben mit Interesse weiter verfolgen 8)

Genau so sieht es auch bei mir aus ;)
Titel: Antw:Node-Red als Frontend
Beitrag von: Master_Nick am 26 Oktober 2017, 15:02:59
Hehe alles gut :-)

Hatte ich nicht negativ aufgefasst.
Titel: Antw:Node-Red als Frontend
Beitrag von: ak323 am 26 Oktober 2017, 16:57:47
... klingt auf jeden Fall nach einem interessanten Ansatz ...
(wenn Du im FHEM Forum suchst, findest Du einige "Schnipsel" an Infos ...)

VG ak323
Titel: Antw:Node-Red als Frontend
Beitrag von: Shojo am 29 Oktober 2017, 16:34:02
Habe mir nun auch mal Node-RED angesehen, mir ist aber nicht klar wie man damit ein Frontend umsetzten soll. :-[
Titel: Antw:Node-Red als Frontend
Beitrag von: Master_Nick am 29 Oktober 2017, 18:54:23
Node Red + Node-Red-Dashboard (UI)

https://flows.nodered.org/node/node-red-dashboard
Titel: Antw:Node-Red als Frontend
Beitrag von: Bapt. Reverend Magersuppe am 29 Oktober 2017, 20:53:50
Wieso nur als Frontend? Das kann man doch dank der Oberfläche schon zur Gestaltung der Automatisierung einsetzen. Nicht nur als Frontend. Lieber anderster herum gedreht den Fall: fhem als Deckel für nodered.
Titel: Antw:Node-Red als Frontend
Beitrag von: Master_Nick am 29 Oktober 2017, 20:56:43
Nun - es kann durchaus sein, dass sich das noch dreht und wendet wenn ich erst mal angefangen habe.

Aber aktuell denke ich erst mal als Frontend.

Effektiv gesehen kannst du FHEM auch weg lassen sofern Node Red vieles anbietet und du den Rest implementieren kannst.
Will ich aber gar nicht - ich mag FHEM und ich mag die mir hier bekannt gewordene Community und die Kraft mit der viele hier was umsetzen.
Titel: Antw:Node-Red als Frontend
Beitrag von: Shojo am 14 Dezember 2017, 14:42:25
Und gibt es hier schon was neues ?  :)

Gruß
Titel: Antw:Node-Red als Frontend
Beitrag von: Master_Nick am 14 Dezember 2017, 14:52:08
 ;) Ich bin aktiv dabei mir den besten Ansatz zu suchen.
Ich überlege aktuell FHEM alles was an Aktionen (Schaltaktionen, Temperaturen, etc) rein kommt über MQTT an Node-Red weiter zu geben und dann dort zu anzuzeigen.
Eine direkte Schnittstelle scheint es nicht zu geben, von daher ist das bisher der einzige Weg der mir offen erscheint.

Ansonsten leider noch nicht viel - generell habe ich mein 10" Touchpanel schon fertig gestellt und kämpfe aktuell noch stark mit anderen Problemen die mich ablenken da sie in die aktuelle Nutzung einstreuen. (FHEM -> FCM -> andFHEM -> Widgets bleiben einfach wie sie sind statt Schaltzustände korrekt wiederzugeben - Da bin ich in regem Kontakt mit dem Entwickler)

Aber der Urlaub kommt. Und wenn ich die ersten Punkte gekaut und geschluckt habe wird es denke ich auch nicht mehr wie ein Berg aussehen.

Kurz um verzögert aber nicht begraben ;-)
Titel: Antw:Node-Red als Frontend
Beitrag von: TWART016 am 14 Dezember 2017, 15:18:33
Eventuell hilft das
https://www.youtube.com/watch?v=AGn_QfPmk-g
Titel: Antw:Node-Red als Frontend
Beitrag von: Master_Nick am 14 Dezember 2017, 15:22:20
Definitiv nicht falsch denke ich.
Man muss das Rad ja nicht immer neu erfinden.

Danke dir!
Titel: Antw:Node-Red als Frontend
Beitrag von: hexenmeister am 15 Dezember 2017, 10:31:01
Zitat von: TWART016 am 14 Dezember 2017, 15:18:33
Eventuell hilft das
https://www.youtube.com/watch?v=AGn_QfPmk-g
Interessanter Ansatz. Bring mich auf eine Idee :) Warum nicht gleich ein Device entwickeln, das diese ganze Scripterei in sich vereint und mqtt-Pub/Sub-Funktionalität für alle Geräte zur Verfügung stellt
Die Definitionen könnte in etwa so aussehen:

defmod mqttBridge MQTT_GENERIC_BRIDGE
attr mqttBridge mqttTopicPrefix /haus/

defmod sensorXYZ bla
...
attr sensorXYZ mqttTopic sensorXYZ
attr sensorXYZ mqttPubNames temperature humidity
...

defmod rolloX blup
...
attr rolloX mqttTopic rolloX
attr rolloX mqttPubNames pct:position state
attr rolloX mqttSubNames set:pct
...


wobei mqttBridge das Gerät wäre, das alles andere intern verwaltet.
Für sensorXYZ würden dann Readings temperature und humidity als /haus/sensorXYZ/temperature bzw. /haus/sensorXYZ/humidity gepublished.
Für rolloX würde die Position und State gepublished (Reading pct als /haus/rolloX/position und state als /haus/rolloX/state) und zusätzlich würde Topic /haus/rolloX/set aboniert und dessen Inhalte per "set rolloX pct VALUE" gesetzt.

Das würde mir meine Konfiguration an vielen Stellen vereinfachen. Vlt. realisiere ich das mal :)
Wie findet ihr die Idee?
Titel: Antw:Node-Red als Frontend
Beitrag von: Master_Nick am 15 Dezember 2017, 12:56:32
Ich denke das wäre ein Träumchen! Super Idee! Man könnte es fast als Schnittstelle verstehen. :-)

Zitat von: hexenmeister am 15 Dezember 2017, 10:31:01
Interessanter Ansatz. Bring mich auf eine Idee :) Warum nicht gleich ein Device entwickeln, das diese ganze Scripterei in sich vereint und mqtt-Pub/Sub-Funktionalität für alle Geräte zur Verfügung stellt
Die Definitionen könnte in etwa so aussehen:

defmod mqttBridge MQTT_GENERIC_BRIDGE
attr mqttBridge mqttTopicPrefix /haus/

defmod sensorXYZ bla
...
attr sensorXYZ mqttTopic sensorXYZ
attr sensorXYZ mqttPubNames temperature humidity
...

defmod rolloX blup
...
attr rolloX mqttTopic rolloX
attr rolloX mqttPubNames pct:position state
attr rolloX mqttSubNames set:pct
...


wobei mqttBridge das Gerät wäre, das alles andere intern verwaltet.
Für sensorXYZ würden dann Readings temperature und humidity als /haus/sensorXYZ/temperature bzw. /haus/sensorXYZ/humidity gepublished.
Für rolloX würde die Position und State gepublished (Reading pct als /haus/rolloX/position und state als /haus/rolloX/state) und zusätzlich würde Topic /haus/rolloX/set aboniert und dessen Inhalte per "set rolloX pct VALUE" gesetzt.

Das würde mir meine Konfiguration an vielen Stellen vereinfachen. Vlt. realisiere ich das mal :)
Wie findet ihr die Idee?
Titel: Antw:Node-Red als Frontend
Beitrag von: hexenmeister am 17 Dezember 2017, 23:17:25
Habe eine erste teil-funktioniernde Version (nur publish, keine subscribe). Bin mir aber noch nicht sicher, wie (Format) ich die KonfigurationsAttribute haben will. Ich werde die Tage ein neuen Thread dafür eröffnen, hier sind wir schon sehr OT.
Titel: Antw:Node-Red als Frontend
Beitrag von: Absolute Beginner am 20 Dezember 2017, 17:18:49
Hochinteressantes Thema! Habe Node-RED als Frontend zu FHEM im Test, nutze ausschließlich MQTT als Interface. Läuft prima und ich wundere mich, warum hier im Forum so wenig über Node-RED steht. Nur leider ist die Einrichtung aller Instanzen über die MQTT-Bridge ziemlich aufwändig. Am ioBroker habe ich mich auch schon versucht - der hat sehr wohl einige Vorteile, mir ist er aber zu 'unhandlich' (dito für Openhab2). Deshalb würde ich mich sehr über eine generische Bridge freuen. Devices mit FHEM einrichten ist einfach und gut! Bei dieser Gelegenheit gefragt: FHEM und Node-RED auf einem Rpi3 - läuft das gut zusammen? Gibt es etwas zu beachten?
Titel: Antw:Node-Red als Frontend
Beitrag von: hexenmeister am 20 Dezember 2017, 17:24:21
Bei mir laufen 3 FHEM Instanzen plus Node-Red auf einem Rpi3 zusammen. Sprechen munter miteinander über mosquitto, das wiederum auf einem Cubietruck (zusammen mit einer weiteren FHEM Instanz) installiert ist.
Keine Probleme.
Titel: Antw:Node-Red als Frontend
Beitrag von: Absolute Beginner am 20 Dezember 2017, 17:55:40
Und wenn der Broker (Mosquitto) auch auf dem einen RPi läuft? Bisher habe ich Node-RED auf einem PC zum Testen laufen.
Titel: Antw:Node-Red als Frontend
Beitrag von: hexenmeister am 20 Dezember 2017, 18:29:32
Wird auch kein Problem sein. Mosquitto braucht nicht viel.
Titel: Antw:Node-Red als Frontend
Beitrag von: Shojo am 20 Dezember 2017, 18:35:25
Zitat von: hexenmeister am 17 Dezember 2017, 23:17:25
Habe eine erste teil-funktioniernde Version (nur publish, keine subscribe). Bin mir aber noch nicht sicher, wie (Format) ich die KonfigurationsAttribute haben will. Ich werde die Tage ein neuen Thread dafür eröffnen, hier sind wir schon sehr OT.

Hast Du da schon was zu testen   ::) ;D
Titel: Antw:Node-Red als Frontend
Beitrag von: hexenmeister am 20 Dezember 2017, 23:43:53
Habe ich. Versuche morgen bereit zu stellen. Sorry, gerade zeitlich sehr knapp.
Titel: Antw:Node-Red als Frontend
Beitrag von: Shojo am 21 Dezember 2017, 00:29:26
Zitat von: hexenmeister am 20 Dezember 2017, 23:43:53
Habe ich. Versuche morgen bereit zu stellen. Sorry, gerade zeitlich sehr knapp.

Bloß kein Stress ;)
Bin aber gespannt! 
Titel: Antw:Node-Red als Frontend
Beitrag von: hexenmeister am 21 Dezember 2017, 22:36:27
Zitat von: Shojo am 21 Dezember 2017, 00:29:26
Bin aber gespannt!

Wie angedroht ;D hier ist das in einem neuen Thread: https://forum.fhem.de/index.php?topic=81418.new#new
Titel: Antw:Node-Red als Frontend
Beitrag von: Darrol am 08 Januar 2018, 22:23:58
Habe heute mein TabletUI durch die MQTT/NodeRed-Dashboardlösung ersetzt und bin begeistert.
Die größte Herausforderung war lediglich den Output von der Log-Datenbank für die Charts aufzubereiten.
Der Rest ging quasi im Handumdrehen.
Das Dashboard lädt schnell und reagiert praktisch ohne erkennbare Verzögerung.
Bei mir laufen Fhem, postgres, Mosquitto und Node-RED auf einer Intel-NUC in Containern.

Allerdings muss ich sagen, dass die TabletUI von Fhem sehr viel umfangreicher und teilweise auch hübscher ist als das Node-Red-Dashboard. Leider ist mir da aber die Einrichtung zu frickelig und die Performance auch nur so lala.
Für mich ist damit der größte Knackpunkt von Fhem, nämlich das fehlende, saubere und flotte  Anwender-Frontend, behoben bzw. umschifft.
Titel: Antw:Node-Red als Frontend
Beitrag von: Master_Nick am 13 Januar 2018, 19:51:45
Hehe,
schön zu hören - leider bin ich immer noch arg zurück geworfen aktuell.
Viel um die Ohren und dann Bau ich gerade eine komplette Steuerung neu weil sie unzuverlässig war.

Aber mein  10" Touchdisplay mit ZeroW hat immerhin schon eine Rahmung und ist Montagebereit.
Titel: Antw:Node-Red als Frontend
Beitrag von: Master_Nick am 05 Februar 2018, 14:31:53
Zitat von: d.mrugalla am 08 Januar 2018, 22:23:58
Habe heute mein TabletUI durch die MQTT/NodeRed-Dashboardlösung ersetzt und bin begeistert.
Die größte Herausforderung war lediglich den Output von der Log-Datenbank für die Charts aufzubereiten.
Der Rest ging quasi im Handumdrehen.
Das Dashboard lädt schnell und reagiert praktisch ohne erkennbare Verzögerung.
Bei mir laufen Fhem, postgres, Mosquitto und Node-RED auf einer Intel-NUC in Containern.

Allerdings muss ich sagen, dass die TabletUI von Fhem sehr viel umfangreicher und teilweise auch hübscher ist als das Node-Red-Dashboard. Leider ist mir da aber die Einrichtung zu frickelig und die Performance auch nur so lala.
Für mich ist damit der größte Knackpunkt von Fhem, nämlich das fehlende, saubere und flotte  Anwender-Frontend, behoben bzw. umschifft.

Kannst du mir deine Lösung etwas offenlegen ;-)

Wie schaltest du deine nicht MQTT Geräte in FHEM mit Node-RED? Das Tool von hexenmeister kann ja bisher nur publishen nicht aber auslesen, soweit ich gelesen habe.

Ich plane aktuell Node-RED wirklich nur als Interface zu nutzen ohne Logik.
Also soll es INFOS von FHEM über alle Devices bekommen (hexenmeisters Modul würde das bringen).
Es soll aber ebenso alle Geräte schalten können (da fehlt mir aktuell die Möglichkeit NICHT MQTT Geräte zu schalten in FHEM (klar könnte man per CURL machen erst mal...)
Titel: Antw:Node-Red als Frontend
Beitrag von: Darrol am 05 Februar 2018, 19:19:14
Zitat von: Master_Nick am 05 Februar 2018, 14:31:53
Kannst du mir deine Lösung etwas offenlegen ;-)


Na klar doch.

Im Grunde ist es bloß eine Umsetzung von dem dieses Tutorials hier:
https://haus-automatisierung.com/nodered/2017/12/13/node-red-tutorial-reihe-part-4-verbindung-fhem.html (https://haus-automatisierung.com/nodered/2017/12/13/node-red-tutorial-reihe-part-4-verbindung-fhem.html)
Ich glaube hexenmeister hat das entsprechende YT-Video dazu ja auch schon hier gepostet.

Das man einen laufenden MQTT-Server und eine dazu passende Broker-Verbindung zu FHEM hat sehe ich mal als gegeben

Zunächst benötigt man dazu zwei zusätzliche Userattribute auf dem Global-Device:
attr global userattr mqttName mqttRoom <und das was hier vorher schon stand>

Als nächstes richtet man ein Notify ein, das alle Events von Geräten published, die diese beiden Attribute gesetzt haben:
defmod ntfy_publish_mqtt notify .*:.* {\
my $mqttRoom = AttrVal($NAME, 'mqttRoom', '');;\
my $mqttName = AttrVal($NAME, 'mqttName', '');;\
\
    if ($mqttRoom ne '' && $mqttName ne '') {\
        my $reading = "";;\
        my $message = "";;\
\
        if ($EVENT =~ qr/(.*?): (.*)/p) {\
            $reading = $1;;\
            $message = $2;;\
        } else {\
            $reading = "state";;\
            $message = $EVENT;;\
        }\
\
        my $topic = "/$mqttRoom/$mqttName/$reading";;\
\
        fhem("set Mosquitto publish $topic $message");;\
    }\
}


Damit wäre die Kommunikation von FHEM in Richtung Node-REd schonmal startklar.

Für die andere Richtung benötigt man ein zusätzliches MQTT_DEVICE welches die Kommandos von NR empfängt
und ein weiters Notify welches diese ausführt:

defmod SYS_MQTT MQTT_DEVICE
attr SYS_MQTT userattr subscribeReading_cmnd
attr SYS_MQTT DbLogExclude .*
attr SYS_MQTT IODev Mosquitto
attr SYS_MQTT alias MQTT-Command
attr SYS_MQTT group Dienste
attr SYS_MQTT icon mqtt_device
attr SYS_MQTT room System
attr SYS_MQTT stateFormat Letztes Kommando: cmnd
attr SYS_MQTT subscribeReading_cmnd /Service/fhem/cmnd


defmod n_SYS_MQTT_cmnd notify SYS_MQTT:cmnd:.* {\
if ($EVENT =~ qr/.*?: (.*)/p) {\
        my $cmnd = $1;;\
        fhem($cmnd);;\
    }\
}


Und nun ein paar dazu passende Nodes:
Hier ein Schieberegler, der ein Homematic-Thermostat regelt
[[{"id":"49c5d0bf.e83b4","type":"mqtt in","z":"4908d1e9.14cae","name":"Zieltemperatur, Wohnzimmer","topic":"/OG/Wohnzimmer/Heizung/desired-temp","qos":"2","broker":"b0a683bf.368fb","x":140,"y":120,"wires":[["8342fd3f.af6d7","113b53a8.f839fc"]]},{"id":"8342fd3f.af6d7","type":"ui_slider","z":"4908d1e9.14cae","name":"Zieltemperatur, Slider","label":"","group":"12eafd47.715883","order":3,"width":"6","height":"1","passthru":false,"topic":"set HM_5AD3D3_Clima desired-temp ","min":"12","max":"30","step":"0.5","x":400,"y":120,"wires":[["2f199b9a.6e7e54","113b53a8.f839fc"]]},{"id":"113b53a8.f839fc","type":"ui_text","z":"4908d1e9.14cae","group":"12eafd47.715883","order":2,"width":"6","height":"1","name":"Zieltemperatur","label":"Zieltemperatur","format":"{{msg.payload}}","layout":"col-center","x":600,"y":160,"wires":[],"inputLabels":["Zieltemperatur"]},{"id":"161c33aa.36577c","type":"mqtt out","z":"4908d1e9.14cae","name":"Heizung stellen","topic":"/Service/fhem/cmnd","qos":"2","retain":"false","broker":"b0a683bf.368fb","x":1020,"y":120,"wires":[]},{"id":"5f2cf46f.806f8c","type":"function","z":"4908d1e9.14cae","name":"Aufbau Fhem-CMD","func":"return { payload: msg.topic + msg.payload };","outputs":1,"noerr":0,"x":810,"y":120,"wires":[["161c33aa.36577c"]]},{"id":"2b9861fa.96dc3e","type":"ui_toast","z":"4908d1e9.14cae","position":"top right","displayTime":"3","highlight":"","outputs":0,"ok":"OK","cancel":"","topic":"neue Zieltemperatur","name":"Zieltemperatur","x":1020,"y":80,"wires":[]},{"id":"10763929.3c1587","type":"function","z":"4908d1e9.14cae","name":"Zieltemp. toString","func":"var pl = msg.payload.toString();\nmsg.payload = pl + \"°C\";\nreturn msg;","outputs":1,"noerr":0,"x":810,"y":80,"wires":[["2b9861fa.96dc3e"]]},{"id":"2f199b9a.6e7e54","type":"trigger","z":"4908d1e9.14cae","op1":"","op2":"","op1type":"nul","op2type":"payl","duration":"1500","extend":true,"units":"ms","reset":"","name":"","x":600,"y":120,"wires":[["5f2cf46f.806f8c","10763929.3c1587"]]},{"id":"b0a683bf.368fb","type":"mqtt-broker","z":"","broker":"100.10.1.6","port":"1883","clientid":"","usetls":false,"compatmode":true,"keepalive":"60","cleansession":true,"willTopic":"","willQos":"0","willPayload":"","birthTopic":"","birthQos":"0","birthPayload":""},{"id":"12eafd47.715883","type":"ui_group","z":"","name":"Raumklima","tab":"3a31dbc6.b1d4e4","order":1,"disp":true,"width":"6"},{"id":"3a31dbc6.b1d4e4","type":"ui_tab","z":"","name":"Wohnzimmer","icon":"dashboard","order":2}]]

Und ein einfacher Schalter, der ein Fibaro-Wandschalter umschaltet:
[{"id":"49c5d0bf.e83b4","type":"mqtt in","z":"4908d1e9.14cae","name":"Zieltemperatur, Wohnzimmer","topic":"/OG/Wohnzimmer/Heizung/desired-temp","qos":"2","broker":"b0a683bf.368fb","x":140,"y":120,"wires":[["8342fd3f.af6d7","113b53a8.f839fc"]]},{"id":"8342fd3f.af6d7","type":"ui_slider","z":"4908d1e9.14cae","name":"Zieltemperatur, Slider","label":"","group":"12eafd47.715883","order":3,"width":"6","height":"1","passthru":false,"topic":"","min":"12","max":"30","step":"0.5","x":400,"y":120,"wires":[["2f199b9a.6e7e54","113b53a8.f839fc"]]},{"id":"113b53a8.f839fc","type":"ui_text","z":"4908d1e9.14cae","group":"12eafd47.715883","order":2,"width":"6","height":"1","name":"Zieltemperatur","label":"Zieltemperatur","format":"{{msg.payload}}","layout":"col-center","x":600,"y":160,"wires":[],"inputLabels":["Zieltemperatur"]},{"id":"161c33aa.36577c","type":"mqtt out","z":"4908d1e9.14cae","name":"Heizung stellen","topic":"/Service/fhem/cmnd","qos":"2","retain":"false","broker":"b0a683bf.368fb","x":1020,"y":120,"wires":[]},{"id":"5f2cf46f.806f8c","type":"function","z":"4908d1e9.14cae","name":"Aufbau Fhem-CMD","func":"return { payload: \"set HM_5AD3D3_Clima desired-temp \" + msg.payload };","outputs":1,"noerr":0,"x":810,"y":120,"wires":[["161c33aa.36577c"]]},{"id":"2b9861fa.96dc3e","type":"ui_toast","z":"4908d1e9.14cae","position":"top right","displayTime":"3","highlight":"","outputs":0,"ok":"OK","cancel":"","topic":"neue Zieltemperatur","name":"Zieltemperatur","x":1020,"y":80,"wires":[]},{"id":"10763929.3c1587","type":"function","z":"4908d1e9.14cae","name":"Zieltemp. toString","func":"var pl = msg.payload.toString();\nmsg.payload = pl + \"°C\";\nreturn msg;","outputs":1,"noerr":0,"x":810,"y":80,"wires":[["2b9861fa.96dc3e"]]},{"id":"2f199b9a.6e7e54","type":"trigger","z":"4908d1e9.14cae","op1":"","op2":"","op1type":"nul","op2type":"payl","duration":"1500","extend":true,"units":"ms","reset":"","name":"","x":600,"y":120,"wires":[["5f2cf46f.806f8c","10763929.3c1587"]]},{"id":"b0a683bf.368fb","type":"mqtt-broker","z":"","broker":"100.10.1.6","port":"1883","clientid":"","usetls":false,"compatmode":true,"keepalive":"60","cleansession":true,"willTopic":"","willQos":"0","willPayload":"","birthTopic":"","birthQos":"0","birthPayload":""},{"id":"12eafd47.715883","type":"ui_group","z":"","name":"Raumklima","tab":"3a31dbc6.b1d4e4","order":1,"disp":true,"width":"6"},{"id":"3a31dbc6.b1d4e4","type":"ui_tab","z":"","name":"Wohnzimmer","icon":"dashboard","order":2}]

Titel: Antw:Node-Red als Frontend
Beitrag von: Master_Nick am 07 Februar 2018, 15:19:31
 ;D ;D Ich gebe zu ich hatte mich versucht mit einigen Tutorials an Node-RED zu bringen - hatte da aber völlig falsche Verwendungszwecke innerhalb der Tutorials, so das es sich nicht adaptieren ließ (Websocket - http und wer weiß was.. völlig verrant).

Habe jetzt meine ersten Dinge fertig und es ist ja unglaublich flach und einfach gehalten (man kann es natürlich auch voll pumpen mit Kram). Aber im eigentlich reichen 4 Nodes (MQTT Input -> JSON -> switch -> MQTT Output) für einen simplen Schalter mit MQTT.

Bin sehr zufrieden und werde nun nach und nach alle devices mittels der hier im Forum oder schon in FHEM angebotenen Wege an den MQTT Broker bringen und dann ist die Visualisierung in Node-Red ein Klacks.

Hier mal ein super minimalistisches Beispiel (der erste in Betrieb genommene SonOff S20) der UI die Node-RED am Handy oder aber auch am Touchscreen bereitstellt :D.
Und die Config die dafür in Node-RED gemacht werden muss (gut der Broker muss auch einmal angelegt werden und der Raum braucht einen Namen und die Gruppe)

Titel: Antw:Node-Red als Frontend
Beitrag von: Darrol am 08 Februar 2018, 21:27:16
Zitat von: Master_Nick am 07 Februar 2018, 15:19:31
Habe jetzt meine ersten Dinge fertig und es ist ja unglaublich flach und einfach gehalten (man kann es natürlich auch voll pumpen mit Kram). Aber im eigentlich reichen 4 Nodes (MQTT Input -> JSON -> switch -> MQTT Output) für einen simplen Schalter mit MQTT.

Yup und v.a. kann man superschnell per copy/paste ganze Node-Gruppen duplizieren und muss, wenn man richtig voregeabreitet hat, nur noch den Raum- und Gerätenamen anpassen damit die laufen.
So hat man im Handumdrehen für alle Räume eine Dashboard-Seite eingerichtet.

Knifflig wird es allerdings wenn man xy-chart aus dem fhem-db-log füttern möchte. Die sind nämlich darauf ausgerichtet erstmal selbst Daten zu sammeln anstatt einfach einen vollständigen Datensatz darzustellen.

Hier eine Node-Kette zum darstellen eines Temperaturplots über die Vergangenen 24h:
[{"id":"134e6bc.9411a94","type":"postgres","z":"4908d1e9.14cae","postgresdb":"","name":"Fhem-Datenbank","output":true,"outputs":1,"x":370,"y":720,"wires":[["fb2198d8.03ff28","7c6a0d59.71e564"]]},{"id":"89378517.545228","type":"inject","z":"4908d1e9.14cae","name":"Datenselektion","topic":"Query Luftfeuchtigkeit","payload":"select timestamp as x, value::float as y1 from public.history  where device='ESP_Wohnzimmer' and reading='Temperatur'  order by timestamp desc limit 576","payloadType":"str","repeat":"150","crontab":"","once":true,"x":120,"y":720,"wires":[["134e6bc.9411a94"]]},{"id":"e9adfacc.187088","type":"ui_chart","z":"4908d1e9.14cae","name":"Verlauf Temperatur","group":"3dfb78a5.81ceb8","order":0,"width":"0","height":"0","label":"Verlauf Temperatur","chartType":"line","legend":"false","xformat":"HH:mm:ss","interpolate":"bezier","nodata":"Lade Daten","dot":false,"ymin":"10","ymax":"30","removeOlder":"24","removeOlderPoints":"576","removeOlderUnit":"3600","cutout":0,"useOneColor":false,"colors":["#ff0000","#aec7e8","#ff7f0e","#2ca02c","#98df8a","#d62728","#ff9896","#9467bd","#c5b0d5"],"useOldStyle":false,"x":930,"y":720,"wires":[[],[]],"outputLabels":["O",""]},{"id":"fb2198d8.03ff28","type":"function","z":"4908d1e9.14cae","name":"SQL-Output zu Chart-Array","func":"// name of the time column values:\nvar tcol = \"x\";\n// list of columns to be charted:\nvar cols = [\"y1\"];\n\n// iterate over each column of the query results\nvar data = cols.map(function(col) {\n    // iterate over each row of the query results\n    var vals = msg.payload.map(function(row) {\n        // return a data point for each col\n        return [ +row[tcol], +row[col] ];\n    });\n\n    // return data object for each line on the chart\n    return { key: \"Temperatur\", values: vals };\n});\n\n// now put the new chart data in the payload\nmsg.payload = data;\nreturn msg;","outputs":1,"noerr":0,"x":660,"y":720,"wires":[["e9adfacc.187088","d65dc628.33a998"]]},{"id":"d65dc628.33a998","type":"debug","z":"4908d1e9.14cae","name":"plotdata","active":true,"console":"false","complete":"payload","x":900,"y":760,"wires":[]},{"id":"7c6a0d59.71e564","type":"debug","z":"4908d1e9.14cae","name":"db-Output","active":true,"console":"false","complete":"payload","x":600,"y":760,"wires":[]},{"id":"3dfb78a5.81ceb8","type":"ui_group","z":"","name":"Zeitlinie","tab":"3a31dbc6.b1d4e4","order":4,"disp":true,"width":"6"},{"id":"3a31dbc6.b1d4e4","type":"ui_tab","z":"","name":"Wohnzimmer","icon":"dashboard","order":2}]

Man muss dazu das Array aus der Datenbank in einen Haufen einzelner Datenpunkte(Arrays) zerlegen.
Dafür hab ich diesen Codeschnipsel hier gefunden:
// name of the time column values:
var tcol = "x";
// list of columns to be charted:
var cols = ["y1"];

// iterate over each column of the query results
var data = cols.map(function(col) {
    // iterate over each row of the query results
    var vals = msg.payload.map(function(row) {
        // return a data point for each col
        return [ +row[tcol], +row[col] ];
    });

    // return data object for each line on the chart
    return { key: "Temperatur", values: vals };
});

// now put the new chart data in the payload
msg.payload = data;
return msg;

Der ist glaube ich auch nicht postgres-spezifisch.


Titel: Antw:Node-Red als Frontend
Beitrag von: Master_Nick am 08 Februar 2018, 21:57:22
Sehr sexy!  ;)

Das kann ich direkt mal schauen wie ich das umsetze.

Temp und Hygro hab ich in jedem Raum.

Du sprichst von Datenbank? Du machst also kein FileLog? :-D Oder welche Datenbank meinst du?
Titel: Antw:Node-Red als Frontend
Beitrag von: Darrol am 09 Februar 2018, 19:04:51
Zitat von: Master_Nick am 08 Februar 2018, 21:57:22

Du sprichst von Datenbank? Du machst also kein FileLog? :-D Oder welche Datenbank meinst du?

Genau das.
Ich nutze sowohl configDB als auch DbLog in einer Postgres-Datenbank.

Gesendet von meinem YD201 mit Tapatalk
Titel: Antw:Node-Red als Frontend
Beitrag von: Master_Nick am 09 Februar 2018, 19:09:53
Ich gestehe ich bin bei beidem noch oldschool.  :-\
Aber muss jetzt eh einmal alles umziehen da neue Hardware.
Titel: Antw:Node-Red als Frontend
Beitrag von: Master_Nick am 27 Februar 2018, 02:12:14
Auf das DbLog bin ich noch immer nich geswitched aber ich habe mittels der MQTT Generic Bridge nun meiner Thermostate schon mal (lesend) drin.

Unfassbar einfach ist das mit Node-Red.


Ich denke, wenn ich einen guten Stand habe, kann ich mal eine Doku schreiben von der Installation von Node-Red über die Nutzung mit der MQTT Generic Bridge (https://forum.fhem.de/index.php/topic,81418.0.html (https://forum.fhem.de/index.php/topic,81418.0.html)) und normalen MQTT Devices. :-)

*Edit* Man kann die Finger nicht von lassen ;-) Hab es erweitert.
Titel: Antw:Node-Red als Frontend
Beitrag von: hexenmeister am 28 Februar 2018, 00:42:31
Sieht gut aus! Postest Du für Nachahmer Nodes-Export?
Titel: Antw:Node-Red als Frontend
Beitrag von: Master_Nick am 28 Februar 2018, 00:51:16
Klar kein Thema hier dann also die fertig gestellte "Klima" Seite

Siehe Anhang - es war einfach zu viel für in den Post  8)
Titel: Antw:Node-Red als Frontend
Beitrag von: Shojo am 28 Februar 2018, 15:10:58
Habe mal eben was für mein Handy fertig gemacht....
Titel: Antw:Node-Red als Frontend
Beitrag von: Master_Nick am 28 Februar 2018, 15:17:12
Auch eine gute Idee spart man 2 Elemente indem man den einen Kringel da nimmt - welcher ist das? ;-) Donut?
Titel: Antw:Node-Red als Frontend
Beitrag von: Shojo am 28 Februar 2018, 15:22:33
Das ist HTML (http://www.ajso.lt/node-red/nest-thermostat-dashboard-html5-widget-node-red/)
Titel: Antw:Node-Red als Frontend
Beitrag von: Master_Nick am 28 Februar 2018, 15:23:30
Ah da war ich vor kurzem auch mal drüber gehuscht :-D

Jo sieht gut aus - mal sehen ;-D
Titel: Antw:Node-Red als Frontend
Beitrag von: Shojo am 01 März 2018, 15:13:24
Zitat von: Shojo am 28 Februar 2018, 15:22:33
Das ist HTML (http://www.ajso.lt/node-red/nest-thermostat-dashboard-html5-widget-node-red/)

Hmm....
Aber so richtig Funktioniert das mit den Steuerelement nicht.
Bei einen Page reload fehlen ab und dann die Werte :(

Titel: Antw:Node-Red als Frontend
Beitrag von: Master_Nick am 01 März 2018, 17:23:57
Das kann durchaus mal am Browser liegen - ich hatte sowas mal bei Firefox und FHEM.... da blieb dann ne ganze Ecke einfach hängen und fehlte und alles war unnutzbar bis man oben in die Adresszeile klickte und Enter drückte. (STRG+SHIFT+R brachte nicht das gleiche Ergebnis)
Titel: Antw:Node-Red als Frontend
Beitrag von: Shojo am 01 März 2018, 20:46:36
Habe schon mehre Browser und Devices durch kommt auf allen leider vor :(
Titel: Antw:Node-Red als Frontend
Beitrag von: Master_Nick am 01 März 2018, 21:04:17
Dann ist das eher raus... :-/ Schade
Oder mal Bescheid sagen bei dem Typen :-D Bug öffnen.
Titel: Antw:Node-Red als Frontend
Beitrag von: SpeedyRS2 am 06 März 2018, 10:45:49
Ich bin mittlerweile auch bei NodeRed als Frontend für FHEM angekommen.
Ich habe vorher unheimlich viel mit dem TabletUI rumprobiert und es immer wieder umgestellt. Optisch hat es mir nachher sogar sehr gut gefallen. Allerdings fand ich die Bedienung über das TabletUI irgendwie doch sehr träge und hakelig. Das hat mich echt gestört. Da bringt das schickste UI nichts.

Daraufhin habe ich mich mal an NodeRed gesetzt.
Ich war von der ersten Sekunde an total begeistert.
Kommunikation zwischen FHEM und NodeRed läuft über MQTT und das ohne Zeitverlust. Ich hatte schon die Befürchtung, dass man die zusätzliche Schicht merkt. Ist aber gar nicht so. Änderungen in FHEM werden direkt in NodeRed dargestellt.
Das Dashboard ist super simpel zu erstellen und wirklich flott in der Bedienung. Gerade die Slider haben mich beim TabletUI in den Wahnsinn getrieben. Im NodeRed Dashboard flutscht es einfach nur perfekt.

Hier mal ein paar Screens, wie Teile meines Dashboards jetzt aussehen:
Titel: Antw:Node-Red als Frontend
Beitrag von: SpeedyRS2 am 06 März 2018, 11:51:03
Für das NEST Widget hab ich, glaube ich  ;) , auch eine Lösung gefunden.

Änderungen habe ich an den im Screenshot markierten Elementen vorgenommen.

Hier mal der exportierte Flow:
[{"id":"2c174318.8fafec","type":"tab","label":"Flow 2","disabled":false,"info":""},{"id":"85e9547.e3387a8","type":"ui_template","z":"2c174318.8fafec","group":"8db78a1d.f45e08","name":"Nest","order":1,"width":"6","height":"6","format":"<div id=\"thermostat\"></div>\n\n<style>\n@import url(http://fonts.googleapis.com/css?family=Open+Sans:300);\n#thermostat {\n  margin: 0 auto;\n  -webkit-tap-highlight-color: rgba(0, 0, 0, 0);\n}\n.dial {\n  -webkit-user-select: none;\n     -moz-user-select: none;\n      -ms-user-select: none;\n          user-select: none;\n}\n.dial.away .dial__ico__leaf {\n  visibility: hidden;\n}\n.dial.away .dial__lbl--target {\n  visibility: hidden;\n}\n.dial.away .dial__lbl--target--half {\n  visibility: hidden;\n}\n.dial.away .dial__lbl--away {\n  opacity: 1;\n}\n.dial .dial__shape {\n  -webkit-transition: fill 0.5s;\n  transition: fill 0.5s;\n}\n.dial__ico__leaf {\n  fill: #13EB13;\n  opacity: 0;\n  -webkit-transition: opacity 0.5s;\n  transition: opacity 0.5s;\n  pointer-events: none;\n}\n.dial.has-leaf .dial__ico__leaf {\n  display: block;\n  opacity: 1;\n  pointer-events: initial;\n}\n.dial__editableIndicator {\n  fill: white;\n  fill-rule: evenodd;\n  opacity: 0;\n  -webkit-transition: opacity 0.5s;\n  transition: opacity 0.5s;\n}\n.dial--edit .dial__editableIndicator {\n  opacity: 1;\n}\n.dial--state--off .dial__shape {\n  fill: #3d3c3c;\n}\n.dial--state--heating .dial__shape {\n  fill: #E36304;\n}\n.dial--state--cooling .dial__shape {\n  fill: #007AF1;\n}\n.dial__ticks path {\n  fill: rgba(255, 255, 255, 0.3);\n}\n.dial__ticks path.active {\n  fill: rgba(255, 255, 255, 0.8);\n}\n.dial text {\n  fill: white;\n  text-anchor: middle;\n  font-family: Helvetica, sans-serif;\n  alignment-baseline: central;\n}\n.dial__lbl--target {\n  font-size: 120px;\n  font-weight: bold;\n}\n.dial__lbl--target--half {\n  font-size: 40px;\n  font-weight: bold;\n  opacity: 0;\n  -webkit-transition: opacity 0.1s;\n  transition: opacity 0.1s;\n}\n.dial__lbl--target--half.shown {\n  opacity: 1;\n  -webkit-transition: opacity 0s;\n  transition: opacity 0s;\n}\n.dial__lbl--ambient {\n  font-size: 22px;\n  font-weight: bold;\n}\n.dial__lbl--away {\n  font-size: 72px;\n  font-weight: bold;\n  opacity: 0;\n  pointer-events: none;\n}\n#controls {\n  font-family: Open Sans;\n  background-color: rgba(255, 255, 255, 0.25);\n  padding: 20px;\n  border-radius: 5px;\n  position: absolute;\n  left: 50%;\n  -webkit-transform: translatex(-50%);\n          transform: translatex(-50%);\n  margin-top: 20px;\n}\n#controls label {\n  text-align: left;\n  display: block;\n}\n#controls label span {\n  display: inline-block;\n  width: 200px;\n  text-align: right;\n  font-size: 0.8em;\n  text-transform: uppercase;\n}\n#controls p {\n  margin: 0;\n  margin-bottom: 1em;\n  padding-bottom: 1em;\n  border-bottom: 2px solid #ccc;\n}\n</style>\n<script>\n    var thermostatDial = (function() {\n\t\n\t/*\n\t * Utility functions\n\t */\n\t\n\t// Create an element with proper SVG namespace, optionally setting its attributes and appending it to another element\n\tfunction createSVGElement(tag,attributes,appendTo) {\n\t\tvar element = document.createElementNS('http://www.w3.org/2000/svg',tag);\n\t\tattr(element,attributes);\n\t\tif (appendTo) {\n\t\t\tappendTo.appendChild(element);\n\t\t}\n\t\treturn element;\n\t}\n\t\n\t// Set attributes for an element\n\tfunction attr(element,attrs) {\n\t\tfor (var i in attrs) {\n\t\t\telement.setAttribute(i,attrs[i]);\n\t\t}\n\t}\n\t\n\t// Rotate a cartesian point about given origin by X degrees\n\tfunction rotatePoint(point, angle, origin) {\n\t\tvar radians = angle * Math.PI/180;\n\t\tvar x = point[0]-origin[0];\n\t\tvar y = point[1]-origin[1];\n\t\tvar x1 = x*Math.cos(radians) - y*Math.sin(radians) + origin[0];\n\t\tvar y1 = x*Math.sin(radians) + y*Math.cos(radians) + origin[1];\n\t\treturn [x1,y1];\n\t}\n\t\n\t// Rotate an array of cartesian points about a given origin by X degrees\n\tfunction rotatePoints(points, angle, origin) {\n\t\treturn points.map(function(point) {\n\t\t\treturn rotatePoint(point, angle, origin);\n\t\t});\n\t}\n\t\n\t// Given an array of points, return an SVG path string representing the shape they define\n\tfunction pointsToPath(points) {\n\t\treturn points.map(function(point, iPoint) {\n\t\t\treturn (iPoint>0?'L':'M') + point[0] + ' ' + point[1];\n\t\t}).join(' ')+'Z';\n\t}\n\t\n\tfunction circleToPath(cx, cy, r) {\n\t\treturn [\n\t\t\t\"M\",cx,\",\",cy,\n\t\t\t\"m\",0-r,\",\",0,\n\t\t\t\"a\",r,\",\",r,0,1,\",\",0,r*2,\",\",0,\n\t\t\t\"a\",r,\",\",r,0,1,\",\",0,0-r*2,\",\",0,\n\t\t\t\"z\"\n\t\t].join(' ').replace(/\\s,\\s/g,\",\");\n\t}\n\t\n\tfunction donutPath(cx,cy,rOuter,rInner) {\n\t\treturn circleToPath(cx,cy,rOuter) + \" \" + circleToPath(cx,cy,rInner);\n\t}\n\t\n\t// Restrict a number to a min + max range\n\tfunction restrictToRange(val,min,max) {\n\t\tif (val < min) return min;\n\t\tif (val > max) return max;\n\t\treturn val;\n\t}\n\t\n\t// Round a number to the nearest 0.5\n\tfunction roundHalf(num) {\n\t\treturn Math.round(num*2)/2;\n\t}\n\t\n\tfunction setClass(el, className, state) {\n\t\tel.classList[state ? 'add' : 'remove'](className);\n\t}\n\t\n\t/*\n\t * The \"MEAT\"\n\t */\n\n\treturn function(targetElement, options) {\n\t\tvar self = this;\n\t\t\n\t\t/*\n\t\t * Options\n\t\t */\n\t\toptions = options || {};\n\t\toptions = {\n\t\t\tdiameter: options.diameter || 400,\n\t\t\tminValue: options.minValue || 10, // Minimum value for target temperature\n\t\t\tmaxValue: options.maxValue || 30, // Maximum value for target temperature\n\t\t\tnumTicks: options.numTicks || 200, // Number of tick lines to display around the dial\n\t\t\tonSetTargetTemperature: options.onSetTargetTemperature || function() {}, // Function called when new target temperature set by the dial\n\t\t};\n\t\t\n\t\t/*\n\t\t * Properties - calculated from options in many cases\n\t\t */\n\t\tvar properties = {\n\t\t\ttickDegrees: 300, //  Degrees of the dial that should be covered in tick lines\n\t\t\trangeValue: options.maxValue - options.minValue,\n\t\t\tradius: options.diameter/2,\n\t\t\tticksOuterRadius: options.diameter / 30,\n\t\t\tticksInnerRadius: options.diameter / 8,\n\t\t\thvac_states: ['off', 'heating', 'cooling'],\n\t\t\tdragLockAxisDistance: 15,\n\t\t}\n\t\tproperties.lblAmbientPosition = [properties.radius, properties.ticksOuterRadius-(properties.ticksOuterRadius-properties.ticksInnerRadius)/2]\n\t\tproperties.offsetDegrees = 180-(360-properties.tickDegrees)/2;\n\t\t\n\t\t/*\n\t\t * Object state\n\t\t */\n\t\tvar state = {\n\t\t\ttarget_temperature: options.minValue,\n\t\t\tambient_temperature: options.minValue,\n\t\t\thvac_state: properties.hvac_states[0],\n\t\t\thas_leaf: false,\n\t\t\taway: false\n\t\t};\n\t\t\n\t\t/*\n\t\t * Property getter / setters\n\t\t */\n\t\tObject.defineProperty(this,'target_temperature',{\n\t\t\tget: function() {\n\t\t\t\treturn state.target_temperature;\n\t\t\t},\n\t\t\tset: function(val) {\n\t\t\t\tstate.target_temperature = restrictTargetTemperature(+val);\n\t\t\t\trender();\n\t\t\t}\n\t\t});\n\t\tObject.defineProperty(this,'ambient_temperature',{\n\t\t\tget: function() {\n\t\t\t\treturn state.ambient_temperature;\n\t\t\t},\n\t\t\tset: function(val) {\n\t\t\t\tstate.ambient_temperature = roundHalf(+val);\n\t\t\t\trender();\n\t\t\t}\n\t\t});\n\t\tObject.defineProperty(this,'hvac_state',{\n\t\t\tget: function() {\n\t\t\t\treturn state.hvac_state;\n\t\t\t},\n\t\t\tset: function(val) {\n\t\t\t\tif (properties.hvac_states.indexOf(val)>=0) {\n\t\t\t\t\tstate.hvac_state = val;\n\t\t\t\t\trender();\n\t\t\t\t}\n\t\t\t}\n\t\t});\n\t\tObject.defineProperty(this,'has_leaf',{\n\t\t\tget: function() {\n\t\t\t\treturn state.has_leaf;\n\t\t\t},\n\t\t\tset: function(val) {\n\t\t\t\tstate.has_leaf = !!val;\n\t\t\t\trender();\n\t\t\t}\n\t\t});\n\t\tObject.defineProperty(this,'away',{\n\t\t\tget: function() {\n\t\t\t\treturn state.away;\n\t\t\t},\n\t\t\tset: function(val) {\n\t\t\t\tstate.away = !!val;\n\t\t\t\trender();\n\t\t\t}\n\t\t});\n\t\t\n\t\t/*\n\t\t * SVG\n\t\t */\n\t\tvar svg = createSVGElement('svg',{\n\t\t\twidth: '100%', //options.diameter+'px',\n\t\t\theight: '100%', //options.diameter+'px',\n\t\t\tviewBox: '0 0 '+options.diameter+' '+options.diameter,\n\t\t\tclass: 'dial'\n\t\t},targetElement);\n\t\t// CIRCULAR DIAL\n\t\tvar circle = createSVGElement('circle',{\n\t\t\tcx: properties.radius,\n\t\t\tcy: properties.radius,\n\t\t\tr: properties.radius,\n\t\t\tclass: 'dial__shape'\n\t\t},svg);\n\t\t// EDITABLE INDICATOR\n\t\tvar editCircle = createSVGElement('path',{\n\t\t\td: donutPath(properties.radius,properties.radius,properties.radius-4,properties.radius-8),\n\t\t\tclass: 'dial__editableIndicator',\n\t\t},svg);\n\t\t\n\t\t/*\n\t\t * Ticks\n\t\t */\n\t\tvar ticks = createSVGElement('g',{\n\t\t\tclass: 'dial__ticks'\t\n\t\t},svg);\n\t\tvar tickPoints = [\n\t\t\t[properties.radius-1, properties.ticksOuterRadius],\n\t\t\t[properties.radius+1, properties.ticksOuterRadius],\n\t\t\t[properties.radius+1, properties.ticksInnerRadius],\n\t\t\t[properties.radius-1, properties.ticksInnerRadius]\n\t\t];\n\t\tvar tickPointsLarge = [\n\t\t\t[properties.radius-1.5, properties.ticksOuterRadius],\n\t\t\t[properties.radius+1.5, properties.ticksOuterRadius],\n\t\t\t[properties.radius+1.5, properties.ticksInnerRadius+20],\n\t\t\t[properties.radius-1.5, properties.ticksInnerRadius+20]\n\t\t];\n\t\tvar theta = properties.tickDegrees/options.numTicks;\n\t\tvar tickArray = [];\n\t\tfor (var iTick=0; iTick<options.numTicks; iTick++) {\n\t\t\ttickArray.push(createSVGElement('path',{d:pointsToPath(tickPoints)},ticks));\n\t\t};\n\t\t\n\t\t/*\n\t\t * Labels\n\t\t */\n\t\tvar lblTarget = createSVGElement('text',{\n\t\t\tx: properties.radius,\n\t\t\ty: properties.radius,\n\t\t\tclass: 'dial__lbl dial__lbl--target'\n\t\t},svg);\n\t\tvar lblTarget_text = document.createTextNode('');\n\t\tlblTarget.appendChild(lblTarget_text);\n\t\t//\n\t\tvar lblTargetHalf = createSVGElement('text',{\n\t\t\tx: properties.radius + properties.radius/2.5,\n\t\t\ty: properties.radius - properties.radius/8,\n\t\t\tclass: 'dial__lbl dial__lbl--target--half'\n\t\t},svg);\n\t\tvar lblTargetHalf_text = document.createTextNode('5');\n\t\tlblTargetHalf.appendChild(lblTargetHalf_text);\n\t\t//\n\t\tvar lblAmbient = createSVGElement('text',{\n\t\t\tclass: 'dial__lbl dial__lbl--ambient'\n\t\t},svg);\n\t\tvar lblAmbient_text = document.createTextNode('');\n\t\tlblAmbient.appendChild(lblAmbient_text);\n\t\t//\n\t\tvar lblAway = createSVGElement('text',{\n\t\t\tx: properties.radius,\n\t\t\ty: properties.radius,\n\t\t\tclass: 'dial__lbl dial__lbl--away'\n\t\t},svg);\n\t\tvar lblAway_text = document.createTextNode('AWAY');\n\t\tlblAway.appendChild(lblAway_text);\n\t\t//\n\t\tvar icoLeaf = createSVGElement('path',{\n\t\t\tclass: 'dial__ico__leaf'\n\t\t},svg);\n\t\t\n\t\t/*\n\t\t * LEAF\n\t\t */\n\t\tvar leafScale = properties.radius/5/100;\n\t\tvar leafDef = [\"M\", 3, 84, \"c\", 24, 17, 51, 18, 73, -6, \"C\", 100, 52, 100, 22, 100, 4, \"c\", -13, 15, -37, 9, -70, 19, \"C\", 4, 32, 0, 63, 0, 76, \"c\", 6, -7, 18, -17, 33, -23, 24, -9, 34, -9, 48, -20, -9, 10, -20, 16, -43, 24, \"C\", 22, 63, 8, 78, 3, 84, \"z\"].map(function(x) {\n\t\t\treturn isNaN(x) ? x : x*leafScale;\n\t\t}).join(' ');\n\t\tvar translate = [properties.radius-(leafScale*100*0.5),properties.radius*1.5]\n\t\tvar icoLeaf = createSVGElement('path',{\n\t\t\tclass: 'dial__ico__leaf',\n\t\t\td: leafDef,\n\t\t\ttransform: 'translate('+translate[0]+','+translate[1]+')'\n\t\t},svg);\n\t\t\t\n\t\t/*\n\t\t * RENDER\n\t\t */\n\t\tfunction render() {\n\t\t\trenderAway();\n\t\t\trenderHvacState();\n\t\t\trenderTicks();\n\t\t\trenderTargetTemperature();\n\t\t\trenderAmbientTemperature();\n\t\t\trenderLeaf();\n\t\t}\n\t\trender();\n\n\t\t/*\n\t\t * RENDER - ticks\n\t\t */\n\t\tfunction renderTicks() {\n\t\t\tvar vMin, vMax;\n\t\t\tif (self.away) {\n\t\t\t\tvMin = self.ambient_temperature;\n\t\t\t\tvMax = vMin;\n\t\t\t} else {\n\t\t\t\tvMin = Math.min(self.ambient_temperature, self.target_temperature);\n\t\t\t\tvMax = Math.max(self.ambient_temperature, self.target_temperature);\n\t\t\t}\n\t\t\tvar min = restrictToRange(Math.round((vMin-options.minValue)/properties.rangeValue * options.numTicks),0,options.numTicks-1);\n\t\t\tvar max = restrictToRange(Math.round((vMax-options.minValue)/properties.rangeValue * options.numTicks),0,options.numTicks-1);\n\t\t\t//\n\t\t\ttickArray.forEach(function(tick,iTick) {\n\t\t\t\tvar isLarge = iTick==min || iTick==max;\n\t\t\t\tvar isActive = iTick >= min && iTick <= max;\n\t\t\t\tattr(tick,{\n\t\t\t\t\td: pointsToPath(rotatePoints(isLarge ? tickPointsLarge: tickPoints,iTick*theta-properties.offsetDegrees,[properties.radius, properties.radius])),\n\t\t\t\t\tclass: isActive ? 'active' : ''\n\t\t\t\t});\n\t\t\t});\n\t\t}\n\t\n\t\t/*\n\t\t * RENDER - ambient temperature\n\t\t */\n\t\tfunction renderAmbientTemperature() {\n\t\t\tlblAmbient_text.nodeValue = Math.floor(self.ambient_temperature);\n\t\t\tif (self.ambient_temperature%1!=0) {\n\t\t\t\tlblAmbient_text.nodeValue += '⁵';\n\t\t\t}\n\t\t\tvar peggedValue = restrictToRange(self.ambient_temperature, options.minValue, options.maxValue);\n\t\t\tdegs = properties.tickDegrees * (peggedValue-options.minValue)/properties.rangeValue - properties.offsetDegrees;\n\t\t\tif (peggedValue > self.target_temperature) {\n\t\t\t\tdegs += 8;\n\t\t\t} else {\n\t\t\t\tdegs -= 8;\n\t\t\t}\n\t\t\tvar pos = rotatePoint(properties.lblAmbientPosition,degs,[properties.radius, properties.radius]);\n\t\t\tattr(lblAmbient,{\n\t\t\t\tx: pos[0],\n\t\t\t\ty: pos[1]\n\t\t\t});\n\t\t}\n\n\t\t/*\n\t\t * RENDER - target temperature\n\t\t */\n\t\tfunction renderTargetTemperature() {\n\t\t\tlblTarget_text.nodeValue = Math.floor(self.target_temperature);\n\t\t\tsetClass(lblTargetHalf,'shown',self.target_temperature%1!=0);\n\t\t}\n\t\t\n\t\t/*\n\t\t * RENDER - leaf\n\t\t */\n\t\tfunction renderLeaf() {\n\t\t\tsetClass(svg,'has-leaf',self.has_leaf);\n\t\t}\n\t\t\n\t\t/*\n\t\t * RENDER - HVAC state\n\t\t */\n\t\tfunction renderHvacState() {\n\t\t\tArray.prototype.slice.call(svg.classList).forEach(function(c) {\n\t\t\t\tif (c.match(/^dial--state--/)) {\n\t\t\t\t\tsvg.classList.remove(c);\n\t\t\t\t};\n\t\t\t});\n\t\t\tsvg.classList.add('dial--state--'+self.hvac_state);\n\t\t}\n\t\t\n\t\t/*\n\t\t * RENDER - away\n\t\t */\n\t\tfunction renderAway() {\n\t\t\tsvg.classList[self.away ? 'add' : 'remove']('away');\n\t\t}\n\t\t\n\t\t/*\n\t\t * Drag to control\n\t\t */\n\t\tvar _drag = {\n\t\t\tinProgress: false,\n\t\t\tstartPoint: null,\n\t\t\tstartTemperature: 0,\n\t\t\tlockAxis: undefined\n\t\t};\n\t\t\n\t\tfunction eventPosition(ev) {\n\t\t\tif (ev.targetTouches && ev.targetTouches.length) {\n\t\t\t\treturn  [ev.targetTouches[0].clientX, ev.targetTouches[0].clientY];\n\t\t\t} else {\n\t\t\t\treturn [ev.x, ev.y];\n\t\t\t};\n\t\t}\n\t\t\n\t\tvar startDelay;\n\t\tfunction dragStart(ev) {\n\t\t\tstartDelay = setTimeout(function() {\n\t\t\t\tsetClass(svg, 'dial--edit', true);\n\t\t\t\t_drag.inProgress = true;\n\t\t\t\t_drag.startPoint = eventPosition(ev);\n\t\t\t\t_drag.startTemperature = self.target_temperature || options.minValue;\n\t\t\t\t_drag.lockAxis = undefined;\n\t\t\t},1000);\n\t\t};\n\t\t\n\t\tfunction dragEnd (ev) {\n\t\t\tclearTimeout(startDelay);\n\t\t\tsetClass(svg, 'dial--edit', false);\n\t\t\tif (!_drag.inProgress) return;\n\t\t\t_drag.inProgress = false;\n\t\t\tif (self.target_temperature != _drag.startTemperature) {\n\t\t\t\tif (typeof options.onSetTargetTemperature == 'function') {\n\t\t\t\t\toptions.onSetTargetTemperature(self.target_temperature);\n\t\t\t\t};\n\t\t\t};\n\t\t};\n\t\t\n\t\tfunction dragMove(ev) {\n\t\t\tev.preventDefault();\n\t\t\tif (!_drag.inProgress) return;\n\t\t\tvar evPos =  eventPosition(ev);\n\t\t\tvar dy = _drag.startPoint[1]-evPos[1];\n\t\t\tvar dx = evPos[0] - _drag.startPoint[0];\n\t\t\tvar dxy;\n\t\t\tif (_drag.lockAxis == 'x') {\n\t\t\t\tdxy  = dx;\n\t\t\t} else if (_drag.lockAxis == 'y') {\n\t\t\t\tdxy = dy;\n\t\t\t} else if (Math.abs(dy) > properties.dragLockAxisDistance) {\n\t\t\t\t_drag.lockAxis = 'y';\n\t\t\t\tdxy = dy;\n\t\t\t} else if (Math.abs(dx) > properties.dragLockAxisDistance) {\n\t\t\t\t_drag.lockAxis = 'x';\n\t\t\t\tdxy = dx;\n\t\t\t} else {\n\t\t\t\tdxy = (Math.abs(dy) > Math.abs(dx)) ? dy : dx;\n\t\t\t};\n\t\t\tvar dValue = (dxy*getSizeRatio())/(options.diameter)*properties.rangeValue;\n\t\t\tself.target_temperature = roundHalf(_drag.startTemperature+dValue);\n\t\t}\n\t\t\n\t\tsvg.addEventListener('mousedown',dragStart);\n\t\tsvg.addEventListener('touchstart',dragStart);\n\t\t\n\t\tsvg.addEventListener('mouseup',dragEnd);\n\t\tsvg.addEventListener('mouseleave',dragEnd);\n\t\tsvg.addEventListener('touchend',dragEnd);\n\t\t\n\t\tsvg.addEventListener('mousemove',dragMove);\n\t\tsvg.addEventListener('touchmove',dragMove);\n\t\t//\n\t\t\n\t\t/*\n\t\t * Helper functions\n\t\t */\n\t\tfunction restrictTargetTemperature(t) {\n\t\t\treturn restrictToRange(roundHalf(t),options.minValue,options.maxValue);\n\t\t}\n\t\t\n\t\tfunction angle(point) {\n\t\t\tvar dx = point[0] - properties.radius;\n\t\t\tvar dy = point[1] - properties.radius;\n\t\t\tvar theta = Math.atan(dx/dy) / (Math.PI/180);\n\t\t\tif (point[0]>=properties.radius && point[1] < properties.radius) {\n\t\t\t\ttheta = 90-theta - 90;\n\t\t\t} else if (point[0]>=properties.radius && point[1] >= properties.radius) {\n\t\t\t\ttheta = 90-theta + 90;\n\t\t\t} else if (point[0]<properties.radius && point[1] >= properties.radius) {\n\t\t\t\ttheta = 90-theta + 90;\n\t\t\t} else if (point[0]<properties.radius && point[1] < properties.radius) {\n\t\t\t\ttheta = 90-theta+270;\n\t\t\t}\n\t\t\treturn theta;\n\t\t};\n\t\t\n\t\tfunction getSizeRatio() {\n\t\t\treturn options.diameter / targetElement.clientWidth;\n\t\t}\n\t\t\n\t};\n})();\n\n/* ==== */\n(function(scope) {\n    \n    var nest = new thermostatDial(document.getElementById('thermostat'),{\n    \tonSetTargetTemperature: function(v) {\n    \t\tscope.send({topic: \"target_temperature\", payload: v});\n    \t}\n    });\n\n\n    scope.$watch('msg', function(data) {\n        //console.log(data.topic+\"  \"+data.payload);\n        if (data.topic == \"ambient_temperature\") {\n            nest.ambient_temperature = data.payload;\n        } if (data.topic == \"target_temperature\") {\n            nest.target_temperature = data.payload;\n        } if (data.topic == \"hvac_state\") {\n            nest.hvac_state = data.payload;\n        } if (data.topic == \"has_leaf\") {\n            nest.has_leaf = data.payload;\n        } if (data.topic == \"away\") {\n            nest.away = data.payload;\n        }\n    });\n})(scope);\n\n</script>","storeOutMessages":true,"fwdInMessages":false,"templateScope":"local","x":710,"y":300,"wires":[["602fed2a.3f1da4"]]},{"id":"121fdb16.06c045","type":"function","z":"2c174318.8fafec","name":"ambient_temperature","func":"flow.set(\"ambient_temp_topic\", msg.topic); \nflow.set(\"ambient_temp_payload\", msg.payload); \nmsg.topic = \"ambient_temperature\";\n\nreturn msg;","outputs":1,"noerr":0,"x":500,"y":300,"wires":[["85e9547.e3387a8"]]},{"id":"cde4777b.ed2518","type":"function","z":"2c174318.8fafec","name":"target_temperature","func":"flow.set(\"target_temp_topic\", msg.topic); \nflow.set(\"target_temp_payload\", msg.payload); \nmsg.topic = \"target_temperature\";\nreturn msg;","outputs":1,"noerr":0,"x":490,"y":340,"wires":[["85e9547.e3387a8"]]},{"id":"919595ac.732ca8","type":"function","z":"2c174318.8fafec","name":"hvac_state","func":"msg.topic = \"hvac_state\";\nreturn msg;","outputs":1,"noerr":0,"x":450,"y":400,"wires":[["85e9547.e3387a8"]]},{"id":"2fb888e5.40db78","type":"function","z":"2c174318.8fafec","name":"has_leaf","func":"msg.topic = \"has_leaf\";\nreturn msg;","outputs":1,"noerr":0,"x":440,"y":440,"wires":[["85e9547.e3387a8"]]},{"id":"d5e8585d.3e0038","type":"function","z":"2c174318.8fafec","name":"away","func":"msg.topic = \"away\";\nreturn msg;","outputs":1,"noerr":0,"x":430,"y":480,"wires":[["85e9547.e3387a8"]]},{"id":"595d8c0.c132074","type":"inject","z":"2c174318.8fafec","name":"","topic":"has_leaf","payload":"true","payloadType":"bool","repeat":"","crontab":"","once":true,"x":170,"y":520,"wires":[["2fb888e5.40db78"]]},{"id":"b99a72e7.57614","type":"inject","z":"2c174318.8fafec","name":"","topic":"has_leaf","payload":"false","payloadType":"bool","repeat":"","crontab":"","once":false,"x":170,"y":560,"wires":[["2fb888e5.40db78"]]},{"id":"62f528cc.4f7cd8","type":"inject","z":"2c174318.8fafec","name":"","topic":"away","payload":"true","payloadType":"bool","repeat":"","crontab":"","once":false,"x":160,"y":600,"wires":[["d5e8585d.3e0038"]]},{"id":"4105734f.e0fb3c","type":"inject","z":"2c174318.8fafec","name":"","topic":"away","payload":"false","payloadType":"bool","repeat":"","crontab":"","once":false,"x":160,"y":640,"wires":[["d5e8585d.3e0038"]]},{"id":"52530d07.ac8b64","type":"inject","z":"2c174318.8fafec","name":"","topic":"hvac_state","payload":"off","payloadType":"str","repeat":"","crontab":"","once":false,"x":170,"y":400,"wires":[["919595ac.732ca8"]]},{"id":"1fa4d3.85fffb2d","type":"inject","z":"2c174318.8fafec","name":"","topic":"hvac_state","payload":"heating","payloadType":"str","repeat":"","crontab":"","once":false,"x":190,"y":440,"wires":[["919595ac.732ca8"]]},{"id":"9cd66a26.829668","type":"inject","z":"2c174318.8fafec","name":"","topic":"hvac_state","payload":"cooling","payloadType":"str","repeat":"","crontab":"","once":false,"x":190,"y":480,"wires":[["919595ac.732ca8"]]},{"id":"fc61a2c4.d585d","type":"inject","z":"2c174318.8fafec","name":"","topic":"ambient_temperature","payload":"21.5","payloadType":"num","repeat":"","crontab":"","once":true,"x":230,"y":300,"wires":[["121fdb16.06c045"]]},{"id":"f38cf4a3.ae78a8","type":"inject","z":"2c174318.8fafec","name":"","topic":"target_temperature","payload":"20","payloadType":"num","repeat":"","crontab":"","once":true,"x":220,"y":340,"wires":[["cde4777b.ed2518"]]},{"id":"602fed2a.3f1da4","type":"function","z":"2c174318.8fafec","name":"target_temperature","func":"if (msg.topic == \"target_temperature\") {\n    flow.set(\"target_temp_topic\", msg.topic); \n    flow.set(\"target_temp_payload\", msg.payload); \n    return msg;\n}","outputs":1,"noerr":0,"x":890,"y":300,"wires":[["89041053.48c4f"]]},{"id":"89041053.48c4f","type":"debug","z":"2c174318.8fafec","name":"","active":true,"console":"false","complete":"false","x":1090,"y":300,"wires":[]},{"id":"e9312d8.4023cd","type":"ui_text","z":"2c174318.8fafec","group":"8db78a1d.f45e08","order":0,"width":0,"height":0,"name":"","label":"AmbientTemp","format":"{{msg.payload}}","layout":"row-spread","x":780,"y":60,"wires":[]},{"id":"18a62e57.881fe2","type":"function","z":"2c174318.8fafec","name":"Ambient_Temp im Flow Speichern","func":"var t_topic = flow.get(\"ambient_temp_topic\");\nvar t_payload = flow.get(\"ambient_temp_payload\");\nmsg.payload = t_payload;\nmsg.topic = t_topic;\n\nreturn msg;\n\n\n","outputs":1,"noerr":0,"x":480,"y":100,"wires":[["e9312d8.4023cd","dd132023.ada06","85e9547.e3387a8"]]},{"id":"dd132023.ada06","type":"debug","z":"2c174318.8fafec","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload","x":770,"y":120,"wires":[]},{"id":"88ebaa81.cc5e78","type":"ui_ui_control","z":"2c174318.8fafec","name":"","x":210,"y":100,"wires":[["18a62e57.881fe2"]]},{"id":"6328dc39.79dae4","type":"ui_text","z":"2c174318.8fafec","group":"8db78a1d.f45e08","order":0,"width":0,"height":0,"name":"","label":"TargetTemp","format":"{{msg.payload}}","layout":"row-spread","x":790,"y":680,"wires":[]},{"id":"7dd3f4ef.cfe85c","type":"function","z":"2c174318.8fafec","name":"Target_Temp im Flow speichern","func":"var t_topic = flow.get(\"target_temp_topic\");\nvar t_payload = flow.get(\"target_temp_payload\");\nmsg.payload = t_payload;\nmsg.topic = t_topic;\n\nreturn msg;\n\n\n","outputs":1,"noerr":0,"x":510,"y":720,"wires":[["6328dc39.79dae4","ab8ff2e2.a6156","85e9547.e3387a8"]]},{"id":"ab8ff2e2.a6156","type":"debug","z":"2c174318.8fafec","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload","x":790,"y":740,"wires":[]},{"id":"712ded86.c2fbe4","type":"ui_ui_control","z":"2c174318.8fafec","name":"","x":230,"y":720,"wires":[["7dd3f4ef.cfe85c"]]},{"id":"8db78a1d.f45e08","type":"ui_group","z":"","name":"Default","tab":"135cb0cd.dc652f","disp":true,"width":"6","collapse":false},{"id":"135cb0cd.dc652f","type":"ui_tab","z":"","name":"Test","icon":"build"}]
Titel: Antw:Node-Red als Frontend
Beitrag von: Master_Nick am 06 März 2018, 12:03:17
Na, ob wir bei 4613 Aufrufe und 50 Antworten bald eine eigene Node-RED Sektion bekommen ;-D ?
Wobei es einfach zu wenig - und das ist ja sehr schön - Probleme gibt mit Node-RED *hehe*

Ich bin auch super zufrieden eben genau die Punkte Geschwindigkeit und Träge Reaktion haben mich von den anderen weg zu Node-RED geführt.
Auch das effektiv soviele Instanzen wie man will mithören könnten ohne eine Trägheit hinein zu bringen ist was feines.
Titel: Antw:Node-Red als Frontend
Beitrag von: Ranseyer am 08 März 2018, 17:00:40
Das Thema finde ich super spannend. Aus Zeitgründungen werde ich mich vermutlich erst im Winter dranhängen. Aber ich lese mal mit.

PS: Ich habe außer NodeRed eigentlich schon alles angetestet. Im Moment nutze ich FTUI. Daran stört mich der für mich sehr hohe Pflegeaufwand und die extreme Trägheit auf alten Tablets. Allerdingt gibt es halt schon sehr viele fertige Zutaten.

IOBroker wäre für mich von der Idee genau richtig, ist mir aber zu unzuverlässig beim Konfigurieren und ich vertraue dem nicht genügend. Außerdem frisst es auch mehr Zeit als man annehmen könnte.
Titel: Antw:Node-Red als Frontend
Beitrag von: Master_Nick am 08 März 2018, 17:30:22
Du wirst dich wundern wie schnell du in Node-RED auf brauchbare Ergebnisse kommst.  :D ;D
Titel: Antw:Node-Red als Frontend
Beitrag von: Shojo am 09 März 2018, 11:16:42
Zitat von: Ranseyer am 08 März 2018, 17:00:40
Im Moment nutze ich FTUI. Daran stört mich der für mich sehr hohe Pflegeaufwand und die extreme Trägheit auf alten Tablets. Allerdingt gibt es halt schon sehr viele fertige Zutaten.

IOBroker wäre für mich von der Idee genau richtig, ist mir aber zu unzuverlässig beim Konfigurieren und ich vertraue dem nicht genügend


Das unterschreibe ich so, das sind auch genau meine Erfahrungen.
Titel: Antw:Node-Red als Frontend
Beitrag von: hermann1514 am 12 März 2018, 10:06:58
Hallo zusammen,

ich habe nun auch mein TabletUI zur Seite gelegt und bin mit NodeRed angefangen. Wenn man sich mal ein wenig eingearbeitet hat ist das aus Supi und läuft wie S.. :-)

Nun, ich habe meine historischen Daten wie Temperatur, ValvePostion usw. in eine INFLUXDB liegen und habe die Graphen mit Grafana erstellt. Ich möchte nun aber mit den INFLUXDB Plugin für NodeRed die Grapgen direkt in NODERED erstellen.
Habe schon einiges probiert - aber dammit bin ich noch nicht weitergekommen.

Kann mir jemand helfen?

Danke.
Gruß
Hermann
Titel: Antw:Node-Red als Frontend
Beitrag von: Master_Nick am 12 März 2018, 11:28:46
Moin  ;)

Schön wie das Thema immer mehr Zuwachs erfährt!  8)

Ich persönlich habe noch keine Graphen in Node-RED realisiert, auch der geplante Umstieg auf eine Log-DB steht noch weiter aus  ::). Ich habe aber wohl eine Seite mit 5 Thermostaten und 6 Hygrometern/Thermometern. Diese Seite hat beim initialen Laden schon eine gewisse Ladezeit (auf S7 edge, Raspi 3 und Windows PC) durch das Abfragen/Verarbeiten der verschiedenen Werte und Anzeigen, die anschließende dauerhafte Live-Anzeige ist verlustfrei.

Daher habe ich für mich entschieden nur aktuelle Werte auf Node-ERED anzeigen zu lassen, da es mir hauptsächlich um eine komfortable übersichtliche Steuerung des Systems geht. Graphen kann ich in der FHEM Web-Instanz einsehen oder wie ich finde noch komfortabler über andFHEM am Handy.
Titel: Antw:Node-Red als Frontend
Beitrag von: Shojo am 12 März 2018, 11:35:34
Zitat von: Master_Nick am 12 März 2018, 11:28:46Diese Seite hat beim initialen Laden schon eine gewisse Ladezeit (auf S7 edge, Raspi 3 und Windows PC) durch das Abfragen/Verarbeiten der verschiedenen Werte und Anzeigen, die anschließende dauerhafte Live-Anzeige ist verlustfrei.

Das hat damit zu tun das Node-Red diese Daten als Json ab Webbrowser gibt und der dann lange rechnet. (da es zum teil doch schon sehr viele Messungen sind die da zusammen kommen)
Wenn man allerdings z.B. vorberechnete 15min, 30min oder Stunden Durchschnittswerte übergibt bleibt es schnell ;)

Titel: Antw:Node-Red als Frontend
Beitrag von: Master_Nick am 12 März 2018, 12:38:55
Mhh - ich glaube das hilft dann nur bei Graphen.

Bei Raumtemperatursteuerung und aktueller Temperatur helfen Durchschnittswerte aus meiner Sicht weniger.  ;D
Titel: Antw:Node-Red als Frontend
Beitrag von: Shojo am 12 März 2018, 13:04:48
Achsooo, bei dir ist das auch langsam ohne Graphen?

Das kann ich dann aber so nicht bestätigen, ich habe 14 auf einer Seite und die kommt so  schnell wie die anderen.
Titel: Antw:Node-Red als Frontend
Beitrag von: Master_Nick am 12 März 2018, 13:13:22
Langsam will ich nicht sagen, es hat 2 Sekunden Ladezeit, bis die Anzeige der Seite steht. Die Seiten die nur Switches haben gehen deutlich fixer. Es ist jammern auf hohem Niveau :-D und geht echt nur um die Ladezeit.

Gehostet habe ich Node-RED auf einem Pi3 und die Anzeige erfolgt auf einem anderen Pi3, PC oder Handy.
Titel: Antw:Node-Red als Frontend
Beitrag von: Shojo am 12 März 2018, 13:18:08
Ok, ich habe da einen Odroid C2 mit NandFlash hinter der ist da einiges Performanter.
Titel: Antw:Node-Red als Frontend
Beitrag von: Master_Nick am 12 März 2018, 13:22:01
 ;D Dann liegt es evtl daran.  8)
Titel: Antw:Node-Red als Frontend
Beitrag von: Shojo am 07 April 2018, 13:05:33
Hier noch mal ein Ansatz von mir.

Jeder Schalter oder Regler bekommt von mir als Topic das Fhem Device mitgegeben.
Wenn ich aber was anderes wie den State schalten möchte z.B. den HSV Wert sieht das so aus WZ.Licht.RGB.Fensterfront hvs

Hier noch die kleine Logik des Fhem Messages bauen:

var temp = msg.payload;
if (typeof temp == "string" && temp.startsWith("hsv"))
{
    temp = temp.replace(/ /g,"");
    temp = temp.replace(/%/g,"");
    temp = temp.replace(/hsv\(/g,"");
    temp = temp.replace(/\)/g,"");
    var x = temp.split(",");
    temp =  x[0] + "," + x[1] + "," + x[2];
}

msg.payload = "set " +  msg.topic + " " + temp;
return msg;


Und das Fhem Device:

defmod MQTT.NodeRed.Input MQTT_DEVICE
attr MQTT.NodeRed.Input IODev MQTTBroker
attr MQTT.NodeRed.Input stateFormat transmission-state
attr MQTT.NodeRed.Input subscribeReading_cmd {fhem("$message")} NodeRed/ToFHEM/cmd qos:1
Titel: Antw:Node-Red als Frontend
Beitrag von: Master_Nick am 12 April 2018, 23:12:42
Ich habe meine gesamte Thermostatgeschichte nun auf Nest Optik gezogen die Benutzbarkeit ist hier viel besser als bei einem Schieberegler.  8) ;D

Finde es richtig geil!
Habe allerdings mit den hier gelieferten Dingen nur teils was arbeiten können - die hatten beim Reload immer noch Probleme bei mir :-)
Habe das Ganze beim Ursprung des Nest Thermostats für NodeRed dann gefunden und mit Ideen von dort gelöst... krieg es schon kaum noch auf die Kette was wie wo... viel Arbeit/Lesen gewesen.
Habe dann noch einige Icons eingebunden und bin sehr zufrieden.

Auch wird hier die Temperatur verglichen und entschieden ob geheizt wird (rotes Nest Thermostat). Und das Blatt kommt bei gewissen Zieltemperaturen die man als "sparend" betrachten könnte (kann man alles selber anpassen). Ansonsten habe ich meinen VOC Sensor im Wohnzimmer noch mit eingebunden ohne Werte sondern mittels 3er Symbole: Daumen hoch grün, Daumen hoch gelb und Daumen runter rot. Auch Window Open wird angezeigt wenn der Wert auf 1 steht - gelber Text "Window" erscheint dann.

Bei mehr als einem Thermostat ist es wichtig das DIV umzubenennen und Im weiteren Code den Namen der vorher ganz oben im DIV Stand erneut zu ändern.
Ich erweitere noch um ein eingeblendetes Fenstersymbol.

Code für NodeRed für das Wohnzimmer Thermostat:
[{"id":"53e6b42a.aabbe4","type":"function","z":"15bc8d4b.90af3b","name":"Konvertieren der Temperatur","func":"msg.payload = parseFloat(msg.payload);\nmsg.topic = 'sensor_temperature';\nnode.status({text:msg.payload + \"°C\"});\nreturn msg;","outputs":1,"noerr":0,"x":810,"y":300,"wires":[["730bb5a.9f6bfcc","c0dc329a.4f59b"]]},{"id":"730bb5a.9f6bfcc","type":"debug","z":"15bc8d4b.90af3b","name":"Temp Sensor","active":false,"console":"false","complete":"payload","x":1130,"y":300,"wires":[]},{"id":"63e04c4d.bb5f6c","type":"ui_template","z":"15bc8d4b.90af3b","group":"d2bea201.d68888","name":"Nest","order":5,"width":"6","height":"6","format":"<div id=\"thermostat-WZ\"></div>\n\n<style>\n@import url(http://fonts.googleapis.com/css?family=Open+Sans:300);\n#thermostat {\n  margin: 0 auto;\n  -webkit-tap-highlight-color: rgba(0, 0, 0, 0);\n}\n.dial {\n  -webkit-user-select: none;\n     -moz-user-select: none;\n      -ms-user-select: none;\n          user-select: none;\n}\n.dial.away .dial__ico__leaf {\n  visibility: hidden;\n}\n.dial.away .dial__lbl--target {\n  visibility: hidden;\n}\n.dial.away .dial__lbl--target--half {\n  visibility: hidden;\n}\n.dial.away .dial__lbl--away {\n  opacity: 1;\n}\n.dial .dial__shape {\n  -webkit-transition: fill 0.5s;\n  transition: fill 0.5s;\n}\n.dial__ico__leaf {\n  fill: #13EB13;\n  opacity: 0;\n  -webkit-transition: opacity 0.5s;\n  transition: opacity 0.5s;\n  pointer-events: none;\n}\n.dial.has-leaf .dial__ico__leaf {\n  display: block;\n  opacity: 1;\n  pointer-events: initial;\n}\n.dial__editableIndicator {\n  fill: white;\n  fill-rule: evenodd;\n  opacity: 0;\n  -webkit-transition: opacity 0.5s;\n  transition: opacity 0.5s;\n}\n.dial--edit .dial__editableIndicator {\n  opacity: 1;\n}\n.dial--state--off .dial__shape {\n  fill: #3d3c3c;\n}\n.dial--state--heating .dial__shape {\n  fill: #E36304;\n}\n.dial--state--cooling .dial__shape {\n  fill: #007AF1;\n}\n.dial__ticks path {\n  fill: rgba(255, 255, 255, 0.3);\n}\n.dial__ticks path.active {\n  fill: rgba(255, 255, 255, 0.8);\n}\n.dial text {\n  fill: white;\n  text-anchor: middle;\n  font-family: Helvetica, sans-serif;\n  alignment-baseline: central;\n}\n.dial__lbl--target {\n  font-size: 120px;\n  font-weight: bold;\n}\n.dial__lbl--target--half {\n  font-size: 40px;\n  font-weight: bold;\n  opacity: 0;\n  -webkit-transition: opacity 0.1s;\n  transition: opacity 0.1s;\n}\n.dial__lbl--target--half.shown {\n  opacity: 1;\n  -webkit-transition: opacity 0s;\n  transition: opacity 0s;\n}\n.dial__lbl--ambient {\n  font-size: 22px;\n  font-weight: bold;\n}\n.dial__lbl--away {\n  font-size: 72px;\n  font-weight: bold;\n  opacity: 0;\n  pointer-events: none;\n}\n#controls {\n  font-family: Open Sans;\n  background-color: rgba(255, 255, 255, 0.25);\n  padding: 20px;\n  border-radius: 5px;\n  position: absolute;\n  left: 50%;\n  -webkit-transform: translatex(-50%);\n          transform: translatex(-50%);\n  margin-top: 20px;\n}\n#controls label {\n  text-align: left;\n  display: block;\n}\n#controls label span {\n  display: inline-block;\n  width: 200px;\n  text-align: right;\n  font-size: 0.8em;\n  text-transform: uppercase;\n}\n#controls p {\n  margin: 0;\n  margin-bottom: 1em;\n  padding-bottom: 1em;\n  border-bottom: 2px solid #ccc;\n}\n</style>\n<script>\n    var thermostatDial = (function() {\n\t\n\t/*\n\t * Utility functions\n\t */\n\t\n\t// Create an element with proper SVG namespace, optionally setting its attributes and appending it to another element\n\tfunction createSVGElement(tag,attributes,appendTo) {\n\t\tvar element = document.createElementNS('http://www.w3.org/2000/svg',tag);\n\t\tattr(element,attributes);\n\t\tif (appendTo) {\n\t\t\tappendTo.appendChild(element);\n\t\t}\n\t\treturn element;\n\t}\n\t\n\t// Set attributes for an element\n\tfunction attr(element,attrs) {\n\t\tfor (var i in attrs) {\n\t\t\telement.setAttribute(i,attrs[i]);\n\t\t}\n\t}\n\t\n\t// Rotate a cartesian point about given origin by X degrees\n\tfunction rotatePoint(point, angle, origin) {\n\t\tvar radians = angle * Math.PI/180;\n\t\tvar x = point[0]-origin[0];\n\t\tvar y = point[1]-origin[1];\n\t\tvar x1 = x*Math.cos(radians) - y*Math.sin(radians) + origin[0];\n\t\tvar y1 = x*Math.sin(radians) + y*Math.cos(radians) + origin[1];\n\t\treturn [x1,y1];\n\t}\n\t\n\t// Rotate an array of cartesian points about a given origin by X degrees\n\tfunction rotatePoints(points, angle, origin) {\n\t\treturn points.map(function(point) {\n\t\t\treturn rotatePoint(point, angle, origin);\n\t\t});\n\t}\n\t\n\t// Given an array of points, return an SVG path string representing the shape they define\n\tfunction pointsToPath(points) {\n\t\treturn points.map(function(point, iPoint) {\n\t\t\treturn (iPoint>0?'L':'M') + point[0] + ' ' + point[1];\n\t\t}).join(' ')+'Z';\n\t}\n\t\n\tfunction circleToPath(cx, cy, r) {\n\t\treturn [\n\t\t\t\"M\",cx,\",\",cy,\n\t\t\t\"m\",0-r,\",\",0,\n\t\t\t\"a\",r,\",\",r,0,1,\",\",0,r*2,\",\",0,\n\t\t\t\"a\",r,\",\",r,0,1,\",\",0,0-r*2,\",\",0,\n\t\t\t\"z\"\n\t\t].join(' ').replace(/\\s,\\s/g,\",\");\n\t}\n\t\n\tfunction donutPath(cx,cy,rOuter,rInner) {\n\t\treturn circleToPath(cx,cy,rOuter) + \" \" + circleToPath(cx,cy,rInner);\n\t}\n\t\n\t// Restrict a number to a min + max range\n\tfunction restrictToRange(val,min,max) {\n\t\tif (val < min) return min;\n\t\tif (val > max) return max;\n\t\treturn val;\n\t}\n\t\n\t// Round a number to the nearest 0.5\n\tfunction roundHalf(num) {\n\t\treturn Math.round(num*2)/2;\n\t}\n\t\n\tfunction setClass(el, className, state) {\n\t\tel.classList[state ? 'add' : 'remove'](className);\n\t}\n\t\n\t/*\n\t * The \"MEAT\"\n\t */\n\n\treturn function(targetElement, options) {\n\t\tvar self = this;\n\t\t\n\t\t/*\n\t\t * Options\n\t\t */\n\t\toptions = options || {};\n\t\toptions = {\n\t\t\tdiameter: options.diameter || 400,\n\t\t\tminValue: options.minValue || 10, // Minimum value for target temperature\n\t\t\tmaxValue: options.maxValue || 30, // Maximum value for target temperature\n\t\t\tnumTicks: options.numTicks || 200, // Number of tick lines to display around the dial\n\t\t\tonSetTargetTemperature: options.onSetTargetTemperature || function() {}, // Function called when new target temperature set by the dial\n\t\t};\n\t\t\n\t\t/*\n\t\t * Properties - calculated from options in many cases\n\t\t */\n\t\tvar properties = {\n\t\t\ttickDegrees: 300, //  Degrees of the dial that should be covered in tick lines\n\t\t\trangeValue: options.maxValue - options.minValue,\n\t\t\tradius: options.diameter/2,\n\t\t\tticksOuterRadius: options.diameter / 30,\n\t\t\tticksInnerRadius: options.diameter / 8,\n\t\t\thvac_states: ['off', 'heating', 'cooling'],\n\t\t\tdragLockAxisDistance: 15,\n\t\t}\n\t\tproperties.lblAmbientPosition = [properties.radius, properties.ticksOuterRadius-(properties.ticksOuterRadius-properties.ticksInnerRadius)/2]\n\t\tproperties.offsetDegrees = 180-(360-properties.tickDegrees)/2;\n\t\t\n\t\t/*\n\t\t * Object state\n\t\t */\n\t\tvar state = {\n\t\t\ttarget_temperature: options.minValue,\n\t\t\tambient_temperature: options.minValue,\n\t\t\thvac_state: properties.hvac_states[0],\n\t\t\thas_leaf: false,\n\t\t\taway: false\n\t\t};\n\t\t\n\t\t/*\n\t\t * Property getter / setters\n\t\t */\n\t\tObject.defineProperty(this,'target_temperature',{\n\t\t\tget: function() {\n\t\t\t\treturn state.target_temperature;\n\t\t\t},\n\t\t\tset: function(val) {\n\t\t\t\tstate.target_temperature = restrictTargetTemperature(+val);\n\t\t\t\trender();\n\t\t\t}\n\t\t});\n\t\tObject.defineProperty(this,'ambient_temperature',{\n\t\t\tget: function() {\n\t\t\t\treturn state.ambient_temperature;\n\t\t\t},\n\t\t\tset: function(val) {\n\t\t\t\tstate.ambient_temperature = roundHalf(+val);\n\t\t\t\trender();\n\t\t\t}\n\t\t});\n\t\tObject.defineProperty(this,'hvac_state',{\n\t\t\tget: function() {\n\t\t\t\treturn state.hvac_state;\n\t\t\t},\n\t\t\tset: function(val) {\n\t\t\t\tif (properties.hvac_states.indexOf(val)>=0) {\n\t\t\t\t\tstate.hvac_state = val;\n\t\t\t\t\trender();\n\t\t\t\t}\n\t\t\t}\n\t\t});\n\t\tObject.defineProperty(this,'has_leaf',{\n\t\t\tget: function() {\n\t\t\t\treturn state.has_leaf;\n\t\t\t},\n\t\t\tset: function(val) {\n\t\t\t\tstate.has_leaf = !!val;\n\t\t\t\trender();\n\t\t\t}\n\t\t});\n\t\tObject.defineProperty(this,'away',{\n\t\t\tget: function() {\n\t\t\t\treturn state.away;\n\t\t\t},\n\t\t\tset: function(val) {\n\t\t\t\tstate.away = !!val;\n\t\t\t\trender();\n\t\t\t}\n\t\t});\n\t\t\n\t\t/*\n\t\t * SVG\n\t\t */\n\t\tvar svg = createSVGElement('svg',{\n\t\t\twidth: '100%', //options.diameter+'px',\n\t\t\theight: '100%', //options.diameter+'px',\n\t\t\tviewBox: '0 0 '+options.diameter+' '+options.diameter,\n\t\t\tclass: 'dial'\n\t\t},targetElement);\n\t\t// CIRCULAR DIAL\n\t\tvar circle = createSVGElement('circle',{\n\t\t\tcx: properties.radius,\n\t\t\tcy: properties.radius,\n\t\t\tr: properties.radius,\n\t\t\tclass: 'dial__shape'\n\t\t},svg);\n\t\t// EDITABLE INDICATOR\n\t\tvar editCircle = createSVGElement('path',{\n\t\t\td: donutPath(properties.radius,properties.radius,properties.radius-4,properties.radius-8),\n\t\t\tclass: 'dial__editableIndicator',\n\t\t},svg);\n\t\t\n\t\t/*\n\t\t * Ticks\n\t\t */\n\t\tvar ticks = createSVGElement('g',{\n\t\t\tclass: 'dial__ticks'\t\n\t\t},svg);\n\t\tvar tickPoints = [\n\t\t\t[properties.radius-1, properties.ticksOuterRadius],\n\t\t\t[properties.radius+1, properties.ticksOuterRadius],\n\t\t\t[properties.radius+1, properties.ticksInnerRadius],\n\t\t\t[properties.radius-1, properties.ticksInnerRadius]\n\t\t];\n\t\tvar tickPointsLarge = [\n\t\t\t[properties.radius-1.5, properties.ticksOuterRadius],\n\t\t\t[properties.radius+1.5, properties.ticksOuterRadius],\n\t\t\t[properties.radius+1.5, properties.ticksInnerRadius+20],\n\t\t\t[properties.radius-1.5, properties.ticksInnerRadius+20]\n\t\t];\n\t\tvar theta = properties.tickDegrees/options.numTicks;\n\t\tvar tickArray = [];\n\t\tfor (var iTick=0; iTick<options.numTicks; iTick++) {\n\t\t\ttickArray.push(createSVGElement('path',{d:pointsToPath(tickPoints)},ticks));\n\t\t};\n\t\t\n\t\t/*\n\t\t * Labels\n\t\t */\n\t\tvar lblTarget = createSVGElement('text',{\n\t\t\tx: properties.radius,\n\t\t\ty: properties.radius,\n\t\t\tclass: 'dial__lbl dial__lbl--target'\n\t\t},svg);\n\t\tvar lblTarget_text = document.createTextNode('');\n\t\tlblTarget.appendChild(lblTarget_text);\n\t\t//\n\t\tvar lblTargetHalf = createSVGElement('text',{\n\t\t\tx: properties.radius + properties.radius/2.5,\n\t\t\ty: properties.radius - properties.radius/8,\n\t\t\tclass: 'dial__lbl dial__lbl--target--half'\n\t\t},svg);\n\t\tvar lblTargetHalf_text = document.createTextNode('5');\n\t\tlblTargetHalf.appendChild(lblTargetHalf_text);\n\t\t//\n\t\tvar lblAmbient = createSVGElement('text',{\n\t\t\tclass: 'dial__lbl dial__lbl--ambient'\n\t\t},svg);\n\t\tvar lblAmbient_text = document.createTextNode('');\n\t\tlblAmbient.appendChild(lblAmbient_text);\n\t\t//\n\t\tvar lblAway = createSVGElement('text',{\n\t\t\tx: properties.radius,\n\t\t\ty: properties.radius,\n\t\t\tclass: 'dial__lbl dial__lbl--away'\n\t\t},svg);\n\t\tvar lblAway_text = document.createTextNode('AWAY');\n\t\tlblAway.appendChild(lblAway_text);\n\t\t//\n\t\tvar icoLeaf = createSVGElement('path',{\n\t\t\tclass: 'dial__ico__leaf'\n\t\t},svg);\n\t\t\n\t\t/*\n\t\t * LEAF\n\t\t */\n\t\tvar leafScale = properties.radius/5/100;\n\t\tvar leafDef = [\"M\", 3, 84, \"c\", 24, 17, 51, 18, 73, -6, \"C\", 100, 52, 100, 22, 100, 4, \"c\", -13, 15, -37, 9, -70, 19, \"C\", 4, 32, 0, 63, 0, 76, \"c\", 6, -7, 18, -17, 33, -23, 24, -9, 34, -9, 48, -20, -9, 10, -20, 16, -43, 24, \"C\", 22, 63, 8, 78, 3, 84, \"z\"].map(function(x) {\n\t\t\treturn isNaN(x) ? x : x*leafScale;\n\t\t}).join(' ');\n\t\tvar translate = [properties.radius-(leafScale*100*0.5),properties.radius*1.5]\n\t\tvar icoLeaf = createSVGElement('path',{\n\t\t\tclass: 'dial__ico__leaf',\n\t\t\td: leafDef,\n\t\t\ttransform: 'translate('+translate[0]+','+translate[1]+')'\n\t\t},svg);\n\t\t\t\n\t\t/*\n\t\t * RENDER\n\t\t */\n\t\tfunction render() {\n\t\t\trenderAway();\n\t\t\trenderHvacState();\n\t\t\trenderTicks();\n\t\t\trenderTargetTemperature();\n\t\t\trenderAmbientTemperature();\n\t\t\trenderLeaf();\n\t\t}\n\t\trender();\n\n\t\t/*\n\t\t * RENDER - ticks\n\t\t */\n\t\tfunction renderTicks() {\n\t\t\tvar vMin, vMax;\n\t\t\tif (self.away) {\n\t\t\t\tvMin = self.ambient_temperature;\n\t\t\t\tvMax = vMin;\n\t\t\t} else {\n\t\t\t\tvMin = Math.min(self.ambient_temperature, self.target_temperature);\n\t\t\t\tvMax = Math.max(self.ambient_temperature, self.target_temperature);\n\t\t\t}\n\t\t\tvar min = restrictToRange(Math.round((vMin-options.minValue)/properties.rangeValue * options.numTicks),0,options.numTicks-1);\n\t\t\tvar max = restrictToRange(Math.round((vMax-options.minValue)/properties.rangeValue * options.numTicks),0,options.numTicks-1);\n\t\t\t//\n\t\t\ttickArray.forEach(function(tick,iTick) {\n\t\t\t\tvar isLarge = iTick==min || iTick==max;\n\t\t\t\tvar isActive = iTick >= min && iTick <= max;\n\t\t\t\tattr(tick,{\n\t\t\t\t\td: pointsToPath(rotatePoints(isLarge ? tickPointsLarge: tickPoints,iTick*theta-properties.offsetDegrees,[properties.radius, properties.radius])),\n\t\t\t\t\tclass: isActive ? 'active' : ''\n\t\t\t\t});\n\t\t\t});\n\t\t}\n\t\n\t\t/*\n\t\t * RENDER - ambient temperature\n\t\t */\n\t\tfunction renderAmbientTemperature() {\n\t\t\tlblAmbient_text.nodeValue = Math.floor(self.ambient_temperature);\n\t\t\tif (self.ambient_temperature%1!=0) {\n\t\t\t\tlblAmbient_text.nodeValue += '⁵';\n\t\t\t}\n\t\t\tvar peggedValue = restrictToRange(self.ambient_temperature, options.minValue, options.maxValue);\n\t\t\tdegs = properties.tickDegrees * (peggedValue-options.minValue)/properties.rangeValue - properties.offsetDegrees;\n\t\t\tif (peggedValue > self.target_temperature) {\n\t\t\t\tdegs += 8;\n\t\t\t} else {\n\t\t\t\tdegs -= 8;\n\t\t\t}\n\t\t\tvar pos = rotatePoint(properties.lblAmbientPosition,degs,[properties.radius, properties.radius]);\n\t\t\tattr(lblAmbient,{\n\t\t\t\tx: pos[0],\n\t\t\t\ty: pos[1]\n\t\t\t});\n\t\t}\n\n\t\t/*\n\t\t * RENDER - target temperature\n\t\t */\n\t\tfunction renderTargetTemperature() {\n\t\t\tlblTarget_text.nodeValue = Math.floor(self.target_temperature);\n\t\t\tsetClass(lblTargetHalf,'shown',self.target_temperature%1!=0);\n\t\t}\n\t\t\n\t\t/*\n\t\t * RENDER - leaf\n\t\t */\n\t\tfunction renderLeaf() {\n\t\t\tsetClass(svg,'has-leaf',self.has_leaf);\n\t\t}\n\t\t\n\t\t/*\n\t\t * RENDER - HVAC state\n\t\t */\n\t\tfunction renderHvacState() {\n\t\t\tArray.prototype.slice.call(svg.classList).forEach(function(c) {\n\t\t\t\tif (c.match(/^dial--state--/)) {\n\t\t\t\t\tsvg.classList.remove(c);\n\t\t\t\t};\n\t\t\t});\n\t\t\tsvg.classList.add('dial--state--'+self.hvac_state);\n\t\t}\n\t\t\n\t\t/*\n\t\t * RENDER - away\n\t\t */\n\t\tfunction renderAway() {\n\t\t\tsvg.classList[self.away ? 'add' : 'remove']('away');\n\t\t}\n\t\t\n\t\t/*\n\t\t * Drag to control\n\t\t */\n\t\tvar _drag = {\n\t\t\tinProgress: false,\n\t\t\tstartPoint: null,\n\t\t\tstartTemperature: 0,\n\t\t\tlockAxis: undefined\n\t\t};\n\t\t\n\t\tfunction eventPosition(ev) {\n\t\t\tif (ev.targetTouches && ev.targetTouches.length) {\n\t\t\t\treturn  [ev.targetTouches[0].clientX, ev.targetTouches[0].clientY];\n\t\t\t} else {\n\t\t\t\treturn [ev.x, ev.y];\n\t\t\t};\n\t\t}\n\t\t\n\t\tvar startDelay;\n\t\tfunction dragStart(ev) {\n\t\t\tstartDelay = setTimeout(function() {\n\t\t\t\tsetClass(svg, 'dial--edit', true);\n\t\t\t\t_drag.inProgress = true;\n\t\t\t\t_drag.startPoint = eventPosition(ev);\n\t\t\t\t_drag.startTemperature = self.target_temperature || options.minValue;\n\t\t\t\t_drag.lockAxis = undefined;\n\t\t\t},1000);\n\t\t};\n\t\t\n\t\tfunction dragEnd (ev) {\n\t\t\tclearTimeout(startDelay);\n\t\t\tsetClass(svg, 'dial--edit', false);\n\t\t\tif (!_drag.inProgress) return;\n\t\t\t_drag.inProgress = false;\n\t\t\tif (self.target_temperature != _drag.startTemperature) {\n\t\t\t\tif (typeof options.onSetTargetTemperature == 'function') {\n\t\t\t\t\toptions.onSetTargetTemperature(self.target_temperature);\n\t\t\t\t};\n\t\t\t};\n\t\t};\n\t\t\n\t\tfunction dragMove(ev) {\n\t\t\tev.preventDefault();\n\t\t\tif (!_drag.inProgress) return;\n\t\t\tvar evPos =  eventPosition(ev);\n\t\t\tvar dy = _drag.startPoint[1]-evPos[1];\n\t\t\tvar dx = evPos[0] - _drag.startPoint[0];\n\t\t\tvar dxy;\n\t\t\tif (_drag.lockAxis == 'x') {\n\t\t\t\tdxy  = dx;\n\t\t\t} else if (_drag.lockAxis == 'y') {\n\t\t\t\tdxy = dy;\n\t\t\t} else if (Math.abs(dy) > properties.dragLockAxisDistance) {\n\t\t\t\t_drag.lockAxis = 'y';\n\t\t\t\tdxy = dy;\n\t\t\t} else if (Math.abs(dx) > properties.dragLockAxisDistance) {\n\t\t\t\t_drag.lockAxis = 'x';\n\t\t\t\tdxy = dx;\n\t\t\t} else {\n\t\t\t\tdxy = (Math.abs(dy) > Math.abs(dx)) ? dy : dx;\n\t\t\t};\n\t\t\tvar dValue = (dxy*getSizeRatio())/(options.diameter)*properties.rangeValue;\n\t\t\tself.target_temperature = roundHalf(_drag.startTemperature+dValue);\n\t\t}\n\t\t\n\t\tsvg.addEventListener('mousedown',dragStart);\n\t\tsvg.addEventListener('touchstart',dragStart);\n\t\t\n\t\tsvg.addEventListener('mouseup',dragEnd);\n\t\tsvg.addEventListener('mouseleave',dragEnd);\n\t\tsvg.addEventListener('touchend',dragEnd);\n\t\t\n\t\tsvg.addEventListener('mousemove',dragMove);\n\t\tsvg.addEventListener('touchmove',dragMove);\n\t\t//\n\t\t\n\t\t/*\n\t\t * Helper functions\n\t\t */\n\t\tfunction restrictTargetTemperature(t) {\n\t\t\treturn restrictToRange(roundHalf(t),options.minValue,options.maxValue);\n\t\t}\n\t\t\n\t\tfunction angle(point) {\n\t\t\tvar dx = point[0] - properties.radius;\n\t\t\tvar dy = point[1] - properties.radius;\n\t\t\tvar theta = Math.atan(dx/dy) / (Math.PI/180);\n\t\t\tif (point[0]>=properties.radius && point[1] < properties.radius) {\n\t\t\t\ttheta = 90-theta - 90;\n\t\t\t} else if (point[0]>=properties.radius && point[1] >= properties.radius) {\n\t\t\t\ttheta = 90-theta + 90;\n\t\t\t} else if (point[0]<properties.radius && point[1] >= properties.radius) {\n\t\t\t\ttheta = 90-theta + 90;\n\t\t\t} else if (point[0]<properties.radius && point[1] < properties.radius) {\n\t\t\t\ttheta = 90-theta+270;\n\t\t\t}\n\t\t\treturn theta;\n\t\t};\n\t\t\n\t\tfunction getSizeRatio() {\n\t\t\treturn options.diameter / targetElement.clientWidth;\n\t\t}\n\t\t\n\t};\n})();\n\n/* ==== */\n(function(scope) {\n    \n    var nest = new thermostatDial(document.getElementById('thermostat-WZ'),{\n    \tonSetTargetTemperature: function(v) {\n    \t\tscope.send({topic: \"target_temperature\", payload: v});\n    \t}\n    });\n\n\n    scope.$watch('msg', function(data) {\n        //console.log(data.topic+\"  \"+data.payload);\n        if (data.topic == \"ambient_temperature\") {\n            nest.ambient_temperature = data.payload;\n        } if (data.topic == \"target_temperature\") {\n            nest.target_temperature = data.payload;\n        } if (data.topic == \"hvac_state\") {\n            nest.hvac_state = data.payload;\n        } if (data.topic == \"has_leaf\") {\n            nest.has_leaf = data.payload;\n        } if (data.topic == \"away\") {\n            nest.away = data.payload;\n        }\n    });\n})(scope);\n\n</script>","storeOutMessages":true,"fwdInMessages":false,"templateScope":"local","x":1030,"y":540,"wires":[["228ed0cb.d3b908"]]},{"id":"773154d5.ab0824","type":"function","z":"15bc8d4b.90af3b","name":"ambient_temperature","func":"msg.topic = \"ambient_temperature\";\nglobal.set(\"wz-ambient\",msg.payload);\nnode.status({text:msg.payload + \"°C\"});\nreturn msg;","outputs":1,"noerr":0,"x":777.6190490722656,"y":388.33333587646484,"wires":[["63e04c4d.bb5f6c"]]},{"id":"1ae634e.7e416cb","type":"function","z":"15bc8d4b.90af3b","name":"hvac_state","func":"global.set(\"wz-state\",msg.payload);\n\nmsg.topic = \"hvac_state\";\nnode.status({text:msg.payload});\nreturn msg;","outputs":1,"noerr":0,"x":1670,"y":340,"wires":[["63e04c4d.bb5f6c"]]},{"id":"228ed0cb.d3b908","type":"function","z":"15bc8d4b.90af3b","name":"target_temperature","func":"if (msg.topic == \"target_temperature\") {\nglobal.set(\"wz-target\",msg.payload);\nreturn msg;\n}","outputs":1,"noerr":0,"x":1210,"y":540,"wires":[["c0dc329a.4f59b","2ab0e1e3.ac211e"]]},{"id":"c0dc329a.4f59b","type":"function","z":"15bc8d4b.90af3b","name":"Temperaturen Vergleichen","func":"context.target = context.target || 0.0;\ncontext.sensor = context.sensor || 0.0;\n\nif (msg.topic === 'sensor_temperature') {\n  context.sensor = msg.payload;\n} else if (msg.topic === 'target_temperature') {\n  context.target = msg.payload;\n} \n\nif (context.target >= context.sensor) {\n  return {payload: 1};\n} else {\n  return {payload: 0};\n}\nnode.status({text:msg.payload});","outputs":1,"noerr":0,"x":1460,"y":500,"wires":[["7dc8f30f.3d7064"]]},{"id":"7dc8f30f.3d7064","type":"function","z":"15bc8d4b.90af3b","name":"Farbstatus Nest","func":"msg.topic = \"hvac_state\";\nnode.status({text:msg.payload});\nreturn msg;","outputs":1,"noerr":0,"x":1700,"y":500,"wires":[["74d1ff15.059858"]]},{"id":"74d1ff15.059858","type":"switch","z":"15bc8d4b.90af3b","name":"","property":"payload","propertyType":"msg","rules":[{"t":"eq","v":"0","vt":"str"},{"t":"eq","v":"1","vt":"str"}],"checkall":"true","repair":false,"outputs":2,"x":1310,"y":360,"wires":[["3d83fd16.3def3a"],["42df8b17.773ac4"]]},{"id":"3d83fd16.3def3a","type":"template","z":"15bc8d4b.90af3b","name":"Heizen OFF","field":"payload","fieldType":"msg","format":"handlebars","syntax":"mustache","template":"off","output":"str","x":1490,"y":300,"wires":[["1ae634e.7e416cb"]]},{"id":"42df8b17.773ac4","type":"template","z":"15bc8d4b.90af3b","name":"Heizen ON","field":"payload","fieldType":"msg","format":"handlebars","syntax":"mustache","template":"heating","output":"str","x":1490,"y":380,"wires":[["1ae634e.7e416cb"]]},{"id":"bcc9c2f.902a54","type":"delay","z":"15bc8d4b.90af3b","name":"","pauseType":"delay","timeout":"50","timeoutUnits":"milliseconds","rate":"1","nbRateUnits":"1","rateUnits":"second","randomFirst":"1","randomLast":"5","randomUnits":"seconds","drop":false,"x":540,"y":540,"wires":[["c6c28969.9dbff8","bf2d08a0.229458","c02295c7.804d98","dc76cf88.de8868"]]},{"id":"c6c28969.9dbff8","type":"function","z":"15bc8d4b.90af3b","name":"global target-temp","func":"msg.payload = global.get(\"wz-target\");\nmsg.topic = 'target_temperature';\nnode.status({text:msg.payload + \"°C\"});\nreturn msg;","outputs":1,"noerr":0,"x":770,"y":440,"wires":[["63e04c4d.bb5f6c"]]},{"id":"ae55f012.dbc9e","type":"ui_ui_control","z":"15bc8d4b.90af3b","name":"ui change","x":360,"y":540,"wires":[["bcc9c2f.902a54"]]},{"id":"c02295c7.804d98","type":"function","z":"15bc8d4b.90af3b","name":"global color-state","func":"msg.payload = global.get(\"wz-state\");\nmsg.topic = \"hvac_state\";\nnode.status({text:msg.payload});\nreturn msg;","outputs":1,"noerr":0,"x":770,"y":560,"wires":[["63e04c4d.bb5f6c"]]},{"id":"8872a148.d992","type":"function","z":"15bc8d4b.90af3b","name":"Leaf","func":"minleaf = 15;\nmaxleaf = 22;\ntemperature = msg.payload;\nmsg.payload=false;\nif (temperature >= minleaf){\n    if (temperature <= maxleaf){\n        msg.payload = true;\n    }\n}\nmsg.topic = \"has_leaf\";\nglobal.set(\"wz-leaf\",msg.payload);\nnode.status({text:msg.payload});\nreturn msg;","outputs":1,"noerr":0,"x":810,"y":720,"wires":[["63e04c4d.bb5f6c"]]},{"id":"4737da1d.f282fc","type":"mqtt in","z":"15bc8d4b.90af3b","name":"","topic":"homeland/haushalt/heizung/Wohnzimmer_Thermostat/desiredTemperature","qos":"2","broker":"34eb4531.5459e2","x":350,"y":720,"wires":[["137bccd8.b66d93","8872a148.d992","19428fcb.0c576"]],"outputLabels":["target_temperature"]},{"id":"b09e0a1f.5122c8","type":"mqtt in","z":"15bc8d4b.90af3b","name":"","topic":"homeland/haushalt/heizung/Wohnzimmer_Thermostat/temperature","qos":"2","broker":"34eb4531.5459e2","x":360,"y":300,"wires":[["773154d5.ab0824","53e6b42a.aabbe4"]],"outputLabels":["ambient_temperature"]},{"id":"137bccd8.b66d93","type":"function","z":"15bc8d4b.90af3b","name":"target_temperature","func":"msg.topic = 'target_temperature';\nglobal.set(\"wz-target\",msg.payload);\nnode.status({text:msg.payload + \"°C\"});\nreturn msg;","outputs":1,"noerr":0,"x":770,"y":680,"wires":[["63e04c4d.bb5f6c","c0dc329a.4f59b"]]},{"id":"7f9ed5e0.953424","type":"mqtt out","z":"15bc8d4b.90af3b","name":"","topic":"homeland/haushalt/heizung/Wohnzimmer_Thermostat/desiredTemperature/set","qos":"2","retain":"false","broker":"34eb4531.5459e2","x":2040,"y":620,"wires":[]},{"id":"bf2d08a0.229458","type":"function","z":"15bc8d4b.90af3b","name":"global target-temp","func":"msg.payload = global.get(\"wz-ambient\");\nmsg.topic = 'ambient_temperature';\nnode.status({text:msg.payload + \"°C\"});\nreturn msg;","outputs":1,"noerr":0,"x":770,"y":500,"wires":[["63e04c4d.bb5f6c"]]},{"id":"dc76cf88.de8868","type":"function","z":"15bc8d4b.90af3b","name":"global target-temp","func":"msg.payload = global.get(\"wz-leaf\");\nmsg.topic = 'has_leaf';\nnode.status({text:msg.payload});\nreturn msg;","outputs":1,"noerr":0,"x":770,"y":620,"wires":[["63e04c4d.bb5f6c"]]},{"id":"a84aee83.b857b","type":"mqtt in","z":"15bc8d4b.90af3b","name":"","topic":"homeland/haushalt/heizung/Wohnzimmer_Thermostat/humidity","qos":"2","broker":"34eb4531.5459e2","x":350,"y":120,"wires":[["6ea3b9b3.85c2e"]],"outputLabels":["humidity"]},{"id":"b04d3a0d.8c6ba8","type":"trigger","z":"15bc8d4b.90af3b","op1":"","op2":"","op1type":"nul","op2type":"payl","duration":"4","extend":true,"units":"s","reset":"","bytopic":"all","name":"","x":1680,"y":620,"wires":[["7f9ed5e0.953424"]]},{"id":"8b86a571.ef3fb","type":"mqtt in","z":"15bc8d4b.90af3b","name":"","topic":"homeland/haushalt/heizung/Wohnzimmer_Thermostat/voc","qos":"2","broker":"34eb4531.5459e2","x":330,"y":180,"wires":[["4b4c2777.fcd59"]]},{"id":"4b4c2777.fcd59","type":"function","z":"15bc8d4b.90af3b","name":"","func":"node.status({text:msg.payload});\nbad = 'Air: <div class=\"fa fa-thumbs-down fa-2x nr-dashboard-error\"></div>';\nok = 'Air: <div class=\"fa fa-thumbs-up fa-2x nr-dashboard-warning\"></div>';\ngood = 'Air: <div class=\"fa fa-thumbs-up fa-2x nr-dashboard-ok\"></div>';\nif (msg.payload > 1750) { \n    msg.payload = (bad);\n    msg.topic = 'air';\n}\nif (msg.payload <= 1750 && msg.payload > 750) {\n    msg.payload = (ok);\n    msg.topic = 'air';\n}\nif (msg.payload <= 750) {\n    msg.payload = (good);\n    msg.topic = 'air';\n}\nreturn msg;","outputs":1,"noerr":0,"x":670,"y":180,"wires":[["44ad4257.3b5dec"]]},{"id":"45291976.3021f","type":"mqtt in","z":"15bc8d4b.90af3b","name":"","topic":"homeland/haushalt/heizung/Wohnzimmer_Thermostat/windowOpen","qos":"2","broker":"34eb4531.5459e2","x":360,"y":240,"wires":[["3708d80.98e8aa8"]]},{"id":"3708d80.98e8aa8","type":"function","z":"15bc8d4b.90af3b","name":"","func":"node.status({text:msg.payload});\ntext = '<div class=\"nr-dashboard-warning\">Window</div>';\nif (msg.payload == \"1\") {\n    msg.payload = (text);\n    msg.topic = 'window';\n}\nreturn msg;","outputs":1,"noerr":0,"x":670,"y":240,"wires":[["44ad4257.3b5dec"]]},{"id":"f6640250.4d957","type":"ui_text","z":"15bc8d4b.90af3b","group":"d2bea201.d68888","order":1,"width":"0","height":"0","name":"Temperatur/Luftfeuchtigkeit","label":"{{msg.payload.humidity.payload}}","format":"{{msg.payload.air.payload}} {{msg.payload.window.payload}}","layout":"row-spread","x":1040,"y":180,"wires":[]},{"id":"6ea3b9b3.85c2e","type":"function","z":"15bc8d4b.90af3b","name":"","func":"node.status({text:msg.payload});\ntext1 = '<div class=\"fa fa-tint fa-2x nr-dashboard-dim\"></div> ';\ntext2 = ' %';\nmsg.payload = (text1 + msg.payload + text2);\nmsg.topic = 'humidity';\nreturn msg;","outputs":1,"noerr":0,"x":670,"y":120,"wires":[["44ad4257.3b5dec"]]},{"id":"44ad4257.3b5dec","type":"join","z":"15bc8d4b.90af3b","name":"","mode":"custom","build":"object","property":"","propertyType":"full","key":"topic","joiner":"\\n","joinerType":"str","accumulate":true,"timeout":"","count":"1","reduceRight":false,"reduceExp":"","reduceInit":"","reduceInitType":"","reduceFixup":"","x":830,"y":180,"wires":[["f6640250.4d957"]]},{"id":"2ab0e1e3.ac211e","type":"function","z":"15bc8d4b.90af3b","name":"Nur bei Abweichung senden","func":"if (msg.topic === 'target_temperature_old')\n{\n  oldvalue = msg.payload;\n} \nelse if (msg.topic === 'target_temperature')\n{\n  newvalue = msg.payload;\n} \n\nif (oldvalue != newvalue) {\n  oldvalue = newvalue;\n  return {payload: newvalue};\n}","outputs":1,"noerr":0,"x":1460,"y":620,"wires":[["b04d3a0d.8c6ba8"]]},{"id":"19428fcb.0c576","type":"function","z":"15bc8d4b.90af3b","name":"target_temperature_old","func":"msg.topic = 'target_temperature_old';\nnode.status({text:msg.payload + \"°C\"});\nreturn msg;","outputs":1,"noerr":0,"x":790,"y":760,"wires":[["2ab0e1e3.ac211e"]]},{"id":"d2bea201.d68888","type":"ui_group","z":"","name":"Wohnzimmer","tab":"7a08a4e0.9f9cf4","disp":true,"width":"6","collapse":false},{"id":"34eb4531.5459e2","type":"mqtt-broker","z":"","name":"","broker":"192.168.0.8","port":"1883","clientid":"","usetls":false,"compatmode":true,"keepalive":"60","cleansession":true,"willTopic":"homeland/haushalt/steuerung/nodered/$online","willQos":"0","willPayload":"false","birthTopic":"","birthQos":"0","birthPayload":""},{"id":"7a08a4e0.9f9cf4","type":"ui_tab","z":"","name":"Klima","icon":"dashboard"}]
Titel: Antw:Node-Red als Frontend
Beitrag von: Master_Nick am 16 April 2018, 19:34:34
So ich habe noch weiter getweakt.

Wenn ihr Fragen oder Code braucht einfach Bescheid geben :-)
Sonst belasse ich es erst mal beim komplexesten dem Wohnzimmerthermostat.
Generell habe ich alle angepasst um den Minimal Wert und den Maximalwert zu ändern (statt 10° 4° beim Balkon -20° bis 50°).
Und Missbrauche die nun der Optik wegen auch für normales Thermo/Hygrometer. Auch habe ich das symbolisieren von Heizen von Zieltemp >= Sensortemp auf nur > geändert.

Ansonsten mal ein Bild von meinem Touch Display - befeuert von einem Raspi3.

[{"id":"53e6b42a.aabbe4","type":"function","z":"15bc8d4b.90af3b","name":"Konvertieren der Temperatur","func":"msg.payload = parseFloat(msg.payload);\nmsg.topic = 'sensor_temperature';\nnode.status({text:msg.payload + \"°C\"});\nreturn msg;","outputs":1,"noerr":0,"x":810,"y":300,"wires":[["730bb5a.9f6bfcc","c0dc329a.4f59b"]]},{"id":"730bb5a.9f6bfcc","type":"debug","z":"15bc8d4b.90af3b","name":"Temp Sensor","active":false,"console":"false","complete":"payload","x":1130,"y":300,"wires":[]},{"id":"63e04c4d.bb5f6c","type":"ui_template","z":"15bc8d4b.90af3b","group":"d2bea201.d68888","name":"Nest","order":5,"width":"6","height":"6","format":"<div id=\"thermostat-WZ\"></div>\n\n<style>\n@import url(http://fonts.googleapis.com/css?family=Open+Sans:300);\n#thermostat {\n  margin: 0 auto;\n  -webkit-tap-highlight-color: rgba(0, 0, 0, 0);\n}\n.dial {\n  -webkit-user-select: none;\n     -moz-user-select: none;\n      -ms-user-select: none;\n          user-select: none;\n}\n.dial.away .dial__ico__leaf {\n  visibility: hidden;\n}\n.dial.away .dial__lbl--target {\n  visibility: hidden;\n}\n.dial.away .dial__lbl--target--half {\n  visibility: hidden;\n}\n.dial.away .dial__lbl--away {\n  opacity: 1;\n}\n.dial .dial__shape {\n  -webkit-transition: fill 0.5s;\n  transition: fill 0.5s;\n}\n.dial__ico__leaf {\n  fill: #13EB13;\n  opacity: 0;\n  -webkit-transition: opacity 0.5s;\n  transition: opacity 0.5s;\n  pointer-events: none;\n}\n.dial.has-leaf .dial__ico__leaf {\n  display: block;\n  opacity: 1;\n  pointer-events: initial;\n}\n.dial__editableIndicator {\n  fill: white;\n  fill-rule: evenodd;\n  opacity: 0;\n  -webkit-transition: opacity 0.5s;\n  transition: opacity 0.5s;\n}\n.dial--edit .dial__editableIndicator {\n  opacity: 1;\n}\n.dial--state--off .dial__shape {\n  fill: #3d3c3c;\n}\n.dial--state--heating .dial__shape {\n  fill: #E36304;\n}\n.dial--state--cooling .dial__shape {\n  fill: #007AF1;\n}\n.dial__ticks path {\n  fill: rgba(255, 255, 255, 0.3);\n}\n.dial__ticks path.active {\n  fill: rgba(255, 255, 255, 0.8);\n}\n.dial text {\n  fill: white;\n  text-anchor: middle;\n  font-family: Helvetica, sans-serif;\n  alignment-baseline: central;\n}\n.dial__lbl--target {\n  font-size: 120px;\n  font-weight: bold;\n}\n.dial__lbl--target--half {\n  font-size: 40px;\n  font-weight: bold;\n  opacity: 0;\n  -webkit-transition: opacity 0.1s;\n  transition: opacity 0.1s;\n}\n.dial__lbl--target--half.shown {\n  opacity: 1;\n  -webkit-transition: opacity 0s;\n  transition: opacity 0s;\n}\n.dial__lbl--ambient {\n  font-size: 22px;\n  font-weight: bold;\n}\n.dial__lbl--away {\n  font-size: 72px;\n  font-weight: bold;\n  opacity: 0;\n  pointer-events: none;\n}\n#controls {\n  font-family: Open Sans;\n  background-color: rgba(255, 255, 255, 0.25);\n  padding: 20px;\n  border-radius: 5px;\n  position: absolute;\n  left: 50%;\n  -webkit-transform: translatex(-50%);\n          transform: translatex(-50%);\n  margin-top: 20px;\n}\n#controls label {\n  text-align: left;\n  display: block;\n}\n#controls label span {\n  display: inline-block;\n  width: 200px;\n  text-align: right;\n  font-size: 0.8em;\n  text-transform: uppercase;\n}\n#controls p {\n  margin: 0;\n  margin-bottom: 1em;\n  padding-bottom: 1em;\n  border-bottom: 2px solid #ccc;\n}\n</style>\n<script>\n    var thermostatDial = (function() {\n\t\n\t/*\n\t * Utility functions\n\t */\n\t\n\t// Create an element with proper SVG namespace, optionally setting its attributes and appending it to another element\n\tfunction createSVGElement(tag,attributes,appendTo) {\n\t\tvar element = document.createElementNS('http://www.w3.org/2000/svg',tag);\n\t\tattr(element,attributes);\n\t\tif (appendTo) {\n\t\t\tappendTo.appendChild(element);\n\t\t}\n\t\treturn element;\n\t}\n\t\n\t// Set attributes for an element\n\tfunction attr(element,attrs) {\n\t\tfor (var i in attrs) {\n\t\t\telement.setAttribute(i,attrs[i]);\n\t\t}\n\t}\n\t\n\t// Rotate a cartesian point about given origin by X degrees\n\tfunction rotatePoint(point, angle, origin) {\n\t\tvar radians = angle * Math.PI/180;\n\t\tvar x = point[0]-origin[0];\n\t\tvar y = point[1]-origin[1];\n\t\tvar x1 = x*Math.cos(radians) - y*Math.sin(radians) + origin[0];\n\t\tvar y1 = x*Math.sin(radians) + y*Math.cos(radians) + origin[1];\n\t\treturn [x1,y1];\n\t}\n\t\n\t// Rotate an array of cartesian points about a given origin by X degrees\n\tfunction rotatePoints(points, angle, origin) {\n\t\treturn points.map(function(point) {\n\t\t\treturn rotatePoint(point, angle, origin);\n\t\t});\n\t}\n\t\n\t// Given an array of points, return an SVG path string representing the shape they define\n\tfunction pointsToPath(points) {\n\t\treturn points.map(function(point, iPoint) {\n\t\t\treturn (iPoint>0?'L':'M') + point[0] + ' ' + point[1];\n\t\t}).join(' ')+'Z';\n\t}\n\t\n\tfunction circleToPath(cx, cy, r) {\n\t\treturn [\n\t\t\t\"M\",cx,\",\",cy,\n\t\t\t\"m\",0-r,\",\",0,\n\t\t\t\"a\",r,\",\",r,0,1,\",\",0,r*2,\",\",0,\n\t\t\t\"a\",r,\",\",r,0,1,\",\",0,0-r*2,\",\",0,\n\t\t\t\"z\"\n\t\t].join(' ').replace(/\\s,\\s/g,\",\");\n\t}\n\t\n\tfunction donutPath(cx,cy,rOuter,rInner) {\n\t\treturn circleToPath(cx,cy,rOuter) + \" \" + circleToPath(cx,cy,rInner);\n\t}\n\t\n\t// Restrict a number to a min + max range\n\tfunction restrictToRange(val,min,max) {\n\t\tif (val < min) return min;\n\t\tif (val > max) return max;\n\t\treturn val;\n\t}\n\t\n\t// Round a number to the nearest 0.5\n\tfunction roundHalf(num) {\n\t\treturn Math.round(num*2)/2;\n\t}\n\t\n\tfunction setClass(el, className, state) {\n\t\tel.classList[state ? 'add' : 'remove'](className);\n\t}\n\t\n\t/*\n\t * The \"MEAT\"\n\t */\n\n\treturn function(targetElement, options) {\n\t\tvar self = this;\n\t\t\n\t\t/*\n\t\t * Options\n\t\t */\n\t\toptions = options || {};\n\t\toptions = {\n\t\t\tdiameter: options.diameter || 400,\n\t\t\tminValue: options.minValue || 4, // Minimum value for target temperature\n\t\t\tmaxValue: options.maxValue || 30, // Maximum value for target temperature\n\t\t\tnumTicks: options.numTicks || 200, // Number of tick lines to display around the dial\n\t\t\tonSetTargetTemperature: options.onSetTargetTemperature || function() {}, // Function called when new target temperature set by the dial\n\t\t};\n\t\t\n\t\t/*\n\t\t * Properties - calculated from options in many cases\n\t\t */\n\t\tvar properties = {\n\t\t\ttickDegrees: 300, //  Degrees of the dial that should be covered in tick lines\n\t\t\trangeValue: options.maxValue - options.minValue,\n\t\t\tradius: options.diameter/2,\n\t\t\tticksOuterRadius: options.diameter / 30,\n\t\t\tticksInnerRadius: options.diameter / 8,\n\t\t\thvac_states: ['off', 'heating', 'cooling'],\n\t\t\tdragLockAxisDistance: 15,\n\t\t}\n\t\tproperties.lblAmbientPosition = [properties.radius, properties.ticksOuterRadius-(properties.ticksOuterRadius-properties.ticksInnerRadius)/2]\n\t\tproperties.offsetDegrees = 180-(360-properties.tickDegrees)/2;\n\t\t\n\t\t/*\n\t\t * Object state\n\t\t */\n\t\tvar state = {\n\t\t\ttarget_temperature: options.minValue,\n\t\t\tambient_temperature: options.minValue,\n\t\t\thvac_state: properties.hvac_states[0],\n\t\t\thas_leaf: false,\n\t\t\taway: false\n\t\t};\n\t\t\n\t\t/*\n\t\t * Property getter / setters\n\t\t */\n\t\tObject.defineProperty(this,'target_temperature',{\n\t\t\tget: function() {\n\t\t\t\treturn state.target_temperature;\n\t\t\t},\n\t\t\tset: function(val) {\n\t\t\t\tstate.target_temperature = restrictTargetTemperature(+val);\n\t\t\t\trender();\n\t\t\t}\n\t\t});\n\t\tObject.defineProperty(this,'ambient_temperature',{\n\t\t\tget: function() {\n\t\t\t\treturn state.ambient_temperature;\n\t\t\t},\n\t\t\tset: function(val) {\n\t\t\t\tstate.ambient_temperature = roundHalf(+val);\n\t\t\t\trender();\n\t\t\t}\n\t\t});\n\t\tObject.defineProperty(this,'hvac_state',{\n\t\t\tget: function() {\n\t\t\t\treturn state.hvac_state;\n\t\t\t},\n\t\t\tset: function(val) {\n\t\t\t\tif (properties.hvac_states.indexOf(val)>=0) {\n\t\t\t\t\tstate.hvac_state = val;\n\t\t\t\t\trender();\n\t\t\t\t}\n\t\t\t}\n\t\t});\n\t\tObject.defineProperty(this,'has_leaf',{\n\t\t\tget: function() {\n\t\t\t\treturn state.has_leaf;\n\t\t\t},\n\t\t\tset: function(val) {\n\t\t\t\tstate.has_leaf = !!val;\n\t\t\t\trender();\n\t\t\t}\n\t\t});\n\t\tObject.defineProperty(this,'away',{\n\t\t\tget: function() {\n\t\t\t\treturn state.away;\n\t\t\t},\n\t\t\tset: function(val) {\n\t\t\t\tstate.away = !!val;\n\t\t\t\trender();\n\t\t\t}\n\t\t});\n\t\t\n\t\t/*\n\t\t * SVG\n\t\t */\n\t\tvar svg = createSVGElement('svg',{\n\t\t\twidth: '100%', //options.diameter+'px',\n\t\t\theight: '100%', //options.diameter+'px',\n\t\t\tviewBox: '0 0 '+options.diameter+' '+options.diameter,\n\t\t\tclass: 'dial'\n\t\t},targetElement);\n\t\t// CIRCULAR DIAL\n\t\tvar circle = createSVGElement('circle',{\n\t\t\tcx: properties.radius,\n\t\t\tcy: properties.radius,\n\t\t\tr: properties.radius,\n\t\t\tclass: 'dial__shape'\n\t\t},svg);\n\t\t// EDITABLE INDICATOR\n\t\tvar editCircle = createSVGElement('path',{\n\t\t\td: donutPath(properties.radius,properties.radius,properties.radius-4,properties.radius-8),\n\t\t\tclass: 'dial__editableIndicator',\n\t\t},svg);\n\t\t\n\t\t/*\n\t\t * Ticks\n\t\t */\n\t\tvar ticks = createSVGElement('g',{\n\t\t\tclass: 'dial__ticks'\t\n\t\t},svg);\n\t\tvar tickPoints = [\n\t\t\t[properties.radius-1, properties.ticksOuterRadius],\n\t\t\t[properties.radius+1, properties.ticksOuterRadius],\n\t\t\t[properties.radius+1, properties.ticksInnerRadius],\n\t\t\t[properties.radius-1, properties.ticksInnerRadius]\n\t\t];\n\t\tvar tickPointsLarge = [\n\t\t\t[properties.radius-1.5, properties.ticksOuterRadius],\n\t\t\t[properties.radius+1.5, properties.ticksOuterRadius],\n\t\t\t[properties.radius+1.5, properties.ticksInnerRadius+20],\n\t\t\t[properties.radius-1.5, properties.ticksInnerRadius+20]\n\t\t];\n\t\tvar theta = properties.tickDegrees/options.numTicks;\n\t\tvar tickArray = [];\n\t\tfor (var iTick=0; iTick<options.numTicks; iTick++) {\n\t\t\ttickArray.push(createSVGElement('path',{d:pointsToPath(tickPoints)},ticks));\n\t\t};\n\t\t\n\t\t/*\n\t\t * Labels\n\t\t */\n\t\tvar lblTarget = createSVGElement('text',{\n\t\t\tx: properties.radius,\n\t\t\ty: properties.radius,\n\t\t\tclass: 'dial__lbl dial__lbl--target'\n\t\t},svg);\n\t\tvar lblTarget_text = document.createTextNode('');\n\t\tlblTarget.appendChild(lblTarget_text);\n\t\t//\n\t\tvar lblTargetHalf = createSVGElement('text',{\n\t\t\tx: properties.radius + properties.radius/2.5,\n\t\t\ty: properties.radius - properties.radius/8,\n\t\t\tclass: 'dial__lbl dial__lbl--target--half'\n\t\t},svg);\n\t\tvar lblTargetHalf_text = document.createTextNode('5');\n\t\tlblTargetHalf.appendChild(lblTargetHalf_text);\n\t\t//\n\t\tvar lblAmbient = createSVGElement('text',{\n\t\t\tclass: 'dial__lbl dial__lbl--ambient'\n\t\t},svg);\n\t\tvar lblAmbient_text = document.createTextNode('');\n\t\tlblAmbient.appendChild(lblAmbient_text);\n\t\t//\n\t\tvar lblAway = createSVGElement('text',{\n\t\t\tx: properties.radius,\n\t\t\ty: properties.radius,\n\t\t\tclass: 'dial__lbl dial__lbl--away'\n\t\t},svg);\n\t\tvar lblAway_text = document.createTextNode('AWAY');\n\t\tlblAway.appendChild(lblAway_text);\n\t\t//\n\t\tvar icoLeaf = createSVGElement('path',{\n\t\t\tclass: 'dial__ico__leaf'\n\t\t},svg);\n\t\t\n\t\t/*\n\t\t * LEAF\n\t\t */\n\t\tvar leafScale = properties.radius/5/100;\n\t\tvar leafDef = [\"M\", 3, 84, \"c\", 24, 17, 51, 18, 73, -6, \"C\", 100, 52, 100, 22, 100, 4, \"c\", -13, 15, -37, 9, -70, 19, \"C\", 4, 32, 0, 63, 0, 76, \"c\", 6, -7, 18, -17, 33, -23, 24, -9, 34, -9, 48, -20, -9, 10, -20, 16, -43, 24, \"C\", 22, 63, 8, 78, 3, 84, \"z\"].map(function(x) {\n\t\t\treturn isNaN(x) ? x : x*leafScale;\n\t\t}).join(' ');\n\t\tvar translate = [properties.radius-(leafScale*100*0.5),properties.radius*1.5]\n\t\tvar icoLeaf = createSVGElement('path',{\n\t\t\tclass: 'dial__ico__leaf',\n\t\t\td: leafDef,\n\t\t\ttransform: 'translate('+translate[0]+','+translate[1]+')'\n\t\t},svg);\n\t\t\t\n\t\t/*\n\t\t * RENDER\n\t\t */\n\t\tfunction render() {\n\t\t\trenderAway();\n\t\t\trenderHvacState();\n\t\t\trenderTicks();\n\t\t\trenderTargetTemperature();\n\t\t\trenderAmbientTemperature();\n\t\t\trenderLeaf();\n\t\t}\n\t\trender();\n\n\t\t/*\n\t\t * RENDER - ticks\n\t\t */\n\t\tfunction renderTicks() {\n\t\t\tvar vMin, vMax;\n\t\t\tif (self.away) {\n\t\t\t\tvMin = self.ambient_temperature;\n\t\t\t\tvMax = vMin;\n\t\t\t} else {\n\t\t\t\tvMin = Math.min(self.ambient_temperature, self.target_temperature);\n\t\t\t\tvMax = Math.max(self.ambient_temperature, self.target_temperature);\n\t\t\t}\n\t\t\tvar min = restrictToRange(Math.round((vMin-options.minValue)/properties.rangeValue * options.numTicks),0,options.numTicks-1);\n\t\t\tvar max = restrictToRange(Math.round((vMax-options.minValue)/properties.rangeValue * options.numTicks),0,options.numTicks-1);\n\t\t\t//\n\t\t\ttickArray.forEach(function(tick,iTick) {\n\t\t\t\tvar isLarge = iTick==min || iTick==max;\n\t\t\t\tvar isActive = iTick >= min && iTick <= max;\n\t\t\t\tattr(tick,{\n\t\t\t\t\td: pointsToPath(rotatePoints(isLarge ? tickPointsLarge: tickPoints,iTick*theta-properties.offsetDegrees,[properties.radius, properties.radius])),\n\t\t\t\t\tclass: isActive ? 'active' : ''\n\t\t\t\t});\n\t\t\t});\n\t\t}\n\t\n\t\t/*\n\t\t * RENDER - ambient temperature\n\t\t */\n\t\tfunction renderAmbientTemperature() {\n\t\t\tlblAmbient_text.nodeValue = Math.floor(self.ambient_temperature);\n\t\t\tif (self.ambient_temperature%1!=0) {\n\t\t\t\tlblAmbient_text.nodeValue += '⁵';\n\t\t\t}\n\t\t\tvar peggedValue = restrictToRange(self.ambient_temperature, options.minValue, options.maxValue);\n\t\t\tdegs = properties.tickDegrees * (peggedValue-options.minValue)/properties.rangeValue - properties.offsetDegrees;\n\t\t\tif (peggedValue > self.target_temperature) {\n\t\t\t\tdegs += 8;\n\t\t\t} else {\n\t\t\t\tdegs -= 8;\n\t\t\t}\n\t\t\tvar pos = rotatePoint(properties.lblAmbientPosition,degs,[properties.radius, properties.radius]);\n\t\t\tattr(lblAmbient,{\n\t\t\t\tx: pos[0],\n\t\t\t\ty: pos[1]\n\t\t\t});\n\t\t}\n\n\t\t/*\n\t\t * RENDER - target temperature\n\t\t */\n\t\tfunction renderTargetTemperature() {\n\t\t\tlblTarget_text.nodeValue = Math.floor(self.target_temperature);\n\t\t\tsetClass(lblTargetHalf,'shown',self.target_temperature%1!=0);\n\t\t}\n\t\t\n\t\t/*\n\t\t * RENDER - leaf\n\t\t */\n\t\tfunction renderLeaf() {\n\t\t\tsetClass(svg,'has-leaf',self.has_leaf);\n\t\t}\n\t\t\n\t\t/*\n\t\t * RENDER - HVAC state\n\t\t */\n\t\tfunction renderHvacState() {\n\t\t\tArray.prototype.slice.call(svg.classList).forEach(function(c) {\n\t\t\t\tif (c.match(/^dial--state--/)) {\n\t\t\t\t\tsvg.classList.remove(c);\n\t\t\t\t};\n\t\t\t});\n\t\t\tsvg.classList.add('dial--state--'+self.hvac_state);\n\t\t}\n\t\t\n\t\t/*\n\t\t * RENDER - away\n\t\t */\n\t\tfunction renderAway() {\n\t\t\tsvg.classList[self.away ? 'add' : 'remove']('away');\n\t\t}\n\t\t\n\t\t/*\n\t\t * Drag to control\n\t\t */\n\t\tvar _drag = {\n\t\t\tinProgress: false,\n\t\t\tstartPoint: null,\n\t\t\tstartTemperature: 0,\n\t\t\tlockAxis: undefined\n\t\t};\n\t\t\n\t\tfunction eventPosition(ev) {\n\t\t\tif (ev.targetTouches && ev.targetTouches.length) {\n\t\t\t\treturn  [ev.targetTouches[0].clientX, ev.targetTouches[0].clientY];\n\t\t\t} else {\n\t\t\t\treturn [ev.x, ev.y];\n\t\t\t};\n\t\t}\n\t\t\n\t\tvar startDelay;\n\t\tfunction dragStart(ev) {\n\t\t\tstartDelay = setTimeout(function() {\n\t\t\t\tsetClass(svg, 'dial--edit', true);\n\t\t\t\t_drag.inProgress = true;\n\t\t\t\t_drag.startPoint = eventPosition(ev);\n\t\t\t\t_drag.startTemperature = self.target_temperature || options.minValue;\n\t\t\t\t_drag.lockAxis = undefined;\n\t\t\t},1000);\n\t\t};\n\t\t\n\t\tfunction dragEnd (ev) {\n\t\t\tclearTimeout(startDelay);\n\t\t\tsetClass(svg, 'dial--edit', false);\n\t\t\tif (!_drag.inProgress) return;\n\t\t\t_drag.inProgress = false;\n\t\t\tif (self.target_temperature != _drag.startTemperature) {\n\t\t\t\tif (typeof options.onSetTargetTemperature == 'function') {\n\t\t\t\t\toptions.onSetTargetTemperature(self.target_temperature);\n\t\t\t\t};\n\t\t\t};\n\t\t};\n\t\t\n\t\tfunction dragMove(ev) {\n\t\t\tev.preventDefault();\n\t\t\tif (!_drag.inProgress) return;\n\t\t\tvar evPos =  eventPosition(ev);\n\t\t\tvar dy = _drag.startPoint[1]-evPos[1];\n\t\t\tvar dx = evPos[0] - _drag.startPoint[0];\n\t\t\tvar dxy;\n\t\t\tif (_drag.lockAxis == 'x') {\n\t\t\t\tdxy  = dx;\n\t\t\t} else if (_drag.lockAxis == 'y') {\n\t\t\t\tdxy = dy;\n\t\t\t} else if (Math.abs(dy) > properties.dragLockAxisDistance) {\n\t\t\t\t_drag.lockAxis = 'y';\n\t\t\t\tdxy = dy;\n\t\t\t} else if (Math.abs(dx) > properties.dragLockAxisDistance) {\n\t\t\t\t_drag.lockAxis = 'x';\n\t\t\t\tdxy = dx;\n\t\t\t} else {\n\t\t\t\tdxy = (Math.abs(dy) > Math.abs(dx)) ? dy : dx;\n\t\t\t};\n\t\t\tvar dValue = (dxy*getSizeRatio())/(options.diameter)*properties.rangeValue;\n\t\t\tself.target_temperature = roundHalf(_drag.startTemperature+dValue);\n\t\t}\n\t\t\n\t\tsvg.addEventListener('mousedown',dragStart);\n\t\tsvg.addEventListener('touchstart',dragStart);\n\t\t\n\t\tsvg.addEventListener('mouseup',dragEnd);\n\t\tsvg.addEventListener('mouseleave',dragEnd);\n\t\tsvg.addEventListener('touchend',dragEnd);\n\t\t\n\t\tsvg.addEventListener('mousemove',dragMove);\n\t\tsvg.addEventListener('touchmove',dragMove);\n\t\t//\n\t\t\n\t\t/*\n\t\t * Helper functions\n\t\t */\n\t\tfunction restrictTargetTemperature(t) {\n\t\t\treturn restrictToRange(roundHalf(t),options.minValue,options.maxValue);\n\t\t}\n\t\t\n\t\tfunction angle(point) {\n\t\t\tvar dx = point[0] - properties.radius;\n\t\t\tvar dy = point[1] - properties.radius;\n\t\t\tvar theta = Math.atan(dx/dy) / (Math.PI/180);\n\t\t\tif (point[0]>=properties.radius && point[1] < properties.radius) {\n\t\t\t\ttheta = 90-theta - 90;\n\t\t\t} else if (point[0]>=properties.radius && point[1] >= properties.radius) {\n\t\t\t\ttheta = 90-theta + 90;\n\t\t\t} else if (point[0]<properties.radius && point[1] >= properties.radius) {\n\t\t\t\ttheta = 90-theta + 90;\n\t\t\t} else if (point[0]<properties.radius && point[1] < properties.radius) {\n\t\t\t\ttheta = 90-theta+270;\n\t\t\t}\n\t\t\treturn theta;\n\t\t};\n\t\t\n\t\tfunction getSizeRatio() {\n\t\t\treturn options.diameter / targetElement.clientWidth;\n\t\t}\n\t\t\n\t};\n})();\n\n/* ==== */\n(function(scope) {\n    \n    var nest = new thermostatDial(document.getElementById('thermostat-WZ'),{\n    \tonSetTargetTemperature: function(v) {\n    \t\tscope.send({topic: \"target_temperature\", payload: v});\n    \t}\n    });\n\n\n    scope.$watch('msg', function(data) {\n        //console.log(data.topic+\"  \"+data.payload);\n        if (data.topic == \"ambient_temperature\") {\n            nest.ambient_temperature = data.payload;\n        } if (data.topic == \"target_temperature\") {\n            nest.target_temperature = data.payload;\n        } if (data.topic == \"hvac_state\") {\n            nest.hvac_state = data.payload;\n        } if (data.topic == \"has_leaf\") {\n            nest.has_leaf = data.payload;\n        } if (data.topic == \"away\") {\n            nest.away = data.payload;\n        }\n    });\n})(scope);\n\n</script>","storeOutMessages":true,"fwdInMessages":false,"templateScope":"local","x":1030,"y":540,"wires":[["228ed0cb.d3b908"]]},{"id":"773154d5.ab0824","type":"function","z":"15bc8d4b.90af3b","name":"ambient_temperature","func":"msg.topic = \"ambient_temperature\";\nglobal.set(\"wz-ambient\",msg.payload);\nnode.status({text:msg.payload + \"°C\"});\nreturn msg;","outputs":1,"noerr":0,"x":777.6190490722656,"y":388.33333587646484,"wires":[["63e04c4d.bb5f6c"]]},{"id":"1ae634e.7e416cb","type":"function","z":"15bc8d4b.90af3b","name":"hvac_state","func":"global.set(\"wz-state\",msg.payload);\n\nmsg.topic = \"hvac_state\";\nnode.status({text:msg.payload});\nreturn msg;","outputs":1,"noerr":0,"x":1670,"y":340,"wires":[["63e04c4d.bb5f6c"]]},{"id":"228ed0cb.d3b908","type":"function","z":"15bc8d4b.90af3b","name":"target_temperature","func":"if (msg.topic == \"target_temperature\") {\nglobal.set(\"wz-target\",msg.payload);\nreturn msg;\n}","outputs":1,"noerr":0,"x":1210,"y":540,"wires":[["c0dc329a.4f59b","2ab0e1e3.ac211e"]]},{"id":"c0dc329a.4f59b","type":"function","z":"15bc8d4b.90af3b","name":"Temperaturen Vergleichen","func":"context.target = context.target || 0.0;\ncontext.sensor = context.sensor || 0.0;\n\nif (msg.topic === 'sensor_temperature') {\n  context.sensor = msg.payload;\n} else if (msg.topic === 'target_temperature') {\n  context.target = msg.payload;\n} \n\nif (context.target > context.sensor) {\n  return {payload: 1};\n} else {\n  return {payload: 0};\n}\nnode.status({text:msg.payload});","outputs":1,"noerr":0,"x":1460,"y":500,"wires":[["7dc8f30f.3d7064"]]},{"id":"7dc8f30f.3d7064","type":"function","z":"15bc8d4b.90af3b","name":"Farbstatus Nest","func":"msg.topic = \"hvac_state\";\nnode.status({text:msg.payload});\nreturn msg;","outputs":1,"noerr":0,"x":1700,"y":500,"wires":[["74d1ff15.059858"]]},{"id":"74d1ff15.059858","type":"switch","z":"15bc8d4b.90af3b","name":"","property":"payload","propertyType":"msg","rules":[{"t":"eq","v":"0","vt":"str"},{"t":"eq","v":"1","vt":"str"}],"checkall":"true","repair":false,"outputs":2,"x":1310,"y":360,"wires":[["3d83fd16.3def3a"],["42df8b17.773ac4"]]},{"id":"3d83fd16.3def3a","type":"template","z":"15bc8d4b.90af3b","name":"Heizen OFF","field":"payload","fieldType":"msg","format":"handlebars","syntax":"mustache","template":"off","output":"str","x":1490,"y":300,"wires":[["1ae634e.7e416cb"]]},{"id":"42df8b17.773ac4","type":"template","z":"15bc8d4b.90af3b","name":"Heizen ON","field":"payload","fieldType":"msg","format":"handlebars","syntax":"mustache","template":"heating","output":"str","x":1490,"y":380,"wires":[["1ae634e.7e416cb"]]},{"id":"bcc9c2f.902a54","type":"delay","z":"15bc8d4b.90af3b","name":"","pauseType":"delay","timeout":"50","timeoutUnits":"milliseconds","rate":"1","nbRateUnits":"1","rateUnits":"second","randomFirst":"1","randomLast":"5","randomUnits":"seconds","drop":false,"x":540,"y":540,"wires":[["c6c28969.9dbff8","bf2d08a0.229458","c02295c7.804d98","dc76cf88.de8868"]]},{"id":"c6c28969.9dbff8","type":"function","z":"15bc8d4b.90af3b","name":"global target-temp","func":"msg.payload = global.get(\"wz-target\");\nmsg.topic = 'target_temperature';\nnode.status({text:msg.payload + \"°C\"});\nreturn msg;","outputs":1,"noerr":0,"x":770,"y":440,"wires":[["63e04c4d.bb5f6c"]]},{"id":"ae55f012.dbc9e","type":"ui_ui_control","z":"15bc8d4b.90af3b","name":"ui change","x":360,"y":540,"wires":[["bcc9c2f.902a54"]]},{"id":"c02295c7.804d98","type":"function","z":"15bc8d4b.90af3b","name":"global color-state","func":"msg.payload = global.get(\"wz-state\");\nmsg.topic = \"hvac_state\";\nnode.status({text:msg.payload});\nreturn msg;","outputs":1,"noerr":0,"x":770,"y":560,"wires":[["63e04c4d.bb5f6c"]]},{"id":"8872a148.d992","type":"function","z":"15bc8d4b.90af3b","name":"Leaf","func":"minleaf = 15;\nmaxleaf = 22;\ntemperature = msg.payload;\nmsg.payload=false;\nif (temperature >= minleaf){\n    if (temperature <= maxleaf){\n        msg.payload = true;\n    }\n}\nmsg.topic = \"has_leaf\";\nglobal.set(\"wz-leaf\",msg.payload);\nnode.status({text:msg.payload});\nreturn msg;","outputs":1,"noerr":0,"x":810,"y":720,"wires":[["63e04c4d.bb5f6c"]]},{"id":"4737da1d.f282fc","type":"mqtt in","z":"15bc8d4b.90af3b","name":"","topic":"homeland/haushalt/heizung/Wohnzimmer_Thermostat/desiredTemperature","qos":"2","broker":"34eb4531.5459e2","x":350,"y":720,"wires":[["137bccd8.b66d93","8872a148.d992","19428fcb.0c576"]],"outputLabels":["target_temperature"]},{"id":"b09e0a1f.5122c8","type":"mqtt in","z":"15bc8d4b.90af3b","name":"","topic":"homeland/haushalt/heizung/Wohnzimmer_Thermostat/temperature","qos":"2","broker":"34eb4531.5459e2","x":360,"y":300,"wires":[["773154d5.ab0824","53e6b42a.aabbe4"]],"outputLabels":["ambient_temperature"]},{"id":"137bccd8.b66d93","type":"function","z":"15bc8d4b.90af3b","name":"target_temperature","func":"msg.topic = 'target_temperature';\nglobal.set(\"wz-target\",msg.payload);\nnode.status({text:msg.payload + \"°C\"});\nreturn msg;","outputs":1,"noerr":0,"x":770,"y":680,"wires":[["63e04c4d.bb5f6c","c0dc329a.4f59b"]]},{"id":"7f9ed5e0.953424","type":"mqtt out","z":"15bc8d4b.90af3b","name":"","topic":"homeland/haushalt/heizung/Wohnzimmer_Thermostat/desiredTemperature/set","qos":"2","retain":"false","broker":"34eb4531.5459e2","x":2040,"y":620,"wires":[]},{"id":"bf2d08a0.229458","type":"function","z":"15bc8d4b.90af3b","name":"global target-temp","func":"msg.payload = global.get(\"wz-ambient\");\nmsg.topic = 'ambient_temperature';\nnode.status({text:msg.payload + \"°C\"});\nreturn msg;","outputs":1,"noerr":0,"x":770,"y":500,"wires":[["63e04c4d.bb5f6c"]]},{"id":"dc76cf88.de8868","type":"function","z":"15bc8d4b.90af3b","name":"global target-temp","func":"msg.payload = global.get(\"wz-leaf\");\nmsg.topic = 'has_leaf';\nnode.status({text:msg.payload});\nreturn msg;","outputs":1,"noerr":0,"x":770,"y":620,"wires":[["63e04c4d.bb5f6c"]]},{"id":"a84aee83.b857b","type":"mqtt in","z":"15bc8d4b.90af3b","name":"","topic":"homeland/haushalt/heizung/Wohnzimmer_Thermostat/humidity","qos":"2","broker":"34eb4531.5459e2","x":350,"y":120,"wires":[["6ea3b9b3.85c2e"]],"outputLabels":["humidity"]},{"id":"b04d3a0d.8c6ba8","type":"trigger","z":"15bc8d4b.90af3b","op1":"","op2":"","op1type":"nul","op2type":"payl","duration":"4","extend":true,"units":"s","reset":"","bytopic":"all","name":"","x":1680,"y":620,"wires":[["7f9ed5e0.953424"]]},{"id":"8b86a571.ef3fb","type":"mqtt in","z":"15bc8d4b.90af3b","name":"","topic":"homeland/haushalt/heizung/Wohnzimmer_Thermostat/voc","qos":"2","broker":"34eb4531.5459e2","x":330,"y":180,"wires":[["4b4c2777.fcd59"]]},{"id":"4b4c2777.fcd59","type":"function","z":"15bc8d4b.90af3b","name":"","func":"node.status({text:msg.payload});\nbad = 'Air: <div class=\"fa fa-thumbs-down fa-2x nr-dashboard-error\"></div>';\nok = 'Air: <div class=\"fa fa-thumbs-up fa-2x nr-dashboard-warning\"></div>';\ngood = 'Air: <div class=\"fa fa-thumbs-up fa-2x nr-dashboard-ok\"></div>';\nif (msg.payload > 1750) { \n    msg.payload = (bad);\n    msg.topic = 'air';\n}\nif (msg.payload <= 1750 && msg.payload > 750) {\n    msg.payload = (ok);\n    msg.topic = 'air';\n}\nif (msg.payload <= 750) {\n    msg.payload = (good);\n    msg.topic = 'air';\n}\nreturn msg;","outputs":1,"noerr":0,"x":670,"y":180,"wires":[["44ad4257.3b5dec"]]},{"id":"45291976.3021f","type":"mqtt in","z":"15bc8d4b.90af3b","name":"","topic":"homeland/haushalt/heizung/Wohnzimmer_Thermostat/windowOpen","qos":"2","broker":"34eb4531.5459e2","x":360,"y":240,"wires":[["3708d80.98e8aa8"]]},{"id":"3708d80.98e8aa8","type":"function","z":"15bc8d4b.90af3b","name":"","func":"node.status({text:msg.payload});\ntext = '<div class=\"nr-dashboard-warning\">Window</div>';\nif (msg.payload == \"1\") {\n    msg.payload = (text);\n    msg.topic = 'window';\n}\nreturn msg;","outputs":1,"noerr":0,"x":670,"y":240,"wires":[["44ad4257.3b5dec"]]},{"id":"f6640250.4d957","type":"ui_text","z":"15bc8d4b.90af3b","group":"d2bea201.d68888","order":1,"width":"0","height":"0","name":"Temperatur/Luftfeuchtigkeit","label":"{{msg.payload.humidity.payload}}","format":"{{msg.payload.air.payload}} {{msg.payload.window.payload}}","layout":"row-spread","x":1040,"y":180,"wires":[]},{"id":"6ea3b9b3.85c2e","type":"function","z":"15bc8d4b.90af3b","name":"","func":"node.status({text:msg.payload});\ntext1 = '<div class=\"fa fa-tint fa-2x nr-dashboard-dim\"></div> ';\ntext2 = ' %';\nmsg.payload = (text1 + msg.payload + text2);\nmsg.topic = 'humidity';\nreturn msg;","outputs":1,"noerr":0,"x":670,"y":120,"wires":[["44ad4257.3b5dec"]]},{"id":"44ad4257.3b5dec","type":"join","z":"15bc8d4b.90af3b","name":"","mode":"custom","build":"object","property":"","propertyType":"full","key":"topic","joiner":"\\n","joinerType":"str","accumulate":true,"timeout":"","count":"1","reduceRight":false,"reduceExp":"","reduceInit":"","reduceInitType":"","reduceFixup":"","x":830,"y":180,"wires":[["f6640250.4d957"]]},{"id":"2ab0e1e3.ac211e","type":"function","z":"15bc8d4b.90af3b","name":"Nur bei Abweichung senden","func":"if (msg.topic === 'target_temperature_old')\n{\n  oldvalue = msg.payload;\n} \nelse if (msg.topic === 'target_temperature')\n{\n  newvalue = msg.payload;\n} \n\nif (oldvalue != newvalue) {\n  oldvalue = newvalue;\n  return {payload: newvalue};\n}","outputs":1,"noerr":0,"x":1460,"y":620,"wires":[["b04d3a0d.8c6ba8"]]},{"id":"19428fcb.0c576","type":"function","z":"15bc8d4b.90af3b","name":"target_temperature_old","func":"msg.topic = 'target_temperature_old';\nnode.status({text:msg.payload + \"°C\"});\nreturn msg;","outputs":1,"noerr":0,"x":790,"y":760,"wires":[["2ab0e1e3.ac211e"]]},{"id":"d2bea201.d68888","type":"ui_group","z":"","name":"Wohnzimmer","tab":"7a08a4e0.9f9cf4","order":1,"disp":true,"width":"7","collapse":false},{"id":"34eb4531.5459e2","type":"mqtt-broker","z":"","name":"","broker":"192.168.0.8","port":"1883","clientid":"","usetls":false,"compatmode":true,"keepalive":"60","cleansession":true,"willTopic":"homeland/haushalt/steuerung/nodered/$online","willQos":"0","willPayload":"false","birthTopic":"","birthQos":"0","birthPayload":""},{"id":"7a08a4e0.9f9cf4","type":"ui_tab","z":"","name":"Klima","icon":"dashboard","order":1}]
Titel: Antw:Node-Red als Frontend
Beitrag von: SamNitro am 16 Mai 2018, 19:34:20
Hat jemand eine schöne Lösung einen WeekdayTimer zu realisieren, wie beim TabletUI?
https://forum.fhem.de/index.php/topic,48106.msg397622.html#msg397622 (https://forum.fhem.de/index.php/topic,48106.msg397622.html#msg397622)
Titel: Antw:Node-Red als Frontend
Beitrag von: Master_Nick am 16 Mai 2018, 20:05:48
 ;) Alos ich nicht, da ich dafür aber auch irgendwie keinerlei Verwendung habe.
Jede tiefere Schaltung die nicht an aus ist frühstücke ich in FHEM ab.
Machbar sind diverse Logiken aber generell auch in Node-Red.
Titel: Antw:Node-Red als Frontend
Beitrag von: SamNitro am 16 Mai 2018, 20:25:44
Ja ein paar Lösungen habe ich auch schon aber leider nicht so komfortabel wie der WeekdayTimer.

node-red-contrib-schedex:
Schöne Funktion mit sunrise sunset.

node-red-contrib-simple-weekly-scheduler:
Einfache Schaltungen.

Aber beide haben eine kleines Problem die wollen immer einen aus und an Befehl haben. Wenn man nur eins haben will muss man filtern.
Titel: Antw:Node-Red als Frontend
Beitrag von: Master_Nick am 23 Mai 2018, 12:17:10
Das kannste dir doch bestimmt mit einem Switch auf JS Basis umbauen.
Oder was meinst du mit AN/AUS Befehl?
Titel: Antw:Node-Red als Frontend
Beitrag von: SamNitro am 23 Mai 2018, 12:25:13
Die fertigen Nodes sind so ausgelegt das man eine einschaltzeit und eine ausschaltzeit programmieren muss.

Wenn ich jetzt aber z.B. einen Rollladen nur um 09:00 Uhr hochfahren möchte muss ich die andere Zeit filtern.

Schön wäre hier ein Node wo man pro Position die man hinzufügt nur eine schalt Befehl hat.

Als Eingabe dann
-Zeit
-On/Off oder Position
-ggf. noch der Wochentag


Mobil unterwegs!
Titel: Antw:Node-Red als Frontend
Beitrag von: Absolute Beginner am 25 Mai 2018, 15:27:51
Lange nicht mehr hier gewesen. Ich möchte kurz über meine Erfahrungen berichten. Mit Node-RED bin ich nach wie vor begeistert unterwegs. Die Möglichkeiten der Bedienoberfläche sind gut, und ich habe diese auf meine Bedürfnisse für ein Smartphone optimiert (siehe Screenshot). Dabei geht es mir um eine Ein-Seiten-Darstellung aller relevanten Informationen (Licht, Rollos, Heizung, Anwesenheit, Temperaturen und Wetter. Solange alles 'im grünen Bereich' ist, sehe ich dies auf einem Blick - sonst tauchen andere Farben auf. So schön und praktisch Slider auch sind - ich habe sie gegen Pull-down-Menüs ausgetauscht, weil sie beim Wischen ständig ungewollt Aktionen ausführten. Auf weiteren Seiten sind Anrufliste, Systemkontrollen und Ansichten von Videokameras.

Angefangen habe ich mit der Brückenfunktion FHEM-MQTT-NodeRED. Inzwischen habe ich mehr und mehr direkt in NodeRED umgesetzt (8266-basierte WLAN-Produkte, Intertechno, Anrufliste, Wettervorhersage, IKEA Tradfri und Alexa-Sprachbedienung), sodass heute nur noch HomeMatic und SOMFY (über CUL) und Viessmann-Heizung (über USB) mittels FHEM/MQTT laufen.

Ich würde mich natürlich freuen, wenn hier mehr Aktivität zum Thema zu finden ist. Gerne beantworte ich Fragen zu meiner Umsetzung.
Titel: Antw:Node-Red als Frontend
Beitrag von: SamNitro am 25 Mai 2018, 21:48:51
Hier mal eine kleine Übersicht von mir. Alles noch ziemlich am Anfang.
Titel: Antw:Node-Red als Frontend
Beitrag von: oetti77 am 23 Juni 2018, 12:24:35
Hallo zusammen,

ich habe mich mal an euren Beispielen orientiert, und habe aktuell eine HUE als Switch im Dashboard zum Testen.
Den State der HUE habe ich per Funktion auseinander genommen, da dieser nicht nur "on","off", sondern auch "dimXX%" sein kann, und dann per String an den Switch übergeben - funktioniert soweit.

Was mir nicht ganz klar ist, wie die Änderung im Dashboard wieder zurück zum FHEM kommt. Nehmt ihr das gleiche Topic, wie für den Input? Was muss im FHEM noch gemacht werden? Reicht da ein Subscribe auf das Topic?

Fragen über Fragen - ich hoffe ihr könnt mir helfen :-)

Danke
Chris

Erledigt! Habe mir nochmal genau Shojo's Flow angeschaut, da ist mir das Prinzipp bewusst geworden  ;D
Titel: Antw:Node-Red als Frontend
Beitrag von: Master_Nick am 25 Juni 2018, 11:39:23
Jeder Device hat ein Topic auf dem er lauscht und danach schaltet und ein Topic auf dem er Rückmeldung gibt.

Beispiel fürs schalten:  wohnung/elektrik/sonoff/sonoff-s20-3/relay/set

Beispiel fürs Rückmelden:  wohnung/elektrik/sonoff/sonoff-s20-3/relay/

Beim ersten würde FHEM ein "set true" auf das topic "wohnung/elektrik/sonoff/sonoff-s20-3/relay/set"  abliefern (publish) beim 2. bekommt FHEM die Rückmeldung über den tatsächlichen Zustand (subscribe auf wohnung/elektrik/sonoff/sonoff-s20-3/relay/).
Konvertierungen von Strings und Co. sind mit NodeRed kein problem. Zur not einfach mal Google anhauen oder deinen Flow mal hier posten.
Titel: Antw:Node-Red als Frontend
Beitrag von: Master_Nick am 09 August 2018, 20:49:25
 ;D Mahlzeit, ich wollte mal wieder meine aktuellen Stand zeigen.

Ich habe mir auf jedem Tab einen globalen Bereich eingerichtet um wichtige Aktoren oder Status immer zu sehen.

Das Ganze hängt weiter im Flur mittels Touchscreen und Bewegungsmelder der im Rahmen des Screens eingelassen ist (Bilderrahmen).
Ansonsten per Handy/Tablet/PC natürlich auch erreichbar solange man im WLAN ist. Als neustes habe ich nun Waschmaschine und Trockner mit eingebunden über jeweils eine TP Link HS110 (Modul für FHEM gab es schon - merkte ich als ich fast schon am bauen war mit einem python Client  ;D ).
Die Waschmaschine und der Trockner haben im abgeschalteten Modus lediglich ein normales Bild - werden sie aktiv so zeigt Node-Red jeweils ein Gif an. Das war einfacher als das ganze selber zu animieren mittes Javascript oder ähnlichem.

Das Gif der Waschmaschine ist mal eben 10 MB :-D das wollte nicht hier im Forum laden ;-)
Das hier ist das Orignal:  https://cdn.dribbble.com/users/893939/screenshots/2223098/washing_machine2.gif (meines ist geschnitten, dass es keinen Rand hat)

Hier kann man es abgefilmt von meinem Touchpanel sehen: https://youtu.be/BkjPVJx2zeQ
Titel: Antw:Node-Red als Frontend
Beitrag von: Master_Nick am 29 Oktober 2018, 19:27:39
Wer sich nach einem Update von NodeRed oder NodeJS ärgert über die hässliche Ansicht seiner Sachen sollte folgende Option prüfen:,

https://forum.fhem.de/index.php?action=dlattach;topic=78512.0;attach=107876;image

Bei mir sahen die Nest Thermometer auf einmal grausig aus....

Titel: Antw:Node-Red als Frontend
Beitrag von: Biervögelhasso am 05 November 2018, 15:50:33
Hallo,

ich habe ein kleines Problem mit Node-RED und komme an dieser Stelle nicht weiter. Ich habe mir ein kleines Dashboard gebaut und kann vom Handy darauf zugreifen. ( Node-Red läuft auf einen PI, Handy ist im WLAN verbunden) Das Problem ist nun leider das ich bei jeden neuem Aufruf meinen Login für Node-Red eingeben muß.  Ist dies bei Euch auch so? Wie habt ihr das gelöst?

Und noch eine zweite Frage. Wie habt ihr das Dashboard aufgebaut. Hab ihr zwei verschiedene eins fürs Handy und eins fürs Tablet?


Vielen Dank für Eure Hilfe.

Titel: Antw:Node-Red als Frontend
Beitrag von: Master_Nick am 05 November 2018, 15:54:52
NodeRed ist bei richtiger Nutzung 100% responsive - soll heißen es passt sich an die Geräte auf denen es aufgerufen wird anhand der erkannten nutzbaren Bildschirmgröße an und sortiert die verschiedenen Elemente so, dass es gut passt.

Ich habe somit genau eine Instanz NodeRed zuhause laufen.

Warum genau nutzt du Zugangsdaten? Bei mir ist das WLAN über Zugangsdaten gesichert, das NodeRed selber kann man sobald man in diesem WLAN ist einfach aufrufen ohne jede Authentifizierung.
Titel: Antw:Node-Red als Frontend
Beitrag von: Biervögelhasso am 06 November 2018, 13:23:35
Zitat von: Master_Nick am 05 November 2018, 15:54:52
NodeRed ist bei richtiger Nutzung 100% responsive - soll heißen es passt sich an die Geräte auf denen es aufgerufen wird anhand der erkannten nutzbaren Bildschirmgröße an und sortiert die verschiedenen Elemente so, dass es gut passt.

Ich habe somit genau eine Instanz NodeRed zuhause laufen.

Warum genau nutzt du Zugangsdaten? Bei mir ist das WLAN über Zugangsdaten gesichert, das NodeRed selber kann man sobald man in diesem WLAN ist einfach aufrufen ohne jede Authentifizierung.

Hallo Master_Nick,

vielen Dank für die Information. Ich habe das Passwort deaktiviert. Nun kann man sehr schnell auf das Dashboard zugreifen.
Ich hätte gern zwei Dashboards da ich auf dem Handy andere Informationen sehen möchte wie auf dem Tablet.  Ich werde nun aber mit dem Dashboard für das Tablet beginnen und dann weiter schauen.

Viele Grüße.
Titel: Antw:Node-Red als Frontend
Beitrag von: Master_Nick am 06 November 2018, 14:36:00
Dann mach dir doch mehrere Tabs?

Und rufe diesen dann jeweils mit seiner festen Adresse auf:

http://IP:1880/ui/#/0 <- wäre der erste Tab z.B für dein Handy
http://IP:1880/ui/#/1 <- wäre der zweite

Und dann kann man dennoch zwischen beiden Wechseln, also sie sind nicht voreinander versteckt - wobei man das Top Menü abschalten kann glaub ich und auch den Swipe zwischen den Tabs kann man deaktivieren.
Dann hättest du deine 2 Uis
Titel: Antw:Node-Red als Frontend
Beitrag von: Biervögelhasso am 09 November 2018, 09:26:20
Zitat von: Master_Nick am 06 November 2018, 14:36:00
Dann mach dir doch mehrere Tabs?

Und rufe diesen dann jeweils mit seiner festen Adresse auf:

http://IP:1880/ui/#/0 <- wäre der erste Tab z.B für dein Handy
http://IP:1880/ui/#/1 <- wäre der zweite

Und dann kann man dennoch zwischen beiden Wechseln, also sie sind nicht voreinander versteckt - wobei man das Top Menü abschalten kann glaub ich und auch den Swipe zwischen den Tabs kann man deaktivieren.
Dann hättest du deine 2 Uis

Hi,
genaus so werde ich es machen. Ich kann nur sagen NODE-RED ist genau das was mir noch gefehlt hat. Ich fand es immer sehr aufwändig mit dem FHEM Tablet UI.
Ich besitze leider keine Programmierkenntnisse aber in nur 3 Tage habe ich mir meine Tablet Seite zusammengebaut. Das Dashboard läuft viel flüssiger auf meinem Samsung TAB3.
Ein paar kleine Nachteile gibt es leider auch. NODE-RED besitzt nicht so schöne Icons. Ein Icon für die Jalousie die langsam runter fährt habe ich leider noch nicht gefunden.
Auch Icons für die Garage oder offene Fenster konnte ich nicht finden.

Titel: Antw:Node-Red als Frontend
Beitrag von: Master_Nick am 09 November 2018, 13:09:27
Du kannst dir eigene Gifs bauen und einbinde, so wie ich es bei meinem Trockner und der Waschmaschine gemacht habe.
Titel: Antw:Node-Red als Frontend
Beitrag von: SamNitro am 09 November 2018, 16:30:54
Garage und Fenster kann ich dir heute Abend rein stellen.



Edit:


[
    {
        "id": "d46c7631.59f9a",
        "type": "function",
        "z": "9d8c568.24a8628",
        "name": "check",
        "func": "if (msg.payload === \"ON\"){\n    msg.payload = \"#FF0000\";\n    return msg;\n}else if (msg.payload === \"OFF\"){\n    msg.payload = \"#00FF00\";\n    return msg;\n}\n",
        "outputs": 1,
        "noerr": 0,
        "x": 410,
        "y": 1320,
        "wires": [
            [
                "1673ba21.82cede"
            ]
        ]
    },
    {
        "id": "1673ba21.82cede",
        "type": "ui_template",
        "z": "9d8c568.24a8628",
        "group": "bf451414.e71f48",
        "name": "Status Garage LED",
        "order": 15,
        "width": "0",
        "height": "0",
        "format": "<style>\n.led {\n    float: right;\n    padding: 3px;\n    width: 10px;\n    height: 10px;\n    margin: 5px 5px 5px 5px;\n    border-radius: 50%;\n    \n}\n</style>\n\n<div>Status Garage<span class=\"led\" style=\"background-color: {{msg.payload}}; box-shadow: black 0 -1px 1px 0px, inset black  0 -1px 4px, {{msg.payload}} 0 3px 15px;\"></span></div>\n",
        "storeOutMessages": true,
        "fwdInMessages": true,
        "templateScope": "local",
        "x": 690,
        "y": 1320,
        "wires": [
            []
        ]
    },
    {
        "id": "bf451414.e71f48",
        "type": "ui_group",
        "z": "",
        "name": "FHEM",
        "tab": "dbb46b72.63ce3",
        "order": 1,
        "disp": true,
        "width": "6",
        "collapse": false
    },
    {
        "id": "dbb46b72.63ce3",
        "type": "ui_tab",
        "z": "",
        "name": "Home",
        "icon": "dashboard",
        "order": 1
    }
]
Titel: Antw:Node-Red als Frontend
Beitrag von: Biervögelhasso am 10 November 2018, 14:32:00
Hallo SamNitro,

ich habe mich vielleicht falsch ausgedrückt. Ich suche direkt ein Icon Garage oder offenes Fenster wie ich es im FHEM Tablet UI hatte.
Wenn ich Zeit habe schau ich mir den Vorschlag von Master_Nick an. Er hat eigene GIFs erstellt und eingebaut.

Viele Grüße
Titel: Antw:Node-Red als Frontend
Beitrag von: stefanm am 27 Dezember 2018, 16:02:39
Hallo

und erstmal danke für die tolle Arbeit.

Ich bin dabei auch alles auf Node-Red umzustellen.
Was nur schwierig ist, sind die Icons, die ich gerne verwenden würde. In Fhem sind ja die OpenAutomation- Icons drin.
Hat jemand eine Idee wie ich die in Node Red einbinden kann ?

Stefan
Titel: Antw:Node-Red als Frontend
Beitrag von: Master_Nick am 27 Dezember 2018, 17:37:12
Du wirst sie zum einen für die Browser die NodeRed aufrufen sollen zugänglich machen müssen und dann einzeln mittels HTLM Code in den Nodes einbinden müssen.
Titel: Antw:Node-Red als Frontend
Beitrag von: stefanm am 28 Dezember 2018, 08:49:31
Hem das dürfte schwierig werden, wenn ich das Frontend auf einem Tablett verwenden will.

Da muss einen andere Weg geben. Bei den Weather Icons klappt das ja auch
Titel: Antw:Node-Red als Frontend
Beitrag von: Master_Nick am 28 Dezember 2018, 13:21:29
Warum genau sollte das nun schwierig werden?
Ich nutze NodeRed auf einem 10" Touchscreen, am PC und am Handy.
Und mache es genau wie ich es mitteilte.
Titel: Antw:Node-Red als Frontend
Beitrag von: stefanm am 28 Dezember 2018, 13:40:24
Für mich schon :)
Ich bin nicht der Programmierprofie und bin froh das ich es so weit geschafft habe.

danke für den Hinweis. Da weis ich wonach ich suchen muss.

Guten Rutsch
Titel: Antw:Node-Red als Frontend
Beitrag von: Master_Nick am 28 Dezember 2018, 13:56:58
 ;) Naja dann sag an woran es hapert und man kann dir helfen :-)

Pauschal sagen ist schwierig - da bleibt mir ja nichts anderes als zu sagen - seh ich anders :-D

Magst du mir mal ein Beispiel zeigen mit dem, was du mit Weathericons meinst? :-)
Titel: Antw:Node-Red als Frontend
Beitrag von: stefanm am 30 Dezember 2018, 12:36:21
Bei den Weathericons, wurden die Icons als Webfont Importiert,
ich müsste also erstmal aus dem den Icons einen Webfont erzeugen um sie Importieren zu können.

Wie ist das denn bei fhem gelöst ? Schön währe, wenn man es auch für andere User vereinfachen könnte.

Titel: Antw:Node-Red als Frontend
Beitrag von: atmelfreak am 31 Dezember 2018, 17:20:26
Hi,

ich habe mich die letzten Tage auch etwas mi Node Red beschäftigt. Zur Übung habe ich mal eine Überwachung des Raspberry Pi gebastelt. Falls Interesse besteht, hier der exportierte Flow:

[{"id":"a28b638e.db624","type":"tab","label":"RPI Monitoring","disabled":false,"info":""},{"id":"407cf732.097d18","type":"ui_gauge","z":"a28b638e.db624","name":"","group":"818f0d45.f2e81","order":1,"width":0,"height":0,"gtype":"gage","title":"CPU Temperature","label":"°C","format":"{{value}}","min":"20","max":"80","colors":["#00b500","#e6e600","#ca3838"],"seg1":"","seg2":"","x":650,"y":40,"wires":[]},{"id":"d2699e9.1f7db6","type":"exec","z":"a28b638e.db624","command":"vcgencmd measure_temp","addpay":false,"append":"","useSpawn":"","timer":"","name":"RPi Temp.","x":290,"y":80,"wires":[["e84257f7.c0e9f8"],[],[]]},{"id":"21cdec28.7154f4","type":"inject","z":"a28b638e.db624","name":"","topic":"","payload":"","payloadType":"date","repeat":"30","crontab":"","once":false,"onceDelay":"","x":104.5,"y":88.25,"wires":[["d2699e9.1f7db6","99e03ce7.ae41f"]]},{"id":"e84257f7.c0e9f8","type":"function","z":"a28b638e.db624","name":"","func":"var result = msg.payload.match( /(\\d+[\\.]?\\d+)/ );\nmsg.payload = result[1];\nreturn msg;\n","outputs":1,"noerr":0,"x":450,"y":60,"wires":[["407cf732.097d18","ffe418a8.bce8b8"]]},{"id":"d6216760.0d0478","type":"ui_button","z":"a28b638e.db624","name":"","group":"2dff1e85.bfb782","order":2,"width":0,"height":0,"label":"Reboot","color":"","bgcolor":"","icon":"","payload":"","payloadType":"str","topic":"","x":80,"y":440,"wires":[["e17e61e1.2998c"]]},{"id":"e17e61e1.2998c","type":"exec","z":"a28b638e.db624","command":"sudo reboot","addpay":false,"append":"","useSpawn":"","timer":"","name":"Reboot","x":298,"y":440,"wires":[[],[],[]]},{"id":"3f3ad9b4.29eab6","type":"ui_button","z":"a28b638e.db624","name":"","group":"2dff1e85.bfb782","order":3,"width":0,"height":0,"label":"Shutdown","color":"","bgcolor":"red","icon":"","payload":"","payloadType":"str","topic":"","x":80,"y":500,"wires":[["69f45d46.9f9494"]]},{"id":"69f45d46.9f9494","type":"exec","z":"a28b638e.db624","command":"sudo shutdown -h now","addpay":false,"append":"","useSpawn":"","timer":"","name":"Shutdown","x":300,"y":500,"wires":[[],[],[]]},{"id":"ffe418a8.bce8b8","type":"ui_chart","z":"a28b638e.db624","name":"CPU Temperatur Chart","group":"818f0d45.f2e81","order":2,"width":0,"height":0,"label":"","chartType":"line","legend":"false","xformat":"HH:mm:ss","interpolate":"linear","nodata":"","dot":false,"ymin":"","ymax":"","removeOlder":"24","removeOlderPoints":"","removeOlderUnit":"3600","cutout":0,"useOneColor":false,"colors":["#1f77b4","#aec7e8","#ff7f0e","#2ca02c","#98df8a","#d62728","#ff9896","#9467bd","#c5b0d5"],"useOldStyle":true,"x":670,"y":80,"wires":[[],[]]},{"id":"22efa698.36131a","type":"ui_gauge","z":"a28b638e.db624","name":"CPU Idle","group":"e2100723.afe1a8","order":1,"width":0,"height":0,"gtype":"gage","title":"CPU Idle","label":"%","format":"{{value}}","min":0,"max":"100","colors":["#c70000","#e6e600","#00bd26"],"seg1":"","seg2":"","x":620,"y":220,"wires":[]},{"id":"7b7d126b.397c4c","type":"ui_gauge","z":"a28b638e.db624","name":"","group":"1af884fa.4a3a8b","order":1,"width":0,"height":0,"gtype":"gage","title":"Memory Usage","label":"%","format":"{{value}}","min":0,"max":"100","colors":["#00b500","#e6e600","#ca3838"],"seg1":"","seg2":"","x":640,"y":300,"wires":[]},{"id":"b1706aa.8d81098","type":"exec","z":"a28b638e.db624","command":"df -h | grep root","addpay":false,"append":"","useSpawn":"","timer":"","oldrc":false,"name":"Disk Usage","x":290,"y":380,"wires":[["1310c126.2c7adf"],[],[]]},{"id":"c78bd1ed.91359","type":"ui_gauge","z":"a28b638e.db624","name":"","group":"acbc6696.90d688","order":1,"width":0,"height":0,"gtype":"gage","title":"Disk","label":"Usage","format":"{{value}}","min":0,"max":"100","colors":["#00b500","#e6e600","#ca3838"],"x":610,"y":380,"wires":[]},{"id":"1310c126.2c7adf","type":"function","z":"a28b638e.db624","name":"","func":"var result = msg.payload.match( /(\\d+)%/ );\nmsg.payload = result[1];\nreturn msg;","outputs":1,"noerr":0,"x":450,"y":380,"wires":[["c78bd1ed.91359"]]},{"id":"419d4b23.127794","type":"inject","z":"a28b638e.db624","name":"","topic":"","payload":"","payloadType":"date","repeat":"300","crontab":"","once":false,"onceDelay":"","x":90,"y":380,"wires":[["b1706aa.8d81098"]]},{"id":"854b4d00.01775","type":"ui_chart","z":"a28b638e.db624","name":"CPU Usage Chart","group":"e2100723.afe1a8","order":2,"width":0,"height":0,"label":"","chartType":"line","legend":"false","xformat":"HH:mm:ss","interpolate":"linear","nodata":"","dot":false,"ymin":"","ymax":"","removeOlder":"24","removeOlderPoints":"","removeOlderUnit":"3600","cutout":0,"useOneColor":false,"colors":["#1f77b4","#aec7e8","#ff7f0e","#2ca02c","#98df8a","#d62728","#ff9896","#9467bd","#c5b0d5"],"useOldStyle":true,"x":650,"y":260,"wires":[[],[]]},{"id":"29c75f42.b7a14","type":"ui_chart","z":"a28b638e.db624","name":"Memory Usage Chart","group":"1af884fa.4a3a8b","order":2,"width":0,"height":0,"label":"","chartType":"line","legend":"false","xformat":"HH:mm:ss","interpolate":"linear","nodata":"","dot":false,"ymin":"","ymax":"","removeOlder":"24","removeOlderPoints":"","removeOlderUnit":"3600","cutout":0,"useOneColor":false,"colors":["#1f77b4","#aec7e8","#ff7f0e","#2ca02c","#98df8a","#d62728","#ff9896","#9467bd","#c5b0d5"],"useOldStyle":true,"x":660,"y":340,"wires":[[],[]]},{"id":"b8f4396c.80d088","type":"ui_gauge","z":"a28b638e.db624","name":"","group":"1a84a357.f6b1fd","order":1,"width":0,"height":0,"gtype":"gage","title":"CPU Load","label":"","format":"{{value}}","min":0,"max":10,"colors":["#00b500","#e6e600","#ca3838"],"seg1":"","seg2":"","x":630,"y":140,"wires":[]},{"id":"a734871f.bfbcc8","type":"ui_chart","z":"a28b638e.db624","name":"CPU Load Chart","group":"1a84a357.f6b1fd","order":2,"width":0,"height":0,"label":"","chartType":"line","legend":"false","xformat":"HH:mm:ss","interpolate":"linear","nodata":"","dot":false,"ymin":"","ymax":"","removeOlder":"24","removeOlderPoints":"","removeOlderUnit":"3600","cutout":0,"useOneColor":false,"colors":["#1f77b4","#aec7e8","#ff7f0e","#2ca02c","#98df8a","#d62728","#ff9896","#9467bd","#c5b0d5"],"useOldStyle":true,"x":640,"y":180,"wires":[[],[]]},{"id":"99e03ce7.ae41f","type":"exec","z":"a28b638e.db624","command":"top -d 1 -b -n2 | grep \"load average\\|Cpu(s)\\|KiB Mem\" | tail -3","addpay":false,"append":"","useSpawn":"false","timer":"","oldrc":false,"name":"CPU top","x":280,"y":160,"wires":[["a484a63.4bfac58"],[],[]]},{"id":"a484a63.4bfac58","type":"function","z":"a28b638e.db624","name":"Parse CPU top cmd","func":"var line = msg.payload.split(\"\\n\")[0];\nvar result = line.match( /load\\saverage:\\s(\\d+[.,]\\d+)/i );\nvar cpu_load1 = result[1].replace(\",\", \".\");\nvar msg1 = { payload: cpu_load1};\n\nvar line = msg.payload.split(\"\\n\")[1];\nresult = line.match( /(\\d+[.,]\\d+)\\sid,/i );\nvar cpu_idle = result[1].replace(\",\", \".\");\nvar msg2 = { payload: cpu_idle};\n\nvar line = msg.payload.split(\"\\n\")[2];\nresult = line.match( /\\s+(\\d+)\\s+total,\\s+(\\d+)\\s+free,\\s+(\\d+)\\s+used,/i );\nvar mem_total = result[1].replace(\",\", \".\");\nvar mem_used = result[3].replace(\",\", \".\");\nvar mem_used = parseFloat(mem_used) / parseFloat(mem_total) * 100;\nvar msg3 = { payload: mem_used.toFixed(2)};\n\nreturn [msg1, msg2, msg3];","outputs":3,"noerr":0,"x":320,"y":240,"wires":[["b8f4396c.80d088","a734871f.bfbcc8"],["22efa698.36131a","854b4d00.01775"],["7b7d126b.397c4c","29c75f42.b7a14"]]},{"id":"818f0d45.f2e81","type":"ui_group","z":"","name":"CPU Temperatur","tab":"e49b76a0.b0c5e8","order":4,"disp":false,"width":"6","collapse":false},{"id":"2dff1e85.bfb782","type":"ui_group","z":"","name":"Actions","tab":"e49b76a0.b0c5e8","order":6,"disp":true,"width":"6","collapse":false},{"id":"e2100723.afe1a8","type":"ui_group","z":"","name":"CPU Usage","tab":"e49b76a0.b0c5e8","order":1,"disp":false,"width":"6","collapse":false},{"id":"1af884fa.4a3a8b","type":"ui_group","z":"","name":"Memory Usage","tab":"e49b76a0.b0c5e8","order":3,"disp":false,"width":"6","collapse":false},{"id":"acbc6696.90d688","type":"ui_group","z":"","name":"Disk Usage","tab":"e49b76a0.b0c5e8","order":5,"disp":false,"width":"6","collapse":false},{"id":"1a84a357.f6b1fd","type":"ui_group","z":"","name":"CPU Load","tab":"e49b76a0.b0c5e8","order":2,"disp":false,"width":"6","collapse":false},{"id":"e49b76a0.b0c5e8","type":"ui_tab","z":"","name":"RPi Control","icon":"dashboard","order":4}]

Titel: Antw:Node-Red als Frontend
Beitrag von: atmelfreak am 31 Dezember 2018, 17:25:47
Und hier mein Energieverbrauch und Gasverbrauch.

Titel: Antw:Node-Red als Frontend
Beitrag von: stefanm am 18 Januar 2019, 14:02:39
So ich hab die openautomation Icons hinbekommen.

Für alle die Sie auch einbinden wollen und sich so blöd anstellen wie ich  ;)

Im TabletUI https://github.com/knowthelist/fhem-tablet-ui (https://github.com/knowthelist/fhem-tablet-ui) sind Sie als Webfont enthalten und mit der Anleitung für das WeatherIcon https://github.com/Paul-Reed/weather-icons (https://github.com/Paul-Reed/weather-icons) funktioniert es.

Zu erst die nano .node-red/settings.js einen Eintrag bearbeiten httpStatic: '/home/pi/.node-red/public',  .

Dann aus der fhem-tablet-ui die /www/tablet/lib/openautomation.css nach .node-red/public/lib kopieren.
Danach das font verzeichniss (/www/tablet/fonts/) nach /.node-red/public kopieren.

node-red neu starten.

[{"id":"2b063255.b5ac8e","type":"ui_template","z":"f1c518d5.322988","group":"bdec015f.d5307","name":"openautomation","order":6,"width":0,"height":0,"format":"<link rel=\"stylesheet\" href=\"/lib/openautomation.css\">\n<div style=\"display: flex;height: 100%;justify-content: center;align-items: center;\">\n<i class=\"fa-4x oa oa-fts_window_2w_open\"></i>\n</div> ","storeOutMessages":true,"fwdInMessages":true,"templateScope":"local","x":340,"y":240,"wires":[[]]},{"id":"b3dc503.45641b","type":"ui_template","z":"f1c518d5.322988","group":"bdec015f.d5307","name":"icon image","order":1,"width":"2","height":"2","format":"<div style=\"display: flex;height: 100%;justify-content: center;align-items: center;\">\n<i class=\"fa-4x oa  oa-fts_window_2w_open\"></i>\n</div>","storeOutMessages":false,"fwdInMessages":true,"templateScope":"local","x":470,"y":380,"wires":[[]]},{"id":"2fee0420.7bd89c","type":"inject","z":"f1c518d5.322988","name":"test inj","topic":"","payload":"oa-fts_window_2w_open","payloadType":"str","repeat":"","crontab":"","once":true,"onceDelay":"","x":230,"y":400,"wires":[["b3dc503.45641b","f48effa0.c5cab"]]},{"id":"f48effa0.c5cab","type":"ui_text","z":"f1c518d5.322988","group":"bdec015f.d5307","order":0,"width":"4","height":"2","name":"Some text","label":"Current icon:","format":"{{msg.payload}}","layout":"row-center","x":482,"y":426,"wires":[]},{"id":"bdec015f.d5307","type":"ui_group","z":"","name":"OA","tab":"f922caf3.52add8","disp":true,"width":"6","collapse":false},{"id":"f922caf3.52add8","type":"ui_tab","z":"","name":"oa","icon":"dashboard","disabled":false,"hidden":false}]

Stefan




Titel: Antw:Node-Red als Frontend
Beitrag von: Master_Nick am 18 Januar 2019, 15:21:22
Genau so mache ich es mit allen Gifs oder Icons die ich nutze. Man legt sie in das httpstatic von Nodered schaltet noch die Verwaltung von NodeRed auf /admin und dann kann man mittels HTML die Icons/Gifs/whatever nutzen.
Da es sozusagen einfach ein Opendir ist. :-)

Da hätte ich dir doch helfen können - hab aber nicht verstanden was du eben mit einbinden meintest :-D Denn so sind sie ja nun nicht eingebunden sondern einfach rein kopiert und man kann sie halt als Inhalt nutzen :-D
Eventuell war ich da zu "Wortstuzig" sorry :-D

Aber schön, dass du dein Zeil errreicht und erarbeitet hast.  :)
Titel: Antw:Node-Red als Frontend
Beitrag von: stefanm am 19 Januar 2019, 09:08:48
Hallo Master_Nick,
das ist nett von dir danke aber das ist ja die Herausforderung es schaffen :)

Ich hatte am Webfont gehangen und dann durch Zufall gesehen, als ich mir angesehen habe wie es WebUI gemacht wird, das es dort als Font vorliegt.

Node_Red ist echt ein mächtiges Werkzeug, das nicht nur eine Oberfläche darstellt, sondern auch ein Teil der Logik übernehmen kann.
Es Wundert mich das mit nicht mehr die Oberfläche Umsetzen, da es auch viel schneller ist .

Stefan
Titel: Antw:Node-Red als Frontend
Beitrag von: SamNitro am 19 Januar 2019, 15:23:13
Ich nutze Node-Red auch sehr gerne. Alleine für Alexa. Aber Thorsten macht es einen nicht leicht mit seinem FUIP. Das ist so leicht zu ,,programmieren" und schon richtig umfangreich.
Titel: Antw:Node-Red als Frontend
Beitrag von: stefanm am 20 Januar 2019, 09:54:53
Das kannte ich noch garnicht.
Bei Fhem gibts andauernd was neues :) Man lernt immer was dazu.
Titel: Antw:Node-Red als Frontend
Beitrag von: maci am 20 Januar 2019, 13:44:59
So soll es ja auch sein.  :) :)

Ich anfangs auch skeptisch, aber inzwischen mag ich mein Fhem.
Es ist so umfangreich, dass man nie alles wissen kann.
Fhem kann mit so vielen Geräten kommunizieren, was keine andere Plattform kann.
Das ist auch der lebendigen und sehr großen Gemeinschaft geschuldet.

Mein Eindruck ist, dass andere Plattformen zwar viel Augenmerk auf die Look & Feel legen, aber auf der anderen Seite eingeschränkt ist.
Wahrscheinlich auch dadurch, weil die Entwickler das nicht wollen.

Das ist bei Fhem einfach anderes.

Ich liebe mein Fhem inzwischen.
Titel: Antw:Node-Red als Frontend
Beitrag von: maci am 27 Februar 2019, 17:37:46
Ich habe mich zwischenzeitlich etwas in Node-Red eingearbeitet.

Wenn ich aber ehrlich bin, bleibe ich bei Fhem. :)
Nur als Anzeige ist mir das zu aufwändig.
Hier nehme ich lieber die TabletUI und Grafana für die Grafiken.
Titel: Antw:Node-Red als Frontend
Beitrag von: Master_Nick am 27 Februar 2019, 17:47:13
Wieso nur als Anzeige?

Ich nutze das als UI für die aktive Steuerung.
Nur ein gaaaanz kleiner Anteilt der Elemente ist für die reine Anzeige.
Die meisten kann man tappen etwas steuern oder kommt dadurch auf eine andere/n Seite/Raum.

Also sofern man alles in MQTT hat - ist NodeRed einfach total einfach um diesen nennen wir es mal "Datenstrom" zu verwerten und mit zu nutzen.

Aber Geschmäcker sind verschieden. :-)
Titel: Antw:Node-Red als Frontend
Beitrag von: SamNitro am 27 Februar 2019, 17:52:34
Ich war auch total für Node-Red aber Thorsten hat bei mir alles umgeworfen mit seinem ,,FUIP"

Einfacher geht es nun wirklich nicht mehr. Wenn er jetzt noch den weekdaytimer mit drin hat habe ich alles was ich brauche. ;)


Mobil unterwegs!
Titel: Antw:Node-Red als Frontend
Beitrag von: Master_Nick am 28 Februar 2019, 01:09:22
Ich müsste mir das mal ansehen - kenne es gar nicht.

Aber für MQTT ist NodeRed soweit ich das bisher sehe "Das" Ding. :-)
FUIP scheint (nach kurzem Blick) nur für FHEM zu sein - das ist sicherlich auch was geniales aber dann nochmal etwas anderes.
Titel: Antw:Node-Red als Frontend
Beitrag von: maci am 28 Februar 2019, 14:25:41
Zitat von: Master_Nick am 27 Februar 2019, 17:47:13
Wieso nur als Anzeige?

Ich nutze das als UI für die aktive Steuerung.
Nur ein gaaaanz kleiner Anteilt der Elemente ist für die reine Anzeige.
Die meisten kann man tappen etwas steuern oder kommt dadurch auf eine andere/n Seite/Raum.

Also sofern man alles in MQTT hat - ist NodeRed einfach total einfach um diesen nennen wir es mal "Datenstrom" zu verwerten und mit zu nutzen.

Aber Geschmäcker sind verschieden. :-)

Ich kenn mich in Fhem jetzt halbwegs aus, nun will ich nicht schon wieder etwas anderes.

Im Prinzip könnte man Fhem mit Node-Red großteils ersetzen. Doch das will ich nicht.
Ich brauche eigentlich nur ein Tool in dem ich meine Ausgaben gut gestalten kann.
Titel: Antw:Node-Red als Frontend
Beitrag von: Master_Nick am 28 Februar 2019, 14:30:00
;-) Naja was anderes... es ist eine Ergänzung.

Ich nutze halt sehr viele Geräte die auf MQTT ihre Infos verbreiten (publishen). Daher bietet es sich einfach an diesen Datenstrom mit zu hören und darauf auch zu publishen um Dinge zu erreichen.

Die Geräte selbst sind alle auch per FHEM schaltbar. Also mein Node Red ist 100% optional - ist es nicht an geht dennoch alles.
Titel: Antw:Node-Red als Frontend
Beitrag von: SamNitro am 28 Februar 2019, 14:45:55
Stimmt in Sachen MQTT ist NodeRed sehr einfach gestrickt. Aber wenn man NodeRed mal Neu Startet stimmen die schaltzustände nicht mehr. Das ist bei FUIP anders. Und ja du hast hast recht FUIP ist rein zur visualisierung da.
Titel: Antw:Node-Red als Frontend
Beitrag von: Master_Nick am 28 Februar 2019, 14:52:46
Zitat von: SamNitro am 28 Februar 2019, 14:45:55
Stimmt in Sachen MQTT ist NodeRed sehr einfach gestrickt. Aber wenn man NodeRed mal Neu Startet stimmen die schaltzustände nicht mehr. Das ist bei FUIP anders. Und ja du hast hast recht FUIP ist rein zur visualisierung da.

Das stimmt nicht. Dann hast du es falsch eingestellt oder nutzt MQTT nicht so wie man es dafür nutzen müsste.

Bei mir ändert sich rein gar nichts durch einen Neustart von NodeRed ODER dem MQTT Broker.
Titel: Antw:Node-Red als Frontend
Beitrag von: SamNitro am 28 Februar 2019, 18:08:12
Zitat von: Master_Nick am 28 Februar 2019, 14:52:46
Dann hast du es falsch eingestellt oder nutzt MQTT nicht so wie man es dafür nutzen müsste.

Dann kann natürlich sein. Aber mir fehlen dann die Anleitungen dazu. Und mein Englisch.... naja für hier und da reicht es, aber für mehr dann auch nicht.
Titel: Antw:Node-Red als Frontend
Beitrag von: hecky456 am 12 April 2019, 10:12:27
Hallo zusammen,

leider komme ich mit der Verbindung zwischen node-red und fhem über MQTT nicht weiter. Habe alles nach den super Tutorials von Matthias eingerichtet und über mqtt.fx klappt auch alles, also schalten von fhem devices und empfangen von Nachrichten klappt auch.
Wenn ich in node-red dann einen mqtt Mode hinzufüge und den fhem Broker eintrage steht zwar verbunden da aber es kommen keine Nachrichten an und senden geht auch nicht. Die Nachrichten kommen weder in fhem noch in mqtt.fx an.

Vielen Dank für eure Unterstützung!
Felix
Titel: Antw:Node-Red als Frontend
Beitrag von: Master_Nick am 12 April 2019, 12:41:51
Moin,

dann gehe ich davon aus (habe die Tutorials nur mal angelesen und mich gegen die Nutzung entschieden), du hast einen großen (nennen wir es) Kanal in dem ALLES von FHEM in MQTT rüber gespamt wird?
Das habe ich so nicht umgesetzt, da es aus meiner Sicht absolut unnötig ist ALLE infos von FHEM in MQTT zu haben.


Ich nutze in FHEM:

Modul 00_MQTT.pm -> für das einbinden des Brokers (Mosquitto bei mir)

Modul 10_MQTT_GENERIC_BRIDGE -> für die Steuerung und gezielte Übetragung von nicht nativen MQTT Devices (also soweit erstmal alles was in FHEM irgendwie geschaltet werden kann - bin  damit noch an keine Grenze gestoßen)

Modul 10_MQTT_DEVICE -> für die Nutzung von nativen MQTT Geräten

Mein NodeRed hängt dann einfach am Mosquitto und ist absolut optional. D.h. ist es abgeschaltet oder nicht verfügbar hat es keine Auswirkungen für die Funktionalität meiner Steuerung. Lediglich kann man dann halt nicht komfortabel im Flur am Touchscreen oder am Handy Devices Ein/Ausschalten oder in Zustände schalten.

Dabei habe ich aber dann auch wirklich nur die für mich interessanten Readings von FHEM an MQTT oder MQTT an FHEM übetragen.
Stumpf gesagt (es gibt ausnahmen) 3 Stück pro nativen MQTT Device (Online, Set und Status Topic). Bei einem nicht nativen sind es meist sogar nur 2 (Status und Set).


Aber mal zurück zu deiner Frage :-D

mqtt.fx <- ? Sagt mir jetzt mal ad hoc gar nichts - wo kommt das vor?

Hast du in MQTT denn mal die Debug Node genutzt und verifiziert, dass wirklich nix ankommt?
Schalte dich mal mittels Shell auf deinen Broker und schaue ob wirklich gar nichts passiert.
Bei Mosquitto wäre das: mosquitto_sub -h localhost -t '#' -v (sofern keine auth konfiguriert)
Titel: Antw:Node-Red als Frontend
Beitrag von: hecky456 am 12 April 2019, 13:56:09
Hey, danke für deine schnelle Unterstützung!
Nicht ganz, nur die Infos der Devices bei denen ich das userattr. MqttName und MqttRoom gesetzt habe. Das Topic baut sich dann aus dem Raum und dem Namen zusammen und darunter werden dann alle readings des device verschickt.
Finde die Lösung eigentlich ganz elegant. (Muss allerdings gestehen, hab mir die Generic Bridge mangels Zeit ach noch nicht angesehen).
Ich gehe davon aus, dass das Problem eigentlich nichts mit fhem zu tun hat da ich über MQTT.fx (ist ein mqtt Client mit GUI) mit fhem kommunizieren kann (subscribe und publish geht, kann auch fhem devices schalten, daher eigentlich etwas Off topic, sry!
Aber ich glaube ich habe noch ein generelles Verständnis Problem... Auf dem Debian auf dem node-red läuft (ein proxmox Container), läuft da auch ein mqtt broker wenn ich da über apt-get Install mosquitto mosquitto-clients mqtt installiere? Im mqtt node in meinem flow trage ich doch den fhem mqtt broker ein oder? Da steht dann auch connected aber auf dem debuggen node der hinten dran hängt kommt keine einzige Nachricht an.

Mit mqtt.fx habe ich alle topics subscribed und da sehe ich alle Nachrichten von fhem aber keine von node-red wenn ich da mit einem mqtt Output node etwas subscribe...

Könnte vielleicht jemand seine Konfiguration des mqtt Eingangs nodes posten?

Sry, habe node Red gerade erst installiert und mit mqtt noch keine Erfahrung!

*EDIT:

Bin jetzt etwas schlauer. Wenn ich mich mit mqtt.fx auf dem Broker des Containers verbinde auf dem auch node-red läuft sehe ich alle Nachrichten die ich aus Nodered publishe und wenn ich über mqtt.fx etwas publishe kommt das auch im Debugfenster in node-red raus. Also sind die nodes mit dem lokalen Broker auf dem node-red Container verbunden obwohl ich in den nodes die IP des fhem Containers angegeben habe. Kann mir das jemand erklären? Und vorallem wie bekomme ich node-red dazu sich zu einem Broker auf einem anderen Rechner im Netz zu verbinden?

Wäre für Hilfe wirklich sehr dankbar...

Viele Grüße
Titel: Antw:Node-Red als Frontend
Beitrag von: Master_Nick am 15 April 2019, 12:33:40
Moin  :o

Vom Gefühl her, ich kann aber auch irren, musst du mal sortieren was was ist und es korrekt benennen.
Lokaler Borker, dann einer im Proxmox? Wieso überhaupt mehrere?

Du brauchst genau einen MQTT Server (auch genannt Broker). Eben dieser wird in FHEM als Broker definiert.
Sofern dein NodeRed diesen Broker finden kann und auch darauf publishen kann, sollte es für FHEM wenn dort im Broker "active" steht es ebenso gehen.

Generell ist aber Container spezifisches hier nicht korrekt aufgehoben - und es "klingt" ein wenig danach, als würde da irgendwo was mit den Zugriffen nicht passen.
Eventuell erstmal ohne versuchen?

"Und vorallem wie bekomme ich node-red dazu sich zu einem Broker auf einem anderen Rechner im Netz zu verbinden?"
Indem du die IP und den Port als Broker nutzt. Man gibt ja mindestens einmal beim verwenden der ersten MQTT Node den Server an. Dort dann die korrekte IP eintragen. Alternativ nun über eine Node in die Eigenschaften zum Server und ändern.
Titel: Antw:Node-Red als Frontend
Beitrag von: hecky456 am 16 April 2019, 10:07:35
Hi,

danke, dass du dir trotzdem die Zeit nimmst!

Ich würde behaupten, dass das erstmal nichts mit der Container Umgebung zu tun hat, das hat sich bisher alles verhalten wie "normale" einzele Debian Rechner...

Nochmal etwas ausführlicher:

mosuitto ist doch ein mqtt-Broker oder? Bei den ganzen Installationsanleitungen von node-red wird doch überall mosuitto und auch mosuitto-clients installiert und auch in den "Autostart" gepackt. Damit läuft doch lokal auf dem Container von node-red auch ein mqtt-Broker... Jetzt hatte ich zuvor aber schon unter fhem ein mqtt device angelegt um einen Sonoff TH16 einzubinden, also läuft auf meinem fhem Container auch schon ein mqtt-Broker (auch moquitto). Jetzt dachte ich, ich verbinde node-red einfach mit dem mqtt-node auf den Broker der auf meinem fhem Container läuft. Wenn ich jetzt die IP des fhem Containers in den mqtt-node eintrage, werden die Nachirchten trotzdem auf dem lokalen Broker (auf dem node-red Container) gepublished...

Die einzige Idee die ich jetzt noch hätte ware, dass node-red ein Problem mit "ungesicherten" externen Brokern hat da mein Broker keinen Benutzer und PW hat sondern noch offen ist. (Siehe https://cookbook.nodered.org/mqtt/connect-to-broker (https://cookbook.nodered.org/mqtt/connect-to-broker) ganz unten "To connect to non-local, secured brokers, other MQTT Config node options will need to be set according to your broker's connectivity requirements.") Kann das ggf. jemand bestätigen?

Wie sehen denn eure Installationen von fhem und node-red aus? Läuft das alles auf einem System oder hat das ggf. noch jemand getrennt?

Vielen Dank und viele Grüße
Titel: Antw:Node-Red als Frontend
Beitrag von: Master_Nick am 17 April 2019, 13:49:21
Moin,

"Installationsanleitungen für Node-Red" Mir scheint du hast da Tutorials die am Ende ein bestimmtest Ergebnis erreichen sollen.
Aber eine reine NodeRed Installationsanleitung würde nur NodeRed installieren.

Ja Mosquitto ist ein Broker -> MQTT Server
Clients für Mosquitto im Autostart würde mich arg wundern - mit denen kann man halt subscriben und publishen um mitzulesen oder werte zum Test auf Topics zu schreiben.

Und wo man in seinem LAN den Broker installiert ist total egal, solange die Maschine/VM auf der NodeRed läuft daran kommt. Es muss also nicht alles auf einer Kiste laufen.
Die Frage wäre ob du bei dir, da beide keine Fehler melden aber gegenseitig anscheind nicht die Payloads bekommen, zwei Broker hast - einen der von FHEM bedient wird einen der von NodeRed bedient wird. Das würde dann erklären warum du nichts zwischen beiden hin und her bekommst.

Du brauchst definitiv keine gesicherte Verbindung für NodeRed. Es funktioniert ohne jede Auth aber genauso auch mit.

Meine Installation:

FHEM in einem Container
NodeRed in einem Container
Mosquitto in einem Container

Alle haben eigene IPs und die notwengiden Ports für die anderen Container zugänglich und je nach dem für Clients aus dem LAN.
Titel: Antw:Node-Red als Frontend
Beitrag von: FHEm2005 am 20 Dezember 2019, 10:00:19
Ich versuche mal hier eine Antwort auf meine Frage zu bekommen. Vielleicht gibt es Jemanden, der sich damit besser auskennt als ich. Ich weiß, dass dies hier kein Node-Red Forum ist, kenne aber keinen deutschsprachigen Anlaufpunkt. Deshalb vorab: Sorry

Situatuion: Ich bekommen über Fhem -> Mqtt _node-red Statusänderungen meiner Sonos-Box, die im Dashboard dargestellt werden sollen. Stationstasten mit Senderlogo sind im Desktop sichtbar und funktionsfähig. Problem: egal, wie groß ich den Wert von "size" wähle, die Größe des Senderlogos bleibt immer gleich. Die Original jpg_Datei ist 600px x 600px und wird auf schlappe 24px x 24px skaliert.

Hier der Code (Node-Red) für den Button:
[{"id":"62bf6e0.b0a5394","type":"ui_button","z":"e87bf2c.7a7039","name":"WDR1","group":"7fffe7a8.859468","order":14,"width":"2","height":"2","passthru":false,"label":"","tooltip":"","color":"","bgcolor":"","icon":"http://cdn-profiles.tunein.com/s25260/images/logog.jpg","payload":"play_mysonos","payloadType":"str","topic":"WDR1 LIVE - Dss junge Radio des WDR","x":710,"y":720,"wires":[["b590178c.a74248"]]},{"id":"7fffe7a8.859468","type":"ui_group","z":"","name":"Sonos","tab":"9a8f8e45.49d28","order":2,"disp":true,"width":"6","collapse":false},{"id":"9a8f8e45.49d28","type":"ui_tab","z":"","name":"Home","icon":"dashboard","order":1,"disabled":false,"hidden":false}]

Optimal wäre ein 70px x 70 px großes Senderlogo bei einer Bittungröße von 2 x 2..

Ich habe bereits 5 Tage Internetsuche hinter mir. Das Thema kam immer wieder auf die fa- und material-Icons aus. Niemand packt mal ein Logo auf den Button und verändert die Größe.

Herzliche Grüße

Eberhard

Titel: Antw:Node-Red als Frontend
Beitrag von: SamNitro am 20 Dezember 2019, 10:58:15
Zitat von: FHEm2005 am 20 Dezember 2019, 10:00:19

Optimal wäre ein 70px x 70 px großes Senderlogo bei einer Bittungröße von 2 x 2..


Ich bin jetzt auch kein Profi, aber vielleicht hilft dir das hier was weiter...


EDIT: Topic hinzugefügt


[{"id":"1765cb2d.8e1b35","type":"ui_template","z":"9aeee83f.213f8","group":"1ffd0ef1.ff17a1","name":"","order":2,"width":"2","height":"2","format":"<button ng-click=\"send({payload: 'play_mysonos', topic: 'WDR1 LIVE - Dss junge Radio des WDR'})\">\n    \n<img src=\"http://cdn-profiles.tunein.com/s25260/images/logog.jpg\" width=70>\n\n</button>","storeOutMessages":true,"fwdInMessages":true,"templateScope":"local","x":280,"y":600,"wires":[["be0c6cd6.1316d"]]},{"id":"1ffd0ef1.ff17a1","type":"ui_group","z":"","name":"Sonos","tab":"64c00bee.33251c","order":2,"disp":true,"width":"6","collapse":false},{"id":"64c00bee.33251c","type":"ui_tab","z":"","name":"Home","icon":"dashboard","order":1,"disabled":false,"hidden":false}]
Titel: Antw:Node-Red als Frontend
Beitrag von: SamNitro am 20 Dezember 2019, 14:31:34
Dann erstelle mal ein ,,template" mit folgendem Inhalt:



<button ng-click="send({payload: 'play_mysonos', topic: 'WDR1 LIVE - Dss junge Radio des WDR'})">
<img src="http://cdn-profiles.tunein.com/s25260/images/logog.jpg" width=70>
</button>

Titel: Antw:Node-Red als Frontend
Beitrag von: FHEm2005 am 20 Dezember 2019, 14:42:40
Hallo SamNitro,

anfangs dachte ich: "Geht nicht!". Der Button war aber in ein anderen Tab gerutscht. Jetzt wird wenigstens etwas angezeigt. Die Größe war bei 2x2 mit den vorgegebenen 70 px völlig gaga. Ich meinte 40px. Einige kosmetische Feinheiten muss ich noch bereingen. Wenn ich Probleme habe melde ich mich. Wenn allse passt ebenso

Danke bis hierhin.

Gruß Eberhard
Titel: Antw:Node-Red als Frontend
Beitrag von: FHEm2005 am 20 Dezember 2019, 18:32:00
Hallo SamNitro,

Die Buttons sehen genau so aus, wie ich es wollte´- dank Deiner Initialzündung. Das Template hat jetzt im Bereich "Template" folgende finale Definition:

<button ng-click="send({payload: 'play_mysonos', topic: 'WDR1 LIVE - Das junge Radio des WDR'})" style="border:none;background-color:#4b7930;width: 92px; height: 72px;">
   
<img src="http://cdn-profiles.tunein.com/s25260/images/logog.jpg" width=60 style="margin-top:5px;">

</button>


Die Werte für 'topic und 'src' habe ich aus der fhem-Definition der Sonos-Box ausgelesen. 'topic' steht unter dem Reading 'Favourites' unter 'title' bzw. 'cover'  (Für die, die ebenfalls auf der Suche sind. - Sind wir nicht alle auf der Suche...  ;) ;)

Unten habe ich das UI als Bild angehängt. Gesteuert werden: Mute on/mute off, Start/Stop, Volume Up, Volume Down und die daruter befindlichen Stationstasten. An der Rückmeldung vom Sonos-System arbeite ich noch.

Danke dir nochmal.

Gruß Eberhard
Titel: Antw:Node-Red als Frontend
Beitrag von: SamNitro am 20 Dezember 2019, 19:31:03
Freut mich wenn ich helfen konnte :)

LG Patrick
Titel: Antw:Node-Red als Frontend
Beitrag von: pattex am 30 November 2021, 00:36:04
Ich bin bei der Suche nach einer Anbindung von node-red an fhem über diesen alten Thread gestolpert. Ich möchte auch grundsätzlich bei FHEM bleiben vermisse aber eine "schöne" Oberfläche für Logik in meinem Smart Home.
Ich fand die hier im Thread erwähnten Möglichkeiten die Beiden zu verbinden zwar machbar aber nicht einfach genug für den nicht versierten Anwender.

Ich habe nun ein npm Paket (node-red-contrib-fhem) für node-red erstellt, welches die rudimentäre Funktionalität bereitstellt. Hier werde ich in nächster Zeit weiter dran arbeiten. Sollte jemand Interesse haben und Input geben wollen, nur her damit.
Titel: Antw:Node-Red als Frontend
Beitrag von: Master_Nick am 02 Dezember 2021, 01:25:48
Das klingt interessant - frühstückt das dann MQTT mit ab? @pattex

Hast du nen Link zum Github?
Titel: Aw: Node-Red als Frontend
Beitrag von: mwuerr am 12 Januar 2024, 16:19:12
Ich nutze Node Red in Verbindung mit FHEM. Angebunden über MQTT (MQTT2Client).
Passend zu dem Thema FHEM zu Node-red über MQTT habe ich etwas geschrieben zum parsen von readings die Wertlisten beinhalten.

Beispiel: 00_THZ.pm device THZ (THZ gerät (https://wiki.fhem.de/wiki/Tecalor_THZ_W%C3%A4rmepumpe)) erzeugt Readings im Format:
Reading: "sDHW"
Wert : "dhwTemp: 43.8 outsideTemp: 0.8 dhwSetTemp: 45 compBlockTime: 0 out: 0008 heatBlockTime: 0 dhwBoosterStage: 0 pasteurisationMode: 0 dhwOpMode: normal x36: FAA7"

Das ist schon fast JSON. Leider nicht ganz.

Lösung in Node Red:

nodeRedFlowConvertKeyValuesToJSon.png

export des flows:
[{"id":"a8f61be078978f20","type":"mqtt in","z":"292283190412ad7e","name":"","topic":"/FhemAtRaspiHeizung2/GGTechnik/Mythz/sDHW","qos":"2","datatype":"utf8","broker":"7aa8da9.1a0f224","nl":false,"rap":false,"inputs":0,"x":210,"y":280,"wires":[["917d50f30d06acab"]]},{"id":"14f0cff4d9ef2f81","type":"json","z":"292283190412ad7e","name":"","property":"payload","action":"obj","pretty":false,"x":790,"y":280,"wires":[["9130f38cb4da1220","874f2f4551a4b413","32d30c519ced2b17","be6d1921b5ac47ed"]]},{"id":"9130f38cb4da1220","type":"debug","z":"292283190412ad7e","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","targetType":"full","statusVal":"","statusType":"auto","x":970,"y":420,"wires":[]},{"id":"874f2f4551a4b413","type":"debug","z":"292283190412ad7e","name":"","active":true,"tosidebar":true,"console":false,"tostatus":true,"complete":"payload.dhwTemp","targetType":"msg","statusVal":"payload.dhwTemp","statusType":"auto","x":1020,"y":220,"wires":[]},{"id":"32d30c519ced2b17","type":"debug","z":"292283190412ad7e","name":"","active":true,"tosidebar":true,"console":false,"tostatus":true,"complete":"payload.dhwSetTemp","targetType":"msg","statusVal":"payload.dhwTemp","statusType":"auto","x":1040,"y":360,"wires":[]},{"id":"917d50f30d06acab","type":"function","z":"292283190412ad7e","name":"convert key values to json","func":"//Takes data in the format\n//key: value key: value key: value \n//and converts it to json\n//{ \"key\": \"value\", \"key\": \"value\", \"key\": \"value\" }\nvar isKey = true;\nvar keys = 0;\nvar values = 0;\n\nvar resultJson = \"\\{\";\nfor (var element of msg.payload.split(\" \")) \n{\n    if(isKey) {\n        if(values > 0) {\n            //for seccond pair and any further\n            resultJson = resultJson.concat(\",\");\n        }\n        \n        //remove : from end of key\n        //add newline, \" and : to key\n        resultJson = resultJson.concat(\n            \"\\n\\\"\",\n            element.substring(0,element.length-1),\n            \"\\\"\",\n            \": \");\n        keys++;\n    }else{\n        //value, add value, in \"\n        resultJson = resultJson.concat(\n            \"\\\"\", element, \"\\\"\\n\");\n        values++;\n    }\n    \n    isKey = !isKey;\n}\n//close json\nresultJson = resultJson.concat(\"\\n\\}\");\n\nmsg.payload = resultJson;\n\nnode.status(keys + \" keys \" + values + \" values\");\nreturn msg;\n","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":580,"y":280,"wires":[["14f0cff4d9ef2f81","e4176194255a01e5"]]},{"id":"e4176194255a01e5","type":"debug","z":"292283190412ad7e","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","targetType":"full","statusVal":"","statusType":"auto","x":870,"y":500,"wires":[]},{"id":"bf84a0bca868b781","type":"inject","z":"292283190412ad7e","name":"testen mit sDHW","props":[{"p":"payload"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"dhwTemp: 44.1 outsideTemp: 0.7 dhwSetTemp: 45 compBlockTime: 0 out: 6008 heatBlockTime: 0 dhwBoosterStage: 0 pasteurisationMode: 0 dhwOpMode: normal x36: FA8E","payloadType":"str","x":360,"y":360,"wires":[["917d50f30d06acab"]]},{"id":"7aa8da9.1a0f224","type":"mqtt-broker","name":"raspberrypi-HeizungMqttBroker","broker":"192.168.188.106","port":"1883","clientid":"","autoConnect":true,"usetls":false,"compatmode":false,"protocolVersion":"4","keepalive":"60","cleansession":true,"birthTopic":"","birthQos":"0","birthPayload":"","birthMsg":{},"closeTopic":"","closeQos":"0","closePayload":"","closeMsg":{},"willTopic":"","willQos":"0","willPayload":"","willMsg":{},"sessionExpiry":""}]
vielleicht hilft es ja jemandem. Oder jemand sieht das hier und sagt "so ein quatsch, das geht einfacher".