diff --git a/CHANGED b/CHANGED index abfd6ce33..00b6c1c19 100644 --- a/CHANGED +++ b/CHANGED @@ -538,3 +538,5 @@ - feature: softfhtbuffer added to CUL - bugfix: pgm3: Pulldown-Menu without selected FHTDEV not possible any more - feature: duplicate buffer added for multi-cul/-fhz setups + - feature: 20_OWFS.pm for communication to 1-Wire via OWFS added (Martin Fischer) + - feature: 21_OWTEMP.pm for 1-Wire Digital Thermometer added (Martin Fischer) diff --git a/FHEM/20_OWFS.pm b/FHEM/20_OWFS.pm new file mode 100644 index 000000000..d8dd0d8b5 --- /dev/null +++ b/FHEM/20_OWFS.pm @@ -0,0 +1,213 @@ +################################################################ +# +# Copyright notice +# +# (c) 2008 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; +use Time::HiRes qw(gettimeofday); +use OW; + +my %models = ( + "DS1420" => "", +); +my %fc = ( + "1:DS9420" => "01", + "2:DS1420" => "81", + "3:DS1820" => "10", +); + +my %gets = ( + "address" => "", + "alias" => "", + "crc8" => "", + "family" => "", + "id" => "", + "locator" => "", + "present" => "", +# "r_address" => "", +# "r_id" => "", +# "r_locator" => "", + "type" => "", +); + +############################################## +sub +OWFS_Initialize($) +{ + my ($hash) = @_; + +# Provider + $hash->{WriteFn} = "OWFS_Write"; + $hash->{Clients} = ":OWTEMP:"; + +# Normal devices + $hash->{DefFn} = "OWFS_Define"; + $hash->{UndefFn} = "OWFS_Undef"; + $hash->{GetFn} = "OWFS_Get"; + $hash->{SetFn} = "OWFS_Set"; + $hash->{AttrList} = "do_not_notify:1,0 dummy:1,0 temp-scale:C,F,K,R " . + "showtime:1,0 loglevel:0,1,2,3,4,5,6"; +} + +##################################### +sub +OWFS_Get($$) +{ + my ($hash,@a) = @_; + + return "argument is missing @a" if (@a != 2); + return "Unknown argument $a[1], choose one of " . join(",", sort keys %gets) + if(!defined($gets{$a[1]})); + + my $ret = OWFS_GetData($hash,$a[1]); + + return "$a[0] $a[1] => $ret"; +} + +##################################### +sub +OWFS_GetData($$) +{ + my ($hash,$query) = @_; + my $name = $hash->{NAME}; + my $path = $hash->{OW_PATH}; + my $ret = undef; + + $ret = OW::get("/uncached/$path/$query"); + if ($ret) { + # strip spaces + $ret =~ s/^\s+//g; + Log 4, "OWFS $name $query $ret"; + $hash->{READINGS}{$query}{VAL} = $ret; + $hash->{READINGS}{$query}{TIME} = TimeNow(); + return $ret; + } else { + return undef; + } +} + +##################################### +sub +OWFS_DoInit($) +{ + my ($hash) = @_; + my $name = $hash->{NAME}; + my $path; + my $ret; + + $path = $hash->{OW_FAMILY}.".".$hash->{OWFS_ID}; + + foreach my $q (sort keys %gets) { + $ret = OWFS_GetData($hash,$q); + } + + $hash->{STATE} = "Initialized" if (!$hash->{STATE}); + return undef; +} + +##################################### +sub +OWFS_Define($$) +{ + my ($hash, $def) = @_; + + # define OWFS + # define foo OWFS 127.0.0.1:4304 DS1420 93302D000000 + + my @a = split("[ \t][ \t]*", $def); + + return "wrong syntax: define OWFS " + if (@a < 2 && int(@a) > 5); + + my $name = $a[0]; + my $dev = $a[2]; +# return "wrong device format: use ip:port" +# if ($device !~ m/^(.+):(0-9)+$/); + + my $model = $a[3]; + return "Define $name: wrong model: specify one of " . join ",", sort keys %models + if (!grep { $_ eq $model } keys %models); + + my $id = $a[4]; + return "Define $name: wrong ID format: specify a 12 digit value" + if (uc($id) !~ m/^[0-9|A-F]{12}$/); + + $hash->{FamilyCode} = \%fc; + my $fc = $hash->{FamilyCode}; + if (defined ($fc)) { + foreach my $c (sort keys %{$fc}) { + if ($c =~ m/$model/) { + $hash->{OW_FAMILY} = $fc->{$c}; + } + } + } + delete ($hash->{FamilyCode}); + $hash->{OW_ID} = $id; + $hash->{OW_PATH} = $hash->{OW_FAMILY}.".".$hash->{OW_ID}; + + $hash->{STATE} = "Defined"; + + # default temperature-scale: C + # C: Celsius, F: Fahrenheit, K: Kelvin, R: Rankine + $attr{$name}{"temp-scale"} = "C"; + + if ($dev eq "none") { + $attr{$name}{dummy} = 1; + Log 1, "OWFS device is none, commands will be echoed only"; + return undef; + } + + Log 3, "OWFS opening OWFS device $dev"; + + my $po; + $po = OW::init($dev); + + return "Can't connect to $dev: $!" if(!$po); + + Log 3, "OWFS opened $dev for $name"; + + $hash->{DeviceName} = $dev; + $hash->{STATE}=""; + my $ret = OWFS_DoInit($hash); + return undef; +} + +##################################### +sub +OWFS_Undef($$) +{ + my ($hash, $arg) = @_; + my $name = $hash->{NAME}; + + foreach my $d (sort keys %defs) { + if (defined($defs{$d}) && defined($defs{$d}{IODev}) && $defs{$d}{IODev} == $hash) { + my $lev = ($reread_active ? 4 : 2); + Log GetLogLevel($name,$lev), "deleting port for $d"; + delete $defs{$d}{IODev}; + } + } + return undef; +} + +1; diff --git a/FHEM/21_OWTEMP.pm b/FHEM/21_OWTEMP.pm new file mode 100644 index 000000000..e797ca053 --- /dev/null +++ b/FHEM/21_OWTEMP.pm @@ -0,0 +1,301 @@ +################################################################ +# +# Copyright notice +# +# (c) 2009 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; +use Time::HiRes qw(gettimeofday); +use OW; + +my %defptr; + +my %gets = ( + "address" => "", + "alias" => "", + "crc8" => "", + "family" => "10", + "id" => "", + "locator" => "", + "power" => "", + "present" => "", +# "r_address" => "", +# "r_id" => "", +# "r_locator" => "", + "temperature" => "", + "temphigh" => "", + "templow" => "", + "type" => "", +); + +my %sets = ( + "alias" => "", + "temphigh" => "", + "templow" => "", + "INTERVAL" => "", + "ALARMINT" => "", +); + +my %updates = ( + "present" => "", + "temperature" => "", +); + +##################################### +sub +OWTEMP_Initialize($) +{ + my ($hash) = @_; + + $hash->{DefFn} = "OWTEMP_Define"; + $hash->{UndefFn} = "OWTEMP_Undef"; + $hash->{GetFn} = "OWTEMP_Get"; + $hash->{SetFn} = "OWTEMP_Set"; + $hash->{AttrList}= "IODev do_not_notify:0,1 showtime:0,1 model:DS18S20 loglevel:0,1,2,3,4,5"; +} + +##################################### +sub +OWTEMP_UpdateReading($$$$$) +{ + my ($hash,$reading,$now,$scale,$value) = @_; + return 0 if (!defined($value) || $value eq ""); + + # trim spaces + $value =~ s/\s//g; + + $value = sprintf("%.4f",$value) if($reading eq "temperature"); + $value = $value . " ($scale)" if($reading eq "temperature" && $scale ne ""); + $hash->{READINGS}{$reading}{TIME} = $now; + $hash->{READINGS}{$reading}{VAL} = $value; + Log 4, "OWTEMP $hash->{NAME} $reading: $value"; + + return 1; +} + +##################################### +sub +OWTEMP_GetUpdate($$) +{ + my ($hash,$a) = @_; + + if (!$hash->{LOCAL}) { + if ($hash->{ALARM} == 0) { + InternalTimer(gettimeofday()+$hash->{INTERVAL}, "OWTEMP_GetUpdate", $hash, 1); + } else { + InternalTimer(gettimeofday()+$hash->{ALARMINT}, "OWTEMP_GetUpdate", $hash, 1); + } + } + + my $name = $hash->{NAME}; + + # get OWTEMP information + my $path = $hash->{OW_PATH}; + my $now = TimeNow(); + my $scale = $attr{$hash->{IODev}->{NAME}}{"temp-scale"}; + my $value = ""; + + $scale = "Celsius" if ($scale eq "C"); + $scale = "Fahrenheit" if ($scale eq "F"); + $scale = "Kelvin" if ($scale eq "K"); + $scale = "Rankine" if ($scale eq "R"); + + my $temp = ""; + + if (!$hash->{LOCAL} || $hash->{LOCAL} == 2) { + if (defined($hash->{LOCAL}) && $hash->{LOCAL} == 2) { + foreach my $r (sort keys %gets) { + $value = OW::get("/uncached/$path/".$r); + $temp = $value if ($r eq "temperature"); + OWTEMP_UpdateReading($hash,$r,$now,$scale,$value); + } + } else { + foreach my $r (sort keys %updates) { + $value = OW::get("/uncached/$path/".$r); + $temp = $value if ($r eq "temperature"); + OWTEMP_UpdateReading($hash,$r,$now,$scale,$value); + } + } + + # trim spaces + $temp =~ s/\s//g; + # set default warning to none + my $warn = "none"; + my $alarm = ""; + $hash->{ALARM} = "0"; + + if ($temp <= $hash->{READINGS}{templow}{VAL}) { + $warn = "templow"; + $hash->{ALARM} = "1"; + OWTEMP_UpdateReading($hash,"warnings",$now,"",$warn); + $alarm = " Alarm: $warn"; + } elsif ($temp >= $hash->{READINGS}{temphigh}{VAL}) { + $warn = "temphigh"; + $hash->{ALARM} = "1"; + OWTEMP_UpdateReading($hash,"warnings",$now,"",$warn); + $alarm = " Alarm: $warn"; + } else { + OWTEMP_UpdateReading($hash,"warnings",$now,"",$warn); + } + + $hash->{STATE} = "T: " . $hash->{READINGS}{temperature}{VAL} . $alarm; + } else { + $value = OW::get("/uncached/$path/".$a); + foreach my $r (sort keys %gets) { + OWTEMP_UpdateReading($hash,$r,$now,$scale,$value) if($r eq $a); + } + return $value; + } + + if(!$hash->{LOCAL} || $hash->{LOCAL} == 2) { + DoTrigger($name, undef) if($init_done); + } + + return 1; +} + +################################### +sub +OWTEMP_Get($@) +{ + my ($hash, @a) = @_; + + return "argument is missing @a" if(int(@a) != 2); + return "Unknown argument $a[1], choose one of " . join(",", sort keys %gets) + if(!defined($gets{$a[1]})); + + my $value; + $hash->{LOCAL} = 1; + $value = OWTEMP_GetUpdate($hash,$a[1]); + delete $hash->{LOCAL}; + + my $reading= $a[1]; + + if(defined($hash->{READINGS}{$reading})) { + $value = $hash->{READINGS}{$reading}{VAL}; + } + + return "$a[0] $reading => $value"; +} + +################################### +sub +OWTEMP_Set($@) +{ + my ($hash, @a) = @_; + return "set needs one parameter" if(int(@a) != 3); + return "Unknown argument $a[1], choose one of " . join(",", sort keys %sets) + if(!defined($sets{$a[1]})); + + my $key = $a[1]; + my $value = $a[2]; + my $path = $hash->{OW_PATH}; + my $ret; + + if ($key eq "INTERVAL" || $key eq "ALARMINT") { + $hash->{$key} = $value; + #RemoveInternalTimer($hash); + if ($hash->{ALARM} == 0) { + InternalTimer(gettimeofday()+$hash->{INTERVAL}, "OWTEMP_GetUpdate", $hash, 0); + } else { + InternalTimer(gettimeofday()+$hash->{ALARMINT}, "OWTEMP_GetUpdate", $hash, 0); + } + Log 4, "OWTEMP $hash->{NAME} $key $value"; + } elsif ($key eq "templow" || $key eq "temphigh") { + return "wrong value: range -55°C - 125°C" if (int($value) < -55 || int($value) > 125); + $ret = OW::put("$path/".$key,$value); + Log 4, "OWTEMP $hash->{NAME} $key $value"; + $hash->{LOCAL} = 1; + OWTEMP_GetUpdate($hash,$key); + delete $hash->{LOCAL}; + } else { + $ret = OW::put("$path/".$key,$value); + $hash->{LOCAL} = 1; + $value = OWTEMP_GetUpdate($hash,$key); + delete $hash->{LOCAL}; + Log 4, "OWTEMP $hash->{NAME} $key $value"; + } + return undef; +} + +################################### +sub +OWTEMP_Define($$) +{ + my ($hash, $def) = @_; + + # define OWTEMP [interval] [alarminterval] + # define flow OWTEMP 332670010800 300 + + my @a = split("[ \t][ \t]*", $def); + + return "wrong syntax: define OWTEMP [interval] [alarminterval]" + if(int(@a) < 2 && int(@a) > 5); + return "Define $a[0]: wrong ID format: specify a 12 digit value" + if(lc($a[2]) !~ m/^[0-9|a-f]{12}$/); + + $hash->{STATE} = "Initialized"; + + my $name = $a[0]; + my $id = $a[2]; + my $interval = 300; + my $alarmint = 300; + if(int(@a)==4) { $interval = $a[3]; } + if(int(@a)==5) { $interval = $a[3]; $alarmint = $a[4] } + + $hash->{OW_ID} = $id; + $hash->{OW_FAMILY} = $gets{family}; + $hash->{OW_PATH} = $gets{family}.".".$hash->{OW_ID}; + $hash->{INTERVAL} = $interval; + $hash->{ALARMINT} = $alarmint; + $hash->{ALARM} = 0; + + $defptr{$a[2]} = $hash; + AssignIoPort($hash); + + $hash->{LOCAL} = 2; + OWTEMP_GetUpdate($hash,""); + delete $hash->{LOCAL}; + + if ($hash->{ALARM} == "0") { + InternalTimer(gettimeofday()+$hash->{INTERVAL}, "OWTEMP_GetUpdate", $hash, 0); + } else { + InternalTimer(gettimeofday()+$hash->{ALARMINT}, "OWTEMP_GetUpdate", $hash, 0); + } + + return undef; +} + +##################################### +sub +OWTEMP_Undef($$) +{ + my ($hash, $name) = @_; + + delete($defptr{$hash->{NAME}}); + RemoveInternalTimer($hash); + + return undef; +} + +1;