diff --git a/fhem/CHANGED b/fhem/CHANGED index 480eb73e3..8354aeb87 100644 --- a/fhem/CHANGED +++ b/fhem/CHANGED @@ -38,6 +38,7 @@ - feature: added support for UV sensors in TRX_LIGHT using RFXtrx433 (Willi Herzig) - feature: added on-till and on-timer for set in TRX_LIGHT using RFXtrx433 (Willi Herzig) - feature: generate devices with hexcodes as state for unknown types in TRX_ELSE using RFXtrx433 (Willi Herzig) + - feature: new modules 10_OWServer.pm and 11_OWDevice.pm to interface with OWFS - 2012-10-28 (5.3) - feature: added functions trim, ltrim, rtrim, UntoggleDirect, diff --git a/fhem/FHEM/10_OWServer.pm b/fhem/FHEM/10_OWServer.pm new file mode 100644 index 000000000..4977ab733 --- /dev/null +++ b/fhem/FHEM/10_OWServer.pm @@ -0,0 +1,268 @@ +# $Id: $ +################################################################ +# +# Copyright notice +# +# (c) 2012 Copyright: Dr. Boris Neubert +# omega at online dot de +# +# 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; +# this must be the latest OWNet from +# http://owfs.cvs.sourceforge.net/viewvc/owfs/owfs/module/ownet/perl5/OWNet/lib/OWNet.pm +# the version at CPAN is outdated and malfunctioning as at 2012-12-19 +use OWNet; + +##################################### +sub +OWServer_Initialize($) +{ + my ($hash) = @_; + +# Provider + $hash->{WriteFn}= "OWServer_Write"; + $hash->{ReadFn} = "OWServer_Read"; + $hash->{Clients}= ":OWDevice:"; + +# Consumer + $hash->{DefFn} = "OWServer_Define"; + $hash->{UndefFn} = "OWServer_Undef"; + $hash->{GetFn} = "OWServer_Get"; +# $hash->{SetFn} = "OWServer_Set"; +# $hash->{AttrFn} = "OWServer_Attr"; + $hash->{AttrList}= "loglevel:0,1,2,3,4,5"; +} + +##################################### +sub +OWServer_Define($$) +{ + my ($hash, $def) = @_; + + my @a = split("[ \t]+", $def, 3); + my $name = $a[0]; + if(@a < 3) { + my $msg = "wrong syntax for $name: define OWServer "; + Log 2, $msg; + return $msg; + } + + my $protocol = $a[2]; + + OWServer_CloseDev($hash); + + $hash->{fhem}{Protocol}= $protocol; + + OWServer_OpenDev($hash, $protocol); + return undef; +} + + +##################################### +sub +OWServer_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 OWServer for $d"; + delete $defs{$d}{IODev}; + } + } + + OWServer_CloseDev($hash); + return undef; +} + +##################################### +sub +OWServer_CloseDev($) +{ + my ($hash) = @_; + + return unless(defined($hash->{fhem}{owserver})); + delete $hash->{fhem}{owserver}; + +} + +######################## +sub +OWServer_OpenDev($$) +{ + my ($hash, $protocol) = @_; + my $name = $hash->{NAME}; + + Log 4, "$name: Opening connection to OWServer $protocol..."; + my $owserver= OWNet->new($protocol); + if($owserver) { + Log 4, "$name: Successfully connected to $protocol."; + $hash->{fhem}{owserver}= $owserver; + DoTrigger($name, "CONNECTED") if($owserver); + } + return $owserver +} + +##################################### +sub +OWServer_DoInit($) +{ + my $hash = shift; + my $name = $hash->{NAME}; + $hash->{STATE} = "Initialized" if(!$hash->{STATE}); + + return undef; +} + +##################################### +sub +OWServer_Read($@) +{ + my ($hash,$path)= @_; + + return undef unless(defined($hash->{fhem}{owserver})); + return $hash->{fhem}{owserver}->read($path); +} + +##################################### +sub +OWServer_Write($@) +{ + my ($hash,$path,$value)= @_; + + return undef unless(defined($hash->{fhem}{owserver})); + return $hash->{fhem}{owserver}->write($path,$value); +} + + +##################################### +sub +OWServer_Get($@) +{ + my ($hash, @a) = @_; + + my $name = $a[0]; + + return "$name: get needs at least one parameter" if(@a < 2); + + my $cmd= $a[1]; + #my $arg = ($a[2] ? $a[2] : ""); + #my @args= @a; shift @args; shift @args; + + my $owserver= $hash->{fhem}{owserver}; + + if($cmd eq "devices") { + my @dir= split(",", $owserver->dir()); + my @devices= grep { m/^\/\d\d\.\d+$/ } @dir; + my $ret; + for my $device (@devices) { + $ret .= substr($device,1) . " " . $owserver->read($device . "/type") . "\n"; + } + return $ret; + } else { + return "Unknown argument $cmd, choose one of devices" + } + +} + +##################################### + + +1; + + +=pod +=begin html + + +

