[70_Klafs.pm] Neues Modul für Klafs Saunasteuerung

Begonnen von xasher, 18 Mai 2022, 12:54:20

Vorheriges Thema - Nächstes Thema

xasher

Hallo zusammen,

ich habe für mich ein Modul entwickelt für eine Klafs Sauna mit Wifi-Modul.

Das Modul empfängt Daten und sendet Befehle an die Klafs App.
In der aktuellen Version kann die Sauna an- bzw. ausgeschaltet werden und dabei die Parameter mitgegeben werden.

Voraussetzungen
Die SaunaID muss bekannt sein. Diese findet sich in der URL direkt nach dem Login an der App (http://sauna-app.klafs.com).
Dort steht die ID mit dem Parameter ?s=xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx

Alternativ können nach Hinterlegung von username und password die SaunaIDs über get abgerufen werden: get "name" SaunaID

Definition und Verwendung
Das Modul wird ohne Pflichtparameter definiert.
Benutzername,  Refresh-Intervall, SaunaID und die am Saunamodul definierte Pin werden als Attribute gesetzt.
Das Passwort wirt mit set gesetzt.

Beispieldefinition

define mySauna KLAFS
attr mySauna saunaid ab0c123d-ef4g-5h67-8ij9-k0l12mn34op5
attr mySauna username user01
attr mySauna pin 1234
attr mySauna interval 60
set mySauna password geheim


In dieser Version wurden noch einige Sicherheiten hinterlegt:
1) Die Zeit zwischen Login und einem Reconnect wurde auf 5 Minuten festgelegt. Hintergrund ist, dass Klafs den Benutzer nach 3 Fehlversuchen sperrt.
   Dann muss bei der Hotline angerufen werden.
2) Ein weiterer Login während der Laufzeit ist eigentlich nicht erforderlich. Das Modul erkennt das und würde ein Reconnect durchführen.
   Die Cookielifetime habe ich dennoch mal auf 2 Tage definiert.
3) Bei einem fehlerhaften Login wird das Reading LoginFailures auf 1 gesetzt. Dann ist ein Connect nicht mehr möglich.
   Dann empfiehlt es sich, an der Klafs App - siehe oben - neu anzumelden. Dann werden dort die fehlerhaften Loginversuche zurückgesetzt.
   Im Modul kann das Reading mit "set mySauna ResetLoginFailures" zurückgesetzt werden.
   
Betriebsmöglichkeiten des Moduls
set mySauna on
ohne Parameter -> Default Sauna 90 Grad

set mySauna on Sauna 80 19:30
3 Parameter: Sauna mit Temperatur [10-100]; Optional Uhrzeit [19:30]

set mySauna on Saunarium 65 5 19:30
4 Parameter: Sanarium mit Temperatur [40-75]; Optional HumidtyLevel [0-10] und Uhrzeit [19:30]

set mySauna on Infrarot 30 5 19:30
4 Parameter: Infrarot mit Temperatur [20-40] und IR Level [0-10]; Optional Uhrzeit [19:30]
--> Infrarot ist nicht supported, da keine Testumgebung verfuegbar.

set mySauna off
ohne Parameter -> Schaltet die Sauna|Sanarium|Infrarot aus.

set mySauna ResetLoginFailures
siehe oben. Lässt ein Login nach einem Fehlversuch wieder zu

set mySauna update
Refresht die Readings.

Darüberhinaus wurden folgende DOIFs und Dummys definiert. Teilweise für FTUI

# Nachricht, wenn Sauna aufgeheizt ist

define DI_SAUNA_BEREIT DOIF ([mySauna:isReadyForUse] eq "true" and [mySauna:Mode] eq "Sauna")\
  (set pushmsg msg device==<devicename> title='Sauna bereit' message='Die Sauna ist bereit!')\
DOELSEIF ([mySauna:isReadyForUse] eq "true" and [mySauna:Mode] eq "Sanarium")\
  (set pushmsg msg device==<devicename> title='Sanarium bereit' message='Das Sanarium ist bereit!')\
DOELSEIF ([mySauna:isReadyForUse] eq "true" and [mySauna:Mode] eq "Infrared")\
  (set pushmsg msg device==<devicename> title='Infrarot bereit' message='Die Infrarotsauna ist bereit!')\
