2
0
mirror of https://github.com/fhem/fhem-mirror.git synced 2025-03-12 16:46:35 +00:00

new TRX modules added

git-svn-id: https://svn.fhem.de/fhem/trunk@1283 2b470e98-0d58-463d-a4d8-8e2adae1ed80
This commit is contained in:
wherzig 2012-02-24 19:08:16 +00:00
parent 11e9841667
commit b10dc6ae9a
7 changed files with 2125 additions and 1 deletions

View File

@ -14,6 +14,7 @@
- feature: FHEMWEB redirectCmds attribute added
- feature: CUL_TX minsecs attribute (by Arno)
- feature: webCmd in smallScreen added
- feature: TRX modules by Willi
- 2011-12-31 (5.2)
- bugfix: applying smallscreen attributes to firefox/opera

363
fhem/FHEM/45_TRX.pm Executable file
View File

@ -0,0 +1,363 @@
#################################################################################
# 45_TRX.pm
# Module for FHEM
#
# Tested with RFXtrx-Receiver (433.92MHz, USB)
# (see http://www.RFXCOM.com/).
# To use this module, you need to define an RFXTRX transceiver:
# define RFXTRX TRX /dev/ttyUSB0
#
# The module also has code to access a RFXtrx transceiver attached via LAN.
#
# To use it define the IP-Adresss and the Port:
# define RFXTRX TRX 192.168.169.111:10001
# optionally you may issue not to initialize the device (useful if you share an RFXtrx device with other programs)
# define RFXTRX TRX 192.168.169.111:10001 noinit
#
# The RFXtrx transceivers supports lots of protocols that may be implemented for FHEM
# writing the appropriate FHEM modules. See the
#
# Willi Herzig, 2012
#
# 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.
#
#################################################################################
# derived from 00_CUL.pm
#
###########################
# $Id:
package main;
use strict;
use warnings;
use Time::HiRes qw(gettimeofday);
my $last_rmsg = "abcd";
my $last_time = 1;
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:";
my %mc = (
"1:TRX_WEATHER" => "^..(50|52|54|55|56).*",
"2:TRX_SECURITY" => "^..(20).*",
"3:TRX_LIGHT" => "^..(10).*",
"4:TRX_ELSE" => "^..(0|3|4|6|7|8|9).*",
);
$hash->{MatchList} = \%mc;
$hash->{ReadyFn} = "TRX_Ready";
# Normal devices
$hash->{DefFn} = "TRX_Define";
$hash->{UndefFn} = "TRX_Undef";
$hash->{GetFn} = "TRX_Get";
$hash->{SetFn} = "TRX_Set";
$hash->{StateFn} = "TRX_SetState";
$hash->{AttrList}= "do_not_notify:1,0 dummy:1,0 do_not_init:1:0 addvaltrigger:1:0 longids loglevel:0,1,2,3,4,5,6";
$hash->{ShutdownFn} = "TRX_Shutdown";
}
#####################################
sub
TRX_Define($$)
{
my ($hash, $def) = @_;
my @a = split("[ \t][ \t]*", $def);
return "wrong syntax: define <name> TRX devicename [noinit]"
if(@a != 3 && @a != 4);
DevIo_CloseDev($hash);
my $name = $a[0];
my $dev = $a[2];
my $opt = $a[3] if(@a == 4);;
if($dev eq "none") {
Log 1, "TRX: $name device is none, commands will be echoed only";
$attr{$name}{dummy} = 1;
return undef;
}
if(defined($opt)) {
if($opt eq "noinit") {
Log 1, "TRX: $name no init is done";
$attr{$name}{do_not_init} = 1;
} else {
return "wrong syntax: define <name> 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};
my $ll5 = GetLogLevel($name,5);
return if(!defined($fn));
my $bstring;
$bstring = "$fn$msg";
Log $ll5, "$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);
Log GetLogLevel($name,$lev), "deleting port for $d";
delete $defs{$d}{IODev};
}
}
DevIo_CloseDev($hash);
return undef;
}
#####################################
sub
TRX_Shutdown($)
{
my ($hash) = @_;
return undef;
}
#####################################
sub
TRX_Set($@)
{
my ($hash, @a) = @_;
my $msg;
my $name=$a[0];
my $reading= $a[1];
$msg="$name => No Set function ($reading) implemented";
return $msg;
}
#####################################
sub
TRX_Get($@)
{
my ($hash, @a) = @_;
my $msg;
my $name=$a[0];
my $reading= $a[1];
$msg="$name => No Get function ($reading) implemented";
Log 1,$msg;
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 ;
# Reset
my $init = pack('H*', "0D00000000000000000000000000");
DevIo_SimpleWrite($hash, $init, 0);
DevIo_TimeoutRead($hash, 0.5);
TRX_Clear($hash);
if(defined($attr{$name}) && defined($attr{$name}{"do_not_init"})) {
Log 1, "TRX: defined with noinit. Do not send init string to device.";
$hash->{STATE} = "Initialized" if(!$hash->{STATE});
# Reset the counter
delete($hash->{XMIT_TIME});
delete($hash->{NR_CMD_LAST_H});
return undef;
}
#
# Get Status
$init = pack('H*', "0D00000102000000000000000000");
DevIo_SimpleWrite($hash, $init, 0);
$buf = DevIo_TimeoutRead($hash, 0.1);
if (! $buf) {
Log 1, "TRX: Initialization Error: No character read";
return "TRX: Initialization Error $name: no char read";
} elsif ($buf !~ m/^\x0d\x01\x00.........../) {
my $hexline = unpack('H*', $buf);
Log 1, "TRX: Initialization Error hexline='$hexline'";
return "TRX: Initialization Error %name expected char=0x2c, but char=$char received.";
} else {
Log 1, "TRX: Init OK";
$hash->{STATE} = "Initialized" if(!$hash->{STATE});
}
#
# 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};
#Log 5, "TRX/RAW: $TRX_data/$mybuf";
$TRX_data .= $mybuf;
#my $hexline = unpack('H*', $TRX_data);
#Log 1, "TRX: TRX_Read '$hexline'";
# first char as byte represents number of bytes of the message
my $num_bytes = ord($TRX_data);
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);
#Log 1, "TRX_Read rmsg '$hexline'";
$TRX_data = substr($TRX_data, $num_bytes+1);;
#$hexline = unpack('H*', $TRX_data);
#Log 1, "TRX_Read TRX_data '$hexline'";
#
TRX_Parse($hash, $hash, $name, unpack('H*', $rmsg));
}
#Log 1, "TRX_Read END";
$hash->{PARTIAL} = $TRX_data;
}
sub
TRX_Parse($$$$)
{
my ($hash, $iohash, $name, $rmsg) = @_;
#Log 1, "TRX_Parse1 '$rmsg'";
Log 5, "TRX_Parse1 '$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) {
#Log 1, "TRX_Dispatch '$rmsg'";
%addvals = (RAWMSG => $rmsg);
Dispatch($hash, $rmsg, \%addvals);
$hash->{"${name}_MSGCNT"}++;
$hash->{"${name}_TIME"} = TimeNow();
$hash->{RAWMSG} = $rmsg;
} else {
#Log 1, "TRX_Dispatch '$rmsg' dup";
#Log 1, "<-duplicate->";
}
$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;

