| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532 |
- ################################################################
- #
- # 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 %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" => "",
- "alarminterval" => "",
- );
- my %updates = (
- "present" => "",
- "temperature" => "",
- "templow" => "",
- "temphigh" => "",
- );
- my %dummy = (
- "crc8" => "4D",
- "alias" => "dummy",
- "locator" => "FFFFFFFFFFFFFFFF",
- "power" => "0",
- "present" => "1",
- "temphigh" => "75",
- "templow" => "10",
- "type" => "DS18S20",
- "warnings" => "none",
- );
- #####################################
- 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,$value) = @_;
- # define vars
- my $temp;
- # exit if empty value
- return 0
- if(!defined($value) || $value eq "");
- # trim value
- $value =~ s/\s//g
- if($reading ne "warnings");
- if($reading eq "temperature") {
- $value = sprintf("%.4f",$value);
- $temp = $value;
- $value = $value . " (".$hash->{OW_SCALE}.")";
- }
- # update readings
- $hash->{READINGS}{$reading}{TIME} = $now;
- $hash->{READINGS}{$reading}{VAL} = $value;
- Log 4, "OWTEMP $hash->{NAME} $reading: $value";
- return $value;
- }
- #####################################
- sub
- OWTEMP_GetUpdate($$)
- {
- my ($hash, $a) = @_;
- # define vars
- my $name = $hash->{NAME};
- my $now = TimeNow();
- my $value = "";
- my $temp = "";
- my $ret = "";
- my $count = 0;
- # define warnings
- my $warn = "none";
- $hash->{ALARM} = "0";
- # check for real sensor
- if($hash->{OW_ID} ne "none") {
- # real sensor
- if(!$hash->{LOCAL} || $a eq "") {
- #####################
- # OW::Get is too slow: do it in the background by fork. After receiving
- # the data from the OW module, the child contacts the parent, and calls
- # "set <NAME> childupdate <data>", which in turn will call this function
- # again with a filled CHILDDATA
- if(!$hash->{CHILDDATA}) {
- if($hash->{CHILDPID}) {
- Log 2, "OWTEMP: Child already forked: timeout too short?";
- return;
- }
-
- return if(($hash->{CHILDPID} = fork));
- my @ret;
- foreach my $r (sort keys %updates) {
- my $ret = OW::get("/uncached/".$hash->{OW_PATH}."/".$r);
- $ret = "" if(!defined($ret));
- push(@ret, $ret);
- last if($ret eq "");
- }
- my @port = split(" ", $attr{global}{port});
- my $server = IO::Socket::INET->new(PeerAddr => "localhost:$port[0]");
- Log 0, "OWTEMP: Can't connect to parent\n" if(!$server);
- syswrite($server, "set $hash->{NAME} childupdate ".join(":",@ret)."\n");
- exit(0);
- } else {
- #####################
- # Digest the data sent by the CHILD.
- my @ret = split(":", $hash->{CHILDDATA});
- delete($hash->{CHILDPID});
- delete($hash->{CHILDDATA});
- foreach my $r (sort keys %updates) {
- $ret = shift(@ret);
- if($ret eq "") {
- #
- $hash->{PRESENT} = "0";
- $r = "present";
- $value = "0";
- $ret = OWTEMP_UpdateReading($hash,$r,$now,$value);
- $hash->{CHANGED}[$count] = "present: ".$value
- } else {
- $hash->{PRESENT} = "1";
- $value = $ret;
- if($r eq "temperature") {
- $temp = sprintf("%.4f",$value);
- $temp =~ s/\s//g;
- }
- $ret = OWTEMP_UpdateReading($hash,$r,$now,$value);
- }
- last if($hash->{PRESENT} eq "0");
- }
- }
- } else {
- $ret = "";
- $ret = OW::get("/uncached/".$hash->{OW_PATH}."/".$a);
- if(!defined($ret)) {
- $hash->{PRESENT} = "0";
- $a = "present";
- $value = "0";
- $ret = OWTEMP_UpdateReading($hash,$a,$now,$value);
- } else {
- $hash->{PRESENT} = "1";
- $value = $ret;
- if($a eq "temperature") {
- $temp = sprintf("%.4f",$value);
- $temp =~ s/\s//g;
- $value = $temp;
- }
- $ret = OWTEMP_UpdateReading($hash,$a,$now,$value);
- }
- }
- } else {
- # dummy sensor
- $temp = sprintf("%.4f",rand(85));
- $dummy{temperature} = $temp;
- $dummy{present} = "1";
- $hash->{PRESENT} = $dummy{present};
-
- if(!$hash->{LOCAL} || $a eq "") {
- foreach my $r (sort keys %updates) {
- $ret = OWTEMP_UpdateReading($hash,$r,$now,$dummy{$r});
- }
- } else {
- $ret = "";
- $ret = $dummy{$a};
- if($ret ne "") {
- $value = $ret;
- if($a eq "temperature") {
- $temp = sprintf("%.4f",$value);
- $temp =~ s/\s//g;
- }
- $ret = OWTEMP_UpdateReading($hash,$a,$now,$value);
- }
- }
- }
- return 1
- if($hash->{LOCAL} && $a eq "" && $hash->{PRESENT} eq "0");
- # check for warnings
- my $templow = $hash->{READINGS}{templow}{VAL};
- my $temphigh = $hash->{READINGS}{temphigh}{VAL};
- if($hash->{PRESENT} eq "1") {
- if($temp <= $templow) {
- # low temperature
- $hash->{ALARM} = "1";
- $warn = "templow";
- } elsif($temp >= $temphigh) {
- # high temperature
- $hash->{ALARM} = "1";
- $warn = "temphigh";
- }
- } else {
- # set old state
- $temp = $hash->{READINGS}{temperature}{VAL};
- ($temp,undef) = split(" ",$temp);
- # sensor is missing
- $hash->{ALARM} = "1";
- $warn = "not present";
- }
- if(!$hash->{LOCAL} || $a eq "") {
- $ret = OWTEMP_UpdateReading($hash,"warnings",$now,$warn);
- }
- $hash->{STATE} = "T: ".$temp." ".
- "L: ".$templow." ".
- "H: ".$temphigh." ".
- "P: ".$hash->{PRESENT}." ".
- "A: ".$hash->{ALARM}." ".
- "W: ".$warn;
- # inform changes
- # state
- $hash->{CHANGED}[$count++] = $hash->{STATE};
- # present
- $hash->{CHANGED}[$count++] = "present: ".$hash->{PRESENT}
- if(defined($hash->{PRESENT}) && $hash->{PRESENT} ne "");
- # temperature
- $hash->{CHANGED}[$count++] = "temperature: ".$temp." (".$hash->{OW_SCALE}.")"
- if(defined($temp) && $temp ne "");
- # temperature raw
- $hash->{CHANGED}[$count++] = "tempraw: ".$temp
- if(defined($temp) && $temp ne "");
- # low temperature
- $hash->{CHANGED}[$count++] = "templow: ".$templow
- if(defined($templow) && $templow ne "");
- # high temperature
- $hash->{CHANGED}[$count++] = "temphigh: ".$temphigh
- if(defined($temphigh) && $temphigh ne "");
- # warnings
- $hash->{CHANGED}[$count++] = "warnings: ".$warn
- if(defined($warn) && $warn ne "");
- if(!$hash->{LOCAL}) {
- # update timer
- RemoveInternalTimer($hash);
- # check alarm
- if($hash->{ALARM} eq "0") {
- $hash->{INTERVAL} = $hash->{INTV_CHECK};
- } else {
- $hash->{INTERVAL} = $hash->{INTV_ALARM};
- }
- InternalTimer(gettimeofday()+$hash->{INTERVAL}, "OWTEMP_GetUpdate", $hash, 1);
- } else {
- return $value;
- }
- if(!$hash->{LOCAL}) {
- DoTrigger($name, undef) if($init_done);
- }
- return $hash->{STATE};
- }
- #####################################
- sub
- OWTEMP_Get($@)
- {
- my ($hash, @a) = @_;
-
- # 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]}));
- # define vars
- my $value;
- # get 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) = @_;
- # check syntax
- return "set needs one parameter"
- if(int(@a) != 3);
- # check arguments
- return "Unknown argument $a[1], choose one of ".join(",", sort keys %sets)
- if(!defined($sets{$a[1]}) && $a[1] ne "childupdate");
- # define vars
- my $key = $a[1];
- my $value = $a[2];
- my $ret;
- if($key eq "childupdate") {
- $hash->{CHILDDATA} = $value;
- OWTEMP_GetUpdate($hash,undef);
- return undef;
- }
- # set new timer
- if($key eq "interval" || $key eq "alarminterval") {
- $key = "INTV_CHECK"
- if($key eq "interval");
- $key = "INTV_ALARM"
- if($key eq "alarminterval");
- # update timer
- $hash->{$key} = $value;
- RemoveInternalTimer($hash);
- # check alarm
- if($hash->{ALARM} eq "0") {
- $hash->{INTERVAL} = $hash->{INTV_CHECK};
- } else {
- $hash->{INTERVAL} = $hash->{INTV_ALARM};
- }
- InternalTimer(gettimeofday()+$hash->{INTERVAL}, "OWTEMP_GetUpdate", $hash, 1);
- }
- # set warnings
- if($key eq "templow" || $key eq "temphigh") {
- # check range
- return "wrong value: range -55°C - 125°C"
- if(int($value) < -55 || int($value) > 125);
- }
- # set value
- Log 4, "OWTEMP set $hash->{NAME} $key $value";
- # check for real sensor
- if($hash->{OW_ID} ne "none") {
- # real senson
- $ret = OW::put($hash->{OW_PATH}."/$key",$value);
- } else {
- # dummy sensor
- $dummy{$key} = $value;
- }
- # update readings
- if($key ne "interval" || $key ne "alarminterval") {
- $hash->{LOCAL} = 1;
- $ret = OWTEMP_GetUpdate($hash,$key);
- delete $hash->{LOCAL};
- }
-
- return undef;
- }
- #####################################
- sub
- OWTEMP_Define($$)
- {
- my ($hash, $def) = @_;
- # define <name> OWTEMP <id> [interval] [alarminterval]
- # e.g.: define flow OWTEMP 332670010800 300
- my @a = split("[ \t][ \t]*", $def);
- # check syntax
- return "wrong syntax: define <name> OWTEMP <id> [interval] [alarminterval]"
- if(int(@a) < 2 && int(@a) > 5);
- # check ID format
- return "Define $a[0]: missing ID or wrong ID format: specify a 12 digit value or set it to none for demo mode"
- if(lc($a[2]) ne "none" && lc($a[2]) !~ m/^[0-9|a-f]{12}$/);
- # define vars
- my $name = $a[0];
- my $id = $a[2];
- my $interval = 300;
- my $alarminterval = 300;
- my $scale = "";
- my $ret = "";
- # overwrite default intervals if set by define
- if(int(@a)==4) { $interval = $a[3]; }
- if(int(@a)==5) { $interval = $a[3]; $alarminterval = $a[4] }
- # define device internals
- $hash->{ALARM} = 0;
- $hash->{INTERVAL} = $interval;
- $hash->{INTV_CHECK} = $interval;
- $hash->{INTV_ALARM} = $alarminterval;
- $hash->{OW_ID} = $id;
- $hash->{OW_FAMILY} = $gets{family};
- $hash->{OW_PATH} = $hash->{OW_FAMILY}.".".$hash->{OW_ID};
- $hash->{PRESENT} = 0;
- $modules{OWTEMP}{defptr}{$a[2]} = $hash;
- # assign IO port
- AssignIoPort($hash);
- return "No I/O device found. Please define a OWFS device first."
- if(!defined($hash->{IODev}->{NAME}));
- # get scale from I/O device
- $scale = $attr{$hash->{IODev}->{NAME}}{"temp-scale"};
- # define scale for temperature values
- $scale = "Celsius" if ($scale eq "C");
- $scale = "Fahrenheit" if ($scale eq "F");
- $scale = "Kelvin" if ($scale eq "K");
- $scale = "Rankine" if ($scale eq "R");
- $hash->{OW_SCALE} = $scale;
- $hash->{STATE} = "Defined";
- # define dummy values for testing
- if($hash->{OW_ID} eq "none") {
- my $now = TimeNow();
- $dummy{address} = $hash->{OW_FAMILY}.$hash->{OW_ID}.$dummy{crc8};
- $dummy{family} = $hash->{OW_FAMILY};
- $dummy{id} = $hash->{OW_ID};
- $dummy{temperature} = "80.0000 (".$hash->{OW_SCALE}.")";
- foreach my $r (sort keys %gets) {
- $hash->{READINGS}{$r}{TIME} = $now;
- $hash->{READINGS}{$r}{VAL} = $dummy{$r};
- Log 4, "OWTEMP $hash->{NAME} $r: ".$dummy{$r};
- }
- }
- $hash->{STATE} = "Initialized";
- # initalize
- $hash->{LOCAL} = 1;
- $ret = OWTEMP_GetUpdate($hash,"");
- delete $hash->{LOCAL};
- # exit if sensor is not present
- return "Define $hash->{NAME}: Sensor is not reachable. Check first your 1-wire connection."
- if(defined($ret) && $ret eq 1);
- if(!$hash->{LOCAL}) {
- if($hash->{ALARM} eq "0") {
- $hash->{INTERVAL} = $hash->{INTV_CHECK};
- } else {
- $hash->{INTERVAL} = $hash->{INTV_ALARM};
- }
- InternalTimer(gettimeofday()+$hash->{INTERVAL}, "OWTEMP_GetUpdate", $hash, 0);
- }
- return undef;
- }
- #####################################
- sub
- OWTEMP_Undef($$)
- {
- my ($hash, $name) = @_;
- delete($modules{OWTEMP}{defptr}{$hash->{NAME}});
- RemoveInternalTimer($hash);
- return undef;
- }
- 1;
|