[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