DOELSEIF ([mySauna:isReadyForUse] eq "false")\
  (set pushmsg msg device==<devicename> title='Sauna aus' message='Die Sauna ist aus!')


# Dummy, damit Button in FTUI hinterlegt ist

define ftui_state dummy

define DI_state DOIF ([mySauna:isPoweredOn] eq "true" and [mySauna:Mode] eq "Sauna")\
  (set ftui_state [mySauna:Mode]|[mySauna:selectedSaunaTemperature])\
DOELSEIF ([mySauna:isPoweredOn] eq "true" and [mySauna:Mode] eq "Sanarium")\
  (set ftui_state [mySauna:Mode]|[mySauna:selectedSanariumTemperature]|[mySauna:selectedHumLevel])\
DOELSEIF ([mySauna:isPoweredOn] eq "true" and [mySauna:Mode] eq "Infrared")\
  (set ftui_state [mySauna:Mode]|[mySauna:selectedIrTemperature]|[mySauna:selectedIrLevel])\
DOELSEIF([mySauna:isPoweredOn] eq "false")\
  (set ftui_state off)


# Code in FTUI
# Code für Kurzauskunft im Bildschirmschoner

# im Head bzw. im css/fhem-tablet-ui-user.css:
<style>
   #flexContainer {
   display: flex;
   flex-direction: row;
   justify-content:center;
   }
   .fbox { padding: 4px; }
</style>
# Im Body
<div id="flexContainer">
               <div class="fbox">
                  <div data-type="symbol"
                     data-device="mySauna"
                     data-get="isPoweredOn"
                     data-get-on="^(?!false).*$"
                     data-get-off="false"
                     data-on-color="#0088CC"
                     data-off-color="#888"
                     data-icon="oa-scene_sauna"
                     data-hide="isPoweredOn"
                     data-hide-on="false"
                     data-on-background-color="#0088CC"
                     data-off-background-color="#888"
                     class="large">
                  </div>
                  <div style='float:left;margin-top:0px;margin-bottom:0px;'>
                     <div data-type="label"
                        data-device="mySauna"
                        data-get="currentTemperature"
                        data-post-text="°C"
                        data-pre-text ="Temperatur:&nbsp;"
                        data-hide="isPoweredOn"
                        data-hide-on="false">
                     </div>
                  </div>
                  <div style='float:left;margin-top:0px;margin-bottom:0px;'>
                     <div data-type="label"
                        data-device="mySauna"
                        data-get="currentHumidity"
                        data-post-text="g/㎥"
                        data-pre-text ="/&nbsp;"
                        data-hide="currentHumidity"
                        data-hide-on="0">
                     </div>
                  </div>
                  <div style='float:auto;margin-top:0px;margin-bottom:0px;'>
                     <div data-type="label"
                        data-device="mySauna"
                        data-get="Badeart"
                        data-post-text=""
                        data-pre-text ="Badeart:&nbsp;"
                        data-hide="isPoweredOn"
                        data-hide-on="false">
                     </div>
                  </div>
                  <div style='float:auto;margin-top:0px;margin-bottom:0px;'>
                     <div data-type="label"
                        data-device="mySauna"
                        data-get="RemainTime"
                        data-post-text=""
                        data-pre-text ="Restzeit:&nbsp;"
                        data-hide="isPoweredOn"
                        data-hide-on="false">
                     </div>
                  </div>
               </div>
</div>