OWServer

+
    +
    + + Define +
      + define <name> OWDevice <protocol> +

      + + Defines a logical OWServer device. OWServer is the server component of the + 1-Wire Filesystem. It serves as abstraction layer + for any 1-wire devices on a host. <protocol> has + format <hostname>:<port>. For details see + owserver documentation. +

      + You need OWNet.pm from owfs.org. Just drop it into your FHEM + folder alongside the 10_OWServer.pm module. As at 2012-12-23 the OWNet module + on CPAN has an issue which renders it useless for remote connections. +

      + The actual 1-wire devices are defined as OWDevice devices. +

      + This module is completely unrelated to the 1-wire modules with names all in uppercase. +

      + Examples: +
        + define myLocalOWServer OWServer localhost:4304
        + define myRemoteOWServer OWServer raspi:4304
        +
      +
      +
    + + + Set +
      + none +
    +

    + + + + Get +
      + get <name> devices +

      + Lists the addresses and types of all 1-wire devices provided by the owserver. +

      +
    +

    + + + + Attributes + +

    + + + +
+ + +=end html +=cut diff --git a/fhem/FHEM/11_OWDevice.pm b/fhem/FHEM/11_OWDevice.pm new file mode 100644 index 000000000..f489658c2 --- /dev/null +++ b/fhem/FHEM/11_OWDevice.pm @@ -0,0 +1,355 @@ +# $Id: $ +############################################################################## +# +# 11_OWDevice.pm +# Copyright by Dr. Boris Neubert +# e-mail: omega at online dot de +# +# 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 +OWDevice_Initialize($) +{ + my ($hash) = @_; + + $hash->{GetFn} = "OWDevice_Get"; + $hash->{SetFn} = "OWDevice_Set"; + $hash->{DefFn} = "OWDevice_Define"; + + $hash->{AttrList} = "trimvalues loglevel:0,1,2,3,4,5"; +} + +################################### +# return array +# 1st element: interface +# 2nd element: array of getters/readings +# 3rd element: array of setters/readings +# 4th element: array of readings to be periodically updated +# the value of the first reading in getters is written to state +sub +OWDevice_GetDetails($) { + + my ($hash)= @_; + my $interface= ""; + my @getters= qw(address alias family id power type); + my @setters= qw(alias); + my @polls; + + # below we use shift such that the potentially + # more important values get listed first and + # that the first reading in getters could be + # defined (it is shown in the STATE). + + # http://owfs.sourceforge.net/family.html + my $family= substr($hash->{fhem}{address}, 0, 2); + if($family eq "10") { + # 18S20 high precision digital thermometer + unshift @getters, qw(temperature templow temphigh); + unshift @setters, qw(templow temphigh); + unshift @polls, qw(temperature); + $interface= "temperature"; + } elsif($family eq "reserved") { + # reserved for other devices + }; + # http://perl-seiten.homepage.t-online.de/html/perl_array.html + return ($interface, \@getters, \@setters, \@polls); +} + +################################### +# This could be IORead in fhem, But there is none. +# Read http://forum.fhem.de/index.php?t=tree&goto=54027&rid=10#msg_54027 +# to find out why. +sub +OWDevice_ReadFromServer($@) +{ + my ($hash, @a) = @_; + + my $dev = $hash->{NAME}; + return if(IsDummy($dev) || IsIgnored($dev)); + my $iohash = $hash->{IODev}; + if(!$iohash || + !$iohash->{TYPE} || + !$modules{$iohash->{TYPE}} || + !$modules{$iohash->{TYPE}}{ReadFn}) { + Log 5, "No I/O device or ReadFn found for $dev"; + return; + } + + no strict "refs"; + my $ret = &{$modules{$iohash->{TYPE}}{ReadFn}}($iohash, @a); + use strict "refs"; + return $ret; +} + +################################### +sub +OWDevice_ReadValue($$) { + + my ($hash,$reading)= @_; + + my $address= $hash->{fhem}{address}; + my $value= OWDevice_ReadFromServer($hash, "/$address/$reading"); + $value= trim($value) if(AttrVal($hash,"trimvalues",1)); + my @getters= @{$hash->{fhem}{getters}}; + Debug join(",", @getters); + Debug $getters[0]; + $hash->{STATE}= $value if($reading eq $getters[0]); + + return $value; +} + +################################### +sub +OWDevice_WriteValue($$$) { + + my ($hash,$reading,$value)= @_; + + my $address= $hash->{fhem}{address}; + IOWrite($hash, "/$address/$reading", $value); + return $value; +} + +################################### +sub +OWDevice_UpdateValues($) { + + my ($hash)= @_; + + my @polls= @{$hash->{fhem}{polls}}; + if($#polls>=0) { + my $address= $hash->{fhem}{address}; + readingsBeginUpdate($hash); + foreach my $reading (@polls) { + my $value= OWDevice_ReadValue($hash,$reading); + readingsBulkUpdate($hash,$reading,$value); + } + readingsEndUpdate($hash,1); + } + InternalTimer(gettimeofday()+$hash->{fhem}{interval}, "OWDevice_UpdateValues", $hash, 0) + if(defined($hash->{fhem}{interval})); + +} + +################################### +sub +OWDevice_Get($@) +{ + my ($hash, @a)= @_; + + my $name= $hash->{NAME}; + return "get $name needs one argument" if(int(@a) != 2); + my $cmdname= $a[1]; + my @getters= @{$hash->{fhem}{getters}}; + if($cmdname ~~ @getters) { + my $value= OWDevice_ReadValue($hash, $cmdname); + readingsSingleUpdate($hash,$cmdname,$value,1); + return $value; + } else { + return "Unknown argument $cmdname, choose one of " . join(" ", @getters); + } +} + +############################# +sub +OWDevice_Set($@) +{ + my ($hash, @a)= @_; + + my $name= $hash->{NAME}; + my $cmdname= $a[1]; + my $value= $a[2]; + my @setters= @{$hash->{fhem}{setters}}; + if($cmdname ~~ @setters) { + return "set $name needs two arguments" if(int(@a) != 3); + OWDevice_WriteValue($hash,$cmdname,$value); + readingsSingleUpdate($hash,$cmdname,$value,1); + return undef; + } else { + return "Unknown argument $cmdname, choose one of " . join(" ", @setters); + } +} + +############################# +sub +OWDevice_Define($$) +{ + my ($hash, $def) = @_; + my @a = split("[ \t]+", $def); + + return "Usage: define OWDevice
[interval]" if($#a < 3 || $#a > 4); + my $name= $a[0]; + + AssignIoPort($hash); + if(defined($hash->{IODev}->{NAME})) { + Log 3, "$name: I/O device is " . $hash->{IODev}->{NAME}; + } else { + Log 1, "$name: no I/O device"; + } + + $hash->{fhem}{address}= $a[2]; + if($#a == 3) { + $hash->{fhem}{interval}= $a[3]; + Log 5, "$name: polling every $a[3] seconds"; + } + my ($interface, $gettersref, $settersref, $pollsref)= OWDevice_GetDetails($hash); + my @getters= @{$gettersref}; + my @setters= @{$settersref}; + my @polls= @{$pollsref}; + if($interface ne "") { + $hash->{fhem}{interfaces}= $interface; + Log 5, "$name: interfaces: $interface"; + } + $hash->{fhem}{getters}= $gettersref; + Log 5, "$name: getters: " . join(" ", @getters); + $hash->{fhem}{setters}= $settersref; + Log 5, "$name: setters: " . join(" ", @setters); + $hash->{fhem}{polls}= $pollsref; + Log 5, "$name: polls: " . join(" ", @polls); + + OWDevice_UpdateValues($hash) if(defined($hash->{fhem}{interval})); + + return undef; +} +################################### + +1; + +################################### +=pod +=begin html + + +

