diff --git a/fhem/CHANGED b/fhem/CHANGED index 0602b6e87..2390f0b70 100644 --- a/fhem/CHANGED +++ b/fhem/CHANGED @@ -1,4 +1,6 @@ - SVN + - feature: new Module 59_HCS.pm for monitoring heating valves (FHT, HM-CC-VD) + to contral a central heating unit. I thank Benjamin for his support! (M. Fischer) - feature: new Module 72_FB_CALLMONITOR for receiving telephone call events (Markus) - feature: new Module 71_YAMAHA_AVR.pm for controlling Yamaha AV receivers over network (by Markus) diff --git a/fhem/FHEM/59_HCS.pm b/fhem/FHEM/59_HCS.pm new file mode 100644 index 000000000..bba2bb4f7 --- /dev/null +++ b/fhem/FHEM/59_HCS.pm @@ -0,0 +1,439 @@ +################################################################ +# $Id: $ +# +# (c) 2012 Copyright: Martin Fischer (m_fischer at gmx dot de) +# All rights reserved +# +# This script 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. +# +################################################################ + +package main; +use strict; +use warnings; + +sub HCS_Initialize($$); +sub HCS_Define($$); +sub HCS_Undef($$); +sub HCS_checkState($); +sub HCS_Get($@); +sub HCS_Set($@); +sub HCS_setState($$); +sub HCS_getValves($$); + +my %gets = ( + "valves" => "", +); + +my %sets = ( + "interval" => "", + "on" => "", + "off" => "", + "valveThresholdOn" => "", + "valveThresholdOff" => "", +); + +##################################### +sub +HCS_Initialize($$) +{ + my ($hash) = @_; + + $hash->{DefFn} = "HCS_Define"; + $hash->{UndefFn} = "HCS_Undef"; + $hash->{GetFn} = "HCS_Get"; + $hash->{SetFn} = "HCS_Set"; + $hash->{AttrList} = "device deviceCmdOn deviceCmdOff ". + "sensor sensorThresholdOn sensorThresholdOff sensorReading ". + "valvesExcluded valveThresholdOn valveThresholdOff ". + "do_not_notify:1,0 event-on-update-reading event-on-change-reading ". + "showtime:1,0 loglevel:0,1,2,3,4,5,6 disable:0,1"; +} + +##################################### +sub +HCS_Define($$) { + my ($hash, $def) = @_; + + # define HCS [interval] [valveThresholdOn] [valveThresholdOff] + # define heatingControl HCS KG.hz.LC.SW1.01 10 40 30 + + my @a = split("[ \t][ \t]*", $def); + + return "Wrong syntax: use 'define HCS [interval] [valveThresholdOn] [valveThresholdOff]'" + if(@a < 3 || @a > 6); + + my $name = $a[0]; + $attr{$name}{device} = $a[2]; + $attr{$name}{deviceCmdOn} = AttrVal($name,"deviceCmdOn","on"); + $attr{$name}{deviceCmdOff} = AttrVal($name,"deviceCmdOff","off"); + $attr{$name}{interval} = AttrVal($name,"interval",(defined($a[3]) ? $a[3] : 10)); + $attr{$name}{valveThresholdOn} = AttrVal($name,"valveThresholdOn",(defined($a[4]) ? $a[4] : 40)); + $attr{$name}{valveThresholdOff} = AttrVal($name,"valveThresholdOff",(defined($a[5]) ? $a[5] : 35)); + + my $type = $hash->{TYPE}; + my $ret; + + if(!defined($defs{$a[2]})) { + $ret = "Device $a[2] not defined. Please add this device first!"; + Log 1, "$type $name $ret"; + return $ret; + } + + $hash->{STATE} = "Defined"; + + my $interval = AttrVal($name,"interval",10); + my $timer; + + $ret = HCS_getValves($hash,0); + HCS_setState($hash,$ret); + + $timer = gettimeofday()+60; + InternalTimer($timer, "HCS_checkState", $hash, 0); + $hash->{NEXTCHECK} = FmtTime($timer); + + return undef; +} + +##################################### +sub +HCS_Undef($$) { + my ($hash, $name) = @_; + + delete($modules{HCS}{defptr}{$hash->{NAME}}); + RemoveInternalTimer($hash); + + return undef; +} + +##################################### +sub +HCS_checkState($) { + my ($hash) = @_; + my $name = $hash->{NAME}; + my $interval = $attr{$name}{interval}; + my $timer; + my $ret; + + $ret = HCS_getValves($hash,0); + HCS_setState($hash,$ret); + + $timer = gettimeofday()+($interval*60); + InternalTimer($timer, "HCS_checkState", $hash, 0); + $hash->{NEXTCHECK} = FmtTime($timer); + + return undef; +} + +##################################### +sub +HCS_Get($@) { + my ($hash, @a) = @_; + my $name = $hash->{NAME}; + my $type = $hash->{TYPE}; + my $ret; + + # check syntax + return "argument is missing @a" + if(int(@a) != 2); + # check argument + return "Unknown argument $a[1], choose one of ".join(" ", sort keys %gets) + if(!defined($gets{$a[1]})); + + # get argument + my $arg = $a[1]; + + if($arg eq "valves") { + $ret = HCS_getValves($hash,1); + return $ret; + } + + return undef; +} + +##################################### +sub +HCS_Set($@) { + my ($hash, @a) = @_; + my $name = $hash->{NAME}; + my $type = $hash->{TYPE}; + my $timer; + my $ret; + + # check syntax + return "argument is missing @a" + if(int(@a) < 2 || int(@a) > 3); + # check argument + return "Unknown argument $a[1], choose one of ".join(" ", sort keys %sets) + if(!defined($sets{$a[1]})); + + # get argument + my $arg = $a[1]; + + if($arg eq "interval") { + + return "Wrong interval format: Only digits are allowed!" + if($a[2] !~ m/^\d+$/); + + my $intervalNew = $a[2]; + my $intervalOld = AttrVal($name,"interval",10); + RemoveInternalTimer($hash); + $attr{$name}{interval} = $intervalNew; + $timer = gettimeofday()+($intervalNew*60); + InternalTimer($timer, "HCS_checkState", $hash, 0); + $hash->{NEXTCHECK} = FmtTime($timer); + Log 1, "$type $name interval changed from $intervalOld to $intervalNew"; + + } elsif($arg eq "valveThresholdOn") { + + return "Wrong interval format: Only digits are allowed!" + if($a[2] !~ m/^\d+$/); + + my $thresholdNew = $a[2]; + my $thresholdOld = AttrVal($name,"valveThresholdOn",40); + $attr{$name}{valveThresholdOn} = $thresholdNew; + Log 1, "$type $name valveThresholdOn changed from $thresholdOld to $thresholdNew"; + + } elsif($arg eq "valveThresholdOff") { + + return "Wrong interval format: Only digits are allowed!" + if($a[2] !~ m/^\d+$/); + + my $thresholdNew = $a[2]; + my $thresholdOld = AttrVal($name,"valveThresholdOff",35); + $attr{$name}{valveThresholdOff} = $thresholdNew; + Log 1, "$type $name valveThresholdOff changed from $thresholdOld to $thresholdNew"; + + } elsif($arg eq "on") { + RemoveInternalTimer($hash); + HCS_checkState($hash); + Log 1, "$type $name monitoring of valves started"; + } elsif($arg eq "off") { + RemoveInternalTimer($hash); + #$hash->{STATE} = "off"; + $hash->{NEXTCHECK} = "offline"; + readingsBeginUpdate($hash); + readingsUpdate($hash, "state", "off"); + readingsEndUpdate($hash, 1); + Log 1, "$type $name monitoring of valves interrupted"; + } + +} + +##################################### +sub +HCS_setState($$) { + my ($hash,$heatDemand) = @_; + my $name = $hash->{NAME}; + my $type = $hash->{TYPE}; + my $device = AttrVal($name,"device",""); + my $deviceCmdOn = AttrVal($name,"deviceCmdOn","on"); + my $deviceCmdOff = AttrVal($name,"deviceCmdOff","off"); + my $sensor = AttrVal($name,"sensor",undef); + my $cmd; + my $overdrive = 0; + my $state; + + if($heatDemand == 1) { + $state = "demand"; + $cmd = $deviceCmdOn; + } elsif($heatDemand == 2) { + $overdrive = 1; + $state = "demand (overdrive)"; + $cmd = $deviceCmdOn; + } elsif($heatDemand == 3) { + $overdrive = 1; + $state = "idle (overdrive)"; + $cmd = $deviceCmdOff; + } else { + $state = "idle"; + $cmd = $deviceCmdOff; + } + + $state = "error" if(!defined($defs{$device})); + + readingsBeginUpdate($hash); + readingsUpdate($hash, "overdrive", $overdrive) if($sensor); + readingsUpdate($hash, "state", $state); + readingsEndUpdate($hash, 1); + + if($defs{$device}) { + my $cmdret = CommandSet(undef,"$device $cmd"); + Log 1, "$type $name An error occurred while switching device '$device': $cmdret" + if($cmdret); + } else { + Log 1, "$type $name device '$device' does not exists."; + } + + return undef; +} + +##################################### +sub +HCS_getValves($$) { + my ($hash,$list) = @_; + my $name = $hash->{NAME}; + my $type = $hash->{TYPE}; + my $excluded = AttrVal($name,"valvesExcluded",""); + my $heatDemand = 0; + my $valveThresholdOn = AttrVal($name,"valveThresholdOn",40); + my $valveThresholdOff = AttrVal($name,"valveThresholdOff",35); + my %valves = (); + my $valvesIdle = 0; + my $valveState; + my $valveLastDemand; + my $valveNewDemand; + my $value; + my $ret; + + # reset counter + my $sumDemand = 0; + my $sumFHT = 0; + my $sumHMCCTC = 0; + my $sumValves = 0; + my $sumExcluded = 0; + my $sumIgnored = 0; + + + foreach my $d (sort keys %defs) { + # skipping unneeded devices + next if($defs{$d}{TYPE} ne "FHT" && $defs{$d}{TYPE} ne "CUL_HM"); + next if($defs{$d}{TYPE} eq "CUL_HM" && $attr{$d}{model} ne "HM-CC-TC"); + + # get current actuator state from each device + $valveState = $defs{$d}{READINGS}{"actuator"}{VAL}; + $valveState =~ s/[\s%]//g; + + if($attr{$d}{ignore}) { + $value = "$valveState% (ignored)"; + $valves{$defs{$d}{NAME}}{state} = $value; + $valves{$defs{$d}{NAME}}{demand} = 0; + $ret .= "$defs{$d}{NAME}: $value\n" if($list); + Log 4, "$type $name $defs{$d}{NAME}: $value"; + $sumIgnored++; + $sumValves++; + $sumFHT++ if($defs{$d}{TYPE} eq "FHT"); + $sumHMCCTC++ if(defined($attr{$d}{model}) && $attr{$d}{model} eq "HM-CC-TC"); + next; + } + + if($excluded =~ m/$d/) { + $value = "$valveState% (excluded)"; + $valves{$defs{$d}{NAME}}{state} = $value; + $valves{$defs{$d}{NAME}}{demand} = 0; + $ret .= "$defs{$d}{NAME}: $value\n" if($list); + Log 4, "$type $name $defs{$d}{NAME}: $value"; + $sumExcluded++; + $sumValves++; + $sumFHT++ if($defs{$d}{TYPE} eq "FHT"); + $sumHMCCTC++ if(defined($attr{$d}{model}) && $attr{$d}{model} eq "HM-CC-TC"); + next; + } + + $value = "$valveState%"; + $valves{$defs{$d}{NAME}}{state} = $value; + $ret .= "$defs{$d}{NAME}: $value" if($list); + Log 4, "$type $name $defs{$d}{NAME}: $value"; + + # get last readings + $valveLastDemand = ReadingsVal($name,$d."_demand",0); + + # check heat demand from each valve + if($valveState >= $valveThresholdOn) { + $heatDemand = 1; + $valveNewDemand = $heatDemand; + $ret .= " (demand)\n" if($list); + $sumDemand++; + } else { + + if($valveLastDemand == 1) { + if($valveState > $valveThresholdOff) { + $heatDemand = 1; + $valveNewDemand = $heatDemand; + $ret .= " (demand)\n" if($list); + $sumDemand++; + } else { + $valveNewDemand = 0; + $ret .= " (idle)\n" if($list); + $valvesIdle++; + } + } else { + $valveNewDemand = 0; + $ret .= " (idle)\n" if($list); + $valvesIdle++; + } + } + + $valves{$defs{$d}{NAME}}{demand} = $valveNewDemand; + + # count devices + $sumFHT++ if($defs{$d}{TYPE} eq "FHT"); + $sumHMCCTC++ if($attr{$d}{model} eq "HM-CC-TC"); + $sumValves++; + } + + # overdrive mode + my $sensor = AttrVal($name,"sensor",undef); + my $sensorReading = AttrVal($name,"sensorReading",undef); + my $sensorThresholdOn = AttrVal($name,"sensorThresholdOn",undef); + my $sensorThresholdOff = AttrVal($name,"sensorThresholdOff",undef); + my $tempValue; + my $overdrive = "no"; + + if(defined($sensor) && defined($sensorThresholdOn) && defined($sensorThresholdOff) && defined($sensorReading)) { + + if(!defined($defs{$sensor})) { + Log 1, "$type $name Device $sensor not defined. Please add this device first!"; + } else { + $tempValue = ReadingsVal($sensor,$sensorReading,""); + if(!$tempValue || $tempValue !~ m/^.*\d+.*$/) { + Log 1, "$type $name Device $sensor has no valid value."; + } else { + $tempValue =~ s/(\s|°|[A-Z]|[a-z])+//g; + + $heatDemand = 2 if($tempValue <= $sensorThresholdOn); + $heatDemand = 3 if($tempValue > $sensorThresholdOff); + $overdrive = "yes" if($heatDemand == 2 || $heatDemand == 3); + } + } + } else { + if(!$sensor) { + delete $hash->{READINGS}{sensor}; + delete $hash->{READINGS}{overdrive}; + delete $attr{$name}{sensorReading}; + delete $attr{$name}{sensorThresholdOn}; + delete $attr{$name}{sensorThresholdOff}; + } + } + + #my $sumDemand = $sumValves-$valvesIdle-$sumIgnored-$sumExcluded; + Log 3, "$type $name Found $sumValves Device(s): $sumFHT FHT, $sumHMCCTC HM-CC-TC. ". + "demand: $sumDemand, idle: $valvesIdle, ignored: $sumIgnored, excluded: $sumExcluded, overdrive: $overdrive"; + + readingsBeginUpdate($hash); + for my $d (sort keys %valves) { + readingsUpdate($hash, $d."_state", $valves{$d}{state}); + readingsUpdate($hash, $d."_demand", $valves{$d}{demand}); + } + readingsUpdate($hash, "sensor", $tempValue) if(defined($tempValue) && $tempValue ne ""); + readingsEndUpdate($hash, 1); + + return ($list) ? $ret : $heatDemand; +} + +# vim: ts=2:et + +1; diff --git a/fhem/docs/commandref.html b/fhem/docs/commandref.html index 478b08009..dacdefa15 100644 --- a/fhem/docs/commandref.html +++ b/fhem/docs/commandref.html @@ -97,6 +97,7 @@ FHT8V   FHZ   FS20   + HCS   HMS   HMLAN   HOL   @@ -2637,6 +2638,138 @@ A line ending with \ will be concatenated with the next one, so long lines + +

