diff --git a/45_TRX.pm b/45_TRX.pm new file mode 100644 index 0000000..f4bf57a --- /dev/null +++ b/45_TRX.pm @@ -0,0 +1,504 @@ +################################################################################# +# 45_TRX.pm +# +# FHEM Module for RFXtrx433 +# +# Derived from 00_CUL.pm: Copyright (C) Rudolf Koenig" +# +# Copyright (C) 2012/2013 Willi Herzig +# +# This program 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. +# +# This program 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 this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# +# The GNU General Public License may also be found at http://www.gnu.org/licenses/gpl-2.0.html . +# +########################### +# $Id: 45_TRX.pm 5957 2014-05-24 13:46:29Z wherzig - patch playwithfree $ +package main; + +use strict; +use warnings; +use Time::HiRes qw(gettimeofday); + +my $last_rmsg = "abcd"; +my $last_time = 1; +my $trx_rssi = 0; + +sub TRX_Clear($); +sub TRX_Read($); +sub TRX_Ready($); +sub TRX_Parse($$$$); + +sub +TRX_Initialize($) +{ + my ($hash) = @_; + + + require "$attr{global}{modpath}/FHEM/DevIo.pm"; + +# Provider + $hash->{ReadFn} = "TRX_Read"; + $hash->{WriteFn} = "TRX_Write"; + $hash->{Clients} = + ":TRX_WEATHER:TRX_SECURITY:TRX_LIGHT:TRX_ELSE:"; + my %mc = ( + "1:TRX_WEATHER" => "^..(40|50|51|52|54|55|56|57|58|5a|5b|5c|5d).*", + "2:TRX_SECURITY" => "^..(20).*", + "3:TRX_LIGHT" => "^..(10|11|12|13|14|15|16|17|18|19).*", + "4:TRX_ELSE" => "^..(0[0-f]|1[a-f]|2[1-f]|3[0-f]|4[1-f]|53|59|5e|5f|[6-f][0-f]).*", + ); + $hash->{MatchList} = \%mc; + + $hash->{ReadyFn} = "TRX_Ready"; + +# Normal devices + $hash->{DefFn} = "TRX_Define"; + $hash->{UndefFn} = "TRX_Undef"; + $hash->{GetFn} = "TRX_Get"; + $hash->{StateFn} = "TRX_SetState"; + $hash->{AttrList}= "do_not_notify:1,0 dummy:1,0 do_not_init:1,0 addvaltrigger:1,0 longids rssi:1,0"; + $hash->{ShutdownFn} = "TRX_Shutdown"; +} + +##################################### +sub +TRX_Define($$) +{ + my ($hash, $def) = @_; + my @a = split("[ \t][ \t]*", $def); + + + if (@a != 3 && @a != 4) { + my $msg = "wrong syntax: define TRX devicename [noinit]"; + Log3 undef, 2, $msg; + return $msg; + } + + DevIo_CloseDev($hash); + + my $name = $a[0]; + my $dev = $a[2]; + my $opt = $a[3] if(@a == 4);; + + if($dev eq "none") { + Log3 $name, 1, "TRX: $name device is none, commands will be echoed only"; + $attr{$name}{dummy} = 1; + return undef; + } + + if(defined($opt)) { + if($opt eq "noinit") { + Log3 $name, 1 , "TRX: $name no init is done"; + $attr{$name}{do_not_init} = 1; + } else { + return "wrong syntax: define TRX devicename [noinit]" + } + } + + $hash->{DeviceName} = $dev; + my $ret = DevIo_OpenDev($hash, 0, "TRX_DoInit"); + return $ret; +} + +##################################### +# Input is hexstring +sub +TRX_Write($$) +{ + my ($hash,$fn,$msg) = @_; + my $name = $hash->{NAME}; + + return if(!defined($fn)); + + my $bstring; + $bstring = "$fn$msg"; + Log3 $name, 5, "$hash->{NAME} sending $bstring"; + + DevIo_SimpleWrite($hash, $bstring, 1); +} + + +##################################### +sub +TRX_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); + Log3 $name, $lev, "deleting port for $d"; + delete $defs{$d}{IODev}; + } + } + + DevIo_CloseDev($hash); + return undef; +} + +##################################### +sub +TRX_Shutdown($) +{ + my ($hash) = @_; + return undef; +} + +##################################### +sub +TRX_Get($@) +{ + my ($hash, @a) = @_; + + my $msg; + my $name=$a[0]; + my $reading= $a[1]; + + $msg="$name => No Get function ($reading) implemented"; + Log3 $name, 1, $msg if ($reading ne "?"); + return $msg; +} + +##################################### +sub +TRX_SetState($$$$) +{ + my ($hash, $tim, $vt, $val) = @_; + return undef; +} + +sub +TRX_Clear($) +{ + my $hash = shift; + my $buf; + + # clear buffer: + if($hash->{USBDev}) { + while ($hash->{USBDev}->lookfor()) { + $buf = DevIo_SimpleRead($hash); + } + } + if($hash->{TCPDev}) { + # TODO + return $buf; + } +} + +##################################### +sub +TRX_DoInit($) +{ + my $hash = shift; + my $name = $hash->{NAME}; + my $err; + my $msg = undef; + my $buf; + my $char = undef ; + + + if(defined($attr{$name}) && defined($attr{$name}{"do_not_init"})) { + Log3 $name, 1, "TRX: defined with noinit. Do not send init string to device."; + $hash->{STATE} = "Initialized"; + + # Reset the counter + delete($hash->{XMIT_TIME}); + delete($hash->{NR_CMD_LAST_H}); + + return undef; + } + + # Reset + my $init = pack('H*', "0D00000000000000000000000000"); + DevIo_SimpleWrite($hash, $init, 0); + DevIo_TimeoutRead($hash, 0.5); + + TRX_Clear($hash); + + # + # Get Status + $init = pack('H*', "0D00000102000000000000000000"); + DevIo_SimpleWrite($hash, $init, 0); + $buf = unpack('H*',DevIo_TimeoutRead($hash, 0.1)); + + if (! $buf) { + Log3 $name, 1, "TRX: Initialization Error: No character read"; + return "TRX: Initialization Error $name: no char read"; + } elsif ($buf !~ m/0d0100....................../) { + Log3 $name, 1, "TRX: Initialization Error hexline='$buf', expected 0d0100......................"; + return "TRX: Initialization Error %name expected 0D0100, but char=$char received."; + } else { + Log3 $name,1, "TRX: Init OK"; + $hash->{STATE} = "Initialized"; + # Analyse result and display it: + if ($buf =~ m/0d0100(..)(..)(..)(..)(..)(..)(..)(..)(..)(..)(..)/) { + my $status = ""; + + my $seqnbr = $1; + my $cmnd = $2; + my $msg1 = $3; + my $msg2 = ord(pack('H*', $4)); + my $msg3 = ord(pack('H*', $5)); + my $msg4 = ord(pack('H*', $6)); + my $msg5 = ord(pack('H*', $7)); + my $freq = { + '50' => '310MHz', + '51' => '315MHz', + '52' => '433.92MHz receiver only', + '53' => '433.92MHz transceiver', + '55' => '868.00MHz', + '56' => '868.00MHz FSK', + '57' => '868.30MHz', + '58' => '868.30MHz FSK', + '59' => '868.35MHz', + '5a' => '868.35MHz FSK', + '5b' => '868.95MHz' + }->{$msg1} || 'unknown Mhz'; + $status .= $freq; + $status .= ", " . sprintf "firmware=%d",$msg2; + $status .= ", protocols enabled: "; + $status .= "undecoded " if ($msg3 & 0x80); + $status .= "RFU " if ($msg3 & 0x40); + $status .= "ByronSX " if ($msg3 & 0x20); + $status .= "RSL " if ($msg3 & 0x10); + $status .= "Lighting4 " if ($msg3 & 0x08); + $status .= "FineOffset/Viking " if ($msg3 & 0x04); + $status .= "Rubicson " if ($msg3 & 0x02); + $status .= "AE/Blyss " if ($msg3 & 0x01); + $status .= "BlindsT1/T2/T3/T4 " if ($msg4 & 0x80); + $status .= "BlindsT0 " if ($msg4 & 0x40); + $status .= "ProGuard " if ($msg4 & 0x20); + $status .= "FS20 " if ($msg4 & 0x10); + $status .= "LaCrosse " if ($msg4 & 0x08); + $status .= "Hideki " if ($msg4 & 0x04); + $status .= "LightwaveRF " if ($msg4 & 0x02); + $status .= "Mertik " if ($msg4 & 0x01); + $status .= "Visonic " if ($msg5 & 0x80); + $status .= "ATI " if ($msg5 & 0x40); + $status .= "OREGON " if ($msg5 & 0x20); + $status .= "KOPPLA " if ($msg5 & 0x10); + $status .= "HOMEEASY " if ($msg5 & 0x08); + $status .= "AC " if ($msg5 & 0x04); + $status .= "ARC " if ($msg5 & 0x02); + $status .= "X10 " if ($msg5 & 0x01); + my $hexline = unpack('H*', $buf); + Log3 $name, 4, "TRX: Init status hexline='$hexline'"; + Log3 $name, 1, "TRX: Init status: '$status'"; + } + } + # + + # Reset the counter + delete($hash->{XMIT_TIME}); + delete($hash->{NR_CMD_LAST_H}); + + return undef; +} + + +##################################### +# called from the global loop, when the select for hash->{FD} reports data +sub +TRX_Read($) +{ + my ($hash) = @_; + + my $name = $hash->{NAME}; + + my $char; + + my $mybuf = DevIo_SimpleRead($hash); + + if(!defined($mybuf) || length($mybuf) == 0) { + DevIo_Disconnected($hash); + return ""; + } + + my $TRX_data = $hash->{PARTIAL}; + Log3 $name, 5, "TRX/RAW: $TRX_data/$mybuf"; + $TRX_data .= $mybuf; + + my $hexline = unpack('H*', $TRX_data); + Log3 $name, 5, "TRX: TRX_Read '$hexline'"; + + # first char as byte represents number of bytes of the message + my $num_bytes = ord(substr($TRX_data,0,1)); + + while(length($TRX_data) > $num_bytes) { + # the buffer contains at least the number of bytes we need + my $rmsg; + $rmsg = substr($TRX_data, 0, $num_bytes+1); + #my $hexline = unpack('H*', $rmsg); + Log3 $name, 5, "TRX_Read rmsg '$hexline'"; + $TRX_data = substr($TRX_data, $num_bytes+1);; + #$hexline = unpack('H*', $TRX_data); + Log3 $name, 5, "TRX_Read TRX_data '$hexline'"; + # + TRX_Parse($hash, $hash, $name, unpack('H*', $rmsg)); + $num_bytes = ord(substr($TRX_data,0,1)); + } + Log3 $name, 5, "TRX_Read END"; + + $hash->{PARTIAL} = $TRX_data; +} + +sub +TRX_Parse($$$$) +{ + my ($hash, $iohash, $name, $rmsg) = @_; + + #Log3 $hash, 5, "TRX_Parse() '$rmsg'"; + + my %addvals; + # Parse only if message is different within 2 seconds + # (some Oregon sensors always sends the message twice, X10 security sensors even sends the message five times) + if (("$last_rmsg" ne "$rmsg") || (time() - $last_time) > 1) { + Log3 $hash, 5, "TRX_Parse() '$rmsg'"; + %addvals = (RAWMSG => $rmsg); + Dispatch($hash, $rmsg, \%addvals); + $hash->{"${name}_MSGCNT"}++; + $hash->{"${name}_TIME"} = TimeNow(); + $hash->{RAWMSG} = $rmsg; + } else { + Log3 $hash, 5, "TRX_Parse() '$rmsg' dup"; + } + + $last_rmsg = $rmsg; + $last_time = time(); + +} + + +##################################### +sub +TRX_Ready($) +{ + my ($hash) = @_; + + return DevIo_OpenDev($hash, 1, "TRX_Ready") + if($hash->{STATE} eq "disconnected"); + + # This is relevant for windows/USB only + my $po = $hash->{USBDev}; + my ($BlockingFlags, $InBytes, $OutBytes, $ErrorFlags) = $po->status; + return ($InBytes>0); +} + +1; + +=pod +=begin html + + +

TRX

