diff --git a/CHANGED b/CHANGED
index cbdeac94a..15f61a991 100644
--- a/CHANGED
+++ b/CHANGED
@@ -1,4 +1,7 @@
+# Add changes at the top of the list.
+
- SVN
+ - feature: THRESHOLD Module by Damian
- change: 30_HUEDevice: use new devStateIcon feature to show device color
in room overview
- feature: added example Setup SQL and configuration for SQLite
diff --git a/FHEM/98_THRESHOLD.pm b/FHEM/98_THRESHOLD.pm
new file mode 100644
index 000000000..9b9ee9419
--- /dev/null
+++ b/FHEM/98_THRESHOLD.pm
@@ -0,0 +1,633 @@
+##############################################
+# 98_THRESHOLD by Damian Sordyl
+#
+# This file is part of fhem.
+#
+# Fhem 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.
+#
+# Fhem 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.
+#
+# You should have received a copy of the GNU General Public License
+# along with fhem. If not, see .
+#
+##############################################################################
+
+
+package main;
+use strict;
+use warnings;
+
+sub THRESHOLD_setValue($$);
+
+##########################
+sub
+THRESHOLD_Initialize($)
+{
+ my ($hash) = @_;
+ $hash->{DefFn} = "THRESHOLD_Define";
+ $hash->{SetFn} = "THRESHOLD_Set";
+ $hash->{NotifyFn} = "THRESHOLD_Notify";
+ $hash->{AttrList} = "disable:0,1 loglevel:0,1,2,3,4,5,6";
+}
+
+
+##########################
+sub
+THRESHOLD_Define($$$)
+{
+ my ($hash, $def) = @_;
+ my @b =split (/\|/,$def);
+ my @a = split("[ \t][ \t]*", $b[0]);
+ my $cmd1;
+ my $cmd2;
+ my $cmd_default;
+ my $actor;
+ my $init_desired_value;
+
+ if (@b > 4 || @a < 3 || @a > 5) {
+ my $msg = "wrong syntax: define THRESHOLD" .
+ "[:[:][:] [AND|OR [:][:]] [][|][|][|]";
+ Log 2, $msg;
+ return $msg;
+ }
+ my $pn = $a[0];
+ # Sensor
+ my ($sensor, $reading, $hysteresis,$init_desired_value) = split(":", $a[2], 4);
+
+ if(!$defs{$sensor}) {
+ my $msg = "$pn: THRESHOLD: Unknown sensor device $sensor specified";
+ Log 2, $msg;
+ return $msg;
+ }
+
+ $hash->{sensor} = $sensor;
+ $reading = "temperature" if (!$reading);
+
+ if (!$hysteresis) {
+ if ($reading eq "temperature") {
+ $hysteresis=1;
+ } else {
+ $hysteresis=20;
+ }
+ } elsif ($hysteresis !~ m/^[\d\.]*$/ ) {
+ my $msg = "$pn: THRESHOLD: hysteresis needs a numeric parameter";
+ Log 2, $msg;
+ return $msg;
+ } elsif ($init_desired_value) {
+ if ($init_desired_value !~ m/^[-\d\.]*$/) {
+ my $msg = "$pn: THRESHOLD: init_desired_value needs a numeric parameter";
+ Log 2, $msg;
+ return $msg;
+ }
+ }
+ $hash->{sensor_reading} = $reading;
+ $hash->{hysteresis} = $hysteresis;
+
+ # Sensor2
+ my $operator=$a[3];
+ if ($operator) {
+ if (($operator eq "AND") or ($operator eq "OR")) {
+ my ($sensor2, $sensor2_reading, $state) = split(":", $a[4], 3);
+ if(!$defs{$sensor2}) {
+ my $msg = "$pn: THRESHOLD: Unknown sensor2 device $sensor2 specified";
+ Log 2, $msg;
+ return $msg;
+ }
+ $sensor2_reading = "state" if (!$sensor2_reading);
+ $state = "open" if (!$state);
+ $hash->{operator} = $operator;
+ $hash->{sensor2} = $sensor2;
+ $hash->{sensor2_reading} = $sensor2_reading;
+ $hash->{sensor2_state} = $state;
+ $actor = $a[5];
+ } else {
+ $actor = $a[3];
+ }
+ }
+ if ($actor) {
+ if (!$defs{$actor}) {
+ my $msg = "$pn: THRESHOLD: Unknown actor device $actor specified";
+ Log 2, $msg;
+ return $msg;
+ }
+ }
+ if (@b == 1) { # no actor parameters
+ $cmd1 = "set $actor off";
+ $cmd2 = "set $actor on";
+ $cmd_default = 2;
+ } else {
+ $cmd1 = $b[1];
+ $cmd2 = $b[2];
+ $cmd_default = $b[3];
+ if (!$cmd_default) {
+ $cmd_default = 0;
+ } elsif ($cmd_default !~ m/^[0-2]*$/ ) {
+ my $msg = "$pn: THRESHOLD: cmd_default_index needs 0,1,2";
+ Log 2, $msg;
+ return $msg;
+ }
+ }
+
+ if ($actor) {
+ $cmd1 =~ s/@/$actor/g;
+ $cmd2 =~ s/@/$actor/g;
+ }
+
+ $hash->{helper}{actor_cmd1} = $cmd1;
+ $hash->{helper}{actor_cmd2} = $cmd2;
+ $hash->{helper}{actor_cmd_default} = $cmd_default;
+ $hash->{STATE} = 'initialized' if (!ReadingsVal($pn,"desired_value",""));
+
+ if ($init_desired_value)
+ {
+ readingsBeginUpdate ($hash);
+ readingsBulkUpdate ($hash, "state", "active $init_desired_value");
+ readingsBulkUpdate ($hash, "threshold_min", $init_desired_value-$hysteresis);
+ readingsBulkUpdate ($hash, "cmd","wait for next cmd");
+ readingsBulkUpdate ($hash, "desired_value", $init_desired_value);
+ readingsEndUpdate ($hash, 1);
+ }
+ return undef;
+}
+
+##########################
+sub
+THRESHOLD_Set($@)
+{
+ my ($hash, @a) = @_;
+ my $pn = $hash->{NAME};
+ my $ret="";
+ return "Need a parameter for set" if(@a < 2);
+ my $arg = $a[1];
+ my $desired_value;
+ if ($arg eq "desired" ) {
+ return "Set desired needs a numeric parameter" if(@a != 3 || $a[2] !~ m/^[-\d\.]*$/);
+ Log GetLogLevel($pn,3), "THRESHOLD set $pn $arg $a[2]";
+ readingsBeginUpdate ($hash);
+ readingsBulkUpdate ($hash, "state", "active $a[2]");
+ readingsBulkUpdate ($hash, "threshold_min",$a[2]-$hash->{hysteresis});
+ readingsBulkUpdate ($hash, "cmd","wait for next cmd");
+ readingsBulkUpdate ($hash, "desired_value", $a[2]);
+ readingsEndUpdate ($hash, 1);
+ } elsif ($arg eq "deactivated" ) {
+ $desired_value = ReadingsVal($pn,"desired_value","");
+ return "Set desired value first" if (!$desired_value);
+ $ret=CommandAttr(undef, "$pn disable 1");
+ if (!$ret) {
+ readingsSingleUpdate ($hash, "state", "deactivated $desired_value",1);
+ }
+ } elsif ($arg eq "active" ) {
+ $desired_value = ReadingsVal($pn,"desired_value","");
+ return "Set desired value first" if (!$desired_value);
+ $ret=CommandDeleteAttr(undef, "$pn disable");
+ if (!$ret) {
+ readingsBeginUpdate ($hash);
+ readingsBulkUpdate ($hash, "state", "active $desired_value");
+ readingsBulkUpdate ($hash, "cmd","wait for next cmd");
+ readingsEndUpdate ($hash, 1);
+ }
+ } elsif ($arg eq "hysteresis" ) {
+ return "Hysteresis needs a numeric parameter" if ($a[2] !~ m/^[\d\.]*$/ );
+ $hash->{hysteresis} = $a[2];
+ $desired_value = ReadingsVal($pn,"desired_value","");
+ if ($desired_value) {
+ readingsBeginUpdate ($hash);
+ readingsBulkUpdate ($hash, "threshold_min",$desired_value-$hash->{hysteresis});
+ readingsBulkUpdate ($hash, "cmd","wait for next cmd");
+ readingsEndUpdate ($hash, 1);
+ }
+ } else {
+ return "Unknown argument $a[1], choose one of desired active deactivated hysteresis"
+ }
+ return $ret;
+}
+
+##########################
+sub
+THRESHOLD_Notify($$)
+{
+ my ($hash, $dev) = @_;
+ my $pn = $hash->{NAME};
+
+ return "" if(($attr{$pn} && $attr{$pn}{disable}) || !ReadingsVal($pn,"desired_value",""));
+
+ my $sensor = $hash->{sensor};
+ my $reading = $hash->{sensor_reading};
+ my $sensor2 = $hash->{sensor2};
+ my $reading2 = $hash->{sensor2_reading};
+
+ if ($dev->{NAME} ne $sensor) {
+ if ($sensor2) {
+ if ($dev->{NAME} ne $sensor2) {
+ return "";
+ }
+ } else {
+ return "";
+ }
+ }
+
+ if(!($defs{$sensor}{READINGS}{$reading})) {
+ my $msg = "$pn: no reading yet for $sensor $reading";
+ Log 2, $msg;
+ return "";
+ }
+
+ my $instr = $defs{$sensor}{READINGS}{$reading}{VAL};
+
+ $instr =~ /[^\d^\-^.]*([-\d.]*)/;
+
+ my $s_value = $1;
+
+
+ my $sensor_max = ReadingsVal($pn,"desired_value","");
+ my $sensor_min = ReadingsVal($pn,"threshold_min","");
+ my $cmd_default = $hash->{helper}{actor_cmd_default};
+
+ readingsSingleUpdate ($hash, "sensor_value",$s_value, 1);
+
+ if (!$hash->{operator}) {
+ if ($s_value > $sensor_max) {
+ THRESHOLD_setValue($hash,1);
+ } elsif ($s_value < $sensor_min) {
+ THRESHOLD_setValue($hash,2);
+ } else {
+ THRESHOLD_setValue($hash,$cmd_default) if (ReadingsVal($pn,"cmd","") eq "wait for next cmd" && $cmd_default != 0);
+ }
+ } else {
+ if (!($defs{$sensor2}{READINGS}{$reading2})) {
+ my $msg = "$pn: no reading yet for $sensor2 $reading2";
+ Log 2, $msg;
+ return "";
+ }
+
+ my $s2_state = $defs{$sensor2}{READINGS}{$reading2}{VAL};
+ my $sensor2_state = $hash->{sensor2_state};
+
+ readingsSingleUpdate ($hash, "sensor2_state",$s2_state, 1);
+
+ if ($hash->{operator} eq "AND") {
+ if (($s_value > $sensor_max) && ($s2_state eq $sensor2_state)) {
+ THRESHOLD_setValue($hash,1);
+ } elsif (($s_value < $sensor_min) || ($s2_state ne $sensor2_state)){
+ THRESHOLD_setValue($hash,2);
+ } else {
+ THRESHOLD_setValue($hash,$cmd_default) if (ReadingsVal($pn,"cmd","") eq "wait for next cmd" && $cmd_default != 0);
+ }
+ } elsif ($hash->{operator} eq "OR") {
+ if (($s_value > $sensor_max) || ($s2_state eq $sensor2_state)) {
+ THRESHOLD_setValue($hash,1);
+ } elsif (($s_value < $sensor_min) && ($s2_state ne $sensor2_state)){
+ THRESHOLD_setValue($hash,2);
+ } else {
+ THRESHOLD_setValue($hash,$cmd_default) if (ReadingsVal($pn,"cmd","") eq "wait for next cmd" && $cmd_default != 0);
+ }
+ }
+ }
+ return "";
+}
+
+sub
+THRESHOLD_setValue($$)
+{
+ my ($hash, $cmd_nr) = @_;
+ my $pn = $hash->{NAME};
+ my $ret=0;
+ my @cmd =($hash->{helper}{actor_cmd1},$hash->{helper}{actor_cmd2});
+ my @cmd_sym = ("cmd1","cmd2");
+ my $cmd_now = $cmd[$cmd_nr-1];
+ my $cmd_sym_now = $cmd_sym[$cmd_nr-1];
+
+ if ($cmd_now) {
+ if (ReadingsVal($pn,"cmd","") ne $cmd_sym_now) {
+ if ($ret = AnalyzeCommandChain(undef, $cmd_now)) {
+ Log GetLogLevel($pn,3), "output of $pn $cmd_now: $ret";
+ } else {
+ readingsSingleUpdate ($hash, "cmd",$cmd_sym_now, 1);
+ }
+ }
+ }
+}
+
+1;
+
+=pod
+=begin html
+
+
+THRESHOLD
+
+ This module reads any sensor that provides values in decimal and execute FHEM/Perl commands, if the value of the sensor is higher or lower than the threshold value.
+ So can be easily implemented a software thermostat, hygrostat and much more.
+
+ It is controlled by setting a desired value with:
+
+ set <name> desired <value>
+
+ The switching behavior can also be influenced by another sensor or sensor group.
+
+
+
+ Define
+
+
+ define <name> THRESHOLD <sensor>[:<reading>][:<hysteresis>][:<init_desired_value>] [AND|OR <sensor2>[:<reading2>][:<state>]] [<actor>][|<cmd1>][|<cmd2>][|<cmd_default_index>]
+
+
+ - sensor
+ a defined sensor in FHEM
+
+ - reading
+ reading of the sensor, which includes a value in decimal
+ default value: temperature
+
+ - hysteresis
+ Hysteresis, this provides the threshold_min = desired_value - hysteresis
+ default value: 1 at temperature, 10 at huminity
+
+ - init_desired_value
+ Initial value, if no value is specified, it must be set with "set desired value".
+ Defaultwert: no value
+
+
+
+ - AND|OR
+ logical operator with an optional second sensor
+
+ - sensor2
+ the second sensor
+
+ - reading2
+ reading of the second sensor
+ default value: state
+
+ - state
+ state of the second sensor
+ default value: open
+
+ - actor
+ actor device defined in FHEM
+
+ - cmd1
+ FHEM/Perl command that is executed, if the value of the sensor is higher than desired value and/or the value of sensor 2 is matchted. @ is a placeholder for the specified actor.
+ default value: set actor off, if actor defined
+
+ - cmd2
+ FHEM/Perl command that is executed, if the value of the sensor is lower than threshold_min or the value of sensor 2 is not matchted. @ is a placeholder for the specified actor.
+ default value: set actor on, if actor defined
+
+ - cmd_default_index
+ Index of command that is executed after setting the desired value until the desired value or threshold_min value is reached.
+ 0 - no command
+ 1 - cmd1
+ 2 - cmd2
+ Defaultwert: 2, if actor defined, else 0
+
+
+
+ Examples:
+
+ Example for heating:
+
+ define Thermostat THRESHOLD temp_sens heating
+
+ set Thermostat desired 20
+
+ Description:
+
+ It is heated up to the desired value of 20. If the value below the threshold_min value of 19 (20-1)
+ the heating is switched on again.
+
+ Example for heating with window contact:
+
+ define Thermostat THRESHOLD temp_sens OR win_sens heating
+
+ Example for heating with multiple window contacts:
+
+ define W_ALL structure W1 W2 W3 ....
+ attr W_ALL clientstate_behavior relative
+ attr W_ALL clientstate_priority closed open
+
+ then:
+
+ define Thermostat THRESHOLD S1 OR W_ALL heating
+
+ More examples:
+
+ define Hygrostat THRESHOLD hym_sens:huminity dehydrator|set @ on|set @ off|1
+ define Hygrostat THRESHOLD hym_sens:huminity AND Sensor2:state:close dehydrator|set @ on|set @ off|1
+ define Thermostat THRESHOLD temp_sens:temperature:1 aircon|set @ on|set @ off|1
+ define Thermostat THRESHOLD temp_sens AND Sensor2:state:close aircon|set @ on|set @ off|1
+ define Hygrostat THRESHOLD hym_sens:huminity:20 watering|set @ off|set @ on|2
+
+ Alternatively, each Perl commands are given..
+
+ Examples:
+
+ define Thermostat THRESHOLD Sensor |{fhem("set Switch1 on;set Switch2 on")|{fhem("set Switch1 off;set Switch2 off")|{fhem("set Switch1 on;set Switch2 on")
+ define Thermostat THRESHOLD Sensor Alarm|{Log 2,"value is exceeded"}|set @ off|{fhem("set @ on;set Switch2 on")}
+ define Thermostat THRESHOLD Sensor ||{Log 2,"value is reached"}||
+
+
+
+ Set
+
+ -
set <name> desired <value>
+ Set the desired value. If no desired value is set, the module is not active.
+
+
+ -
set <name> deactivated <value>
+ Module is disabled.
+
+
+ -
set <name> active <value>
+ Module is activated again.
+
+
+ -
set <name> hysteresis <value>
+ Set hysteresis value.
+
+
+
+
+
+ Get
+
+
+
+
+ Attributes
+
+
+
+=end html
+=begin html_DE
+
+
+THRESHOLD
+
+ Dieses Modul liest einen beliebigen Sensor aus, der Werte als Dezimalzahlen liefert und führt beim Überschreiten der Schwellen-Obergrenze (Sollwert) bzw. beim Unterschreiten der Schwellen-Untergrenze beliebige FHEM/Perl-Befehle aus. Damit lässt sich leicht z. B. ein Software-Thermostat oder -Hygrostat realisieren.
+
+ Nach der Definition eines Moduls vom Typ THRESHOLD erfolgt die eigentliche Steuerung über die Vorgabe eines Sollwertes.
+
+ Das geschieht über:
+
+ set <name> desired <value>
+
+ Das Modul beginnt mit der Steuerung erst dann, wenn ein Sollwert gesetzt wird.
+
+ Optional kann das Schaltverhalten zusätzlich durch einen weiteren Sensor oder eine Sensorgruppe,
+ definiert über structure (z. B. Fensterkontakte), beeinflusst werden.
+
+
+
+ Define
+
+
+ define <name> THRESHOLD <sensor>[:<reading>][:<hysteresis>][:<init_desired_value>] [AND|OR <sensor2>[:<reading2>][:<state>]] [<actor>][|<cmd1>][|<cmd2>][|<cmd_default_index>]
+
+
+ - sensor
+ ein in FHEM definierter Sensor
+
+ - reading
+ Reading des Sensors, der einen Wert als Dezimalzahl beinhaltet
+ Defaultwert: temperature
+
+ - hysteresis
+ Hysterese, daraus errechnet sich die Untergrenze = Sollwert - hysteresis
+ Defaultwert: 1 Temperaturen, 10 bei Feuchtigkeit
+
+ - init_desired_value
+ Initial-Sollwert, wenn kein Wert vorgegeben wird, muss er mit "set desired value" gesetzt werden
+ Defaultwert: kein
+
+
+
+ - AND|OR
+ Verknüpfung mit einem optionalen zweiten Sensor
+
+ - sensor2
+ ein definierter Sensor, dessen Status abgefragt wird
+
+ - reading2
+ Reading, der den Status des Sensors beinhaltet
+ Defaultwert: state
+
+ - state
+ Status des Sensors, der zu einer Aktion führt
+ Defaultwert: open
+
+ - actor
+ ein in FHEM definierter Aktor
+
+ - cmd1
+ FHEM/Perl Befehl, der beim Überschreiten des Sollwertes ausgeführt wird bzw.
+ wenn status des sensor2 übereinstimmt. @ ist ein Platzhalter für den angegebenen Aktor.
+
+ Defaultwert: set actor off, wenn Aktor angegeben ist
+
+ - cmd2
+ FHEM/Perl Befehl, der beim Unterschreiten der Untergrenze (Sollwert-Hysterese) ausgeführt wird bzw.
+ wenn status des sensor2 nicht übereinstimmt. @ ist ein Platzhalter für den angegebenen Aktor.
+
+ Defaultwert: set actor on, wenn Aktor angegeben ist
+
+ - cmd_default_index
+ FHEM/Perl Befehl, der nach dem Setzen des Sollwertes ausgeführt wird, bis Sollwert oder die Untergrenze erreicht wird.
+ 0 - kein Befehl
+ 1 - cmd1
+ 2 - cmd2
+ Defaultwert: 2, wenn Aktor angegeben ist, sonst 0
+
+
+ Beispiele:
+
+ Beispiel für Heizung:
+
+ define Thermostat THRESHOLD temp_sens heating
+
+ set Thermostat desired 20
+
+ Beschreibung:
+
+ Es wird geheizt bis zum Maximalwert 20. Beim Unterschreiten des Untergrenze von 19 (20-1) wird die Heizung wieder eingeschaltet.
+
+ Beispiel für Heizung mit Fensterkontakt:
+
+ define Thermostat THRESHOLD temp_sens OR win_sens heating
+
+ Beispiel für Heizung mit mehreren Fensterkontakten:
+
+ define W_ALL structure W1 W2 W3 ....
+ attr W_ALL clientstate_behavior relative
+ attr W_ALL clientstate_priority closed open
+
+ danach:
+
+ define Thermostat THRESHOLD S1 OR W_ALL heating
+
+ einige weitere Bespiele:
+
+ define Hygrostat THRESHOLD hym_sens:huminity dehydrator|set @ on|set @ off|1
+ define Hygrostat THRESHOLD hym_sens:huminity AND Sensor2:state:close dehydrator|set @ on|set @ off|1
+ define Thermostat THRESHOLD temp_sens:temperature:1 aircon|set @ on|set @ off|1
+ define Thermostat THRESHOLD temp_sens AND Sensor2:state:close aircon|set @ on|set @ off|1
+ define Hygrostat THRESHOLD hym_sens:huminity:20 watering|set @ off|set @ on|2
+
+ Alternativ können jeweils Perl-Befehle angegeben werden.
+
+ Beispiele:
+
+ define Thermostat THRESHOLD Sensor |{fhem("set Switch1 on;set Switch2 on")|{fhem("set Switch1 off;set Switch2 off")|{fhem("set Switch1 on;set Switch2 on")
+ define Thermostat THRESHOLD Sensor Alarm|{Log 2,"Wert überschritten"}|set @ off|{fhem("set @ on;set Switch2 on")}
+ define Thermostat THRESHOLD Sensor ||{Log 2,"Wert unterschritten"}||
+
+
+
+ Set
+
+ set <name> desired <value>
+ Setzt den Sollwert. Wenn kein Sollwert gesetzt ist, ist das Modul nicht aktiv.
+
+
+ set <name> deactivated <value>
+ Modul wird deaktiviert.
+
+
+ set <name> active <value>
+ Modul wird wieder aktiviert.
+
+
+ set <name> hysteresis <value>
+ Setzt Hysterese-Wert.
+
+
+
+
+ Get
+
+
+
+
+ Attributes
+
+
+
+=end html_DE
+=cut