OWDevice

+
    +
    + + Define +
      + define <name> OWDevice <address> [<interval>] +

      + + Defines a 1-wire device. The 1-wire device is identified by its <address>. It is + served by the most recently defined OWServer. +

      + + If <interval> is given, the OWServer is polled every <interval> seconds for + a subset of readings. +

      + + OWDevice is a generic device. Its characteristics are retrieved at the time of the device's + definition. The available readings that you can get or set as well as those that are + regularly retrieved by polling can be seen when issuing the + list <name> command. +

      + This module is completely unrelated to the 1-wire modules with names all in uppercase. +

      + + Example: +
        + + define myOWServer localhost:4304

        + get myOWServer devices
        + 10.487653020800 DS18S20

        + define myT1 10.487653020800

        + list myT1 10.487653020800
        + Internals:
        + ...
        + Readings:
        + 2012-12-22 20:30:07 temperature 23.1875
        + Fhem:
        + ...
        + getters:
        + address
        + alias
        + family
        + id
        + power
        + type
        + temperature
        + templow
        + temphigh
        + polls:
        + temperature
        + setters:
        + alias
        + templow
        + temphigh
        + ...
        +
        +
      +
      +
    + + + Set +
      + set <name> <reading> <value> +

      + Sets <reading> to <value> for the 1-wire device <name>. The permitted values are defined by the underlying + 1-wire device type. +

      + Example: +
        + set myT1 templow 5
        +
      +
      +
    + + + + Get +
      + get <name> <reading> <value> +

      + Gets <reading> for the 1-wire device <name>. The permitted values are defined by the underlying + 1-wire device type. +

      + Example: +
        + get myT1 temperature
        +
      +
      +
    + + + + Attributes + +

    + + +
+ + + + +=end html +=cut