+ + +=end html +=cut diff --git a/46_TRX_ELSE.pm b/46_TRX_ELSE.pm new file mode 100644 index 0000000..3274937 --- /dev/null +++ b/46_TRX_ELSE.pm @@ -0,0 +1,185 @@ +################################################################################# +# 46_TRX_ELSE.pm +# +# FHEM module unkown RFXtrx433 messages +# +# Copyright (C) 2012 Willi Herzig +# +# This program 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. +# +# This program 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 this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# +# The GNU General Public License may also be found at http://www.gnu.org/licenses/gpl-2.0.html . +################################### +# +# values for "set global verbose" +# 4: log unknown protocols +# 5: log decoding hexlines for debugging +# +# $Id: 46_TRX_ELSE.pm 5719 2014-05-01 19:18:38Z wherzig $ +package main; + +use strict; +use warnings; + +my $time_old = 0; + +my $DOT = q{_}; + +sub +TRX_ELSE_Initialize($) +{ + my ($hash) = @_; + + $hash->{Match} = "^..(0[0-f]|1[a-f]|2[1-f]|3[0-f]|4[1-f]|53|59|5e|5f|[6-f][0-f]).*"; + $hash->{DefFn} = "TRX_ELSE_Define"; + $hash->{UndefFn} = "TRX_ELSE_Undef"; + $hash->{ParseFn} = "TRX_ELSE_Parse"; + $hash->{AttrList} = "IODev ignore:1,0 do_not_notify:1,0 ". + $readingFnAttributes; + + Log3 $hash, 5, "TRX_ELSE_Initialize() Initialize"; +} + +##################################### +sub +TRX_ELSE_Define($$) +{ + my ($hash, $def) = @_; + my @a = split("[ \t][ \t]*", $def); + + my $a = int(@a); + + return "wrong syntax: define TRX_ELSE code" if(int(@a) != 3); + + my $name = $a[0]; + my $code = $a[2]; + + my $device_name = "TRX_UNKNOWN".$DOT.$code; + + $hash->{CODE} = $code; + $modules{TRX_ELSE}{defptr}{$device_name} = $hash; + AssignIoPort($hash); + + return undef; +} + +##################################### +sub +TRX_ELSE_Undef($$) +{ + my ($hash, $name) = @_; + delete($modules{TRX_ELSE}{defptr}{$name}); + return undef; +} + +sub +TRX_ELSE_Parse($$) +{ + my ($hash, $msg) = @_; + + my $time = time(); + if ($time_old ==0) { + Log3 $hash, 5, "TRX_ELSE_Parse() decoding delay=0 hex=$msg"; + } else { + my $time_diff = $time - $time_old ; + Log3 $hash, 5, "TRX_ELSE_Parse() decoding delay=$time_diff hex=$msg"; + } + $time_old = $time; + + # convert to binary + my $bin_msg = pack('H*', $msg); + Log3 $hash, 5, "TRX_ELSE_Parse() 2 hex=$msg"; + + # convert string to array of bytes. Skip length byte + my @rfxcom_data_array = (); + foreach (split(//, substr($bin_msg,1))) { + push (@rfxcom_data_array, ord($_) ); + } + + my $num_bytes = ord(substr($bin_msg,0,1)); + + if ($num_bytes < 4) { + return; + } + + my $type = $rfxcom_data_array[0]; + + Log3 $hash, 5, "TRX_ELSE_Parse() num_bytes=$num_bytes hex=$msg type=$type"; + my $res = ""; + if ($type == 0x02) { + my $subtype = $rfxcom_data_array[1]; + my $msg = $rfxcom_data_array[3]; + if (($msg != 0x00) && ($msg != 0x01)) { + Log3 $hash, 1, "TRX_ELSE_Parse() error transmit NACK=".sprintf("%02x",$msg); + } + return ""; + } + + my $type_hex = sprintf("%02x", $type); + + my $device_name = "TRX".$DOT."UNKNOWN".$DOT.$type_hex; + + my $def = $modules{TRX_ELSE}{defptr}{$device_name}; + if (!$def) { + Log3 $hash, 3, "TRX_ELSE: Unknown device $device_name, please define it"; + return "UNDEFINED $device_name TRX_ELSE $type_hex"; + + } + my $name = $def->{NAME}; + return "" if(IsIgnored($name)); + + readingsBeginUpdate($def); + + my $current = $msg; + #my $sensor = "hexline"; + #readingsBulkUpdate($def, $sensor, $current); + + readingsBulkUpdate($def, "state", $current); + + readingsEndUpdate($def, 1); + + return $name; +} + +1; + +=pod +=begin html + + +

TRX_ELSE

+
    + The TRX_ELSE module is invoked by TRX if a code is received by RFXCOM RFXtrx433 RF receiver that is currently not handled by a TRX_-Module. You need to define an RFXtrx433 receiver first. +See TRX. +
    + +
    + Define +
      + define <name> TRX_ELSE <hextype>
      +
      + <hextype> +
        + specifies the hexvalue (00 - ff) of the type received by the RFXtrx433 transceiver.
        +
      +
      + Example:
      + define TRX_UNKNOWN_9A TRX_ELSE 9A +
      +
    +
    +