Aus meiner Sauna-Übersichtseite FTUI

         <section>
            <table cellpadding="10" cellspacing="10" border="0">
               <tr>
                  <td colspan="4" align="center">
                     <div data-type="switch"
                        data-device="mySauna"
                        data-get="isPoweredOn"
                        data-get-on="false"
                        data-get-off="^(?!false).*$"
                        data-set-on="off"
                        data-set-off=""
                        data-icon="oa-scene_sauna"
                        data-background-icon="fa-circle-thin"
                        data-on-color="#0088CC"
                        data-off-color="#888"
                        data-on-background-color="#0088CC"
                        data-off-background-color="#3D4C66"
                        class="">
                     </div>
                     <div class="">Aus
                     </div>
                  </td>
               </tr>
               <tr>
                  <td>
                     <div data-type="switch"
                        data-device="mySauna"
                        data-get="ftui_state:STATE"
                        data-get-on="Sauna\|100"
                        data-get-off="^(?!Sauna\|100).*$"
                        data-set-on="on Sauna 100"
                        data-set-off="off"
                        data-icon="oa-scene_sauna"
                        data-background-icon="fa-circle-thin"
                        data-on-color="#0088CC"
                        data-off-color="#888"
                        data-on-background-color="#0088CC"
                        data-off-background-color="#3D4C66"
                        class="">
                     </div>
                     <div class="">Banja Sauna<br>100°C
                     </div>
                  </td>
                  <td>
                     <div data-type="switch"
                        data-device="mySauna"
                        data-get="ftui_state:STATE"
                        data-get-on="Sauna\|80"
                        data-get-off="^(?!Sauna\|80).*$"
                        data-set-on="on Sauna 80"
                        data-set-off="off"
                        data-icon="oa-scene_sauna"
                        data-background-icon="fa-circle-thin"
                        data-on-color="#0088CC"
                        data-off-color="#888"
                        data-on-background-color="#0088CC"
                        data-off-background-color="#3D4C66"
                        class="">
                     </div>
                     <div class="">Chillout Sauna<br>80°C
                     </div>
                  </td>
                  <td>
                     <div data-type="switch"
                        data-device="mySauna"
                        data-get="ftui_state:STATE"
                        data-get-on="Sanarium\|75\|10"
                        data-get-off="^(?!Sanarium\|75\|10).*$"
                        data-set-on="on Sanarium 75 10"
                        data-set-off="off"
                        data-icon="oa-scene_sauna"
                        data-background-icon="fa-circle-thin"
                        data-on-color="#0088CC"
                        data-off-color="#888"
                        data-on-background-color="#0088CC"
                        data-off-background-color="#3D4C66"
                        class="">
                     </div>
                     <div class="">Tropen Sanarium<br>75°C - Feuchte: 10
                     </div>
                  </td>
                  <td>
                     <div data-type="switch"
                        data-device="mySauna"
                        data-get="ftui_state:STATE"
                        data-get-on="Sanarium\|60\|8"
                        data-get-off="^(?!Sanarium\|60\|8).*$"
                        data-set-on="on Sanarium 60 8"
                        data-set-off="off"
                        data-icon="oa-scene_sauna"
                        data-background-icon="fa-circle-thin"
                        data-on-color="#0088CC"
                        data-off-color="#888"
                        data-on-background-color="#0088CC"
                        data-off-background-color="#3D4C66"
                        class="">
                     </div>
                     <div class="">Beauty Sanarium<br>60°C - Feuchte: 8
                     </div>
                  </td>
               </tr>
               <tr>
                  <td>
                     <div data-type="switch"
                        data-device="mySauna"
                        data-get="ftui_state:STATE"
                        data-get-on="Sauna\|65"
                        data-get-off="^(?!Sauna\|65).*$"
                        data-set-on="on Sauna 65"
                        data-set-off="off"
                        data-icon="oa-scene_sauna"
                        data-background-icon="fa-circle-thin"
                        data-on-color="#0088CC"
                        data-off-color="#888"
                        data-on-background-color="#0088CC"
                        data-off-background-color="#3D4C66"
                        class="">
                     </div>
                     <div class="">Soft Sauna<br>65°C
                     </div>
                  </td>
                  <td>
                     <div data-type="switch"
                        data-device="mySauna"
                        data-get="ftui_state:STATE"
                        data-get-on="Sauna\|90"
                        data-get-off="^(?!Sauna\|90).*$"
                        data-set-on="on Sauna 90"
                        data-set-off="off"
                        data-icon="oa-scene_sauna"
                        data-background-icon="fa-circle-thin"
                        data-on-color="#0088CC"
                        data-off-color="#888"
                        data-on-background-color="#0088CC"
                        data-off-background-color="#3D4C66"
                        class="">
                     </div>
                     <div class="">Classic Sauna<br>90°C
                     </div>
                  </td>
                  <td>
                     <div data-type="switch"
                        data-device="mySauna"
                        data-get="ftui_state:STATE"
                        data-get-on="Sanarium\|50\|4"
                        data-get-off="^(?!Sanarium\|50\|4).*$"
                        data-set-on="on Sanarium 50 4"
                        data-set-off="off"
                        data-icon="oa-scene_sauna"
                        data-background-icon="fa-circle-thin"
                        data-on-color="#0088CC"
                        data-off-color="#888"
                        data-on-background-color="#0088CC"
                        data-off-background-color="#3D4C66"
                        class="">
                     </div>
                     <div class="">Relax Sanarium<br>50°C - Feuchte: 4
                     </div>
                  </td>
                  <td>
                     <div data-type="switch"
                        data-device="mySauna"
                        data-get="ftui_state:STATE"
                        data-get-on="Sanarium\|70\|8"
                        data-get-off="^(?!Sanarium\|70\|8).*$"
                        data-set-on="on Sanarium 70 8"
                        data-set-off="off"
                        data-icon="oa-scene_sauna"
                        data-background-icon="fa-circle-thin"
                        data-on-color="#0088CC"
                        data-off-color="#888"
                        data-on-background-color="#0088CC"
                        data-off-background-color="#3D4C66"
                        class="">
                     </div>
                     <div class="">Subtropen Sanarium<br>70°C - Feuchte: 8
                     </div>
                  </td>
               </tr>
               <tr>
                  <td>
                     <div data-type="switch"
                        data-device="mySauna"
                        data-get="ftui_state:STATE"
                        data-get-on="Sauna\|75"
                        data-get-off="^(?!Sauna\|75).*$"
                        data-set-on="on Sauna 75"
                        data-set-off="off"
                        data-icon="oa-scene_sauna"
                        data-background-icon="fa-circle-thin"
                        data-on-color="#0088CC"
                        data-off-color="#888"
                        data-on-background-color="#0088CC"
                        data-off-background-color="#3D4C66"
                        class="">
                     </div>
                     <div class="">Summer Sauna Classic<br>75°C
                     </div>
                  </td>
                  <td>
                     <div data-type="switch"
                        data-device="mySauna"
                        data-get="ftui_state:STATE"
                        data-get-on="Sauna\|60"
                        data-get-off="^(?!Sauna\|60).*$"
                        data-set-on="on Sauna 60"
                        data-set-off="off"
                        data-icon="oa-scene_sauna"
                        data-background-icon="fa-circle-thin"
                        data-on-color="#0088CC"
                        data-off-color="#888"
                        data-on-background-color="#0088CC"
                        data-off-background-color="#3D4C66"
                        class="">
                     </div>
                     <div class="">Mediations Sauna<br>60°C
                     </div>
                  </td>
                  <td>
                     <div data-type="switch"
                        data-device="mySauna"
                        data-get="ftui_state:STATE"
                        data-get-on="Sanarium\|50\|8"
                        data-get-off="^(?!Sanarium\|50\|8).*$"
                        data-set-on="on Sanarium 50 8"
                        data-set-off="off"
                        data-icon="oa-scene_sauna"
                        data-background-icon="fa-circle-thin"
                        data-on-color="#0088CC"
                        data-off-color="#888"
                        data-on-background-color="#0088CC"
                        data-off-background-color="#3D4C66"
                        class="">
                     </div>
                     <div class="">Family Sanarium<br>50°C - Feuchte: 8
                     </div>
                  </td>
                  <td>
                     <div data-type="switch"
                        data-device="mySauna"
                        data-get="ftui_state:STATE"
                        data-get-on="Sanarium\|45\|6"
                        data-get-off="^(?!Sanarium\|45\|6).*$"
                        data-set-on="on Sanarium 45 6"
                        data-set-off="off"
                        data-icon="oa-scene_sauna"
                        data-background-icon="fa-circle-thin"
                        data-on-color="#0088CC"
                        data-off-color="#888"
                        data-on-background-color="#0088CC"
                        data-off-background-color="#3D4C66"
                        class="">
                     </div>
                     <div class="">Vital Sanarium<br>45°C - Feuchte: 6
                     </div>
                  </td>
               </tr>
               <tr>
                  <td>
                     <div data-type="switch"
                        data-device="mySauna"
                        data-get="ftui_state:STATE"
                        data-get-on="Sauna\|85"
                        data-get-off="^(?!Sauna\|85).*$"
                        data-set-on="on Sauna 85"
                        data-set-off="off"
                        data-icon="oa-scene_sauna"
                        data-background-icon="fa-circle-thin"
                        data-on-color="#0088CC"
                        data-off-color="#888"
                        data-on-background-color="#0088CC"
                        data-off-background-color="#3D4C66"
                        class="">
                     </div>
                     <div class="">After Fitness Sauna<br>85°C
                     </div>
                  </td>
                  <td>
                     <div data-type="switch"
                        data-device="mySauna"
                        data-get="ftui_state:STATE"
                        data-get-on="Sauna\|70"
                        data-get-off="^(?!Sauna\|70).*$"
                        data-set-on="on Sauna 70"
                        data-set-off="off"
                        data-icon="oa-scene_sauna"
                        data-background-icon="fa-circle-thin"
                        data-on-color="#0088CC"
                        data-off-color="#888"
                        data-on-background-color="#0088CC"
                        data-off-background-color="#3D4C66"
                        class="">
                     </div>
                     <div class="">Summer Sauna Soft<br>70°C
                     </div>
                  </td>
                  <td>
                     <div data-type="switch"
                        data-device="mySauna"
                        data-get="ftui_state:STATE"
                        data-get-on="Sanarium\|65\|10"
                        data-get-off="^(?!Sanarium\|65\|10).*$"
                        data-set-on="on Sanarium 65 10"
                        data-set-off="off"
                        data-icon="oa-scene_sauna"
                        data-background-icon="fa-circle-thin"
                        data-on-color="#0088CC"
                        data-off-color="#888"
                        data-on-background-color="#0088CC"
                        data-off-background-color="#3D4C66"
                        class="">
                     </div>
                     <div class="">Fitness Sanarium<br>65°C - Feuchte: 10
                     </div>
                  </td>
                  <td>
                     <div data-type="switch"
                        data-device="mySauna"
                        data-get="ftui_state:STATE"
                        data-get-on="Sanarium\|50\|6"
                        data-get-off="^(?!Sanarium\|50\|6).*$"
                        data-set-on="on Sanarium 50 6"
                        data-set-off="off"
                        data-icon="oa-scene_sauna"
                        data-background-icon="fa-circle-thin"
                        data-on-color="#0088CC"
                        data-off-color="#888"
                        data-on-background-color="#0088CC"
                        data-off-background-color="#3D4C66"
                        class="">
                     </div>
                     <div class="">Immun-Power Sanarium<br>50°C - Feuchte: 6
                     </div>
                  </td>
               </tr>
            </table>
         </section>