102
fhem/FHEM/46_TRX_ELSE.pm Executable file
View File

@ -0,0 +1,102 @@
#################################################################################
# 46_TRX_ELSE.pm
# Modul for FHEM for unkown RFXTRX messages
#
# 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.
#
##################################
#
# values for "set global verbose"
# 4: log unknown protocols
# 5: log decoding hexlines for debugging
#
# $Id:
package main;
use strict;
use warnings;
use Switch;
my $time_old = 0;
sub
TRX_ELSE_Initialize($)
{
my ($hash) = @_;
$hash->{Match} = "^.*";
$hash->{DefFn} = "TRX_ELSE_Define";
$hash->{UndefFn} = "TRX_ELSE_Undef";
$hash->{ParseFn} = "TRX_ELSE_Parse";
$hash->{AttrList} = "IODev do_not_notify:1,0 loglevel:0,1,2,3,4,5,6";
Log 1, "TRX_ELSE: Initialize";
}
#####################################
sub
TRX_ELSE_Define($$)
{
my ($hash, $def) = @_;
my @a = split("[ \t][ \t]*", $def);
my $a = int(@a);
#print "a0 = $a[0]";
return "wrong syntax: define <name> TRX_ELSE code" if(int(@a) != 3);
my $name = $a[0];
my $code = $a[2];
$hash->{CODE} = $code;
#$modules{TRX_ELSE}{defptr}{$name} = $hash;
$modules{TRX_ELSE}{defptr}{$code} = $hash;
AssignIoPort($hash);
return undef;
}
#####################################
sub
TRX_ELSE_Undef($$)
{
my ($hash, $name) = @_;
delete($modules{TRX_ELSE}{defptr}{$name});
return undef;
}
my $DOT = q{_};
sub
TRX_ELSE_Parse($$)
{
my ($hash, $msg) = @_;
my $time = time();
if ($time_old ==0) {
Log 5, "TRX_ELSE: decoding delay=0 hex=$msg";
} else {
my $time_diff = $time - $time_old ;
Log 5, "TRX_ELSE: decoding delay=$time_diff hex=$msg";
}
$time_old = $time;
# convert to binary
my $bin_msg = pack('H*', $msg);
#my $hexline = unpack('H*', $bin_msg);
#Log 1, "TRX_ELSE: 2 hex=$hexline";
# convert string to array of bytes. Skip length byte
my @rfxcom_data_array = ();
foreach (split(//, substr($bin_msg,1))) {
push (@rfxcom_data_array, ord($_) );
}
Log 0, "TRX_ELSE: hex=$msg";
return "Test";
}
1;

425
fhem/FHEM/46_TRX_LIGHT.pm Executable file
View File

@ -0,0 +1,425 @@
#################################################################################
# 46_TRX_LIGHT.pm
#
# Modul for FHEM for
# "X10" -> X10 lighting
# "ARC" -> ARC
# "AB400D" -> ELRO AB400D
# "WAVEMAN" -> Waveman
# "EMW200" -> Chacon EMW200
# "IMPULS" -> IMPULS
#
# - ms14a: motion sensor
# - x10: generic X10 sensor
#
# 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.
#
##################################
#
# values for "set global verbose"
# 4: log unknown protocols
# 5: log decoding hexlines for debugging
#
# $Id: 43_TRX_LIGHT.pm 1098 2011-11-12 07:51:08Z rudolfkoenig $
package main;
use strict;
use warnings;
use Switch;
# Debug this module? YES = 1, NO = 0
my $TRX_LIGHT_debug = 0;
my $time_old = 0;
my $TRX_LIGHT_type_default = "ds10a";
my $TRX_LIGHT_X10_type_default = "x10";
my $DOT = q{_};
my %light_device_codes = ( # HEXSTRING => "NAME", "name of reading",
0x00 => [ "X10", "light" ],
0x01 => [ "ARC", "light" ],
0x02 => [ "AB400D", "light" ],
0x03 => [ "WAVEMAN", "light" ],
0x04 => [ "EMW200", "light"],
0x05 => [ "IMPULS", "light"],
);
my %light_device_commands = ( # HEXSTRING => commands
0x00 => [ "off", "on", "dim", "bright", "all_off", "all_on", ""],
0x01 => [ "off", "on", "", "", "all_off", "all_on", "chime"],
0x02 => [ "off", "on", "", "", "", "", ""],
0x03 => [ "off", "on", "", "", "", "", ""],
0x04 => [ "off", "on", "", "", "all_off", "all_on", ""],
0x05 => [ "off", "on", "", "", "", "", ""],
);
my %light_device_c2b; # DEVICE_TYPE->hash (reverse of light_device_codes)
# Get the binary value for a command
# return -1 if command not valid dor dev_type
sub TRX_LIGHT_cmd_to_binary {
my ($dev_type, $command) = @_;
return -1;
}
sub
TRX_LIGHT_Initialize($)
{
my ($hash) = @_;
foreach my $k (keys %light_device_codes) {
$light_device_c2b{$light_device_codes{$k}->[0]} = $k;
}
#$hash->{Match} = "^\\).*"; # 0x29
$hash->{Match} = "^(\\ |\\)).*"; # 0x20 or 0x29
$hash->{SetFn} = "TRX_LIGHT_Set";
$hash->{DefFn} = "TRX_LIGHT_Define";
$hash->{UndefFn} = "TRX_LIGHT_Undef";
$hash->{ParseFn} = "TRX_LIGHT_Parse";
$hash->{AttrList} = "IODev do_not_notify:1,0 loglevel:0,1,2,3,4,5,6";
}
#####################################
sub
TRX_LIGHT_SetState($$$$)
{
my ($hash, $tim, $vt, $val) = @_;
$val = $1 if($val =~ m/^(.*) \d+$/);
# to be done. Just accept everything right now.
#return "Undefined value $val" if(!defined($fs20_c2b{$val}));
return undef;
}
###################################
sub
TRX_LIGHT_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 $device_type = $hash->{TRX_LIGHT_type};
my $deviceid = $hash->{TRX_LIGHT_deviceid};
my $house;
my $unit;
if ($deviceid =~ /(.)(.*)/ ) {
$house = ord("$1");
$unit = $2;
} else {
Log 4,"TRX_LIGHT_Set wrong deviceid: name=$name device_type=$device_type, deviceid=$deviceid";
return "error set name=$name deviveid=$deviceid";
}
my $device_type_num = $light_device_c2b{$device_type};
if(!defined($device_type_num)) {
return "Unknown device_type, choose one of " .
join(" ", sort keys %light_device_c2b);
}
# Now check if the command is valid and retrieve the command id:
my $rec = $light_device_commands{$device_type_num};
my $i;
for ($i=0; $i <= $#$rec && ($rec->[$i] ne $command); $i++) { ;}
if($i > $#$rec) {
my $error = "Unknown command $command, choose one of " . join(" ", sort @$rec);
Log 4, $error;
return $error;
}
my $seqnr = 0;
my $cmnd = $i;
my $hex_command = sprintf "0710%02x%02x%02x%02x%02x00", $device_type_num, $seqnr, $house, $unit, $cmnd;
Log 1,"TRX_LIGHT_Set name=$name device_type=$device_type, deviceid=$deviceid house=$house, unit=$unit command=$command" if ($TRX_LIGHT_debug == 1);
Log 1,"TRX_LIGHT_Set hexline=$hex_command" if ($TRX_LIGHT_debug == 1);
#IOWrite($hash, pack('H*', $hex_command));
IOWrite($hash, "", $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_LIGHT_Define($$)
{
my ($hash, $def) = @_;
my @a = split("[ \t][ \t]*", $def);
my $a = int(@a);
if(int(@a) != 5 && int(@a) != 7) {
Log 1,"TRX_LIGHT wrong syntax '@a'. \nCorrect syntax is 'define <name> TRX_LIGHT <type> <deviceid> <devicelog> [<deviceid2> <devicelog2>]'";
return "wrong syntax: define <name> TRX_LIGHT <type> <deviceid> <devicelog> [<deviceid2> <devicelog2>]";
}
my $name = $a[0];
my $type = lc($a[2]);
my $deviceid = $a[3];
my $devicelog = $a[4];
$type = uc($type);
my $device_name = "TRX".$DOT.$type.$DOT.$deviceid;
if ($type ne "X10" && $type ne "ARC" && $type ne "MS14A" && $type ne "AB400D" && $type ne "WAVEMAN" && $type ne "EMW200" && $type ne "IMPULS") {
Log 1,"RFX10SEC define: wrong type: $type";
return "RFX10SEC: wrong type: $type";
}
$hash->{TRX_LIGHT_deviceid} = $deviceid;
$hash->{TRX_LIGHT_devicelog} = $devicelog;
$hash->{TRX_LIGHT_type} = $type;
#$hash->{TRX_LIGHT_CODE} = $deviceid;
$modules{TRX_LIGHT}{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".$DOT.$type.$DOT.$deviceid2;
$hash->{TRX_LIGHT_deviceid2} = $deviceid2;
$hash->{TRX_LIGHT_devicelog2} = $devicelog2;
#$hash->{TRX_LIGHT_CODE2} = $deviceid2;
$modules{TRX_LIGHT}{defptr2}{$device_name2} = $hash;
}
AssignIoPort($hash);
return undef;
}
#####################################
sub
TRX_LIGHT_Undef($$)
{
my ($hash, $name) = @_;
delete($modules{TRX_LIGHT}{defptr}{$name});
return undef;
}
#####################################
sub TRX_LIGHT_parse_X10 {
my $bytes = shift;
my $error;
#my $device;
my $subtype = $bytes->[1];
my $dev_type;
my $dev_reading;
my $rest;
if (exists $light_device_codes{$subtype}) {
my $rec = $light_device_codes{$subtype};
($dev_type, $dev_reading) = @$rec;
} else {
$error = sprintf "TRX_LIGHT: error undefined subtype=%02x", $subtype;
Log 1, $error;
return $error;
}
my $dev_first = "?";
my %x10_housecode =
(
0x41 => "A",
0x42 => "B",
0x43 => "C",
0x44 => "D",
0x45 => "E",
0x46 => "F",
0x47 => "G",
0x48 => "H",
0x49 => "I",
0x4A => "J",
0x4B => "K",
0x4C => "L",
0x4D => "M",
0x4E => "N",
0x4F => "O",
0x50 => "P",
);
my $devnr = $bytes->[3]; # housecode
if (exists $x10_housecode{$devnr}) {
$dev_first = $x10_housecode{$devnr};
} else {
$error = sprintf "TRX_SECURITY: x10_housecode wrong housecode=%02x", $devnr;
Log 1, $error;
return $error;
}
my $unit = $bytes->[4]; # unitcode
my $device = sprintf '%s%0d', $dev_first, $unit;
my $data = $bytes->[5];
my $hexdata = sprintf '%02x', $data;
my $command = "";
if ($data == 0xff) {
$command = "illegal_cmd";
} else {
if (exists $light_device_commands{$subtype}) {
my $code = $light_device_commands{$subtype};
if (exists $code->[$data]) {
$command = $code->[$data];
} else {
$error = sprintf "TRX_LIGHT: out of range for subtype=%02x data=%02x", $subtype, $data;
Log 1, $error;
return $error;
}
}
}
#my @res;
my $current = "";
#--------------
my $device_name = "TRX".$DOT.$dev_type.$DOT.$device;
Log 1, "TRX_LIGHT: device_name=$device_name data=$hexdata" if ($TRX_LIGHT_debug == 1);
my $firstdevice = 1;
my $def = $modules{TRX_LIGHT}{defptr}{$device_name};
if(!$def) {
$firstdevice = 0;
$def = $modules{TRX_LIGHT}{defptr2}{$device_name};
if (!$def) {
Log 1, "UNDEFINED $device_name TRX_SECURITY $dev_type $device $dev_reading" if ($TRX_LIGHT_debug == 1);
Log 3, "TRX_LIGHT: TRX_LIGHT Unknown device $device_name, please define it";
return "UNDEFINED $device_name TRX_LIGHT $dev_type $device $dev_reading";
}
}
# Use $def->{NAME}, because the device may be renamed:
my $name = $def->{NAME};
Log 1, "TRX_LIGHT: $name devn=$device_name first=$firstdevice command=$command, cmd=$hexdata" if ($TRX_LIGHT_debug == 1);
my $n = 0;
my $tm = TimeNow();
my $val = "";
my $device_type = $def->{TRX_LIGHT_type};
my $sensor = "";
if ($device_type eq "MS14A") {
# for ms14a behave like x10, but flip second deviceid
$device_type = "X10";
if ($firstdevice == 1) {
$command = ($command eq "on") ? "alert" : "normal" ;
} else {
$command = ($command eq "on") ? "off" : "on" ;
}
}
#if ($device_type eq "X10") {
if (1) {
# try to use it for all types:
$current = $command;
$sensor = $firstdevice == 1 ? $def->{TRX_LIGHT_devicelog} : $def->{TRX_LIGHT_devicelog2};
$val .= $current;
$def->{READINGS}{$sensor}{TIME} = $tm;
$def->{READINGS}{$sensor}{VAL} = $current;
$def->{CHANGED}[$n++] = $sensor . ": " . $current;
} else {
Log 1, "TRX_LIGHT: X10 error unknown sensor type=$device_type $name devn=$device_name first=$firstdevice type=$command, user=$device (hex $hexdata)";
return "TRX_LIGHT X10 error unknown sensor type=$device_type for $device_name device=$device";
}
if (($firstdevice == 1) && $val) {
$def->{STATE} = $val;
$def->{TIME} = $tm;
$def->{CHANGED}[$n++] = $val;
}
DoTrigger($name, undef);
return "";
}
#####################################
sub
TRX_LIGHT_Parse($$)
{
my ($iohash, $hexline) = @_;
my $time = time();
# convert to binary
my $msg = pack('H*', $hexline);
if ($time_old ==0) {
Log 5, "TRX_LIGHT: decoding delay=0 hex=$hexline";
} else {
my $time_diff = $time - $time_old ;
Log 5, "TRX_LIGHT: 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];
#Log 1, "TRX_LIGHT: num_bytes=$num_bytes hex=$hexline type=$type" if ($TRX_LIGHT_debug == 1);
my $res = "";
if ($type == 0x10) {
Log 1, "TRX_LIGHT: X10 num_bytes=$num_bytes hex=$hexline" if ($TRX_LIGHT_debug == 1);
$res = TRX_LIGHT_parse_X10(\@rfxcom_data_array);
Log 1, "TRX_LIGHT: unsupported hex=$hexline" if ($res ne "" && $res !~ /^UNDEFINED.*/);
return $res;
} else {
Log 0, "TRX_LIGHT: not implemented num_bytes=$num_bytes hex=$hexline";
}
return "";
}
1;

401
fhem/FHEM/46_TRX_SECURITY.pm Executable file
View File

@ -0,0 +1,401 @@
#################################################################################
# 46_TRX_SECURITY.pm
#
# Modul for FHEM for X10, KD101, Visonic
# - X10 security messages tested for
# - ds10a: X10 Door / Window Sensor or compatible devices
# - ss10a: X10 motion sensor
# - sd90: Marmitek smoke detector
# - kr18: X10 remote control
#
##################################
#
# Willi Herzig, 2012
#
# 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.
##################################
#
# Some code from X10security code is derived from http://www.xpl-perl.org.uk/.
# xpl-perl/lib/xPL/RF/X10Security.pm:
# Thanks a lot to Mark Hindess who wrote xPL.
#
#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 protocols
# 5: log decoding hexlines for debugging
#
# $Id: 43_TRX_SECURITY.pm 1098 2011-11-12 07:51:08Z rudolfkoenig $
package main;
use strict;
use warnings;
use Switch;
# Debug this module? YES = 1, NO = 0
my $TRX_SECURITY_debug = 0;
my $time_old = 0;
my $TRX_SECURITY_type_default = "ds10a";
my $DOT = q{_};
sub
TRX_SECURITY_Initialize($)
{
my ($hash) = @_;
$hash->{Match} = "^(\\ |\\)).*"; # 0x20 or 0x29
$hash->{DefFn} = "TRX_SECURITY_Define";
$hash->{UndefFn} = "TRX_SECURITY_Undef";
$hash->{ParseFn} = "TRX_SECURITY_Parse";
$hash->{AttrList} = "IODev do_not_notify:1,0 loglevel:0,1,2,3,4,5,6";
}
#####################################
sub
TRX_SECURITY_Define($$)
{
my ($hash, $def) = @_;
my @a = split("[ \t][ \t]*", $def);
my $a = int(@a);
if(int(@a) != 5 && int(@a) != 7) {
Log 1,"TRX_SECURITY wrong syntax '@a'. \nCorrect syntax is 'define <name> TRX_SECURITY <type> <deviceid> <devicelog> [<deviceid2> <devicelog2>]'";
return "wrong syntax: define <name> TRX_SECURITY <type> <deviceid> <devicelog> [<deviceid2> <devicelog2>]";
}
my $name = $a[0];
my $type = lc($a[2]);
my $deviceid = $a[3];
my $devicelog = $a[4];
$type = uc($type);
my $device_name = "TRX".$DOT.$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_REMOTE") {
Log 1,"RFX10SEC define: wrong type: $type";
return "RFX10SEC: 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 $bytes = shift;
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", "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";
Log 1, $error;
return $error;
}
} else {
$error = "TRX_SECURITY: error undefined subtype=$subtype";
Log 1, $error;
return $error;
}
#Log 4, "device_type=$device_type";
#--------------
my $device_name = "TRX".$DOT.$dev_type.$DOT.$device;
Log 4, "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) {
Log 1, "UNDEFINED $device_name TRX_SECURITY $dev_type $device $dev_reading";
Log 3, "TRX_SECURITY: TRX_SECURITY 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};
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', 'normal', '', ''],
0x08 => ['X10Sec', 'tamper', '', ''],
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-on', '', ''], # kr18
0x11 => ['X10Sec', 'ButtonA-off', '', ''], # kr18
0x12 => ['X10Sec', 'ButtonB-on', '', ''], # kr18
0x13 => ['X10Sec', 'ButtonB-off', '', ''], # kr18
0x14 => ['X10Sec', 'dark', '', ''],
0x15 => ['X10Sec', 'light', '', ''],
0x16 => ['X10Sec', 'normal', '', 'batt_low'],
0x17 => ['X10Sec', 'pair KD101', '', ''],
);
my $command = "";
my $type = "";
my $delay = "";
my $battery = "";
my @res;
if (exists $x10_security{$data}) {
my $rec = $x10_security{$data};
if (ref $rec) {
($type, $command, $delay, $battery) = @$rec;
} else {
$command = $rec;
}
} else {
Log 1, "TRX_SECURITY undefined command cmd=$data device-nr=$device, hex=$hexdata";
return "TRX_SECURITY undefined command";
}
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 {
Log 1,"TRX-X10: X10Sec unkown battery_level=$battery_level";
}
}
my $current = "";
Log 1, "TRX_SECURITY: $name devn=$device_name first=$firstdevice subtype=$subtype command=$command, delay=$delay, batt=$battery cmd=$hexdata" if ($TRX_SECURITY_debug == 1);
my $n = 0;
my $tm = TimeNow();
my $val = "";
my $device_type = $def->{TRX_SECURITY_type};
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")) {
$current = "Error";
$current = "Open" if ($command eq "alert");
$current = "Closed" if ($command eq "normal");
}
if (($dev_type ne "kr18") || ($dev_type ne "VISONIC_REMOTE")) {
if ($firstdevice == 1) {
$val .= $current;
}
$def->{READINGS}{$sensor}{TIME} = $tm;
$def->{READINGS}{$sensor}{VAL} = $current;
$def->{CHANGED}[$n++] = $sensor . ": " . $current;
if (($def->{STATE} ne $val)) {
$sensor = "statechange";
$def->{READINGS}{$sensor}{TIME} = $tm;
$def->{READINGS}{$sensor}{VAL} = $current;
$def->{CHANGED}[$n++] = $sensor . ": " . $current;
}
} else {
# kr18 remote control or VISONIC_REMOTE
$current = $command;
#$sensor = $def->{TRX_SECURITY_devicelog};
$val = $current;
#$def->{READINGS}{$sensor}{TIME} = $tm;
#$def->{READINGS}{$sensor}{VAL} = $current;
#$def->{CHANGED}[$n++] = $sensor . ": " . $current;
my @cmd_split = split(/-/, $command);
$sensor = $cmd_split[0];
$current = $cmd_split[1];
$def->{READINGS}{$sensor}{TIME} = $tm;
$def->{READINGS}{$sensor}{VAL} = $current;
$def->{CHANGED}[$n++] = $sensor . ": " . $current;
}
if ($battery ne "") {
$sensor = "battery";
$current = "Error";
$current = "ok" if ($battery eq "batt_ok");
$current = "low" if ($battery eq "batt_low");
$def->{READINGS}{$sensor}{TIME} = $tm;
$def->{READINGS}{$sensor}{VAL} = $current;
$def->{CHANGED}[$n++] = $sensor . ": " . $current;
}
if ($delay ne '') {
$sensor = "delay";
$current = "Error";
$current = "min" if ($delay eq "min_delay");
$current = "max" if ($delay eq "max_delay");
$def->{READINGS}{$sensor}{TIME} = $tm;
$def->{READINGS}{$sensor}{VAL} = $current;
$def->{CHANGED}[$n++] = $sensor . ": " . $current;
}
if (($firstdevice == 1) && $val) {
$def->{STATE} = $val;
$def->{TIME} = $tm;
$def->{CHANGED}[$n++] = $val;
}
DoTrigger($name, undef);
return "";
}
sub
TRX_SECURITY_Parse($$)
{
my ($iohash, $hexline) = @_;
my $time = time();
# convert to binary
my $msg = pack('H*', $hexline);
if ($time_old ==0) {
Log 5, "TRX_SECURITY: decoding delay=0 hex=$hexline";
} else {
my $time_diff = $time - $time_old ;
Log 5, "TRX_SECURITY: 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];
#Log 1, "TRX_SECURITY: X10Sec num_bytes=$num_bytes hex=$hexline type=$type" if ($TRX_SECURITY_debug == 1);
my $res = "";
if ($type == 0x20) {
Log 1, "TRX_SECURITY: X10Sec num_bytes=$num_bytes hex=$hexline" if ($TRX_SECURITY_debug == 1);
$res = TRX_SECURITY_parse_X10Sec(\@rfxcom_data_array);
Log 1, "TRX_SECURITY: unsupported hex=$hexline" if ($res ne "" && $res !~ /^UNDEFINED.*/);
return $res;
} else {
Log 0, "TRX_SECURITY: not implemented num_bytes=$num_bytes hex=$hexline";
}
return "";
}
1;

828
fhem/FHEM/46_TRX_WEATHER.pm Executable file
View File

@ -0,0 +1,828 @@
#################################################################################
# 46_TRX_WEATHER.pm
# Module for FHEM to decode weather sensor messages for RFXtrx
#
# The following devices are implemented to be received:
#
# 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
#
# 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
#
# 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
#
# wind sensors (WIND):
# * "WTGR800_A" is WTGR800
# * "WGR800_A" is WGR800
# * "WGR918_A" is STR918, WGR918
# * "TFA_WIND" is TFA
#
# derived from 41_OREGON.pm
#
# Willi Herzig, 2012
#
# 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.
#
##################################
#
# values for "set global verbose"
# 4: log unknown protocols
# 5: log decoding hexlines for debugging
#
# $Id:
package main;
use strict;
use warnings;
# Hex-Debugging into READING hexline? YES = 1, NO = 0
my $TRX_HEX_debug = 0;
my $time_old = 0;
sub
TRX_WEATHER_Initialize($)
{
my ($hash) = @_;
$hash->{Match} = "^[\x38-\x78].*";
#$hash->{Match} = "^[^\x30]";
$hash->{DefFn} = "TRX_WEATHER_Define";
$hash->{UndefFn} = "TRX_WEATHER_Undef";
$hash->{ParseFn} = "TRX_WEATHER_Parse";
$hash->{AttrList} = "IODev do_not_notify:1,0 loglevel:0,1,2,3,4,5,6";
}
#####################################
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 <name> TRX_WEATHER code" if(int(@a) != 3);
my $name = $a[0];
my $code = $a[2];
$hash->{CODE} = $code;
#$modules{TRX_WEATHER}{defptr}{$name} = $hash;
$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;
}
#########################################
# From xpl-perl/lib/xPL/Util.pm:
sub hi_nibble {
($_[0]&0xf0)>>4;
}
sub lo_nibble {
$_[0]&0xf;
}
sub nibble_sum {
my $c = $_[0];
my $s = 0;
foreach (0..$_[0]-1) {
$s += hi_nibble($_[1]->[$_]);
$s += lo_nibble($_[1]->[$_]);
}
$s += hi_nibble($_[1]->[$_[0]]) if (int($_[0]) != $_[0]);
return $s;
}
# --------------------------------------------
# From xpl-perl/lib/xPL/RF/Oregon.pm:
# This function creates a simple key from a device type and message
# length (in bits). It is used to as the index for the parts table.
sub type_length_key {
($_[0] << 8) + $_[1]
}
# --------------------------------------------
# sensor types
my %types =
(
# TEMP
type_length_key(0x50, 0x08) =>
{
part => 'TEMP', method => \&common_temp,
},
# TEMP HYDRO
type_length_key(0x52, 0x0a) =>
{
part => 'TEMPHYDRO', method => \&common_temphydro,
},
# TEMP HYDRO BARO
type_length_key(0x54, 0x0d) =>
{
part => 'TEMPHYDROBARO', method => \&common_temphydrobaro,
},
# RAIN
type_length_key(0x55, 0x0b) =>
{
part => 'RAIN', method => \&common_rain,
},
# WIND
type_length_key(0x56, 0x10) =>
{
part => 'WIND', method => \&common_anemometer,
},
);
# --------------------------------------------
#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 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 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 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 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,
}
}
sub 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 @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 uv_string {
$uv_str[$_[0]] || 'dangerous';
}
# Test if to use longid for device type
sub 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 common_anemometer {
my $type = shift;
my $longids = shift;
my $bytes = shift;
my $subtype = sprintf "%02x", $bytes->[1];
#Log 1,"subtype=$subtype";
my $dev_type;
my %devname =
( # HEXSTRING => "NAME"
0x01 => "WTGR800_A",
0x02 => "WGR800_A",
0x03 => "WGR918_A",
0x04 => "TFA_WIND",
);
if (exists $devname{$bytes->[1]}) {
$dev_type = $devname{$bytes->[1]};
} else {
Log 1,"TRX_WEATHER: error undefined subtype=$subtype";
my @res = ();
return @res;
}
#my $seqnbr = sprintf "%02x", $bytes->[2];
#Log 1,"seqnbr=$seqnbr";
my $dev_str = $dev_type;
if (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[$dir/22.5];
my $avspeed = $bytes->[7]*256 + $bytes->[8];
my $speed = $bytes->[9]*256 + $bytes->[10];
push @res, {
device => $dev_str,
type => 'speed',
current => $speed,
average => $avspeed,
units => 'mps',
} , {
device => $dev_str,
type => 'direction',
current => $dir,
string => $dirname,
units => 'degrees',
}
;
simple_battery($bytes, $dev_str, \@res, 15);
return @res;
}
# -----------------------------
sub common_temp {
my $type = shift;
my $longids = shift;
my $bytes = shift;
my $subtype = sprintf "%02x", $bytes->[1];
#Log 1,"subtype=$subtype";
my $dev_type;
my %devname =
( # HEXSTRING => "NAME"
0x01 => "THR128",
0x02 => "THGR132N", # was THGR228N,
0x03 => "THWR800",
0x04 => "RTHN318",
0x05 => "TX3_T",
);
if (exists $devname{$bytes->[1]}) {
$dev_type = $devname{$bytes->[1]};
} else {
Log 1,"RFX_WEATHER: error undefined subtype=$subtype";
my @res = ();
return @res;
}
#my $seqnbr = sprintf "%02x", $bytes->[2];
#Log 1,"seqnbr=$seqnbr";
my $dev_str = $dev_type;
if (use_longid($longids,$dev_type)) {
$dev_str .= $DOT.sprintf("%02x", $bytes->[3]);
}
if ($bytes->[4] > 0) {
$dev_str .= $DOT.sprintf("%d", $bytes->[4]);
}
#Log 1,"dev_str=$dev_str";
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', };
}
temperature($bytes, $dev_str, \@res, 5);
simple_battery($bytes, $dev_str, \@res, 7);
return @res;
}
# -----------------------------
sub common_temphydro {
my $type = shift;
my $longids = shift;
my $bytes = shift;
my $subtype = sprintf "%02x", $bytes->[1];
#Log 1,"subtype=$subtype";
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",
);
if (exists $devname{$bytes->[1]}) {
$dev_type = $devname{$bytes->[1]};
} else {
Log 1,"RFX_WEATHER: error undefined subtype=$subtype";
my @res = ();
return @res;
}
my $dev_str = $dev_type;
if (use_longid($longids,$dev_type)) {
$dev_str .= $DOT.sprintf("%02x", $bytes->[3]);
}
if ($bytes->[4] > 0) {
$dev_str .= $DOT.sprintf("%d", $bytes->[4]);
}
#Log 1,"dev_str=$dev_str";
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', };
}
temperature($bytes, $dev_str, \@res, 5);
humidity($bytes, $dev_str, \@res, 7);
simple_battery($bytes, $dev_str, \@res, 9);
return @res;
}
# -----------------------------
sub common_temphydrobaro {
my $type = shift;
my $longids = shift;
my $bytes = shift;
my $subtype = sprintf "%02x", $bytes->[1];
#Log 1,"subtype=$subtype";
my $dev_type;
my %devname =
( # HEXSTRING => "NAME"
0x01 => "BTHR918",
0x02 => "BTHR918N",
);
if (exists $devname{$bytes->[1]}) {
$dev_type = $devname{$bytes->[1]};
} else {
Log 1,"RFX_WEATHER: error undefined subtype=$subtype";
my @res = ();
return @res;
}
my $dev_str = $dev_type;
if (use_longid($longids,$dev_type)) {
$dev_str .= $DOT.sprintf("%02x", $bytes->[3]);
}
if ($bytes->[4] > 0) {
$dev_str .= $DOT.sprintf("%d", $bytes->[4]);
}
#Log 1,"dev_str=$dev_str";
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', };
}
temperature($bytes, $dev_str, \@res, 5);
humidity($bytes, $dev_str, \@res, 7);
pressure($bytes, $dev_str, \@res, 9);
simple_battery($bytes, $dev_str, \@res, 12);
return @res;
}
# -----------------------------
sub common_rain {
my $type = shift;
my $longids = shift;
my $bytes = shift;
my $subtype = sprintf "%02x", $bytes->[1];
#Log 1,"subtype=$subtype";
my $dev_type;
my %devname =
( # HEXSTRING => "NAME"
0x01 => "RGR918",
0x02 => "PCR800",
0x03 => "TFA_RAIN",
);
if (exists $devname{$bytes->[1]}) {
$dev_type = $devname{$bytes->[1]};
} else {
Log 1,"RFX_WEATHER: error undefined subtype=$subtype";
my @res = ();
return @res;
}
my $dev_str = $dev_type;
if (use_longid($longids,$dev_type)) {
$dev_str .= $DOT.sprintf("%02x", $bytes->[3]);
}
if ($bytes->[4] > 0) {
$dev_str .= $DOT.sprintf("%d", $bytes->[4]);
}
#Log 1,"dev_str=$dev_str";
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];
my $train = $bytes->[7]*256*256 + $bytes->[8]*256 + $bytes->[9];
push @res, {
device => $dev_str,
type => 'rain',
current => $rain,
units => 'mm/h',
} ;
push @res, {
device => $dev_str,
type => 'train',
current => $train,
units => 'mm',
};
battery($bytes, $dev_str, \@res, 10);
return @res;
}
sub raw {
$_[0]->{raw} or $_[0]->{raw} = pack 'H*', $_[0]->{hex};
}
# -----------------------------
sub
TRX_WEATHER_Parse($$)
{
my ($iohash, $hexline) = @_;
#my $hashname = $iohash->{NAME};
#my $longid = AttrVal($hashname,"longids","");
#Log 1,"2: name=$hashname, attr longids = $longid";
my $longids = 0;
if (defined($attr{$iohash->{NAME}}{longids})) {
$longids = $attr{$iohash->{NAME}}{longids};
#Log 1,"0: attr longids = $longids";
}
my $time = time();
# convert to binary
my $msg = pack('H*', $hexline);
if ($time_old ==0) {
Log 5, "TRX_WEATHER: decoding delay=0 hex=$hexline";
} else {
my $time_diff = $time - $time_old ;
Log 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);
#Log 1, "TRX_WEATHER: sensor_id=$sensor_id";
my $key = type_length_key($type, $num_bytes);
my $rec = $types{$key} || $types{$key&0xfffff};
unless ($rec) {
#Log 3, "TRX_WEATHER: ERROR: Unknown sensor_id=$sensor_id num_bytes=$num_bytes message='$hexline'.";
Log 4, "TRX_WEATHER: ERROR: Unknown sensor_id=$sensor_id message='$hexline'";
Log 1, "TRX_WEATHER: ERROR: Unknown sensor_id=$sensor_id message='$hexline'";
return "TRX_WEATHER: ERROR: Unknown sensor_id=$sensor_id \n";
}
my $method = $rec->{method};
unless ($method) {
Log 4, "TRX_WEATHER: Possible message from Oregon part '$rec->{part}'";
Log 4, "TRX_WEATHER: sensor_id=$sensor_id";
return;
}
my @res;
if (! defined(&$method)) {
Log 4, "TRX_WEATHER: Error: Unknown function=$method. Please define it in file $0";
Log 4, "TRX_WEATHER: sensor_id=$sensor_id\n";
return "TRX_WEATHER: Error: Unknown function=$method. Please define it in file $0";
} else {
#Log 1, "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};
#Log 1, "device_name=$device_name";
if (! defined($device_name)) {
Log 4, "TRX_WEATHER: error device_name undefined\n";
return "TRX_WEATHER: Error: Unknown devicename.";
}
my $def = $modules{TRX_WEATHER}{defptr}{"$device_name"};
if(!$def) {
Log 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};
#Log 1, "name=$new_name";
my $n = 0;
my $tm = TimeNow();
my $i;
my $val = "";
my $sensor = "";
foreach $i (@res){
#print "!> i=".$i."\n";
#printf "%s\t",$i->{device};
if ($i->{type} eq "temp") {
#printf "Temperatur %2.1f %s ; ",$i->{current},$i->{units};
$val .= "T: ".$i->{current}." ";
$sensor = "temperature";
$def->{READINGS}{$sensor}{TIME} = $tm;
$def->{READINGS}{$sensor}{VAL} = $i->{current};
$def->{CHANGED}[$n++] = $sensor . ": " . $i->{current};
}
elsif ($i->{type} eq "humidity") {
#printf "Luftfeuchtigkeit %d%s, %s ;",$i->{current},$i->{units},$i->{string};
$val .= "H: ".$i->{current}." ";
$sensor = "humidity";
$def->{READINGS}{$sensor}{TIME} = $tm;
$def->{READINGS}{$sensor}{VAL} = $i->{current};
$def->{CHANGED}[$n++] = $sensor . ": " . $i->{current};;
}
elsif ($i->{type} eq "battery") {
#printf "Batterie %d%s; ",$i->{current},$i->{units};
my $tmp_battery = $i->{current};
my @words = split(/\s+/,$i->{current});
$val .= "BAT: ".$words[0]." "; #user only first word
$sensor = "battery";
$def->{READINGS}{$sensor}{TIME} = $tm;
$def->{READINGS}{$sensor}{VAL} = $i->{current};
$def->{CHANGED}[$n++] = $sensor . ": " . $i->{current};;
}
elsif ($i->{type} eq "pressure") {
#printf "Luftdruck %d %s, Vorhersage=%s ; ",$i->{current},$i->{units},$i->{forecast};
# do not add it due to problems with hms.gplot
$val .= "P: ".$i->{current}." ";
$sensor = "pressure";
$def->{READINGS}{$sensor}{TIME} = $tm;
$def->{READINGS}{$sensor}{VAL} = $i->{current};
$def->{CHANGED}[$n++] = $sensor . ": " . $i->{current};;
$sensor = "forecast";
$def->{READINGS}{$sensor}{TIME} = $tm;
$def->{READINGS}{$sensor}{VAL} = $i->{forecast};
$def->{CHANGED}[$n++] = $sensor . ": " . $i->{forecast};;
}
elsif ($i->{type} eq "speed") {
$val .= "W: ".$i->{current}." ";
$val .= "WA: ".$i->{average}." ";
$sensor = "wind_speed";
$def->{READINGS}{$sensor}{TIME} = $tm;
$def->{READINGS}{$sensor}{VAL} = $i->{current};
$def->{CHANGED}[$n++] = $sensor . ": " . $i->{current};;
$sensor = "wind_avspeed";
$def->{READINGS}{$sensor}{TIME} = $tm;
$def->{READINGS}{$sensor}{VAL} = $i->{average};
$def->{CHANGED}[$n++] = $sensor . ": " . $i->{average};;
}
elsif ($i->{type} eq "direction") {
$val .= "WD: ".$i->{current}." ";
$val .= "WDN: ".$i->{string}." ";
$sensor = "wind_dir";
$def->{READINGS}{$sensor}{TIME} = $tm;
$def->{READINGS}{$sensor}{VAL} = $i->{current} . " " . $i->{string};
$def->{CHANGED}[$n++] = $sensor . ": " . $i->{current} . " " . $i->{string};;
}
elsif ($i->{type} eq "rain") {
$val .= "RR: ".$i->{current}." ";
$sensor = "rain_rate";
$def->{READINGS}{$sensor}{TIME} = $tm;
$def->{READINGS}{$sensor}{VAL} = $i->{current};
$def->{CHANGED}[$n++] = $sensor . ": " . $i->{current};;
}
elsif ($i->{type} eq "train") {
$val .= "TR: ".$i->{current}." ";
$sensor = "rain_total";
$def->{READINGS}{$sensor}{TIME} = $tm;
$def->{READINGS}{$sensor}{VAL} = $i->{current};
$def->{CHANGED}[$n++] = $sensor . ": " . $i->{current};;
}
elsif ($i->{type} eq "flip") {
$val .= "F: ".$i->{current}." ";
$sensor = "rain_flip";
$def->{READINGS}{$sensor}{TIME} = $tm;
$def->{READINGS}{$sensor}{VAL} = $i->{current};
$def->{CHANGED}[$n++] = $sensor . ": " . $i->{current};;
}
elsif ($i->{type} eq "uv") {
$val .= "UV: ".$i->{current}." ";
$val .= "UVR: ".$i->{risk}." ";
$sensor = "uv_val";
$def->{READINGS}{$sensor}{TIME} = $tm;
$def->{READINGS}{$sensor}{VAL} = $i->{current};
$def->{CHANGED}[$n++] = $sensor . ": " . $i->{current};;
$sensor = "uv_risk";
$def->{READINGS}{$sensor}{TIME} = $tm;
$def->{READINGS}{$sensor}{VAL} = $i->{risk};
$def->{CHANGED}[$n++] = $sensor . ": " . $i->{risk};;
}
elsif ($i->{type} eq "hexline") {
$sensor = "hexline";
$def->{READINGS}{$sensor}{TIME} = $tm;
$def->{READINGS}{$sensor}{VAL} = $i->{current};
$def->{CHANGED}[$n++] = $sensor . ": " . $i->{current};;
}
else {
print "\nTRX_WEATHER: Unknown: ";
print "Type: ".$i->{type}.", ";
print "Value: ".$i->{current}."\n";
}
}
if ("$val" ne "") {
# remove heading and trailing space chars from $val
$val =~ s/^\s+|\s+$//g;
$def->{STATE} = $val;
$def->{TIME} = $tm;
$def->{CHANGED}[$n++] = $val;
}
#
#$def->{READINGS}{state}{TIME} = $tm;
#$def->{READINGS}{state}{VAL} = $val;
#$def->{CHANGED}[$n++] = "state: ".$val;
DoTrigger($name, undef);
return $val;
}
1;

View File

@ -489,4 +489,8 @@
- Sun Jan 29 2012 (Maz Rashid)
- Improving 10_EIB.pm by introducing Get and interpreting received value according
to the selected model (based on datapoint types.)
- Introduced documentation for TUL / EIB modules.
- Introduced documentation for TUL / EIB modules.
- Fr Feb 24 2012 (Willi)
- New modules TRX for RFXCOM RFXtrx transceiver