+ +=end html +=cut diff --git a/46_TRX_SECURITY.pm b/46_TRX_SECURITY.pm new file mode 100644 index 0000000..f82f6c7 --- /dev/null +++ b/46_TRX_SECURITY.pm @@ -0,0 +1,672 @@ +# +############################################################################## +# +# 46_TRX_SECURITY.pm +# FHEM module for X10, KD101, Visonic +# +# Copyright (C) 2012/2013 Willi Herzig +# +# 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 . +# +############################################################################## +# +# +# values for "set global verbose" +# 4: log unknown protocols +# 5: log decoding hexlines for debugging +# +package main; + +use strict; +use warnings; + +# Debug this module? YES = 1, NO = 0 +my $TRX_SECURITY_debug = 0; + +my $time_old = 0; +my $trx_rssi; + +my $TRX_SECURITY_type_default = "ds10a"; + +my $DOT = q{_}; + +my %security_device_codes = ( # HEXSTRING => "NAME", "name of reading", + # 0x20: X10, KD101, Visonic, Meiantech + 0x2000 => [ "DS10A", "Window" ], + 0x2001 => [ "MS10A", "motion" ], + 0x2002 => [ "KR18", "key" ], + 0x2003 => [ "KD101", "smoke" ], + 0x2004 => [ "VISONIC_WINDOW", "window" ], + 0x2005 => [ "VISONIC_MOTION", "motion" ], + 0x2006 => [ "VISONIC_REMOTE", "key" ], + 0x2007 => [ "VISONIC_WINDOW_AUX", "window" ], + 0x2008 => [ "MEIANTECH", "alarm" ], + 0x2009 => [ "SA30", "alarm" ], +); + +my %security_device_commands = ( # HEXSTRING => commands + # 0x20: X10, KD101, Visonic, Meiantech + 0x2000 => [ "Closed", "", "Open", "", "", "", ""], # DS10A + 0x2001 => [ "", "", "", "", "alert", "normal", ""], # MS10A + 0x2002 => [ "", "", "", "", "", "", "Panic", "EndPanic", "", "Arm_Away", "Arm_Away_Delayed", "Arm_Home", "Arm_Home_Delayed", "Disarm"], # KR18 + 0x2003 => [ "", "", "", "", "", "", "alert", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "pair"], # KD101 + 0x2004 => [ "normal", "", "alert"], #VISONIC_WINDOW + 0x2005 => [ "", "", "", "", "motion", "nomotion", "alert"], #VISONIC_MOTION + 0x2008 => [ "", "", "", "", "", "", "Panic", "", "IR", "Arm_Away", "", "Arm_Home", "", "Disarm"], #MEIANTECH + 0x2009 => [ "", "", "", "", "", "", "alert", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "pair"], #SA30 + +); + +my %security_device_c2b; # DEVICE_TYPE->hash (reverse of security_device_codes) + +##################################### +sub +TRX_SECURITY_Initialize($) +{ + my ($hash) = @_; + + foreach my $k (keys %security_device_codes) { + $security_device_c2b{$security_device_codes{$k}->[0]} = $k; + } + + $hash->{Match} = "^..(20).*"; + $hash->{SetFn} = "TRX_SECURITY_Set"; + $hash->{DefFn} = "TRX_SECURITY_Define"; + $hash->{UndefFn} = "TRX_SECURITY_Undef"; + $hash->{ParseFn} = "TRX_SECURITY_Parse"; + $hash->{AttrList} = "IODev ignore:1,0 do_not_notify:1,0 ". + $readingFnAttributes; + +} + +################################### +sub +TRX_SECURITY_Set($@) +{ + my ($hash, @a) = @_; + my $ret = undef; + my $na = int(@a); + + return "no set value specified" if($na < 2 || $na > 3); + + # look for device_type + + my $name = $a[0]; + my $command = $a[1]; + my $level; + + if ($na == 3) { + $level = $a[2]; + } else { + $level = 0; + } + + my $device_type = $hash->{TRX_SECURITY_type}; + my $deviceid = $hash->{TRX_SECURITY_deviceid}; + + if ($device_type ne "KD101" && $device_type ne "DS10A" && $device_type ne "MS10A" && $device_type ne "KR18") { + return "No set implemented for $device_type"; + } + + my $device_type_num = $security_device_c2b{$device_type}; + if(!defined($device_type_num)) { + return "Unknown device_type, choose one of " . + join(" ", sort keys %security_device_c2b); + } + my $protocol_type = $device_type_num >> 8; # high bytes + + # Now check if the command is valid and retrieve the command id: + my $rec = $security_device_commands{$device_type_num}; + my $i; + for ($i=0; $i <= $#$rec && ($rec->[$i] ne $command); $i++) { ;} + + if($i > $#$rec) { + my $l = join(" ", sort @$rec); + if ($device_type eq "AC" || $device_type eq "HOMEEASY" || $device_type eq "ANSLUT") { + $l =~ s/ level / level:slider,0,1,15 /; + } + my $error = "Unknown command $command, choose one of $l"; + + Log3 $name, 1, "TRX_SECURITY_Set() ".$error if ($command ne "?" ); + return $error; + } + + if ($na == 4 && $command ne "level") { + my $error = "Error: level not possible for command $command"; + } + + my $seqnr = 0; + my $cmnd = $i; + + my $hex_prefix; + my $hex_command; + if ($protocol_type == 0x20) { + my $id1; + my $id2; + my $id3; + if ($deviceid =~ /^(..)(..)$/) { + $id1 = $2; + $id2 = "00"; + $id3 = $1; + } elsif ($deviceid =~ /^(..)(..)(..)$/) { + $id1 = $1; + $id2 = $2; + $id3 = $3; + } else { + Log3 $name, 1,"TRX_SECURITY_Set() security1 wrong deviceid: name=$name device_type=$device_type, deviceid=$deviceid"; + return "error set name=$name deviceid=$deviceid"; + } + + # security1 + $hex_prefix = sprintf "0820"; + $hex_command = sprintf "%02x%02x%02s%02s%02s%02x00", $device_type_num & 0xff, $seqnr, $id1, $id2, $id3, $cmnd; + Log3 $name, 5,"TRX_SECURITY_Set() name=$name device_type=$device_type, deviceid=$deviceid id1=$id1, id2=$id2, id3=$id3, command=$command"; + Log3 $name, 5,"TRX_SECURITY_Set() hexline=$hex_prefix$hex_command"; + + if ($device_type ne "KD101") { + my $sensor = ""; + + readingsBeginUpdate($hash); + + # Now set the statechange: + if ($hash->{STATE} ne $command) { + $sensor = "statechange"; + readingsBulkUpdate($hash, $sensor, $command); + } + + # Now set the devicelog: + $sensor = $hash->{TRX_SECURITY_devicelog}; + if ($sensor ne "none") { readingsBulkUpdate($hash, $sensor, $command); } + + # Set battery + $sensor = "battery"; + readingsBulkUpdate($hash, $sensor, "ok"); + + readingsEndUpdate($hash, 1); + } + + } else { + return "No set implemented for $device_type . Unknown protocol type"; + } + + IOWrite($hash, $hex_prefix, $hex_command); + + my $tn = TimeNow(); + $hash->{CHANGED}[0] = $command; + $hash->{STATE} = $command; + $hash->{READINGS}{state}{TIME} = $tn; + $hash->{READINGS}{state}{VAL} = $command; + + return $ret; +} + +##################################### +sub +TRX_SECURITY_Define($$) +{ + my ($hash, $def) = @_; + my @a = split("[ \t][ \t]*", $def); + + my $a = int(@a); + + if(int(@a) != 5 && int(@a) != 7) { + Log3 $hash, 1,"TRX_SECURITY_Define() wrong syntax '@a'. \nCorrect syntax is 'define TRX_SECURITY [ ]'"; + return "wrong syntax: define TRX_SECURITY [ ]"; + } + + + my $name = $a[0]; + + my $type = lc($a[2]); + my $deviceid = $a[3]; + my $devicelog = $a[4]; + + + $type = uc($type); + + my $my_type; + if ($type eq "WD18" || $type eq "GD18" || $type eq "SD18" || $type eq "COD18" || $type eq "GB10E" ) { + $my_type = "DS10A"; # device will be received as DS10A + } else { + $my_type = $type; + } + my $device_name = "TRX".$DOT.$my_type.$DOT.$deviceid; + + if ($type ne "DS10A" && $type ne "SD90" && $type ne "MS10A" && $type ne "MS14A" && $type ne "KR18" && $type ne "KD101" && $type ne "VISONIC_WINDOW" && $type ne "VISONIC_MOTION" && $type ne "VISONIC_WINDOW_AUX" && $type ne "VISONIC_REMOTE" && $type ne "MEIANTECH" && $type ne "SA30" && $type ne "GD18" && $type ne "WD18" && $type ne "SD18" && $type ne "COD18" && $type ne "GB10E") { + Log3 $hash, 1,"TRX_SECURITY_Define() wrong type: $type"; + return "TRX_SECURITY: wrong type: $type"; + } + + $hash->{TRX_SECURITY_deviceid} = $deviceid; + $hash->{TRX_SECURITY_devicelog} = $devicelog; + $hash->{TRX_SECURITY_type} = $type; + #$hash->{TRX_SECURITY_CODE} = $deviceid; + $modules{TRX_SECURITY}{defptr}{$device_name} = $hash; + + + if (int(@a) == 7) { + # there is a second deviceid: + # + my $deviceid2 = $a[5]; + my $devicelog2 = $a[6]; + + my $device_name2 = "TRX_SECURITY".$DOT.$deviceid2; + + $hash->{TRX_SECURITY_deviceid2} = $deviceid2; + $hash->{TRX_SECURITY_devicelog2} = $devicelog2; + #$hash->{TRX_SECURITY_CODE2} = $deviceid2; + $modules{TRX_SECURITY}{defptr2}{$device_name2} = $hash; + } + + AssignIoPort($hash); + + return undef; +} + +##################################### +sub +TRX_SECURITY_Undef($$) +{ + my ($hash, $name) = @_; + delete($modules{TRX_SECURITY}{defptr}{$name}); + return undef; +} + + + +##################################### +sub TRX_SECURITY_parse_X10Sec($$) { + my ($hash, $bytes) = @_; + + my $error; + + my $subtype = $bytes->[1]; + + my $device; + if ($subtype >= 3) { + $device = sprintf '%02x%02x%02x', $bytes->[3], $bytes->[4], $bytes->[5]; + } else { + # that's how we do it on 43_RFXX10REC.pm + $device = sprintf '%02x%02x', $bytes->[5], $bytes->[3]; + } + + my %security_devtype = + ( # HEXSTRING => + 0x00 => [ "DS10A", "Window" ], # X10 security door/window sensor + 0x01 => [ "MS10A", "motion" ], # X10 security motion sensor + 0x02 => [ "KR18", "key" ], # X10 security remote (no alive packets) + 0x03 => [ "KD101", "smoke" ], # KD101 (no alive packets) + 0x04 => [ "VISONIC_WINDOW", "window" ], # Visonic PowerCode door/window sensor – primary contact (with alive packets) + 0x05 => [ "VISONIC_MOTION", "motion" ], # Visonic PowerCode motion sensor (with alive packets) + 0x06 => [ "VISONIC_REMOTE", "key" ], # Visonic CodeSecure (no alive packets) + 0x07 => [ "VISONIC_WINDOW_AUX", "window" ], # Visonic PowerCode door/window sensor – auxiliary contact (no alive packets) + ); + + my $dev_type; + my $dev_reading; + if (exists $security_devtype{$subtype}) { + my $rec = $security_devtype{$subtype}; + if (ref $rec) { + ($dev_type, $dev_reading ) = @$rec; + } else { + $error = "TRX_SECURITY: x10_devtype wrong for subtype=$subtype"; + Log3 $hash, 1, "TRX_SECURITY_parse_X10Sec() ".$error; + return ""; + } + } else { + $error = "TRX_SECURITY: error undefined subtype=$subtype"; + Log3 $hash, 1, "TRX_SECURITY_parse_X10Sec() ".$error; + return ""; + } + + #-------------- + my $device_name = "TRX".$DOT.$dev_type.$DOT.$device; + Log3 $hash, 5, "TRX_SECURITY_parse_X10Sec() device_name=$device_name"; + + my $firstdevice = 1; + my $def = $modules{TRX_SECURITY}{defptr}{$device_name}; + if(!$def) { + $firstdevice = 0; + $def = $modules{TRX_SECURITY}{defptr2}{$device_name}; + if (!$def) { + Log3 $hash, 1, "TRX_SECURITY_parse_X10Sec() UNDEFINED $device_name TRX_SECURITY $dev_type $device $dev_reading"; + Log3 $hash, 3, "TRX_SECURITY_parse_X10Sec() Unknown device $device_name, please define it"; + return "UNDEFINED $device_name TRX_SECURITY $dev_type $device $dev_reading"; + } + } + + # Use $def->{NAME}, because the device may be renamed: + my $name = $def->{NAME}; + return "" if(IsIgnored($name)); + + my $data = $bytes->[6]; + + my $hexdata = sprintf '%02x', $data; + + my %x10_security = + ( + 0x00 => ['X10Sec', 'normal', 'min_delay', '', ''], + 0x01 => ['X10Sec', 'normal', 'max_delay', '', ''], + + 0x02 => ['X10Sec', 'alert', 'min_delay', '', ''], + 0x03 => ['X10Sec', 'alert', 'max_delay', '', ''], + + 0x04 => ['X10Sec', 'alert', '', '', ''], + 0x05 => ['X10Sec', 'normal', '', '', ''], + + 0x06 => ['X10Sec', 'alert', '', '', ''], + 0x07 => ['X10Sec', 'Security-EndPanic', '', '', ''], + + 0x08 => ['X10Sec', 'IR', '', '', ''], + + 0x09 => ['X10Sec', 'Security-Arm_Away', 'min_delay', '', ''], # kr18 + 0x0a => ['X10Sec', 'Security-Arm_Away', 'max_delay', '', ''], # kr18 + 0x0b => ['X10Sec', 'Security-Arm_Home', 'min_delay', '', ''], # kr18 + 0x0c => ['X10Sec', 'Security-Arm_Home', 'max_delay', '', ''], # kr18 + 0x0d => ['X10Sec', 'Security-Disarm', 'min_delay', '', ''], # kr18 + + 0x10 => ['X10Sec', 'ButtonA-off', '', '', ''], # kr18 + 0x11 => ['X10Sec', 'ButtonA-on', '', '', ''], # kr18 + 0x12 => ['X10Sec', 'ButtonB-off', '', '', ''], # kr18 + 0x13 => ['X10Sec', 'ButtonB-on', '', '', ''], # kr18 + + 0x14 => ['X10Sec', 'dark', '', '', ''], + 0x15 => ['X10Sec', 'light', '', '', ''], + 0x16 => ['X10Sec', 'normal', '', 'batt_low', ''], + + 0x17 => ['X10Sec', 'pair KD101', '', '', ''], + + 0x80 => ['X10Sec', 'normal', 'max_delay', '', 'tamper'], + 0x81 => ['X10Sec', 'normal', 'min_delay', '', 'tamper'], + 0x82 => ['X10Sec', 'alert', 'max_delay', '', 'tamper'], + 0x83 => ['X10Sec', 'alert', 'min_delay', '', 'tamper'], + 0x84 => ['X10Sec', 'alert', '', '', 'tamper'], + 0x85 => ['X10Sec', 'normal', '', '', 'tamper'], + + ); + + my $command = ""; + my $type = ""; + my $delay = ""; + my $battery = ""; + my $rssi = ""; + my $option = ""; + my @res; + if (exists $x10_security{$data}) { + my $rec = $x10_security{$data}; + if (ref $rec) { + ($type, $command, $delay, $battery, $option) = @$rec; + } else { + $command = $rec; + } + } else { + Log3 $name, 1, "TRX_SECURITY_parse_X10Sec() undefined command cmd=$data device-nr=$device, hex=$hexdata"; + return ""; + } + + my $battery_level = $bytes->[7] & 0x0f; + if (($battery eq "") && ($dev_type ne "KD101")) { + if ($battery_level == 0x9) { $battery = 'batt_ok'} + elsif ($battery_level == 0x0) { $battery = 'batt_low'} + else { + Log3 $name, 1,"TRX_SECURITY_parse_X10Sec() unknown battery_level=$battery_level"; + } + } + + if ($trx_rssi == 1) { + $rssi = sprintf("%d", ($bytes->[7] & 0xf0) >> 4); + Log3 $name, 5, "TRX_SECURITY_parse_X10Sec() $name devn=$device_name rssi=$rssi"; + } + + my $current = ""; + + my $n = 0; + my $tm = TimeNow(); + my $val = ""; + + my $device_type = uc($def->{TRX_SECURITY_type}); + + Log3 $name, 5, "TRX_SECURITY_parse_X10Sec() $name devn=$device_name first=$firstdevice subtype=$subtype device_type=$device_type command=$command, delay=$delay, batt=$battery cmd=$hexdata"; + + my $sensor = ""; + + if ($device_type eq "SD90") { + $sensor = $firstdevice == 1 ? $def->{TRX_SECURITY_devicelog} : $def->{TRX_SECURITY_devicelog2}; + } else { + $sensor = $def->{TRX_SECURITY_devicelog}; + } + + $current = $command; + if (($device_type eq "DS10A") || ($device_type eq "VISONIC_WINDOW") || ($device_type eq "VISONIC_WINDOW_AUX")) { + $current = "Error"; + $current = "Open" if ($command eq "alert"); + $current = "Closed" if ($command eq "normal"); + } elsif ($device_type eq "WD18" || $device_type eq "GD18") { + $current = "Error"; + $current = "normal" if ($command eq "alert"); + $current = "alert" if ($command eq "normal"); + $delay = ""; + $option = ""; + } + + readingsBeginUpdate($def); + + if (($device_type ne "KR18") && ($device_type ne "VISONIC_REMOTE")) { + if ($firstdevice == 1) { + $val .= $current; + } + if ($sensor ne "none") { readingsBulkUpdate($def, $sensor, $current); } + + # KD101 does not show normal, so statechange does not make sense + if (($def->{STATE} ne $val) && ($device_type ne "KD101")) { + $sensor = "statechange"; + readingsBulkUpdate($def, $sensor, $current); + } + } else { + # kr18 remote control or VISONIC_REMOTE + $current = $command; + + #$sensor = $def->{TRX_SECURITY_devicelog}; + #$val = $current; + #readingsBulkUpdate($def, $sensor, $current); + + $current = "Security-Panic" if ($command eq "alert"); + + my @cmd_split = split(/-/, $current); + $sensor = $cmd_split[0]; + $current = $cmd_split[1]; + readingsBulkUpdate($def, $sensor, $current); + + $val .= $current; + } + + if ($battery ne "") { + $sensor = "battery"; + $current = "Error"; + $current = "ok" if ($battery eq "batt_ok"); + $current = "low" if ($battery eq "batt_low"); + readingsBulkUpdate($def, $sensor, $current); + } + + if ($rssi ne "") { + $sensor = "rssi"; + readingsBulkUpdate($def, $sensor, $rssi); + } + + + if ($delay ne '') { + $sensor = "delay"; + $current = "Error"; + $current = "min" if ($delay eq "min_delay"); + $current = "max" if ($delay eq "max_delay"); + readingsBulkUpdate($def, $sensor, $current); + } + if ($option ne '') { + $val .= " ".$option; + } + + if (($firstdevice == 1) && $val) { + $def->{STATE} = $val; + readingsBulkUpdate($def, "state", $val); + } + + readingsEndUpdate($def, 1); + + return $name; +} + + +sub +TRX_SECURITY_Parse($$) +{ + my ($iohash, $hexline) = @_; + + $trx_rssi = 0; + if (defined($attr{$iohash->{NAME}}{rssi})) { + $trx_rssi = $attr{$iohash->{NAME}}{rssi}; + Log3 $iohash, 5,"TRX_SECURITY_Parse() attr rssi = $trx_rssi"; + } + + my $time = time(); + # convert to binary + my $msg = pack('H*', $hexline); + if ($time_old ==0) { + Log3 $iohash, 5, "TRX_SECURITY_Parse() decoding delay=0 hex=$hexline"; + } else { + my $time_diff = $time - $time_old ; + Log3 $iohash, 5, "TRX_SECURITY_Parse() decoding delay=$time_diff hex=$hexline"; + } + $time_old = $time; + + # convert string to array of bytes. Skip length byte + my @rfxcom_data_array = (); + foreach (split(//, substr($msg,1))) { + push (@rfxcom_data_array, ord($_) ); + } + + my $num_bytes = ord($msg); + + if ($num_bytes < 3) { + return ""; + } + + my $type = $rfxcom_data_array[0]; + + Log3 $iohash, 5, "TRX_SECURITY_Parse() X10Sec num_bytes=$num_bytes hex=$hexline type=$type"; + my $res = ""; + if ($type == 0x20) { + Log3 $iohash, 5, "TRX_SECURITY_Parse() X10Sec num_bytes=$num_bytes hex=$hexline"; + $res = TRX_SECURITY_parse_X10Sec($iohash, \@rfxcom_data_array); + Log3 $iohash, 1, "TRX_SECURITY_Parse() unsupported hex=$hexline" if ($res eq ""); + return $res; + } else { + Log3 $iohash, 1, "TRX_SECURITY_Parse() not implemented num_bytes=$num_bytes hex=$hexline"; + } + + return ""; +} + +1; + +=pod +=begin html + + +