EDIT: Code Review wurde durchgeführt. Vielen Dank an Beta-User!!!
Checke das Modul ein. Vielleicht kann jemand mal was damit anfangen.

Viele Grüße
Alex

ChristianLo

Xasher - Mega - bin unglaublich froh, cooles Modul. Habe FHEM im Docker nur wegen diesem Modul installiert. Funktioniert perfekt, Danke dir! Sende die Daten an Loxone und verwende dort den nativen Saunabaustein, gefüttert mit deinen Daten. Bin grad richtig glücklich Dank Deiner Arbeit! ;)

ChristianLo

Was mich interessieren würde - gibt es eine Möglichkeit, die Soll Temperatur und Zeit als seperaten SET Befehl zu senden? Wäre das ein großer Erweiterungsaufwand?
...an noch eine Frage zum Türkontakt - gibt es eine Möglichkeit, den Status als seperaten GET Befehl zu erkennen?

Die beiden Punkte würden die Anwendung für mich perfektionieren, falls möglich...

xasher

Hi ChritianLo,


Hier ein Auszug aus der Hilfe, was aktuell möglich ist.
| set "name" on Sauna 90 - 3 Parameter: Sauna mit Temperatur [10-100]; Optional Uhrzeit [19:30]                                       
| set "name" on Saunarium 65 5 - 4 Parameter: Sanarium mit Temperatur [40-75]; Optional HumidtyLevel [0-10] und Uhrzeit [19:30]        |                   | set "name" on Infrared 30 5 - 4 Parameter: Infrarot mit Temperatur [20-40] und IR Level [0-10]; Optional Uhrzeit [19:30]   