HCS

+
    + Defines a virtual device for monitoring heating valves (FHT, HM-CC-VD) to control + a central heating unit.

    + + + Define +
      + define <name> HCE <device> <interval> <valveThresholdOn> <valveThresholdOff> +

      + +
        +
      • <device> the name of a predefined device to switch.
      • +
      • <interval> is a digit number. The unit is minutes.
      • +
      • <valveThresholdOn> is a digit number. Threshold upon which device is switched on (heating required).
      • +
      • <valveThresholdOff> is a digit number. Threshold upon which device is switched off (idle).
      • +
      +
      + + The HCS (heating control system) device monitors the state of all detected + valves in a free definable interval (by default: 10 min). +

      + + Regulation for heating requirement or suppression of the request can be controlled by + valve position using also free definable thresholds. +

      + + In doing so, the HCS device also includes the hysteresis between two states. +

      + + Example:
      + Threshold valve position for heating requirement: 40% (default) + Threshold valve position for idle: 35% (default) +

      + + Heating is required when the "open" position of a valve is more than 40%. HCS + then activates the defined device until the "open" position of the valve has + lowered to 35% or less (threshold for idle). +

      + + In addition, the HCS device supports an optional temp-sensor. The valve-position oriented + regulation can be overriden by the reading of the temp-sensor. +

      + + Example:
      + Threshold temperature reading for heating requirement: 10° Celsius + Threshold temperature reading for idle: 18° Celsius +

      + + Is a valve reaching or exceeding the threshold for heating requirement (>=40%), but the + temperature reading is more than 18° Celcius, the selected device will stay deactivated. + The valve-position oriented regulation has been overridden by the temperature reading in this example. +

      + + The HCS device automatically detects devices which are ignored. Furthermore, certain + devices can also be excluded of the monitoring manually. +

      + + + Get +
        +
      • valves
        + returns the actual valve positions +

      • +
      +
      + + + Set +
        +
      • interval
        + modifies the interval of reading the actual valve positions. The unit is minutes. +

      • +
      • on
        + restarts the monitoring after shutdown by off switch.
        + HCS device starts up automatically upon FHEM start or after new device implementation! +

      • +
      • off
        + shutdown of monitoring, can be restarted by using the on command. +

      • +
      • valveThresholdOn
        + defines threshold upon which device is switched on (heating required). +

      • +
      • valveThresholdOff
        + defines threshold upon which device is switched off (idle). +

      • +
      +
      + + + Attributes +
        +
      • device
        + optional; used to change the device. This is normally done in the define tag. +

      • +
      • deviceCmdOn
        + command to activate the device, e.g. on. +

      • +
      • deviceCmdOff
        + command to deactivate the device, e.g. off. +

      • +
      • sensor
        + device name of the temp-sensor (optional). +

      • +
      • sensorThresholdOn
        + threshold for temperature reading activating the defined device + Must be set if sensor has been defined +

      • +
      • sensorThresholdOff
        + threshold for temperature reading deactivating the defined device. + Must be set if sensor has been defined +

      • +
      • sensorReading
        + name which is used for saving the "reading" of the defined temp-sensor. +

      • +
      • valveThresholdOn
        + see Set +

      • +
      • valveThresholdOff
        +
      • see Set
        +
      • do_not_notify

      • +
      • showtime

      • +
      • event-on-update-reading

      • +
      • event-on-change-reading

      • +
      +
      + +
    +
    + +

HMS