TRX_SECURITY

+
    + The TRX_SECURITY module interprets X10, KD101 and Visonic security sensors received by a RFXCOM RFXtrx433 RF receiver. You need to define an RFXtrx433 receiver first. See TRX. + +

    + + + Define +
      + define <name> TRX_SECURITY <type> <deviceid> <devicelog> [<deviceid2> <devicelog2>]
      +
      + <type> +
        + specifies one of the following security devices: +
          +
        • DS10A (X10 security ds10a Door/Window Sensor or compatible devices. This device type reports the status of the switch [Open/Closed], status of the delay switch [min|max]], and battery status [ok|low].)
        • +
        • SD18 or COD18(X10 security sd18 smoke Sensor and COD18 CO Sensor). These device types report the status of the sensor [alert/normal] and battery status [ok|low])
        • +
        • MS10A (X10 security ms10a motion sensor. This device type reports the status of motion sensor [normal|alert] and battery status [ok|low].))
        • +
        • SD90 (Marmitek sd90 smoke detector. This device type reports the status of the smoke detector [normal|alert] and battery status [ok|low].)
        • +
        • KR18 (X10 security remote control. Report the Reading "Security" with values [Arm|Disarm], "ButtonA" and "ButtonB" with values [on|off] )
        • +
        • KD101 (KD101 smoke sensor. Report the Reading "smoke" with values [normal|alert])
        • +
        • VISONIC_WINDOW (VISONIC security Door/Window Sensor or compatible devices. This device type reports the status of the switch [Open/Closed] and battery status [ok|low].)
        • +
        • VISONIC_MOTION (VISONIC security motion sensor. This device type reports the status of motion sensor [normal|alert] and battery status [ok|low].))
        • +
        +
      +
      + <deviceid> +
        + specifies the first device id of the device. X10 security (DS10A, MS10A) and SD90 have a a 16 bit device id which has to be written as a hex-string (example "5a54"). All other devices have a 24 bit device id. +
      +
      + <devicelog> +
        + is the name of the Reading used to report. Suggested: "Window" or "Door" for ds10a, "motion" for motion sensors, "smoke" for sd90, sd18 and cod18. If you use "none" then no additional Reading is reported. Just the state is used to report the change. +
      +
      + <deviceid2> +
        + is optional and specifies the second device id of the device if it exists. For example sd90 smoke sensors can be configured to report two device ids. +
      +
      + <devicelog2> +
        + is optional for the name used for the Reading of <deviceid2>. If you use "none" then no additional Reading is reported. Just the state is used to report the change. +
      +
      + Example:
      + define livingroom_window TRX_SECURITY ds10a 72cd Window +
      + define motion_sensor1 TRX_SECURITY ms10a 55c6 motion +
      + define smoke_sensor1 TRX_SECURITY sd90 54d3 Smoke 54d3 Smoketest +
      +
    +
    + + + Set +
      + set <name> <value> +

      + where value is one of:
      +
      +    alert              # only for KD101
      +    pair               # only for KD101
      +    
      + Example:
      + set TRX_KD101_a5ca00 alert +
      +

    + + + Get
      N/A

    + + + Attributes + +