Viel Spaß damit und vielen Dank für das positive Feedback.

VG
Alex

ChristianLo

Guten Morgen! Leider hat Klafs die https://sauna-app.klafs.com/ offline genommen und somit funktioniert auch die Integration nicht mehr. Super schade - und ärgerlich. Verstehe nicht, warum Klafs nicht eine API zur Verfügung stellt für Smart Homes - die denken hier schon sehr altmodisch...

Danke trotzdem - die letzten Monate ist das PlugIn perfekt gelaufen / eventuell kann das PlugIn aktualisiert werden, die App funktioniert ja noch und evtl. müssen nur URLs angepasst werden?

CoolTux

Einfach mal den Hersteller anschreiben und nach einer API fragen. Je höher die Nachfrage der Kunden um so früher denkt der Hersteller vielleicht mal drüber nach.
Du musst nicht wissen wie es geht! Du musst nur wissen wo es steht, wie es geht.
Support me to buy new test hardware for development: https://www.paypal.com/paypalme/MOldenburg
My FHEM Git: https://git.cooltux.net/FHEM/
Das TuxNet Wiki:
https://www.cooltux.net

xasher

#6
Ja, danke für den Hinweis. Wie schade!
Ich habe eine Mail an den technischen Support geschrieben

Andrew9024

