fhem-mirror/FHEM/98_MAX_Temperature.pm
2020-01-08 19:00:48 +00:00

1273 lines
43 KiB
Perl

#######################################################################################################################################################
# $Id$
#
# Original von Wzut https://forum.fhem.de/index.php?topic=77678.0
# modifiziert von Bismosa
#
# Dieses Modul erweitert die Heizkörpersteuerung MAX um eine genauere Einstellmöglichkeit.
#
# This code is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
# The GNU General Public License can be found at
# http://www.gnu.org/copyleft/gpl.html.
# A copy is found in the textfile GPL.txt and important notices to the license
# from the author is found in LICENSE.txt distributed with these scripts.
# This script is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
#######################################################################################################################################################
#
# Mögliche Kombinationen:
# HT/WT | <auto|manual> | <temp|eco|comfort|boost|off|on> | <until> (auto nzw. mannual wird ignoriert. Es wird in den Urlaubsmodus gesetzt)
#######################################################################################################################################################
#
#TODO:
#-
#-
#
#######################################################################################################################################################
package main;
use strict;
use warnings;
use Date::Parse;
#####################################
sub MAX_Temperature_Initialize($)
{
my ($hash) = @_;
$hash->{DefFn} = "MAX_Temperature_Define";
$hash->{UndefFn} = "MAX_Temperature_Undef";
$hash->{AttrFn} = "MAX_Temperature_Attr";
$hash->{SetFn} = "MAX_Temperature_Set";
#$hash->{GetFn} = "MAX_Temperature_Get"; #Derzeit kein Get-Befehl
#$hash->{ParseFn} = "MAX_Temperature_Parse";
$hash->{AttrList} = "maxDay:1,2,3,4,5,6,7,14,21,28,35 "
."maxHour:6,12,18,24 "
."createAT:0,1 "
."autoAT_room "
."ignoreDevices "
."addDevices "
."addDevicesFirst:0,1 "
."addGroups "
."addGroupsFirst:0,1 "
."DevicesAlias "
."ShowMSg:0,1 "
."SendButton "
."ResetButton "
."Layout:textField-long "
.$readingFnAttributes;
$hash->{NotifyFn} = "MAX_Temperature_Notify";
$hash->{FW_summaryFn} = "MAX_Temperature_summaryFn"; # displays html instead of status icon in fhemweb room-view
#$hash->{FW_hideDisplayName} = 1; # Forum 88667
#$hash->{FW_detailFn} = "MAX_Temperature_summaryFn"; # displays html instead of status icon in fhemweb room-view
#$data{webCmdFn}{MAX_Temperature} = "MAX_Temperature_webCmdFn"; # displays rc instead of device-commands on the calling device
#$hash->{FW_atPageEnd} = 1; # wenn 1 -> kein Longpoll ohne informid in HTML-Tag
}
###################################
sub MAX_Temperature_Define($$){
my ($hash, $def) = @_;
my $name = $hash->{NAME};
my @a = split("[ \t][ \t]*", $def);
Log3 $name, 4, "$name: Anzahl Argumente = ".int(@a);
Log3 $name, 4, "$name: Argument0 = ".$a[0] if(int(@a) > 0);
Log3 $name, 4, "$name: Argument1 = ".$a[1] if(int(@a) > 1);
Log3 $name, 4, "$name: Argument2 = ".$a[2] if(int(@a) > 2);
Log3 $name, 4, "$name: Argument3 = ".$a[3] if(int(@a) > 3);
if ($init_done == 1){
#nur beim ersten define setzen:
$attr{$name}{icon} = "sani_heating" if( not defined( $attr{$name}{icon} ) );
$attr{$name}{ShowMSg} = "1" if( not defined( $attr{$name}{ShowMSg} ) );
$attr{$name}{room} = "MAX" if( not defined( $attr{$name}{room} ) );
$attr{$name}{SendButton} = "audio_play" if( not defined( $attr{$name}{SendButton} ) );
$attr{$name}{ResetButton} = "control_x" if( not defined( $attr{$name}{ResetButton} ) );
$attr{$name}{Layout} = "[DEVICE][MODE][TEMP][SEND][DATE][CLOCK][SEND]<br>[STATE]" if( not defined( $attr{$name}{Layout} ) );
} else {
#Log3 $name, 1, "$name: already defined";
}
$hash->{STATE} = "Defined";
my $ok;
if (!defined($a[2])){
$a[2] = "T";
$ok=1;
} else {
if (($a[2] eq "T") || ($a[2] eq "HT") || ($a[2] eq "WT")){
$ok=1;
}
if ($a[2] =~ m/^[A-F0-9]{6}$/i){
$ok=1;
}
}
return "Wrong syntax: use define <name> MAX_Temperature <Optional:T|HT|WT|addr>" if (!$ok);
#T = Alle Max-Devices
#HT = nur Thermostate
#WT = nur Wandthermostate
#addr = nur ein Device
Log3 $name, 1, "$name: $a[2]";
$hash->{MAXDEVICE} = $a[2];
#Rücksetzen des Devices
readingsBeginUpdate($hash);
readingsBulkUpdate($hash,"Selected_Device","---");
readingsBulkUpdate($hash,"Selected_MaxDevice","---");
readingsEndUpdate($hash,1);
#Auf keine Events von außerhalb reagieren
$hash->{NOTIFYDEV} = 'global';
#Standards setzen, die unbedingt vorhanden sein sollten
$attr{$name}{maxHour}= '12' unless (exists($attr{$name}{maxHour}));
$attr{$name}{maxDay}= '14' unless (exists($attr{$name}{maxDay}));
return undef;
}
#####################################
sub MAX_Temperature_Undef($$) {
my ($hash, $name) = @_;
delete($modules{MAX_Temperature}{defptr}{$hash->{DEF}}) if(defined($hash->{DEF}) && defined($modules{MAX_Temperature}{defptr}{$hash->{DEF}}));
return undef;
}
#####################################
sub MAX_Temperature_Attr(@) {
my ($cmd, $name, $attrName, $attrValue) = @_;
my $hash = $defs{$name};
if ($init_done == 1) {
if ($cmd eq "set") {
}
##disabled?
if ($attrName eq "disable"){
}
if ($attrName eq "disable"){
}
Log3 $name, 4, "$name: $cmd attr $attrName to $attrValue" if (defined $attrValue);
Log3 $name, 4, "$name: $cmd attr $attrName" if (not defined $attrValue);
}
return undef;
}
#####################################
sub MAX_Temperature_Set($@){
my ($hash, @a) = @_;
my $name = shift @a;
#return undef if(IsDisabled($name) || !$init_done);
#return "no set value specified" if(int(@a) < 1);
my $cmd = shift @a;
my $val = shift @a;
if ($cmd eq "reset"){
#Zuletzt gewähltes Device verwenden!
$val = ReadingsVal("$name", "Selected_Device", "---");
MAX_Temperature_SetDevice($hash, $val);
#MAX_Temperature_Reset($hash);
return "ok";
}
if ($cmd eq "start"){
my $retValue = MAX_Temperature_Execute($hash);
#Nicht zurücksetzen, wenn ERROR!
if ($retValue =~ /^ERROR/){
return $retValue;
}
$val = ReadingsVal("$name", "Selected_Device", "---");
MAX_Temperature_SetDevice($hash, $val);
readingsSingleUpdate($hash,"state","OK",1);
return $retValue;
}
if ($cmd eq "device"){
MAX_Temperature_SetDevice($hash, $val);
return;
}
return;
}
#####################################
sub MAX_Temperature_Execute($) {
my $hash = shift;
my $name = $hash->{NAME};
my $device = ReadingsVal($name,"Selected_MaxDevice","---");
return "ERROR: please set Device first !" if ($device eq "---");
my $mode = ReadingsVal($name,"Selected_Mode","");
my $temp = ReadingsVal($name,"Selected_Temperatur","---");
return "ERROR: please set mode first !" if ($mode eq "");
return "ERROR: please set Temperature or mode first !" if ($temp eq "---") and ($mode eq "---");
$mode = "" if ($mode eq "---");
my $time_s = ReadingsVal($name,"Selected_Uhrzeit","---");
return "ERROR: please set Temperature first!" if ($temp eq "---") and ($time_s ne "---");
my $timeDay = ReadingsVal($name,"Selected_Datum","---");
return "ERROR: please set Date first!" if ($timeDay eq "---") and ($time_s ne "---");
#Zeit zusammensetzen
if ($time_s eq "---"){
$time_s = "";
} else {
$time_s = $timeDay.".".$time_s;
}
#CreateAT
my $stxt;
my $ATtxt;
my $error;
my @ar;
my $Sekunden = 10;
if ($time_s && int(AttrVal($name,"createAT",0))){
@ar = split("\\.",$time_s.":".sprintf("%02d",$Sekunden));
my $time_at = $ar[2]."-".$ar[1]."-".$ar[0]."T".$ar[3];
my $autoAT = "autoAT_".$device;
#Illegale Zeichen entfernen/ersetzen
$autoAT =~ s/[^A-Za-z0-9]/_/g; # Replace all non-alphanumericals with "_"
$ATtxt = $autoAT." at ".$time_at." set $device desiredTemperature auto";
$error = CommandDefine(undef, $ATtxt);
if (($defs{$autoAT}) && !$error && (AttrVal($name,"autoAT_room","MAX") ne "Unsorted")){
$attr{$autoAT}{room} = AttrVal($name,"autoAT_room","MAX");
}
Log3 $name,3,"$name, error -> ".$error if($error);
Log3 $name,4,"$name, define -> $ATtxt";
}
if ($time_s){
my @ar = split("\\.",$time_s);
$time_s = $ar[0].".".$ar[1].".".$ar[2]." ".$ar[3];
#<mode> ist hier nicht erforderlich!
$stxt = "$device desiredTemperature $temp until ".$time_s;
} else {
# keine Zeitangabe
#Es muss eine Temperatur übermittelt werden.
#Wird ein --- übertragen kommt es zu einem Log-Eintrag der Modus wird aber umgeschaltet
#Wird die Temperatur "0" übergeben, kommt keine Fehlermeldung
if ("$temp" eq "---"){
$temp = 0;
}
$stxt = "$device desiredTemperature $mode $temp";
}
Log3 $name,4,"$name, set -> ".$stxt;
$error = CommandSet(undef, $stxt);
if($error){
Log3 $name,3,"$name, error -> ".$error;
return "ERROR: $error";
} else {
my $showMsg = AttrVal($name,"ShowMsg",1);
if ($showMsg eq 1){
if (defined ($ATtxt)){
return "OK: set ".$ATtxt."<br>".$stxt;
} else {
return "OK: set ".$stxt;
}
} else {
if (defined ($ATtxt)){
return $ATtxt."<br>".$stxt;
} else {
return $stxt;
}
}
}
}
#####################################
sub MAX_Temperature_SetDevice($$){
my $hash = shift;
my $name = $hash->{NAME};
my $device = shift;
#Zuerst alle Werte löschen!
MAX_Temperature_Reset($hash);
#Standardwerte setzen
readingsBeginUpdate($hash);
readingsBulkUpdate($hash,"Selected_Device",$device);
my $MaxDevice = MAX_Temperature_GetDeviceFromName($hash,$device);
readingsBulkUpdate($hash,"Selected_MaxDevice",$MaxDevice);
my $Temperatur = ReadingsVal("$MaxDevice", "desiredTemperature", "---");
readingsBulkUpdate($hash,"Selected_Temperatur","$Temperatur");
my $mode = ReadingsVal("$MaxDevice", "mode", "---");
readingsBulkUpdate($hash,"Selected_Mode","$mode");
readingsBulkUpdate($hash,"Selected_Datum","---");
readingsBulkUpdate($hash,"Selected_Uhrzeit","---");
readingsEndUpdate($hash,1);
return;
}
#####################################
sub MAX_Temperature_Reset($){
my $hash = shift;
my $name = $hash->{NAME};
readingsBeginUpdate($hash);
readingsBulkUpdate($hash,"Selected_Device","---");
readingsBulkUpdate($hash,"Selected_Temperatur","---");
readingsBulkUpdate($hash,"Selected_Mode","---");
readingsBulkUpdate($hash,"Selected_Datum","---");
readingsBulkUpdate($hash,"Selected_Uhrzeit","---");
readingsEndUpdate($hash,1);
return;
}
#####################################
sub MAX_Temperature_Notify($$){
# $hash is my hash, $dev_hash is the hash of the changed device
my ($hash, $dev_hash) = @_;
my $events = deviceEvents($dev_hash,0);
my $name = $hash->{NAME};
my $device = $dev_hash->{NAME};
#Log3 $name,3,"$name, event from $device -> $events";
my @MaxDevices = split(/,/, ReadingsVal($name,"Selected_MaxDevice",""));
my $IsMyDevice = 0;
for my $MaxDev(@MaxDevices){
if ($MaxDev eq $device){
$IsMyDevice = 1;
}
}
#Wenn Structure
if ($IsMyDevice == 0){
my @StrucDevices = MAX_Temperature_GetStructureDevices($hash, ReadingsVal($name,"Selected_MaxDevice",""));
#Log3 $name,3,"$name, $device scalar:".scalar @StrucDevices;
if (scalar @StrucDevices == 0){
#keine structure
} else {
#Log3 $name,3,"$name, $device Structure!";
for my $MaxDev(@StrucDevices){
if ($MaxDev eq $device){
$IsMyDevice = 1;
}
}
}
}
#Log3 $name,3,"$name, $device IsmyDevice -> $IsMyDevice";
return undef if ($IsMyDevice eq 0);
my $state = MAX_Temperature_GetDevState($hash);
return undef;
}
#####################################
sub MAX_Temperature_summaryFn($$$$){
my ($FW_wname, $d, $room, $pageHash) = @_; # pageHash is set fosummaryFn.
my $hash = $defs{$d};
my $name = $hash->{NAME};
my $stateFormat = AttrVal($name, "stateFormat", undef);
if (defined($stateFormat)){
return ;
}
my $html;
#$html = "<div><table class=\"block wide\"><tr>";
$html=MAX_Temperature_HTML($hash);
#Log3 $name,1,"$name, $html";
return $html;
}
#####################################
#HTML
sub MAX_Temperature_HTML($){
my $hash = shift;
my $name = $hash->{NAME};
my $Layout = AttrVal($name, "Layout", "[DEVICE][MODE][TEMP][SEND][DATE][CLOCK][SEND]<br>[STATE]");
#Log3 $name,1,"$name, $Layout";
my $htmlState = MAX_Temperature_GetDevState($hash);
my $htmlDev = MAX_Temperature_GetHTMLSelectMaxDevice($hash);
my $htmlMode = MAX_Temperature_GetHTMLSelectMode($hash);
my $htmlTemp = MAX_Temperature_GetHTMLSelectTemperaturen($hash);
my $htmlDatum = MAX_Temperature_GetHTMLSelectDatum($hash);
my $htmlUhrzeit = MAX_Temperature_GetHTMLSelectUhrzeit($hash);
my $htmlSend = MAX_Temperature_GetHTMLSend($hash);
my $htmlReset = MAX_Temperature_GetHTMLReset($hash);
my $html=$Layout;
#Perl ausdrücke auswerten:
#ACHTUNG! VOR SEND! Sonst fehlen die geschweiften klammern!
if ($html =~ "{(.*)}") {
my @list = ($html =~ m/{(.*?)}/g);
foreach my $perlv(@list){
my $val = eval($perlv);
$html =~ s/\Q{$perlv}\E/$val/g;
}
}
$html =~ s/\[STATE\]/$htmlState/g;
$html =~ s/\[DEVICE\]/$htmlDev/g;
$html =~ s/\[MODE\]/$htmlMode/g;
$html =~ s/\[TEMP\]/$htmlTemp/g;
$html =~ s/\[DATE\]/$htmlDatum/g;
$html =~ s/\[CLOCK\]/$htmlUhrzeit/g;
$html =~ s/\[SEND\]/$htmlSend/g;
$html =~ s/\[RESET\]/$htmlReset/g;
return $html;
}
#####################################
#Devices der Structure
sub MAX_Temperature_GetStructureDevices($$){
my $hash = shift;
my $name = $hash->{NAME};
my $structureName = shift;
my @StrucDevices;
#Prüfen, ob es eine structure ist:
my $Devhash = $defs{$structureName};
if (defined($Devhash->{TYPE})){
if ($Devhash->{TYPE} eq "structure"){
@StrucDevices = split(/ /, $Devhash->{DEF});
splice @StrucDevices, 0, 1;
return @StrucDevices;
}
}
return @StrucDevices;
}
#####################################
#Stauts: mode / keepAuto / temperature / desiredTemperature
sub MAX_Temperature_GetDevState($){
my $hash = shift;
my $name = $hash->{NAME};
my $maxdev = ReadingsVal($name,"Selected_MaxDevice","???");
my $state = "";
my @MaxDevices;
my $AddDeviceName = 0;
#Prüfen, ob es eine structure ist:
my @StrucDevices = MAX_Temperature_GetStructureDevices($hash, $maxdev);
if (scalar @StrucDevices == 0){
#Keine structure - Normal weiter
@MaxDevices=split(/,/, $maxdev);
if (scalar(@MaxDevices) > 1){
$AddDeviceName = 1;
}
} else {
#structure
@MaxDevices = @StrucDevices;
$AddDeviceName = 1;
}
for my $maxdev(@MaxDevices){
if ($state ne ""){
$state .= "<br>";
}
$state .= "Mode:".ReadingsVal($maxdev,"mode","???")." keepAuto:".AttrVal($maxdev,"keepAuto","?");
$state .= " Temp:".ReadingsVal($maxdev,"temperature","???")."°C desiredTemp:".ReadingsVal($maxdev,"desiredTemperature","???")."°C";
if ($AddDeviceName){
my $DeviceAlias = AttrVal($maxdev,"alias","?");
if ("$DeviceAlias" eq "?"){
$state .= " ($maxdev)";
} else {
$state .= " ($DeviceAlias)";
}
}
}
readingsSingleUpdate($hash,"state",$state,1);
#Anzahl der zu verarbeitenden Events beggrenzen
#TODO! Das ist nur, wenn Device Aufgerufen! Nicht nach neustarrt!!!
$hash->{NOTIFYDEV} = join(",",@MaxDevices);
return $state;
}
#####################################
#HTML Select Max-Device
sub MAX_Temperature_GetHTMLSelectMaxDevice($){
my $hash = shift;
my $name = $hash->{NAME};
my @Devices=MAX_Temperature_GetDevices($hash);
my $Reading = "Selected_Device";
my $DDSelected = ReadingsVal($name, $Reading, "");
$DDSelected =~ s/ /&nbsp;/g;
$DDSelected =~ s/\xC2\xA0/&nbsp;/g; #non-page-breaking-space
my $html="";
if (scalar @Devices > 1){
#Select
my $changecmd = "cmd.$name=set $name device ";
$html.= "<select id=\"$name$Reading\" name=\"val.$name\" onchange=\"FW_cmd('$FW_ME$FW_subdir?XHR=1&$changecmd ' + this.options[this.selectedIndex].value.replace(' ','&nbsp;'))\">";
#Log3 $name,1,"DDESELECTED $DDSelected";
if ("$DDSelected" eq "---"){
$html.= "<option selected value=\"---\">---</option>";
}
foreach my $Device(@Devices) {
if ("$DDSelected" eq "$Device"){
$html.= "<option selected value=\"".($Device)."\">".($Device)."</option>";
} else {
$html.= "<option value=\"".($Device)."\">".($Device)."</option>";
}
}
$html.="</select>";
} else {
#Nur Device-Name
if (scalar @Devices eq 1){
$html.=$Devices[0];
#Setzen des richtigen Namens, wenn erforderlich
if (ReadingsVal($name, "Selected_Device", "---") eq "---"){
MAX_Temperature_SetDevice($hash, $Devices[0]);
}
} else {
$html.="not defined";
}
}
return $html;
}
#####################################
#Eine Liste der Devices bekommen
#Namen mit leerzeichen -> Immer mit &nbsp;
sub MAX_Temperature_GetDevices($){
my $hash = shift;
my $name = $hash->{NAME};
my $def = $hash->{DEF};
my @ar;
my $type = "?";
#Ignored Devices
my $ignoreDevices = AttrVal($name,"ignoreDevices","");
my @ignoreDevicesList = split(/,/, $ignoreDevices);
#Add Devices/Alias
my $addDevices = AttrVal($name,"addDevices","");
my @addDevicesList = split(/,/, $addDevices);
my $addDevicesFirst = AttrVal($name,"addDevicesFirst",0);
if (($hash->{MAXDEVICE} eq "T") || ($hash->{MAXDEVICE} eq "HT") || ($hash->{MAXDEVICE} eq "WT")){
$type = ".*Thermostat" if ($hash->{MAXDEVICE} eq "T");
$type = "HeatingThermostat" if ($hash->{MAXDEVICE} eq "HT");
$type = "WallMountedThermostat" if ($hash->{MAXDEVICE} eq "WT");
@ar = devspec2array("DEF=".$type.".*");
}else {
@ar = devspec2array("DEF=.*Thermostat ".$hash->{MAXDEVICE});
}
#Filtern der ignoreDevices
my %h;
@h{@ignoreDevicesList} = undef;
@ar = grep {not exists $h{$_}} @ar;
#hinzufügen manueller Devices
if ($addDevicesFirst){
my @arNEU = @addDevicesList;
push(@arNEU, @ar);
@ar = @arNEU;
}else{
push(@ar, @addDevicesList);
}
#hinzufügen von Gruppen
#Grp1:dev1,dev2 Grp2:dev3,dev4
my $AddGroup = AttrVal($name,"addGroups","");
my @AddGroups = split(/ /, $AddGroup);
my $addGroupsFirst = AttrVal($name,"addGroupsFirst",0);
#Liste erstellen
my @Groups;
for my $Grp(@AddGroups){
my @split=split(/:/, $Grp);
my $GrpName=$split[0];
#my @GrpDevices=split(/,/, $split[1]);
push (@Groups,$GrpName);
}
if ($addGroupsFirst){
my @arNEU = @Groups;
push(@arNEU, @ar);
@ar = @arNEU;
}else{
push(@ar, @Groups);
}
#Umbenennen von den Devices
#<Device>:<Alias>,<Device2>:Alias mit leerzeichen,<Device3>:<Alias3>
#Max_HT_Buero:
my @aliase = split(/,/, AttrVal($name,"DevicesAlias",""));
for my $dev(@ar){
for my $alias(@aliase){
my @list=split(/:/,$alias);
my $AliasDevice=$list[0];
my $AliasDeviceName=$list[1];
#Log3 $name,1,"$name $dev $AliasDeviceName";
if ($AliasDevice eq $dev){
$dev=$AliasDeviceName;
#Log3 $name,1,"$name $dev $AliasDeviceName";
}
}
}
#Leerzeichen mit &nbsp; ersetzen
for my $dev(@ar){
$dev =~ s/ /&nbsp;/g;
}
return @ar;
}
#####################################
#Das Max-Device (ggf. Kommaliste) zurückbekommen
sub MAX_Temperature_GetDeviceFromName($$){
my $hash = shift;
my $caption = shift;
my $name = $hash->{NAME};
my $MaxDevice = $caption; #ggf. bereits das Device
$caption =~ s/ /&nbsp;/g;
$caption =~ s/\xC2\xA0/&nbsp;/g; #non-page-breaking-space
#Gruppe?
my $AddGroup = AttrVal($name,"addGroups","");
my @AddGroups = split(/ /, $AddGroup);
my @Groups;
for my $Grp(@AddGroups){
my @split=split(/:/, $Grp);
my $GrpName=$split[0];
$GrpName =~ s/ /&nbsp;/g;
$GrpName =~ s/\xC2\xA0/&nbsp;/g; #non-page-breaking-space
#my @GrpDevices=split(/,/, $split[1]);
#Log3 $name,1,"$name $caption $GrpName";
if ($caption eq $GrpName){
$MaxDevice = $split[1];
}
}
#Alias?
my @aliase = split(/,/, AttrVal($name,"DevicesAlias",""));
for my $alias(@aliase){
my @list=split(/:/,$alias);
my $AliasDevice=$list[0];
my $AliasDeviceName=$list[1];
$AliasDeviceName =~ s/ /&nbsp;/g;
$AliasDeviceName =~ s/\xC2\xA0/&nbsp;/g; #non-page-breaking-space
#Log3 $name,1,"$name $dev $AliasDeviceName";
if ($caption eq $AliasDeviceName){
$MaxDevice = $AliasDevice;
}
}
return $MaxDevice;
}
#####################################
#HTML Select Mode
sub MAX_Temperature_GetHTMLSelectMode($){
my $hash = shift;
my $name = $hash->{NAME};
#Log3 $name,1,"$name, event MAX_Temperature_GetHTMLSelectMode";
my $mode = "---,auto,manual";
my $html = MAX_Temperature_GetHTMLSelectListe($hash, $mode,"Selected_Mode");
return $html;
}
#####################################
#HTML Select Temperaturen
sub MAX_Temperature_GetHTMLSelectTemperaturen($){
my $hash = shift;
my $name = $hash->{NAME};
my $temp = "---,off,eco,comfort,boost,";
my $i;
for ($i=0;$i<52;$i++) { $temp .= sprintf("%.1f",5+(0.5*$i)).","; }
$temp .="on";
my $html = MAX_Temperature_GetHTMLSelectListe($hash, $temp,"Selected_Temperatur");
return $html;
}
#####################################
#HTML Select Datum
sub MAX_Temperature_GetHTMLSelectDatum($){
my $hash = shift;
my $name = $hash->{NAME};
my $maxDay = int(AttrVal($name,"maxDay",7));
my ($Sekunden, $Minuten, $Stunden, $Monatstag, $Monat, $Jahr, undef, undef, undef) = localtime(time);
my $Heute = sprintf("%02d.%02d.%02d",$Monatstag,$Monat+1,$Jahr+1900);
my $d = "---,";
for (my $i=0;$i<$maxDay;$i++)
{
(undef, $Minuten, $Stunden, $Monatstag, $Monat, $Jahr, undef, undef, undef) = localtime(time+($i*24*60*60));
$d .= sprintf("%02d.%02d.%02d",$Monatstag,$Monat+1,$Jahr+1900).",";
}
chop($d);
my $html = MAX_Temperature_GetHTMLSelectListe($hash, $d,"Selected_Datum");
return $html;
}
#####################################
#HTML Select Uhrzeit
sub MAX_Temperature_GetHTMLSelectUhrzeit($){
my $hash = shift;
my $name = $hash->{NAME};
my $maxHour = int(AttrVal($name,"maxHour",12));
my ($Sekunden, $Minuten, $Stunden, $Monatstag, $Monat, $Jahr, undef, undef, undef) = localtime(time);
my $m = (int($Minuten) < 30) ? 30-int($Minuten) : 60-int($Minuten);
my $t = "---,";
for (my $i=0;$i<$maxHour*2;$i++)
{
(undef, $Minuten, $Stunden, $Monatstag, $Monat, $Jahr, undef, undef, undef) = localtime(time+($i*1800)+($m*60)-$Sekunden);
$t .= sprintf("%02d\:%02d",$Stunden,$Minuten).",";
}
chop($t);
my $html = MAX_Temperature_GetHTMLSelectListe($hash, $t,"Selected_Uhrzeit");
return $html;
}
#####################################
#HTML Select nach Liste
sub MAX_Temperature_GetHTMLSelectListe($$$){
my $hash = shift;
my $Names = shift;
my $ReadingName = shift;
my $name = $hash->{NAME};
my @modes = split(/,/, $Names);
my $html = "";
my $DDSelected = ReadingsVal($name, $ReadingName, "");
my $changecmd = "cmd.$name=setreading $name $ReadingName ";
$html.= "<select name=\"val.$name\" onchange=\"FW_cmd('$FW_ME$FW_subdir?XHR=1&$changecmd ' + this.options[this.selectedIndex].value)\">";
foreach my $mode (@modes) {
if ($DDSelected eq "$mode"){
$html.= "<option selected value=".($mode).">".($mode)."</option>";
} else {
$html.= "<option value=".($mode).">".($mode)."</option>";
}
}
$html.="</select>";
return $html;
}
#####################################
#HTML Send Bild oder Text
sub MAX_Temperature_GetHTMLSend($){
#entweder Button oder Text
my $hash = shift;
my $name = $hash->{NAME};
my $Icon = AttrVal($name, "SendButton", "audio_play");
my $html;
my $img = FW_makeImage("$Icon");
#Log3 $name,1,"$name, $img";
my $cmd = "cmd.$name=set $name start";
my $showMsg = AttrVal($name,"ShowMsg",1);
if ($showMsg eq 0){
$html.="<a style=\"cursor: pointer;\" onClick=\"FW_cmd('$FW_ME$FW_subdir?XHR=1&$cmd')\">$img</a>";
} else {
$html.="<a style=\"cursor: pointer;\" onClick=\"FW_cmd('$FW_ME$FW_subdir?XHR=1&$cmd',function(data){FW_okDialog(data)})\">$img</a>";
}
# $html.=<<'EOF';
# <script type="text/javascript">
# function myFunction(data){alert(data)}
# </script>
#EOF
#Make sure "EOF" is on a line by itself with no preceeding or trailing spaces, tabs, etc.
#FW_cmd(FW_root+'?cmd={FW_makeImage("fts_shutter_10")}&XHR=1', function(data){FW_okDialog(data)});
#Log3 $name,1,"$name, $html";
return $html;
}
#####################################
#HTML Reset Bild oder Text
sub MAX_Temperature_GetHTMLReset($){
#entweder Button oder Text
my $hash = shift;
my $name = $hash->{NAME};
my $Icon = AttrVal($name, "ResetButton", "control_x");
my $html;
my $img = FW_makeImage("$Icon");
#Log3 $name,1,"$name, $img";
my $cmd = "cmd.$name=set $name reset";
$html.="<a style=\"cursor: pointer;\" onClick=\"FW_cmd('$FW_ME$FW_subdir?XHR=1&$cmd')\">$img</a>";
return $html;
}
# Eval-Rückgabewert für erfolgreiches
# Laden des Moduls
1;
# Beginn der Commandref
=pod
=item [helper]
=item summary Set MAX-Heating Devices
=item summary_DE MAX-Heizkörperthermostate setzen
=begin html
<a name="MAX_Temperature"></a>
<h3>MAX_Temperature</h3>
<div>
<ul>
<p>This module extends the MAX radiator control by additional setting options.<br>
Possibilities:<br>
- Set the temperature for one or more radiator thermostats. <br>
- Set the temperature and time (vacation mode). <br>
- Groups can be defined for this module. <br>
- Individual devices can be added or excluded. <br>
- The layout for the selection fields can be adjusted as required. <br>
</p>
<h4>Examples:</h4>
<p>
<code>define MaxTemp MAX_Temperature</code><br>
<code>define MaxTemp MAX_Temperature T</code><br>
<code>define MaxTemp MAX_Temperature HT</code><br>
<code>define MaxTemp MAX_Temperature WT</code><br>
<code>define MaxTemp MAX_Temperature 123456</code><br>
</p>
<a name="MAX_Temperature_Define"></a>
<h4>Define</h4>
<p><code>define &lt;NAME&gt; MAX_Temperature</code><br>
Definition of a temperature module for all radiator thermostats and wall thermostats.<br>
</p>
<p><code>define &lt;NAME&gt; MAX_Temperature T</code><br>
Definition of a temperature module for all radiator thermostats and wall thermostats.<br>
</p>
<p><code>define &lt;NAME&gt; MAX_Temperature HT</code><br>
Definition of a temperature module only for radiator thermostats.<br>
</p>
<p><code>define &lt;NAME&gt; MAX_Temperature WT</code><br>
Definition of a temperature module only for wall thermostats.<br>
</p>
<p><code>define &lt;NAME&gt; MAX_Temperature 123456</code><br>
Definition of a temperature module for a specific thermostat.<br>
</p>
</ul>
<h4>Attributes</h4>
<ul><a name="MAX_Temperature_Attr"></a>
<li><a name="Layout">Layout</a><br>
<code>attr &lt;NAME&gt; Layout &lt;HTML&gt;</code><br>
Possibility of layout of the advertisement. <br> <br>
It is possible to define the layout yourself. HTML code is possible. Examples: <br>
Standard: <br>
[DEVICE][MODE][TEMP][SEND][DATE][CLOCK][SEND]&lt;br&gt;\n[STATE]<br><br>
Structure as a table:<br>
[STATE]&lt;br&gt;&lt;table class=\"block wide\"&gt;&lt;tr&gt;&lt;td&gt;[DEVICE]&lt;/td&gt;&lt;td&gt;[MODE]&lt;/td&gt;&lt;td&gt;[DATE]&lt;/td&gt;&lt;td&gt;[CLOCK]&lt;/td&gt;&lt;td&gt;[SEND]&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
<br><br>
Without setting the Holiday mode:<br>
[STATE]&lt;br&gt; [DEVICE][MODE][TEMP][SEND]<br><br>
Holiday mode setting only:<br>
[STATE]&lt;br&gt; [DEVICE][TEMP][DATE][CLOCK][SEND]<br><br>
All entries one below the other:<br>
[STATE]&lt;br&gt;<br>
[DEVICE]&lt;br&gt;<br>
[MODE]&lt;br&gt;<br>
[TEMP]&lt;br&gt;<br>
[DATE]&lt;br&gt;<br>
[CLOCK]&lt;br&gt;<br>
[SEND]&lt;br&gt;<br><br>
It is also possible to add Perl code (in curly brackets)<br>
{ReadingsVal("myDevice","MyReading","Default_Value")}[STATE][DEVICE][MODE][TEMP][SEND][DATE][CLOCK][SEND]<br><br>
<b>Special entries:</b><br>
<table>
<colgroup> <col width="120"></colgroup>
<tr>
<td>[STATE]</td>
<td>Status<br>
Display of readings of the selected MAX device. <br>
Standard: Mode: (auto | manual | temporary) keepAuto: (0 | 1) Temp: (current temperature) desiredTemperature: (set temperature) <br>
If a group is selected, the name of the group is displayed here.
</td>
</tr>
<tr>
<td>[DEVICE]</td>
<td>Selection box or name of the selected MAX device.</td>
</tr>
<tr>
<td>[MODE]</td>
<td>Selection box for the mode (auto | manual).</td>
</tr>
<tr>
<td>[TEMP]</td>
<td>Selection box for the desired temperature.</td>
</tr>
<tr>
<td>[DATE]</td>
<td>Selection box for the date.</td>
</tr>
<tr>
<td>[CLOCK]</td>
<td>Selection box for the time.</td>
</tr>
<tr>
<td>[SEND]</td>
<td>Button to send the new settings.</td>
</tr>
<tr>
<td>[RESET]</td>
<td>Button to reset the settings made.</td>
</tr>
</table>
</li>
<li><a name="maxDay">maxDay</a><br>
<code>attr &lt;NAME&gt; maxDay (1,2,3,4,5,6,7,14,21,28,35)</code><br>
Setting how many days are entered in the selection field for the date.<br>
</li>
<li><a name="maxHour">maxHour</a><br>
<code>attr &lt;NAME&gt; maxHour (6,12,18,24)</code><br>
Setting how many hours are entered in the selection field for the time.<br>
</li>
<li><a name="ignoreDevices">ignoreDevices</a><br>
<code>attr &lt;NAME&gt; ignoreDevices &lt;listing&gt;</code><br>
List of devices to be ignored by the module. <br>
Comma as separator. <br>
Example: <br>
MyMax_Wohnzimmer,MyMax_Schlafzimmer<br>
</li>
<li><a name="addDevices">addDevices</a><br>
<code>attr &lt;NAME&gt; addDevices &lt;listing&gt;</code><br>
List of devices to be added to the selection list. <br>
This means that e.g. Add "structure". <br>
Comma as separator. <br>
Example: <br>
Struc_heating1,Struc_heating2<br>
</li>
<li><a name="addDevicesFirst">addDevicesFirst</a><br>
<code>attr &lt;NAME&gt; addDevicesFirst (0|1)</code><br>
Show the manually added devices first in the list.<br>
</li>
<li><a name="addGroups">addGroups</a><br>
<code>attr &lt;NAME&gt; addGroups (Text)</code><br>
Create groups. <br>
Examples: <br>
<code>Basement:Max_Device1,Max_Device2,Max_Device3 Upstairs:Max_Device4,Max_Device5</code><br>
<code>Group&amp;nbsp;1:Max_Device1,Max_Device2,Max_Device3 Group&amp;nbsp;2:Max_Device4,Max_Device5</code><br>
To insert a space in the group name, can be entered <code>&amp;nbsp;</code>. Example:<br>
<code>My&amp;nbsp;Group:Device1,Device2</code>
</li>
<li><a name="addGroupsFirst">addGroupsFirst</a><br>
<code>attr &lt;NAME&gt; addGroupsFirst (0|1)</code><br>
Show the groups first in the list.<br>
</li>
<li><a name="DevicesAlias">DevicesAlias</a><br>
<code>attr &lt;NAME&gt; addDevicesAlias &lt;listing&gt;</code><br>
Assign an alias for the MAX devices. <br>
Comma as separator. <br>
Examples: <br>
Struc_Radiator1:Radiator below,Struc_Radiator2:Radiator above<br>
Max-Device1:Radiator bathroom,Max-Device2:Radiator kitchen<br>
</li>
<li><a name="ShowMSg">ShowMSg</a><br>
<code>attr &lt;NAME&gt; ShowMSg (0|1)</code><br>
After sending settings, display a success notification in a dialog.<br>
</li>
<li><a name="SendButton">SendButton</a><br>
<code>attr &lt;NAME&gt; SendButton (Text)</code><br>
Either the name of a symbol or a text. Standard: <br>
audio_play<br>
</li>
<li><a name="ResetButton">ResetButton</a><br>
<code>attr &lt;NAME&gt; ResetButton (Text)</code><br>
Either the name of a symbol or a text. Standard: <br>
control_x<br>
</li>
<li><a name="createAT">createAT</a><br>
<code>attr &lt;NAME&gt; createAT (0|1)</code><br>
Automatically creates a temporary AT to the device after the time has expired <br>
to switch back to automatic mode. <br>
This is usually not necessary<br>
</li>
<li><a name="autoAT_room">autoAT_room</a><br>
<code>attr &lt;NAME&gt; autoAT_room MAX</code><br>
The room in which the temporary AT should be created. Default:<br>
MAX<br>
</li>
</ul>
<h4>Readings</h4>
<ul><a name="MAX_Temperature_Readings"></a>
<li><a name="Selected_*">Selected_*</a><br>
The currently selected values of the selection boxes.<br>
</li>
</ul>
</div>
=end html
=begin html_DE
<a name="MAX_Temperature"></a>
<h3>MAX_Temperature</h3>
<div>
<ul>
<p>Dieses Modul erweitert die Heizkörpersteuerung MAX um weitere Einstellmöglichkeiten.<br>
Möglichkeiten:<br>
- Setzen der Temperatur für ein oder mehrere Heizkörperthermostate.<br>
- Setzen der Temperatur und der Zeit (Urlaubsmodus).<br>
- Gruppen können für dieses Modul festgelegt werden.<br>
- Es können einzelne Devices hinzugefügt oder auch ausgeschlossen werden.<br>
- Das Layout für die Auswahlfelder kann beliebig angepasst werden.<br>
</p>
<h4>Beispiele:</h4>
<p>
<code>define MaxTemp MAX_Temperature</code><br>
<code>define MaxTemp MAX_Temperature T</code><br>
<code>define MaxTemp MAX_Temperature HT</code><br>
<code>define MaxTemp MAX_Temperature WT</code><br>
<code>define MaxTemp MAX_Temperature 123456</code><br>
</p>
<a name="MAX_Temperature_Define"></a>
<h4>Define</h4>
<p><code>define &lt;NAME&gt; MAX_Temperature</code><br>
Definition eines Temperatur-Moduls für alle Heizkörperthermostate und Wandthermostate.<br>
</p>
<p><code>define &lt;NAME&gt; MAX_Temperature T</code><br>
Definition eines Temperatur-Moduls für alle Heizkörperthermostate und Wandthermostate.<br>
</p>
<p><code>define &lt;NAME&gt; MAX_Temperature HT</code><br>
Definition eines Temperatur-Moduls nur für Heizkörperthermostate.<br>
</p>
<p><code>define &lt;NAME&gt; MAX_Temperature WT</code><br>
Definition eines Temperatur-Moduls nur für Wandthermostate.<br>
</p>
<p><code>define &lt;NAME&gt; MAX_Temperature 123456</code><br>
Definition eines Temperatur-Moduls für einen bestimmten Thermostaten.<br>
</p>
</ul>
<h4>Attributes</h4>
<ul><a name="MAX_Temperature_Attr"></a>
<li><a name="Layout">Layout</a><br>
<code>attr &lt;NAME&gt; Layout &lt;HTML&gt;</code><br>
Layoutmöglichkeit der Anzeige.<br><br>
Es ist möglich das Layout selbst zu definieren. HTML-Code ist möglich. Beispiele:<br>
Standard:<br>
[DEVICE][MODE][TEMP][SEND][DATE][CLOCK][SEND]&lt;br&gt;\n[STATE]<br><br>
Aufbau als Tabelle:<br>
[STATE]&lt;br&gt;&lt;table class=\"block wide\"&gt;&lt;tr&gt;&lt;td&gt;[DEVICE]&lt;/td&gt;&lt;td&gt;[MODE]&lt;/td&gt;&lt;td&gt;[DATE]&lt;/td&gt;&lt;td&gt;[CLOCK]&lt;/td&gt;&lt;td&gt;[SEND]&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
<br><br>
Ohne Einstellung des Urluabsmodus:<br>
[STATE]&lt;br&gt; [DEVICE][MODE][TEMP][SEND]<br><br>
Nur Einstellung des Urlaubsmodus:<br>
[STATE]&lt;br&gt; [DEVICE][TEMP][DATE][CLOCK][SEND]<br><br>
Alle Einträge untereinander:
[STATE]&lt;br&gt;<br>
[DEVICE]&lt;br&gt;<br>
[MODE]&lt;br&gt;<br>
[TEMP]&lt;br&gt;<br>
[DATE]&lt;br&gt;<br>
[CLOCK]&lt;br&gt;<br>
[SEND]&lt;br&gt;<br><br>
Es ist auch möglich Perl-Code hinzuzufügen (in geschweiften Klammern)<br>
{ReadingsVal("meinDevice","MeinReading","Standardwert")}[STATE][DEVICE][MODE][TEMP][SEND][DATE][CLOCK][SEND]<br><br>
<b>Spezielle Einträge:</b><br>
<table>
<colgroup> <col width="120"></colgroup>
<tr>
<td>[STATE]</td>
<td>Status<br>
Anzeige von Readings des gewählten MAX-Gerätes.<br>
Standard: Mode: (auto|manual|temporary) keepAuto: (0|1) Temp: (aktuelle Temperatur) desiredTemperature: (eingestellte Temperatur)<br>
Wird eine Gruppe ausgewählt, wird hier der Name der Gruppe angezeigt.
</td>
</tr>
<tr>
<td>[DEVICE]</td>
<td>Auswahlbox oder Name des gewählten MAX-Gerätes.</td>
</tr>
<tr>
<td>[MODE]</td>
<td>Auswahlbox für den Modus (auto|manual).</td>
</tr>
<tr>
<td>[TEMP]</td>
<td>Auswahlbox für die gewünschte Temperatur.</td>
</tr>
<tr>
<td>[DATE]</td>
<td>Auswahlbox für das Datum.</td>
</tr>
<tr>
<td>[CLOCK]</td>
<td>Auswahlbox für die Uhrzeit.</td>
</tr>
<tr>
<td>[SEND]</td>
<td>Button zum senden der neuen Einstellungen.</td>
</tr>
<tr>
<td>[RESET]</td>
<td>Button zum zurücksetzen der gemachten Einstellungen.</td>
</tr>
</table>
</li>
<li><a name="maxDay">maxDay</a><br>
<code>attr &lt;NAME&gt; maxDay (1,2,3,4,5,6,7,14,21,28,35)</code><br>
Einstellung, wie viele Tage im Auswahlfeld für das Datum eingetragen werden.<br>
</li>
<li><a name="maxHour">maxHour</a><br>
<code>attr &lt;NAME&gt; maxHour (6,12,18,24)</code><br>
Einstellung, wie viele Stunden im Auswahlfeld für das Zeit eingetragen werden.<br>
</li>
<li><a name="ignoreDevices">ignoreDevices</a><br>
<code>attr &lt;NAME&gt; ignoreDevices &lt;Auflistung&gt;</code><br>
Auflistung von Geräten, die von dem Modul ignoriert werden sollen.<br>
Komma als Trenner.<br>
Beispiel:<br>
MyMax_Wohnzimmer,MyMax_Schlafzimmer<br>
</li>
<li><a name="addDevices">addDevices</a><br>
<code>attr &lt;NAME&gt; addDevices &lt;Auflistung&gt;</code><br>
Auflistung von Geräten, die zur Auswahlliste hinzugefügt werden sollen.<br>
Somit lassen sich dann auch z.B. "structure" hinzufügen.<br>
Komma als Trenner.<br>
Beispiel:<br>
Struc_HeizungenUnten,Struc_HeizungenOben<br>
</li>
<li><a name="addDevicesFirst">addDevicesFirst</a><br>
<code>attr &lt;NAME&gt; addDevicesFirst (0|1)</code><br>
Die manuell hinzugefügten Geräte als erstes in der Liste anzeigen.<br>
</li>
<li><a name="addGroups">addGroups</a><br>
<code>attr &lt;NAME&gt; addGroups (Text)</code><br>
Gruppen erstellen.<br>
Beispiele:<br>
<code>Untergeschoss:Max_Device1,Max_Device2,Max_Device3 Obergeschoss:Max_Device4,Max_Device5</code><br>
<code>Gruppe&amp;nbsp;1:Max_Device1,Max_Device2,Max_Device3 Gruppe&amp;nbsp;2:Max_Device4,Max_Device5</code><br>
Um ein leerzeichen in den Namen der Gruppe einzufügen, kann <code>&amp;nbsp;</code> eingegeben werden. Beispiel:<br>
<code>Meine&amp;nbsp;Gruppe:Device1,Device2</code>
</li>
<li><a name="addGroupsFirst">addGroupsFirst</a><br>
<code>attr &lt;NAME&gt; addGroupsFirst (0|1)</code><br>
Die Gruppen als erstes in der Liste anzeigen.<br>
</li>
<li><a name="DevicesAlias">DevicesAlias</a><br>
<code>attr &lt;NAME&gt; addDevicesAlias &lt;Auflistung&gt;</code><br>
Ein Aliasnamen für die MAX-Geräte vergeben.<br>
Komma als Trenner.<br>
Beispiele:<br>
Struc_HeizungenUnten:Heizkörper unten,Struc_HeizungenOben:Heizkörper oben<br>
Max-Device1:Heizkörper Bad,Max-Device2:Heizkörper Küche<br>
</li>
<li><a name="ShowMSg">ShowMSg</a><br>
<code>attr &lt;NAME&gt; ShowMSg (0|1)</code><br>
Nach dem Senden von Einstellungen eine Erfolgsbenachrichtigung in einem Dialog anzeigen.<br>
</li>
<li><a name="SendButton">SendButton</a><br>
<code>attr &lt;NAME&gt; SendButton (Text)</code><br>
Entweder der Name eines Symbols oder ein Text. Standard:<br>
audio_play<br>
</li>
<li><a name="ResetButton">ResetButton</a><br>
<code>attr &lt;NAME&gt; ResetButton (Text)</code><br>
Entweder der Name eines Symbols oder ein Text. Standard:<br>
control_x<br>
</li>
<li><a name="createAT">createAT</a><br>
<code>attr &lt;NAME&gt; createAT (0|1)</code><br>
Legt automatisch ein temporäres AT an, um das Gerät nach Ablauf der Zeit<br>
wieder in den Automatik Modus zu schalten.<br>
Dies ist normalerweise nicht notwendig.<br>
</li>
<li><a name="autoAT_room">autoAT_room</a><br>
<code>attr &lt;NAME&gt; autoAT_room MAX</code><br>
Der Raum in dem das temporäre AT erstellt werden soll. Standard:<br>
MAX<br>
</li>
</ul>
<h4>Readings</h4>
<ul><a name="MAX_Temperature_Readings"></a>
<li><a name="Selected_*">Selected_*</a><br>
Die derzeit ausgewählten Werte der Auswahlboxen.<br>
</li>
</ul>
</div>
=end html_DE
=cut