+ +=end html +=cut diff --git a/46_TRX_WEATHER.pm b/46_TRX_WEATHER.pm new file mode 100644 index 0000000..dafd53a --- /dev/null +++ b/46_TRX_WEATHER.pm @@ -0,0 +1,1787 @@ +# $Id: 46_TRX_WEATHER.pm 5719 2014-05-01 19:18:38Z wherzig $ +############################################################################## +# +# 46_TRX_WEATHER.pm +# FHEM module to decode weather sensor messages for RFXtrx +# +# The following devices are implemented to be received: +# +# thermostat sensors (THERMOSTAT): +# * "TH10" is XDOM TH10 +# * "TLX7506" is Digimax TLX7506 +# temperature sensors (TEMP): +# * "THR128" is THR128/138, THC138 +# * "THGR132N" is THC238/268,THN132,THWR288,THRN122,THN122,AW129/131 +# * "THWR800" is THWR800 +# * "RTHN318" is RTHN318 +# * "TX3_T" is LaCrosse TX3, TX4, TX17 +# * "TS15C" is TS15C +# * "VIKING_02811" is Viking 02811 +# * "WS2300" is La Crosse WS2300 +# * "RUBICSON" is RUBiCSON +# * "TFA_303133" is TFA 30.3133 +# +# humidity sensors (HYDRO): +# * "TX3" is LaCrosse TX3 +# * "WS2300" is # LaCrosse WS2300 +# +# temperature/humidity sensors (TEMPHYDRO): +# * "THGR228N" is THGN122/123, THGN132, THGR122/228/238/268 +# * "THGR810" is THGR810 +# * "RTGR328" is RTGR328 +# * "THGR328" is THGR328 +# * "WTGR800_T" is WTGR800 +# * "THGR918" is THGR918, THGRN228, THGN500 +# * "TFATS34C" is TFA TS34C, Cresta +# * "WT450H" is WT260,WT260H,WT440H,WT450,WT450H +# * "VIKING_02038" is Viking 02035,02038 (02035 has no humidity) +# * "RUBICSON" is Rubicson +# * "EW109" is EW109 +# +# temperature/humidity/pressure sensors (TEMPHYDROBARO): +# * "BTHR918" is BTHR918 +# * "BTHR918N" is BTHR918N, BTHR968 +# +# rain gauge sensors (RAIN): +# * "RGR918" is RGR126/682/918 +# * "PCR800" is PCR800 +# * "TFA_RAIN" is TFA +# * "RG700" is UPM RG700 +# * "WS2300_RAIN" is WS2300 +# * "TX5_RAIN" is La Crosse TX5 +# +# wind sensors (WIND): +# * "WTGR800_A" is WTGR800 +# * "WGR800_A" is WGR800 +# * "WGR918" is STR918, WGR918 +# * "TFA_WIND" is TFA +# * "WDS500" is UPM WDS500u +# * "WS2300_WIND" is WS2300 +# +# UV Sensors: +# "UVN128" is Oregon UVN128, UV138 +# "UVN800" is Oregon UVN800 +# "TFA_UV" is TFA_UV-Sensor +# +# Date/Time Sensors: +# * "RTGR328_DATE" is RTGR328N +# +# Energy Sensors: +# * "CM160" is OWL CM119, CM160 +# * "CM180" is OWL CM180 +# * "REVOLT" is Revolt +# +# Weighing scales (WEIGHT): +# * "BWR101" is Oregon Scientific BWR101 +# * "GR101" is Oregon Scientific GR101 +# +# Copyright (C) 2012/2013 Willi Herzig +# +# 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. +# +# Due to RFXCOM SDK requirements this code may only be used with a RFXCOM device. +# +# Some code was derived and modified from xpl-perl +# from the following two files: +# xpl-perl/lib/xPL/Utils.pm: +# xpl-perl/lib/xPL/RF/Oregon.pm: +# +#SEE ALSO +# Project website: http://www.xpl-perl.org.uk/ +# AUTHOR: Mark Hindess, soft-xpl-perl@temporalanomaly.com +# +# Copyright (C) 2007, 2009 by Mark Hindess +# +# This library is free software; you can redistribute it and/or modify +# it under the same terms as Perl itself, either Perl version 5.8.7 or, +# at your option, any later version of Perl 5 you may have available. +################################## +# +# values for "set global verbose" +# 4: log unknown prologtocols +# 5: log decoding hexlines for debugging +# +package main; + +use strict; +use warnings; + +# Hex-Debugging into READING hexline? YES = 1, NO = 0 +my $TRX_HEX_debug = 0; + +my $time_old = 0; +my $trx_rssi; + +sub +TRX_WEATHER_Initialize($) +{ + my ($hash) = @_; + + $hash->{Match} = "^..(40|50|51|52|54|55|56|57|58|5a|5c|5d).*"; + $hash->{DefFn} = "TRX_WEATHER_Define"; + $hash->{UndefFn} = "TRX_WEATHER_Undef"; + $hash->{ParseFn} = "TRX_WEATHER_Parse"; + $hash->{AttrList} = "IODev ignore:1,0 do_not_notify:1,0 ". + $readingFnAttributes; + +} + +##################################### +sub +TRX_WEATHER_Define($$) +{ + my ($hash, $def) = @_; + my @a = split("[ \t][ \t]*", $def); + + #my $a = int(@a); + #print "a0 = $a[0]"; + + return "wrong syntax: define TRX_WEATHER code" if (int(@a) < 3); + + my $name = $a[0]; + my $code = $a[2]; + + if (($code =~ /^CM160/) || ($code =~ /^CM180/) || ($code =~ /^REVOLT/)) { + return "wrong syntax: define TRX_WEATHER code [scale_current scale_total add_total]" if (int(@a) != 3 && int(@a) != 6); + $hash->{scale_current} = ((int(@a) == 6) ? $a[3] : 1); + $hash->{scale_total} = ((int(@a) == 6) ? $a[4] : 1.0); + $hash->{add_total} = ((int(@a) == 6) ? $a[5] : 0.0); + } else { + return "wrong syntax: define TRX_WEATHER code" if(int(@a) > 3); + } + + + $hash->{CODE} = $code; + $modules{TRX_WEATHER}{defptr}{$code} = $hash; + AssignIoPort($hash); + + return undef; +} + +##################################### +sub +TRX_WEATHER_Undef($$) +{ + my ($hash, $name) = @_; + delete($modules{TRX_WEATHER}{defptr}{$name}); + return undef; +} + +# -------------------------------------------- +# sensor types + +my %types = + ( + # THERMOSTAT + 0x4009 => { part => 'THERMOSTAT', method => \&TRX_WEATHER_common_therm, }, + # TEMP + 0x5008 => { part => 'TEMP', method => \&TRX_WEATHER_common_temp, }, + # HYDRO + 0x5108 => { part => 'HYDRO', method => \&TRX_WEATHER_common_hydro, }, + # TEMP HYDRO + 0x520a => { part => 'TEMPHYDRO', method => \&TRX_WEATHER_common_temphydro, }, + # TEMP HYDRO BARO + 0x540d => { part => 'TEMPHYDROBARO', method => \&TRX_WEATHER_common_temphydrobaro, }, + # RAIN + 0x550b => { part => 'RAIN', method => \&TRX_WEATHER_common_rain, }, + # WIND + 0x5610 => { part => 'WIND', method => \&TRX_WEATHER_common_anemometer, }, + # UV + 0x5709 => { part => 'UV', method => \&TRX_WEATHER_common_uv, }, + # Date/Time sensors + 0x580D => { part => 'DATE', method => \&TRX_WEATHER_common_datetime, }, + # Energy usage sensors + 0x5A11 => { part => 'ENERGY', method => \&TRX_WEATHER_common_energy, }, + 0x5B13 => { part => 'ENERGY2', method => \&TRX_WEATHER_common_energy2, }, + 0x5c0f => { part => 'ENERGY3', method => \&TRX_WEATHER_common_energy3, }, + + # WEIGHT + 0x5D08 => { part => 'WEIGHT', method => \&TRX_WEATHER_common_weight, }, + ); + +# -------------------------------------------- + +#my $DOT = q{.}; +# Important: change it to _, because FHEM uses regexp +my $DOT = q{_}; + +my @TRX_WEATHER_winddir_name=("N","NNE","NE","ENE","E","ESE","SE","SSE","S","SSW","SW","WSW","W","WNW","NW","NNW"); + +# -------------------------------------------- +# The following functions are changed: +# - some parameter like "parent" and others are removed +# - @res array return the values directly (no usage of xPL::Message) + +sub TRX_WEATHER_temperature { + my ($bytes, $dev, $res, $off) = @_; + + my $temp = + ( + (($bytes->[$off] & 0x80) ? -1 : 1) * + (($bytes->[$off] & 0x7f)*256 + $bytes->[$off+1]) + )/10; + + push @$res, { + device => $dev, + type => 'temp', + current => $temp, + units => 'Grad Celsius' + } + +} + +sub TRX_WEATHER_chill_temperature { + my ($bytes, $dev, $res, $off) = @_; + + my $temp = + ( + (($bytes->[$off] & 0x80) ? -1 : 1) * + (($bytes->[$off] & 0x7f)*256 + $bytes->[$off+1]) + )/10; + + push @$res, { + device => $dev, + type => 'chilltemp', + current => $temp, + units => 'Grad Celsius' + } + +} + +sub TRX_WEATHER_humidity { + my ($bytes, $dev, $res, $off) = @_; + my $hum = $bytes->[$off]; + my $hum_str = ['dry', 'comfortable', 'normal', 'wet']->[$bytes->[$off+1]]; + push @$res, { + device => $dev, + type => 'humidity', + current => $hum, + string => $hum_str, + units => '%' + } +} + +sub TRX_WEATHER_pressure { + my ($bytes, $dev, $res, $off) = @_; + + #my $offset = 795 unless ($offset); + my $hpa = ($bytes->[$off])*256 + $bytes->[$off+1]; + my $forecast = { 0x00 => 'noforecast', + 0x01 => 'sunny', + 0x02 => 'partly', + 0x03 => 'cloudy', + 0x04 => 'rain', + }->{$bytes->[$off+2]} || 'unknown'; + push @$res, { + device => $dev, + type => 'pressure', + current => $hpa, + units => 'hPa', + forecast => $forecast, + }; +} + +sub TRX_WEATHER_simple_battery { + my ($bytes, $dev, $res, $off) = @_; + + my $battery; + + my $battery_level = $bytes->[$off] & 0x0f; + if ($battery_level == 0x9) { $battery = 'ok'} + elsif ($battery_level == 0x0) { $battery = 'low'} + else { + $battery = sprintf("unknown-%02x",$battery_level); + } + + push @$res, { + device => $dev, + type => 'battery', + current => $battery, + }; + + my $rssi = ($bytes->[$off] & 0xf0) >> 4; + + if ($trx_rssi == 1) { + push @$res, { + device => $dev, + type => 'rssi', + current => sprintf("%d",$rssi), + }; + } + +} + +sub TRX_WEATHER_battery { + my ($bytes, $dev, $res, $off) = @_; + + my $battery; + + my $battery_level = ($bytes->[$off] & 0x0f) + 1; + + if ($battery_level > 5) { + $battery = sprintf("ok %d0%%",$battery_level); + } else { + $battery = sprintf("low %d0%%",$battery_level); + } + + push @$res, { + device => $dev, + type => 'battery', + current => $battery, + }; + + my $rssi = ($bytes->[$off] & 0xf0) >> 4; + + if ($trx_rssi == 1) { + push @$res, { + device => $dev, + type => 'rssi', + current => sprintf("%d",$rssi), + }; + } + +} + + +# Test if to use longid for device type +sub TRX_WEATHER_use_longid { + my ($longids,$dev_type) = @_; + + return 0 if ($longids eq ""); + return 0 if ($longids eq "0"); + + return 1 if ($longids eq "1"); + return 1 if ($longids eq "ALL"); + + return 1 if(",$longids," =~ m/,$dev_type,/); + + return 0; +} + + +# ------------------------------------------------------------ +# +sub TRX_WEATHER_common_anemometer { + my $type = shift; + my $longids = shift; + my $bytes = shift; + + my $subtype = sprintf "%02x", $bytes->[1]; + my $dev_type; + + my %devname = + ( # HEXSTRING => "NAME" + 0x01 => "WTGR800_A", + 0x02 => "WGR800", + 0x03 => "WGR918", + 0x04 => "TFA_WIND", + 0x05 => "WDS500", # UPM WDS500 + 0x06 => "WS2300_WIND", # WS2300 + ); + + if (exists $devname{$bytes->[1]}) { + $dev_type = $devname{$bytes->[1]}; + } else { + Log3 undef, 3, "TRX_WEATHER: common_anemometer error undefined subtype=$subtype"; + my @res = (); + return @res; + } + + my $dev_str = $dev_type; + if (TRX_WEATHER_use_longid($longids,$dev_type)) { + $dev_str .= $DOT.sprintf("%02x", $bytes->[3]); + } + if ($bytes->[4] > 0) { + $dev_str .= $DOT.sprintf("%d", $bytes->[4]); + } + + my @res = (); + + # hexline debugging + if ($TRX_HEX_debug) { + my $hexline = ""; for (my $i=0;$i<@$bytes;$i++) { $hexline .= sprintf("%02x",$bytes->[$i]);} + push @res, { device => $dev_str, type => 'hexline', current => $hexline, units => 'hex', }; + } + + my $dir = $bytes->[5]*256 + $bytes->[6]; + my $dirname = $TRX_WEATHER_winddir_name[int((($dir + 11.25) % 360) / 22.5)]; + + my $avspeed = ($bytes->[7]*256 + $bytes->[8]) / 10; + my $speed = ($bytes->[9]*256 + $bytes->[10]) / 10; + + if ($dev_type eq "TFA_WIND") { + TRX_WEATHER_temperature($bytes, $dev_str, \@res, 11); + TRX_WEATHER_chill_temperature($bytes, $dev_str, \@res, 13); + } + + push @res, { + device => $dev_str, + type => 'speed', + current => $speed, + average => $avspeed, + units => 'mps', + } , { + device => $dev_str, + type => 'direction', + current => $dir, + string => $dirname, + units => 'degrees', + }; + + TRX_WEATHER_battery($bytes, $dev_str, \@res, 15); + + return @res; +} + + +# ----------------------------- +sub TRX_WEATHER_common_therm { + my $type = shift; + my $longids = shift; + my $bytes = shift; + + my $subtype = sprintf "%02x", $bytes->[1]; + my $dev_type; + + my %devname = + ( # HEXSTRING => "NAME" + 0x00 => "TLX7506", + 0x01 => "TH10", + ); + + if (exists $devname{$bytes->[1]}) { + $dev_type = $devname{$bytes->[1]}; + } else { + Log3 undef, 3, "TRX_WEATHER: common_therm error undefined subtype=$subtype"; + my @res = (); + return @res; + } + + #my $seqnbr = sprintf "%02x", $bytes->[2]; + + my $dev_str = $dev_type; + if (TRX_WEATHER_use_longid($longids,$dev_type)) { + $dev_str .= $DOT.sprintf("%02x", $bytes->[3]); + } + if ($bytes->[4] > 0) { + $dev_str .= $DOT.sprintf("%d", $bytes->[4]); + } + + my @res = (); + + # hexline debugging + if ($TRX_HEX_debug) { + my $hexline = ""; for (my $i=0;$i<@$bytes;$i++) { $hexline .= sprintf("%02x",$bytes->[$i]);} + push @res, { device => $dev_str, type => 'hexline', current => $hexline, units => 'hex', }; + } + + #TRX_WEATHER_temperature($bytes, $dev_str, \@res, 5); + #TRX_WEATHER_simple_battery($bytes, $dev_str, \@res, 7); + + my $temp = ($bytes->[5]); + if ($temp) { + push @res, { + device => $dev_str, + type => 'temp', + current => $temp, + units => 'Grad Celsius' + } + } + + my $setpoint =($bytes->[6]); + if ($setpoint) { + push @res, { + device => $dev_str, + type => 'setpoint', + current => $setpoint, + units => 'Grad Celsius' + } + } + + my $demand; + my $t_status = ($bytes->[7] & 0x03); + if ($t_status == 0) { $demand = 'n/a'} + elsif ($t_status == 1) { $demand = 'on'} + elsif ($t_status == 2) { $demand = 'off'} + elsif ($t_status == 3) { $demand = 'initializing'} + else { + $demand = sprintf("unknown-%02x",$t_status); + } + +# Log3 undef, 3, "TRX_WEATHER: demand = $bytes->[7] $t_status $demand"; + push @res, { + device => $dev_str, + type => 'demand', + current => sprintf("%s",$demand), + }; + + #my $t_mode = ($bytes->[7] & 0x01); + + my $rssi = ($bytes->[8] & 0xf0) >> 4; + + if ($trx_rssi == 1) { + push @res, { + device => $dev_str, + type => 'rssi', + current => sprintf("%d",$rssi), + }; + } + return @res; +} + +# ----------------------------- +sub TRX_WEATHER_common_temp { + my $type = shift; + my $longids = shift; + my $bytes = shift; + + my $subtype = sprintf "%02x", $bytes->[1]; + my $dev_type; + + my %devname = + ( # HEXSTRING => "NAME" + 0x01 => "THR128", + 0x02 => "THGR132N", # was THGR228N, + 0x03 => "THWR800", + 0x04 => "RTHN318", + 0x05 => "TX3", # LaCrosse TX3 + 0x06 => "TS15C", + 0x07 => "VIKING_02811", # Viking 02811 + 0x08 => "WS2300", # La Crosse WS2300 + 0x09 => "RUBICSON", # RUBiCSON + 0x0a => "TFA_303133", # TFA 30.3133 + 0x0b => "RES_TEMP1", # Reserved for future use + 0x0c => "RES_TEMP2", # Reserved for future use + 0x0d => "RES_TEMP3", # Reserved for future use + 0x0e => "RES_TEMP4", # Reserved for future use + 0x0f => "RES_TEMP5", # Reserved for future use + 0x10 => "RES_TEMP6", # Reserved for future use + 0x11 => "RES_TEMP7", # Reserved for future use + 0x12 => "RES_TEMP8", # Reserved for future use + ); + + if (exists $devname{$bytes->[1]}) { + $dev_type = $devname{$bytes->[1]}; + } else { + Log3 undef, 3, "TRX_WEATHER: common_temp error undefined subtype=$subtype"; + my @res = (); + return @res; + } + + #my $seqnbr = sprintf "%02x", $bytes->[2]; + + my $dev_str = $dev_type; + if (TRX_WEATHER_use_longid($longids,$dev_type)) { + $dev_str .= $DOT.sprintf("%02x", $bytes->[3]); + } + if ($bytes->[4] > 0) { + $dev_str .= $DOT.sprintf("%d", $bytes->[4]); + } + + my @res = (); + + # hexline debugging + if ($TRX_HEX_debug) { + my $hexline = ""; for (my $i=0;$i<@$bytes;$i++) { $hexline .= sprintf("%02x",$bytes->[$i]);} + push @res, { device => $dev_str, type => 'hexline', current => $hexline, units => 'hex', }; + } + + TRX_WEATHER_temperature($bytes, $dev_str, \@res, 5); + TRX_WEATHER_simple_battery($bytes, $dev_str, \@res, 7); + return @res; +} + +# ----------------------------- +sub TRX_WEATHER_common_hydro { + my $type = shift; + my $longids = shift; + my $bytes = shift; + + my $subtype = sprintf "%02x", $bytes->[1]; + my $dev_type; + + my %devname = + ( # HEXSTRING => "NAME" + 0x01 => "TX3", # LaCrosse TX3 + 0x02 => "WS2300", # LaCrosse WS2300 Humidity + ); + + if (exists $devname{$bytes->[1]}) { + $dev_type = $devname{$bytes->[1]}; + } else { + Log3 undef, 3, "TRX_WEATHER: common_hydro error undefined subtype=$subtype"; + my @res = (); + return @res; + } + + my $dev_str = $dev_type; + if (TRX_WEATHER_use_longid($longids,$dev_type)) { + $dev_str .= $DOT.sprintf("%02x", $bytes->[3]); + } + if ($bytes->[4] > 0) { + $dev_str .= $DOT.sprintf("%d", $bytes->[4]); + } + + my @res = (); + + # hexline debugging + if ($TRX_HEX_debug) { + my $hexline = ""; for (my $i=0;$i<@$bytes;$i++) { $hexline .= sprintf("%02x",$bytes->[$i]);} + push @res, { device => $dev_str, type => 'hexline', current => $hexline, units => 'hex', }; + } + + TRX_WEATHER_humidity($bytes, $dev_str, \@res, 5); + TRX_WEATHER_simple_battery($bytes, $dev_str, \@res, 7); + return @res; +} + +# ----------------------------- +sub TRX_WEATHER_common_temphydro { + my $type = shift; + my $longids = shift; + my $bytes = shift; + + my $subtype = sprintf "%02x", $bytes->[1]; + my $dev_type; + + my %devname = + ( # HEXSTRING => "NAME" + 0x01 => "THGR228N", # THGN122/123, THGN132, THGR122/228/238/268 + 0x02 => "THGR810", + 0x03 => "RTGR328", + 0x04 => "THGR328", + 0x05 => "WTGR800_T", + 0x06 => "THGR918", + 0x07 => "TFATS34C", + 0x08 => "WT450H", # WT260,WT260H,WT440H,WT450,WT450H + 0x09 => "VIKING_02038", # Viking 02035,02038 (02035 has no humidity) + 0x0a => "RUBICSON", # Rubicson + 0x0b => "EW109", # EW109 + 0x0c => "RES_TEMPHUM1", # Reserved for future use + 0x0d => "RES_TEMPHUM2", # Reserved for future use + 0x0e => "RES_TEMPHUM3", # Reserved for future use + 0x0f => "RES_TEMPHUM4", # Reserved for future use + 0x10 => "RES_TEMPHUM5", # Reserved for future use + 0x11 => "RES_TEMPHUM6", # Reserved for future use + 0x12 => "RES_TEMPHUM7", # Reserved for future use + 0x13 => "RES_TEMPHUM8", # Reserved for future use + ); + + if (exists $devname{$bytes->[1]}) { + $dev_type = $devname{$bytes->[1]}; + } else { + Log3 undef, 3, "TRX_WEATHER: common_temphydro error undefined subtype=$subtype"; + my @res = (); + return @res; + } + + my $dev_str = $dev_type; + if (TRX_WEATHER_use_longid($longids,$dev_type)) { + $dev_str .= $DOT.sprintf("%02x", $bytes->[3]); + } elsif ($dev_type eq "TFATS34C") { + #Log3 undef, 1,"TRX_WEATHER: TFA"; + if ($bytes->[3] > 0x20 && $bytes->[3] <= 0x3F) { + #Log3 undef, 1,"TRX_WEATHER: TFA 1"; + $dev_str .= $DOT."1"; + } elsif ($bytes->[3] >= 0x40 && $bytes->[3] <= 0x5F) { + #Log3 undef, 1,"TRX_WEATHER: TFA 2"; + $dev_str .= $DOT."2"; + } elsif ($bytes->[3] >= 0x60 && $bytes->[3] <= 0x7F) { + #Log3 undef, 1,"TRX_WEATHER: TFA 3"; + $dev_str .= $DOT."3"; + } elsif ($bytes->[3] >= 0xA0 && $bytes->[3] <= 0xBF) { + #Log3 undef, 1,"TRX_WEATHER: TFA 4"; + $dev_str .= $DOT."4"; + } elsif ($bytes->[3] >= 0xC0 && $bytes->[3] <= 0xDF) { + #Log3 undef, 1,"TRX_WEATHER: TFA 5"; + $dev_str .= $DOT."5"; + } else { + #Log3 undef, 1,"TRX_WEATHER: TFA 9"; + $dev_str .= $DOT."9"; + } + } + if ($dev_type ne "TFATS34C" && $bytes->[4] > 0) { + $dev_str .= $DOT.sprintf("%d", $bytes->[4]); + } + + my @res = (); + + # hexline debugging + if ($TRX_HEX_debug) { + my $hexline = ""; for (my $i=0;$i<@$bytes;$i++) { $hexline .= sprintf("%02x",$bytes->[$i]);} + push @res, { device => $dev_str, type => 'hexline', current => $hexline, units => 'hex', }; + } + + TRX_WEATHER_temperature($bytes, $dev_str, \@res, 5); + TRX_WEATHER_humidity($bytes, $dev_str, \@res, 7); + if ($dev_type eq "THGR918") { + TRX_WEATHER_battery($bytes, $dev_str, \@res, 9); + } else { + TRX_WEATHER_simple_battery($bytes, $dev_str, \@res, 9); + } + return @res; +} + +# ----------------------------- +sub TRX_WEATHER_common_temphydrobaro { + my $type = shift; + my $longids = shift; + my $bytes = shift; + + my $subtype = sprintf "%02x", $bytes->[1]; + my $dev_type; + + my %devname = + ( # HEXSTRING => "NAME" + 0x01 => "BTHR918", + 0x02 => "BTHR918N", + ); + + if (exists $devname{$bytes->[1]}) { + $dev_type = $devname{$bytes->[1]}; + } else { + Log3 undef, 3, "TRX_WEATHER: common_temphydrobaro error undefined subtype=$subtype"; + my @res = (); + return @res; + } + + my $dev_str = $dev_type; + if (TRX_WEATHER_use_longid($longids,$dev_type)) { + $dev_str .= $DOT.sprintf("%02x", $bytes->[3]); + } + if ($bytes->[4] > 0) { + $dev_str .= $DOT.sprintf("%d", $bytes->[4]); + } + + my @res = (); + + # hexline debugging + if ($TRX_HEX_debug) { + my $hexline = ""; for (my $i=0;$i<@$bytes;$i++) { $hexline .= sprintf("%02x",$bytes->[$i]);} + push @res, { device => $dev_str, type => 'hexline', current => $hexline, units => 'hex', }; + } + + TRX_WEATHER_temperature($bytes, $dev_str, \@res, 5); + TRX_WEATHER_humidity($bytes, $dev_str, \@res, 7); + TRX_WEATHER_pressure($bytes, $dev_str, \@res, 9); + TRX_WEATHER_simple_battery($bytes, $dev_str, \@res, 12); + return @res; +} + +# ----------------------------- +sub TRX_WEATHER_common_rain { + my $type = shift; + my $longids = shift; + my $bytes = shift; + + + my $subtype = sprintf "%02x", $bytes->[1]; + my $dev_type; + + my %devname = + ( # HEXSTRING => "NAME" + 0x01 => "RGR918", + 0x02 => "PCR800", + 0x03 => "TFA_RAIN", + 0x04 => "RG700", + 0x05 => "WS2300_RAIN", # WS2300 + 0x06 => "TX5_RAIN", # La Crosse TX5 + + ); + + if (exists $devname{$bytes->[1]}) { + $dev_type = $devname{$bytes->[1]}; + } else { + Log3 undef, 3, "TRX_WEATHER: common_rain error undefined subtype=$subtype"; + my @res = (); + return @res; + } + + my $dev_str = $dev_type; + if (TRX_WEATHER_use_longid($longids,$dev_type)) { + $dev_str .= $DOT.sprintf("%02x", $bytes->[3]); + } + if ($bytes->[4] > 0) { + $dev_str .= $DOT.sprintf("%d", $bytes->[4]); + } + + my @res = (); + + # hexline debugging + if ($TRX_HEX_debug) { + my $hexline = ""; for (my $i=0;$i<@$bytes;$i++) { $hexline .= sprintf("%02x",$bytes->[$i]);} + push @res, { device => $dev_str, type => 'hexline', current => $hexline, units => 'hex', }; + } + + my $rain = $bytes->[5]*256 + $bytes->[6]; + if ($dev_type eq "RGR918") { + push @res, { + device => $dev_str, + type => 'rain', + current => $rain, + units => 'mm/h', + }; + } elsif ($dev_type eq "PCR800") { + $rain = $rain / 100; + push @res, { + device => $dev_str, + type => 'rain', + current => $rain, + units => 'mm/h', + }; + } + + if ($dev_type ne "TX5_RAIN") { + my $train = ($bytes->[7]*256*256 + $bytes->[8]*256 + $bytes->[9])/10; # total rain + push @res, { + device => $dev_str, + type => 'train', + current => $train, + units => 'mm', + }; + } + + TRX_WEATHER_battery($bytes, $dev_str, \@res, 10); + return @res; +} + +my @uv_str = + ( + qw/low low low/, # 0 - 2 + qw/medium medium medium/, # 3 - 5 + qw/high high/, # 6 - 7 + 'very high', 'very high', 'very high', # 8 - 10 + ); + +sub TRX_WEATHER_uv_string { + $uv_str[$_[0]] || 'dangerous'; +} + +# ----------------------------- +sub TRX_WEATHER_common_uv { + my $type = shift; + my $longids = shift; + my $bytes = shift; + + my $subtype = sprintf "%02x", $bytes->[1]; + my $dev_type; + + my %devname = + ( # HEXSTRING => "NAME" + 0x01 => "UVN128", # Oregon UVN128, UV138 + 0x02 => "UVN800", # Oregon UVN800 + 0x03 => "TFA_UV", # TFA_UV-Sensor + ); + + if (exists $devname{$bytes->[1]}) { + $dev_type = $devname{$bytes->[1]}; + } else { + Log3 undef, 3, "TRX_WEATHER: common_uv error undefined subtype=$subtype"; + my @res = (); + return @res; + } + + #my $seqnbr = sprintf "%02x", $bytes->[2]; + + my $dev_str = $dev_type; + if (TRX_WEATHER_use_longid($longids,$dev_type)) { + $dev_str .= $DOT.sprintf("%02x", $bytes->[3]); + } + if ($bytes->[4] > 0) { + $dev_str .= $DOT.sprintf("%d", $bytes->[4]); + } + + my @res = (); + + # hexline debugging + if ($TRX_HEX_debug) { + my $hexline = ""; for (my $i=0;$i<@$bytes;$i++) { $hexline .= sprintf("%02x",$bytes->[$i]);} + push @res, { device => $dev_str, type => 'hexline', current => $hexline, units => 'hex', }; + } + + my $uv = $bytes->[5]/10; # UV + my $risk = TRX_WEATHER_uv_string(int($uv)); + + push @res, { + device => $dev_str, + type => 'uv', + current => $uv, + risk => $risk, + units => '', + }; + + + if ($dev_type eq "TFA_UV") { + TRX_WEATHER_temperature($bytes, $dev_str, \@res, 6); + } + TRX_WEATHER_simple_battery($bytes, $dev_str, \@res, 8); + return @res; +} + +# ------------------------------------------------------------ +# +sub TRX_WEATHER_common_datetime { + my $type = shift; + my $longids = shift; + my $bytes = shift; + + my $subtype = sprintf "%02x", $bytes->[1]; + my $dev_type; + + my %devname = + ( # HEXSTRING => "NAME" + 0x01 => "RTGR328_DATE", # RTGR328N datetime datagram + ); + + if (exists $devname{$bytes->[1]}) { + $dev_type = $devname{$bytes->[1]}; + } else { + Log3 undef, 3, "TRX_WEATHER: common_datetime error undefined subtype=$subtype"; + my @res = (); + return @res; + } + + my $dev_str = $dev_type; + if (TRX_WEATHER_use_longid($longids,$dev_type)) { + $dev_str .= $DOT.sprintf("%02x%02x", $bytes->[3],$bytes->[4]); + } + + my @res = (); + + # hexline debugging + if ($TRX_HEX_debug) { + my $hexline = ""; for (my $i=0;$i<@$bytes;$i++) { $hexline .= sprintf("%02x",$bytes->[$i]);} + push @res, { device => $dev_str, type => 'hexline', current => $hexline, units => 'hex', }; + } + + push @res, { + device => $dev_str, + type => 'date', + current => sprintf("%02d-%02d-%02d", $bytes->[5],$bytes->[6],$bytes->[7]), + units => 'yymmdd', + }; + + push @res, { + device => $dev_str, + type => 'time', + current => sprintf("%02d:%02d:%02d", $bytes->[9],$bytes->[10],$bytes->[11]), + units => 'hhmmss', + }; + + TRX_WEATHER_simple_battery($bytes, $dev_str, \@res, 12); + + return @res; +} + + +# ------------------------------------------------------------ +# T R X _ W E A T H E R _ c o m m o n _ e n e r g y ( ) +# +# devices: CM119, CM160, CM180 +sub TRX_WEATHER_common_energy { + my $type = shift; + my $longids = shift; + my $bytes = shift; + + my $subtype = sprintf "%02x", $bytes->[1]; + my $dev_type; + + my %devname = + ( # HEXSTRING => "NAME" + 0x01 => "CM160", # CM119, CM160 + 0x02 => "CM180", # CM180 + ); + + if (exists $devname{$bytes->[1]}) { + $dev_type = $devname{$bytes->[1]}; + } else { + Log3 undef, 3, "TRX_WEATHER: common_energy error undefined subtype=$subtype"; + my @res = (); + return @res; + } + + #my $seqnbr = sprintf "%02x", $bytes->[2]; + + my $dev_str = $dev_type; + $dev_str .= $DOT.sprintf("%02x%02x", $bytes->[3],$bytes->[4]); + + my @res = (); + + # hexline debugging + if ($TRX_HEX_debug) { + my $hexline = ""; for (my $i=0;$i<@$bytes;$i++) { $hexline .= sprintf("%02x",$bytes->[$i]);} + push @res, { device => $dev_str, type => 'hexline', current => $hexline, units => 'hex', }; + } + + my $energy_current = ( + $bytes->[6] * 256*256*256 + + $bytes->[7] * 256*256 + + $bytes->[8] * 256 + + $bytes->[9] + ); + + push @res, { + device => $dev_str, + type => 'energy_current', + current => $energy_current, + units => 'W', + }; + + my $energy_total = ( + $bytes->[10] * 256*256*256*256*256 + + $bytes->[11] * 256*256*256*256 + + $bytes->[12] * 256*256*256 + + $bytes->[13] * 256*256 + + $bytes->[14] * 256 + + $bytes->[15] + ) / 223.666; + $energy_total = $energy_total / 1000; + + push @res, { + device => $dev_str, + type => 'energy_total', + current => $energy_total, + units => 'kWh', + }; + + my $count = $bytes->[5]; + # TRX_WEATHER_simple_battery($bytes, $dev_str, \@res, 16) if ($count==0 || $count==1 || $count==2 || $count==3 || $count==8 || $count==9); + TRX_WEATHER_simple_battery($bytes, $dev_str, \@res, 16); + + return @res; +} + +# ------------------------------------------------------------ +# T R X _ W E A T H E R _ c o m m o n _ e n e r g y 2 +# +# devices: CM180i +sub TRX_WEATHER_common_energy2 { + my $type = shift; + my $longids = shift; + my $bytes = shift; + + my $subtype = sprintf "%02x", $bytes->[1]; + my $dev_type; + + my %devname = + ( # HEXSTRING => "NAME" + 0x01 => "CM180I", # CM180i + ); + + if (exists $devname{$bytes->[1]}) { + $dev_type = $devname{$bytes->[1]}; + } else { + Log3 undef, 3, "TRX_WEATHER: common_energy2 error undefined subtype=$subtype"; + my @res = (); + return @res; + } + + #my $seqnbr = sprintf "%02x", $bytes->[2]; + + my $dev_str = $dev_type; + $dev_str .= $DOT.sprintf("%02x%02x", $bytes->[3],$bytes->[4]); + + my @res = (); + + # hexline debugging + if ($TRX_HEX_debug) { + my $hexline = ""; for (my $i=0;$i<@$bytes;$i++) { $hexline .= sprintf("%02x",$bytes->[$i]);} + push @res, { device => $dev_str, type => 'hexline', current => $hexline, units => 'hex', }; + } + + my $energy_count = $bytes->[5]; + + if (1) { + my $energy_current_ch1 = ($bytes->[6] * 256 + $bytes->[7])/10; + my $energy_current_ch2 = ($bytes->[8] * 256 + $bytes->[9])/10; + my $energy_current_ch3 = ($bytes->[10] * 256 + $bytes->[11]/10); + + push @res, { + device => $dev_str, + type => 'energy_ch1', + current => $energy_current_ch1, + units => 'A', + }; + push @res, { + device => $dev_str, + type => 'energy_ch2', + current => $energy_current_ch2, + units => 'A', + }; + push @res, { + device => $dev_str, + type => 'energy_ch3', + current => $energy_current_ch3, + units => 'A', + }; + } + if ($energy_count == 0) { + my $energy_total = ( + $bytes->[12] * 256*256*256*256*256 + + $bytes->[13] * 256*256*256*256 + + $bytes->[14] * 256*256*256 + + $bytes->[15] * 256*256 + + $bytes->[16] * 256 + + $bytes->[17] + ) / 223.666; + $energy_total = $energy_total / 1000; + + + push @res, { + device => $dev_str, + type => 'energy_total', + current => $energy_total, + units => 'kWh', + }; + + } + # TRX_WEATHER_simple_battery($bytes, $dev_str, \@res, 16) if ($count==0 || $count==1 || $count==2 || $count==3 || $count==8 || $count==9); + TRX_WEATHER_simple_battery($bytes, $dev_str, \@res, 18); + + return @res; +} + +# ------------------------------------------------------------ +# T R X _ W E A T H E R _ c o m m o n _ e n e r g y 3 +# +# devices: REVOLT +sub TRX_WEATHER_common_energy3 { + my $type = shift; + my $longids = shift; + my $bytes = shift; + + my $subtype = sprintf "%02x", $bytes->[1]; + my $dev_type; + + my %devname = + ( # HEXSTRING => "NAME" + 0x01 => "REVOLT", # Revolt + ); + + if (exists $devname{$bytes->[1]}) { + $dev_type = $devname{$bytes->[1]}; + } else { + Log3 undef, 3, "TRX_WEATHER: common_energy3 error undefined subtype=$subtype"; + my @res = (); + return @res; + } + + #my $seqnbr = sprintf "%02x", $bytes->[2]; + + my $dev_str = $dev_type; + $dev_str .= $DOT.sprintf("%02x%02x", $bytes->[3],$bytes->[4]); + + my @res = (); + + # hexline debugging + if ($TRX_HEX_debug) { + my $hexline = ""; for (my $i=0;$i<@$bytes;$i++) { $hexline .= sprintf("%02x",$bytes->[$i]);} + push @res, { device => $dev_str, type => 'hexline', current => $hexline, units => 'hex', }; + } + + my $energy_voltage = $bytes->[5]; + push @res, { + device => $dev_str, + type => 'energy_voltage', + current => $energy_voltage, + units => 'V', + }; + + my $energy_current = ($bytes->[6] * 256 + $bytes->[7]) / 100; + + push @res, { + device => $dev_str, + type => 'energy_current_revolt', + current => $energy_current, + units => 'A', + }; + + my $energy_power = ($bytes->[8] * 256 + $bytes->[9]) / 10; + + push @res, { + device => $dev_str, + type => 'energy_power', + current => $energy_power, + units => 'W', + }; + + my $energy_total = ($bytes->[10] * 256 + $bytes->[11]) / 100; + + push @res, { + device => $dev_str, + type => 'energy_total', + current => $energy_total, + units => 'kWh', + }; + + my $energy_pf = $bytes->[12] / 100; + push @res, { + device => $dev_str, + type => 'energy_pf', + current => $energy_pf, + units => '', + }; + + my $energy_freq = $bytes->[13]; + push @res, { + device => $dev_str, + type => 'energy_freq', + current => $energy_freq, + units => 'Hz', + }; + + my $rssi = ($bytes->[14] & 0xf0) >> 4; + + if ($trx_rssi == 1) { + push @res, { + device => $dev_str, + type => 'rssi', + current => sprintf("%d",$rssi), + }; + } + + return @res; +} + +# ------------------------------------------------------------ +# +sub TRX_WEATHER_common_weight { + my $type = shift; + my $longids = shift; + my $bytes = shift; + + my $subtype = sprintf "%02x", $bytes->[1]; + my $dev_type; + + my %devname = + ( # HEXSTRING => "NAME" + 0x01 => "BWR101", + 0x02 => "GR101", + ); + + if (exists $devname{$bytes->[1]}) { + $dev_type = $devname{$bytes->[1]}; + } else { + Log3 undef, 3, "TRX_WEATHER: common_weight error undefined subtype=$subtype"; + my @res = (); + return @res; + } + + #my $seqnbr = sprintf "%02x", $bytes->[2]; + + my $dev_str = $dev_type; + if (TRX_WEATHER_use_longid($longids,$dev_type)) { + $dev_str .= $DOT.sprintf("%02x", $bytes->[3]); + } + if ($bytes->[4] > 0) { + $dev_str .= $DOT.sprintf("%d", $bytes->[4]); + } + + my @res = (); + + # hexline debugging + if ($TRX_HEX_debug) { + my $hexline = ""; for (my $i=0;$i<@$bytes;$i++) { $hexline .= sprintf("%02x",$bytes->[$i]);} + push @res, { device => $dev_str, type => 'hexline', current => $hexline, units => 'hex', }; + } + + my $weight = ($bytes->[5]*256 + $bytes->[6])/10; + + push @res, { + device => $dev_str, + type => 'weight', + current => $weight, + units => 'kg', + }; + + #TRX_WEATHER_simple_battery($bytes, $dev_str, \@res, 7); + + return @res; +} + + +# ----------------------------- +sub +TRX_WEATHER_Parse($$) +{ + my ($iohash, $hexline) = @_; + + #my $hashname = $iohash->{NAME}; + #my $longid = AttrVal($hashname,"longids",""); + #Log3 $iohash, $iohash, 5 ,"2: name=$hashname, attr longids = $longid"; + + my $longids = 0; + if (defined($attr{$iohash->{NAME}}{longids})) { + $longids = $attr{$iohash->{NAME}}{longids}; + #Log3 $iohash, 5,"0: attr longids = $longids"; + } + + $trx_rssi = 0; + if (defined($attr{$iohash->{NAME}}{rssi})) { + $trx_rssi = $attr{$iohash->{NAME}}{rssi}; + #Log3 $iohash, 5, "0: attr rssi = $trx_rssi"; + } + + my $time = time(); + # convert to binary + my $msg = pack('H*', $hexline); + if ($time_old ==0) { + Log3 $iohash, 5, "TRX_WEATHER: decoding delay=0 hex=$hexline"; + } else { + my $time_diff = $time - $time_old ; + Log3 $iohash, 5, "TRX_WEATHER: decoding delay=$time_diff hex=$hexline"; + } + $time_old = $time; + + # convert string to array of bytes. Skip length byte + my @rfxcom_data_array = (); + foreach (split(//, substr($msg,1))) { + push (@rfxcom_data_array, ord($_) ); + } + + my $num_bytes = ord($msg); + + if ($num_bytes < 3) { + return; + } + + my $type = $rfxcom_data_array[0]; + + my $sensor_id = unpack('H*', chr $type); + + my $key = ($type << 8) + $num_bytes; + + my $rec = $types{$key}; + + unless ($rec) { + Log3 $iohash, 1, "TRX_WEATHER: ERROR: Unknown sensor_id=$sensor_id message='$hexline'"; + return ""; + } + + my $method = $rec->{method}; + unless ($method) { + Log3 $iohash, 4, "TRX_WEATHER: Possible message from Oregon part '$rec->{part}'"; + Log3 $iohash, 4, "TRX_WEATHER: sensor_id=$sensor_id"; + return; + } + + my @res; + + if (! defined(&$method)) { + Log3 $iohash, 4, "TRX_WEATHER: Error: Unknown function=$method. Please define it in file $0"; + Log3 $iohash, 4, "TRX_WEATHER: sensor_id=$sensor_id\n"; + return ""; + } else { + Log3 $iohash, 5, "TRX_WEATHER: parsing sensor_id=$sensor_id message='$hexline'"; + @res = $method->($rec->{part}, $longids, \@rfxcom_data_array); + } + + # get device name from first entry + my $device_name = $res[0]->{device}; + #Log3 $iohash, 5, "device_name=$device_name"; + + if (! defined($device_name)) { + Log3 $iohash, 4, "TRX_WEATHER: error device_name undefined\n"; + return ""; + } + + my $def = $modules{TRX_WEATHER}{defptr}{"$device_name"}; + if(!$def) { + Log3 $iohash, 3, "TRX_WEATHER: Unknown device $device_name, please define it"; + return "UNDEFINED $device_name TRX_WEATHER $device_name"; + } + # Use $def->{NAME}, because the device may be renamed: + my $name = $def->{NAME}; + return "" if(IsIgnored($name)); + + my $n = 0; + my $tm = TimeNow(); + + my $i; + my $val = ""; + my $sensor = ""; + + readingsBeginUpdate($def); + foreach $i (@res){ + #print "!> i=".$i."\n"; + #printf "%s\t",$i->{device}; + if ($i->{type} eq "temp") { + Log3 $name, 5, "TRX_WEATHER: name=$name device=$device_name Temperatur ".$i->{current}." ".$i->{units}; + $val .= "T: ".$i->{current}." "; + + $sensor = "temperature"; + readingsBulkUpdate($def, $sensor, $i->{current}); + } + elsif ($i->{type} eq "chilltemp") { + Log3 $name, 5, "TRX_WEATHER: name=$name device=$device_name windchill ".$i->{current}." ".$i->{units}; + $val .= "CT: ".$i->{current}." "; + + $sensor = "windchill"; + readingsBulkUpdate($def, $sensor, $i->{current}); + } + elsif ($i->{type} eq "humidity") { + Log3 $name, 5, "TRX_WEATHER: name=$name device=$device_name Luftfeuchtigkeit ".$i->{current}.$i->{units}; + $val .= "H: ".$i->{current}." "; + + $sensor = "humidity"; + readingsBulkUpdate($def, $sensor, $i->{current}); + } + elsif ($i->{type} eq "battery") { + Log3 $name, 5, "TRX_WEATHER: name=$name device=$device_name Batterie ".$i->{current}; + my $tmp_battery = $i->{current}; + my @words = split(/\s+/,$i->{current}); + $val .= "BAT: ".$words[0]." "; #use only first word + + $sensor = "battery"; + readingsBulkUpdate($def, $sensor, $i->{current}); + } + elsif ($i->{type} eq "pressure") { + Log3 $name, 5, "TRX_WEATHER: name=$name device=$device_name Luftdruck ".$i->{current}." ".$i->{units}." Vorhersage=".$i->{forecast}; + # do not add it due to problems with hms.gplot + $val .= "P: ".$i->{current}." "; + + $sensor = "pressure"; + readingsBulkUpdate($def, $sensor, $i->{current}); + + $sensor = "forecast"; + readingsBulkUpdate($def, $sensor, $i->{forecast}); + } + elsif ($i->{type} eq "speed") { + Log3 $name, 5, "TRX_WEATHER: name=$name device=$device_name wind_speed ".$i->{current}." wind_avspeed ".$i->{average}; + $val .= "W: ".$i->{current}." "; + $val .= "WA: ".$i->{average}." "; + + $sensor = "wind_speed"; + readingsBulkUpdate($def, $sensor, $i->{current}); + + $sensor = "wind_avspeed"; + readingsBulkUpdate($def, $sensor, $i->{average}); + } + elsif ($i->{type} eq "direction") { + Log3 $name, 5, "TRX_WEATHER: name=$name device=$device_name wind_dir ".$i->{current}." ".$i->{string}; + $val .= "WD: ".$i->{current}." "; + $val .= "WDN: ".$i->{string}." "; + + $sensor = "wind_dir"; + readingsBulkUpdate($def, $sensor, $i->{current} . " " . $i->{string}); + } + elsif ($i->{type} eq "rain") { + Log3 $name, 5, "TRX_WEATHER: name=$name device=$device_name rain ".$i->{current}; + $val .= "RR: ".$i->{current}." "; + + $sensor = "rain_rate"; + readingsBulkUpdate($def, $sensor, $i->{current}); + } + elsif ($i->{type} eq "train") { + Log3 $name, 5, "TRX_WEATHER: name=$name device=$device_name train ".$i->{current}; + $val .= "TR: ".$i->{current}." "; + + $sensor = "rain_total"; + readingsBulkUpdate($def, $sensor, $i->{current}); + } + elsif ($i->{type} eq "flip") { + Log3 $name, 5, "TRX_WEATHER: name=$name device=$device_name flip ".$i->{current}; + $val .= "F: ".$i->{current}." "; + + $sensor = "rain_flip"; + readingsBulkUpdate($def, $sensor, $i->{current}); + } + elsif ($i->{type} eq "uv") { + Log3 $name, 5, "TRX_WEATHER: name=$name device=$device_name uv_val ".$i->{current}." uv_risk ".$i->{risk}; + $val .= "UV: ".$i->{current}." "; + $val .= "UVR: ".$i->{risk}." "; + + $sensor = "uv_val"; + readingsBulkUpdate($def, $sensor, $i->{current}); + + $sensor = "uv_risk"; + readingsBulkUpdate($def, $sensor, $i->{risk}); + } + elsif ($i->{type} eq "energy_current") { + my $energy_current = $i->{current}; + if (defined($def->{scale_current})) { + $energy_current = $energy_current * $def->{scale_current}; + Log3 $name, 5, "TRX_WEATHER: name=$name device=$device_name scale_current=".$def->{scale_current}; + } + Log3 $name, 5, "TRX_WEATHER: name=$name device=$device_name energy_current=".$energy_current; + $val .= "ECUR: ".$energy_current." "; + + $sensor = "energy_current"; + #readingsBulkUpdate($def, $sensor, $energy_current." ".$i->{units}); + readingsBulkUpdate($def, $sensor, $energy_current); + } + elsif ($i->{type} eq "energy_ch1") { + my $energy_current = $i->{current}; + if (defined($def->{scale_current})) { + $energy_current = $energy_current * $def->{scale_current}; + Log3 $name, 5, "TRX_WEATHER: name=$name device=$device_name energy_ch1 scale_current=".$def->{scale_current}; + } + Log3 $name, 5, "TRX_WEATHER: device=$device_name CH1 energy_current=$energy_current"; + $val .= "CH1: ".$energy_current." "; + + $sensor = "energy_ch1"; + readingsBulkUpdate($def, $sensor, $energy_current); + } + elsif ($i->{type} eq "energy_ch2") { + my $energy_current = $i->{current}; + if (defined($def->{scale_current})) { + $energy_current = $energy_current * $def->{scale_current}; + Log3 $name, 5, "TRX_WEATHER: name=$name device=$device_name energy_ch2 scale_current=".$def->{scale_current}; + } + Log3 $device_name, 5, "TRX_WEATHER: name=$name device=$device_name CH2 energy_current=$energy_current"; + $val .= "CH2: ".$energy_current." "; + + $sensor = "energy_ch2"; + readingsBulkUpdate($def, $sensor, $energy_current); + } + elsif ($i->{type} eq "energy_ch3") { + my $energy_current = $i->{current}; + if (defined($def->{scale_current})) { + $energy_current = $energy_current * $def->{scale_current}; + Log3 $name, 5, "TRX_WEATHER: name=$name device=$device_name energy_ch3 scale_current=".$def->{scale_current}; + } + Log3 $name, 5, "TRX_WEATHER: name=$name device=$device_name CH3 energy_current=".$energy_current; + $val .= "CH3: ".$energy_current." "; + + $sensor = "energy_ch3"; + readingsBulkUpdate($def, $sensor, $energy_current); + } + elsif ($i->{type} eq "energy_current_revolt") { + my $energy_current = $i->{current}; + if (defined($def->{scale_current})) { + $energy_current = $energy_current * $def->{scale_current}; + Log3 $name, 5, "TRX_WEATHER: name=$name device=$device_name scale_current=".$def->{scale_current}; + } + Log3 $name, 5, "TRX_WEATHER: name=$name device=$device_name energy_current=".$energy_current; + #$val .= "ECUR: ".$energy_current." "; + + $sensor = "energy_current"; + readingsBulkUpdate($def, $sensor, $energy_current." ".$i->{units}); + } + elsif ($i->{type} eq "energy_total") { + my $energy_total = $i->{current}; + if (defined($def->{scale_total}) && defined($def->{add_total})) { + $energy_total = sprintf("%.4f",$energy_total * $def->{scale_total} + $def->{add_total}); + Log3 $name, 5, "TRX_WEATHER: name=$name device=$device_name energy_total scale_total=".$def->{scale_total}; + } + Log3 $name, 5, "TRX_WEATHER: name=$name device=$device_name energy_total=$energy_total"; + $val .= "ESUM: ".$energy_total." "; + + $sensor = "energy_total"; + #readingsBulkUpdate($def, $sensor, $energy_total." ".$i->{units}); + readingsBulkUpdate($def, $sensor, $energy_total); + } + elsif ($i->{type} eq "energy_power") { + my $energy_power = $i->{current}; + Log3 $name, 5, "TRX_WEATHER: name=$name device=$device_name energy_power=$energy_power"; + $val .= "EPOW: ".$energy_power." "; + + $sensor = "energy_power"; + readingsBulkUpdate($def, $sensor, $energy_power." ".$i->{units}); + } + elsif ($i->{type} eq "energy_voltage") { + my $energy_voltage = $i->{current}; + Log3 $name, 5, "TRX_WEATHER: name=$name device=$device_name energy_voltage=$energy_voltage"; + #$val .= "V: ".$energy_voltage." "; + + $sensor = "voltage"; + readingsBulkUpdate($def, $sensor, $energy_voltage." ".$i->{units}); + } + elsif ($i->{type} eq "energy_pf") { + my $energy_pf = $i->{current}; + Log3 $name, 5, "TRX_WEATHER: name=$name device=$device_name energy_pf=$energy_pf"; + #$val .= "PF: ".$energy_pf." "; + + $sensor = "energy_pf"; + readingsBulkUpdate($def, $sensor, $energy_pf." ".$i->{units}); + } + elsif ($i->{type} eq "energy_freq") { + my $energy_freq = $i->{current}; + Log3 $name, 5, "TRX_WEATHER: name=$name device=$device_name energy_freq=$energy_freq"; + #$val .= "FREQ: ".$energy_freq." "; + + $sensor = "frequency"; + readingsBulkUpdate($def, $sensor, $energy_freq." ".$i->{units}); + + } + elsif ($i->{type} eq "weight") { + Log3 $name, 5, "TRX_WEATHER: name=$name device=$device_name weight ".$i->{current}; + $val .= "W: ".$i->{current}." "; + + $sensor = "weight"; + readingsBulkUpdate($def, $sensor, $i->{current}); + } + elsif ($i->{type} eq "hexline") { + Log3 $name, 5, "TRX_WEATHER: name=$name device=$device_name hexline ".$i->{current}; + $sensor = "hexline"; + readingsBulkUpdate($def, $sensor, $i->{current}); + } + elsif ($i->{type} eq "rssi") { + Log3 $name, 5, "TRX_WEATHER: name=$name device=$device_name rssi ".$i->{current}; + $sensor = "rssi"; + readingsBulkUpdate($def, $sensor, $i->{current}); + } + elsif ($i->{type} eq "date") { + Log3 $name, 5, "TRX_WEATHER: name=$name device=$device_name date ".$i->{current}; + $val .= $i->{current}." "; + $sensor = "date"; + readingsBulkUpdate($def, $sensor, $i->{current}); + } + elsif ($i->{type} eq "time") { + Log3 $name, 5, "TRX_WEATHER: name=$name device=$device_name time ".$i->{current}; + $val .= $i->{current}." "; + $sensor = "time"; + readingsBulkUpdate($def, $sensor, $i->{current}); + } + elsif ($i->{type} eq "setpoint") { + Log3 $name, 5, "TRX_WEATHER: name=$name device=$device_name setpoint ".$i->{current}." ".$i->{units}; + $val .= "SP: ".$i->{current}." "; + + $sensor = "setpoint"; + readingsBulkUpdate($def, $sensor, $i->{current}); + } + elsif ($i->{type} eq "demand") { + Log3 $name, 5, "TRX_WEATHER: name=$name device=$device_name state ".$i->{current}; + if ($val eq "") { + $val = "$i->{current}"; + } + else { + $val .= "D: ".$i->{current}." "; + } + + $sensor = "demand"; + readingsBulkUpdate($def, $sensor, $i->{current}); + } + else { + Log3 $name, 1, "TRX_WEATHER: name=$name device=$device_name UNKNOWN Type: ".$i->{type}." Value: ".$i->{current} + } + } + + if ("$val" ne "") { + # remove heading and trailing space chars from $val + $val =~ s/^\s+|\s+$//g; + + #$def->{STATE} = $val; + readingsBulkUpdate($def, "state", $val); + } + + readingsEndUpdate($def, 1); + + return $name; +} + +1; + +=pod +=begin html + + +