Zitat von: ChristianLo am 10 Dezember 2023, 09:25:50Guten Morgen! Leider hat Klafs die offline genommen und somit funktioniert auch die Integration nicht mehr. Super schade - und ärgerlich. Verstehe nicht, warum Klafs nicht eine API zur Verfügung stellt für Smart Homes - die denken hier Ghostwriter schon sehr altmodisch...

Danke trotzdem - die letzten Monate ist das PlugIn perfekt gelaufen / eventuell kann das PlugIn aktualisiert werden, die App funktioniert ja noch und evtl. müssen nur URLs angepasst werden?
Wie kann ich das Modul nutzen, um die Sauna über die Klafs App zu steuern? Welche Voraussetzungen und Attribute sind erforderlich?

RappaSan

Was soll das mit dem link auf "Ghostwriter"???

xasher

Zitat von: Andrew9024 am 17 Dezember 2023, 19:00:50Wie kann ich das Modul nutzen, um die Sauna über die Klafs App zu steuern? Welche Voraussetzungen und Attribute sind erforderlich?

Aktuell hat Klafs die Funktion abgeschalten. Vom Support keine Antwort. Ich bin leider nicht fit in Wireshark um die Kommunikation zwischen Handy-App und Klafs WIFI Modul auszulesen.

xasher

Also, ich habe mich in den anderen Foren zu dem Thema durchgefragt. Die Web App hat sich nur geändert und ist nicht entfallen:


https://sauna-app-19.klafs.com

Dh. ich muss es mir anschauen. Dann kann ich die App vmtl. anpassen.

VG
Alex

ChristianLo

Das klingt doch sehr vielversprechend! Wäre genial!

xasher

#12
Da hat sich einiges geändert. Bei mir scheints aber wieder zu funktionieren. Ich teste das die nächsten Tage. Wenn alles klappt, dann check ich das Modul ein.

NACHTRAG: Soweit ich sehe, müssten die bisherigen Funktionen wieder zur Verfügung stehen. Das Paket steht morgen per Update bereit.

VG
Alex

ChristianLo

Hallo - läuft wieder perfekt! Danke dir!! Kleine Schönheitsfrage - das Licht in der Sauna lässt sich nicht einschalten via FHEM, oder? Danke und LG

xasher

Hallo ChristianLo,

freut mich, wenn es bei dir auch wieder funktioniert. Ich kann leider nur Funktionen programmieren, die es in der Web-App gibt. Also alles was man innerhalb https://sauna-app-19.klafs.com/ nutzen kann.
Die Lichtfunktion ist nicht dabei.

Viele Grüße
Alex