TRX_WEATHER

+
    + The TRX_WEATHER module interprets weather sensor messages received by a RTXtrx receiver. See http://www.rfxcom.com/oregon.htm for a list of + Oregon Scientific weather sensors that could be received by the RFXtrx433 tranmitter. You need to define a RFXtrx433 receiver first. See + See TRX. + +

    + + + Define +
      + define <name> TRX_WEATHER <deviceid>
      +
      + <deviceid> +
        + is the device identifier of the sensor. It consists of the sensors name and (only if the attribute longids is set of the RFXtrx433) an a one byte hex string (00-ff) that identifies the sensor. If a sensor uses a switch to set an additional is then this is also added. The define statement with the deviceid is generated automatically by autocreate. The following sensor names are used:
        + "THR128" (for THR128/138, THC138),
        + "THGR132N" (for THC238/268,THN132,THWR288,THRN122,THN122,AW129/131),
        + "THWR800",
        + "RTHN318",
        + "TX3_T" (for LaCrosse TX3, TX4, TX17),
        + "THGR228N" (for THGN122/123, THGN132, THGR122/228/238/268),
        + "THGR810",
        + "RTGR328",
        + "THGR328",
        + "WTGR800_T" (for temperature of WTGR800),
        + "THGR918" (for THGR918, THGRN228, THGN500),
        + "TFATS34C" (for TFA TS34C),
        + "BTHR918",
        + "BTHR918N (for BTHR918N, BTHR968),
        + "RGR918" (for RGR126/682/918),
        + "PCR800",
        + "TFA_RAIN" (for TFA rain sensor),
        + "WTGR800_A" (for wind sensor of WTGR800),
        + "WGR800" (for wind sensor of WGR800),
        + "WGR918" (for wind sensor of STR918 and WGR918),
        + "TFA_WIND" (for TFA wind sensor),
        + "BWR101" (for Oregon Scientific BWR101),
        + "GR101" (for Oregon Scientific GR101) + "TLX7506" (for Digimax TLX7506),
        + "TH10" (for Digimax with short format),
        +
      +
      + Example:
      +
        + define Tempsensor TRX_WEATHER TX3_T
        + define Tempsensor3 TRX_WEATHER THR128_3
        + define Windsensor TRX_WEATHER WGR918_A
        + define Regensensor TRX_WEATHER RGR918
        +
      +
    +

    +
      + define <name> TRX_WEATHER <deviceid> [<scale_current> <scale_total> <add_total>]
      +
      + <deviceid> +
        + is the device identifier of the energy sensor. It consists of the sensors name and (only if the attribute longids is set of the RFXtrx433) an a two byte hex string (0000-ffff) that identifies the sensor. The define statement with the deviceid is generated automatically by autocreate. The following sensor names are used:
        + "CM160" (for OWL CM119 or CM160),
        + "CM180" (for OWL CM180),

        + "CM180i"(for OWL CM180i),

        +
      + The following Readings are generated:
      +
        + "energy_current:": +
          + Only for CM160 and CM180: current usage in Watt. If <scale_current> is defined the result is: energy_current * <scale_current>. +
        + "energy_chx:": +
          + Only for CM180i (where chx is ch1, ch2 or ch3): current usage in Ampere. If <scale_current> is defined the result is: energy_chx * <scale_current>. +
        + "energy_total:": +
          + current usage in kWh. If scale_total and add_total is defined the result is: energy_total * <scale_total> + <add_total>. +
        +
        +
      + Example:
      +
        + define Tempsensor TRX_WEATHER CM160_1401
        + define Tempsensor TRX_WEATHER CM180_1401 1 1 0
        + define Tempsensor TRX_WEATHER CM180_1401 0.9 0.9 -1000
        +
      +
    +
    + + + Set
      N/A

    + + + Get
      N/A

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