############################################################################# # # 21_N4HMODULE.pm # # net4home Busconnector Device # # (c) 2014-2023 Oliver Koerber # # $Id$ # ############################################################################## package main; use strict; use warnings; use POSIX; use SetExtensions; sub N4HMODULE_Set($@); sub N4HMODULE_Get ($$@); sub N4HMODULE_Update($@); sub N4HMODULE_DbLog_splitFn($$); my $n4hmodule_Version = "1.0.4.0 - 07.11.2023"; my %OT_devices = ( "1" => {"name" => "leer", "OTcanSet" => "false", "OTcanReq" => "false", "fields" => [] }, "2" => {"name" => "Eingang, Binär, Sx", "OTcanSet" => "false", "OTcanReq" => "true", "fields" => [] }, "3" => {"name" => "Ausgang, Binär, Relais", "OTcanSet" => "true", "OTcanReq" => "true", "fields" => [ { "cmd" => "350000", "ID" => "0", "text" => "UM", "Type" => "Button" , "set" => "toggle:noArg" }, { "cmd" => "326400", "ID" => "1", "text" => "EIN", "Type" => "Button" , "set" => "on:noArg" }, { "cmd" => "320000", "ID" => "2", "text" => "AUS", "Type" => "Button" , "set" => "off:noArg" }, { "cmd" => "428100", "ID" => "3", "text" => "Zwangsgeführt EIN", "Type" => "Button" , "set" => "lockon:noArg" }, { "cmd" => "428000", "ID" => "4", "text" => "Zwangsgeführt AUS", "Type" => "Button" , "set" => "lockoff:noArg" }, { "cmd" => "420000", "ID" => "5", "text" => "Zwangsführung deaktiv", "Type" => "Button" , "set" => "unlock:noArg" }, ]}, "4" => {"name" => "Ausgang, Binär, Timer, Relais", "OTcanSet" => "true", "OTcanReq" => "true", "fields" => [ { "cmd" => "350000", "ID" => "0", "text" => "EIN für Dauer: Zeit 1", "Type" => "Button" , "set" => "toggle:noArg" }, { "cmd" => "320000", "ID" => "2", "text" => "AUS", "Type" => "Button" , "set" => "off:noArg" }, { "cmd" => "428100", "ID" => "3", "text" => "Zwangsgeführt EIN", "Type" => "Button" , "set" => "lockon:noArg" }, { "cmd" => "428000", "ID" => "4", "text" => "Zwangsgeführt AUS", "Type" => "Button" , "set" => "lockoff:noArg" }, { "cmd" => "420000", "ID" => "5", "text" => "Zwangsführung deaktiv", "Type" => "Button" , "set" => "unlock:noArg" }, ]}, "5" => {"name" => "Ausgang, Dimmer", "OTcanSet" => "true", "OTcanReq" => "true", "fields" => [ { "cmd" => "320000", "ID" => "3", "text" => "AUS", "Type" => "Button" , "set" => "off:noArg" }, { "cmd" => "326500", "ID" => "4", "text" => "EIN mit letztem Wert", "Type" => "Button" , "set" => "on:noArg" }, { "cmd" => "350000", "ID" => "5", "text" => "UM", "Type" => "Button" , "set" => "toggle:noArg" }, { "cmd" => "330000", "ID" => "7", "text" => "Heller", "Type" => "Button" , "set" => "dimup:noArg" }, { "cmd" => "340000", "ID" => "8", "text" => "Dunkler", "Type" => "Button" , "set" => "dimdown:noArg" }, { "cmd" => "428100", "ID" => "9", "text" => "Zwangsgeführt EIN", "Type" => "Button" , "set" => "lockon:noArg" }, { "cmd" => "428000", "ID" => "10","text" => "Zwangsgeführt AUS", "Type" => "Button" , "set" => "lockoff:noArg" }, { "cmd" => "32" , "ID" => "10","text" => "Wert setzen auf", "Type" => "Button" , "set" => "pct:slider,0,1,100" }, { "cmd" => "420000", "ID" => "11", "Text" => "Zwangsführung deaktiv", "Type" => "Button" , "set" => "unlock:noArg" }, ]}, "24" => {"name" => "Messwert,Temperatur", "OTcanSet" => "false", "OTcanReq" => "true", "fields" => [] }, "25" => {"name" => "Messwert,Helligkeit", "OTcanSet" => "false", "OTcanReq" => "true", "fields" => [] }, "26" => {"name" => "Messwert,Feuchte", "OTcanSet" => "false", "OTcanReq" => "true", "fields" => [] }, "34" => {"name" => "TLH-Regler H,Sollwert,Temperatur", "OTcanSet" => "true", "OTcanReq" => "true", "fields" => [ { "cmd" => "3B0000", "ID" => "0", "Text" => "AUS", "Type" => "Button" , "set" => "off:noArg" }, { "cmd" => "3B0100", "ID" => "1", "Text" => "Heizen", "Type" => "Button" , "set" => "heat:noArg" }, { "cmd" => "3B0200", "ID" => "2", "Text" => "Kühlen", "Type" => "Button" , "set" => "cool:noArg" }, { "cmd" => "3B0300", "ID" => "3", "Text" => "Auto", "Type" => "Button" , "set" => "auto:noArg" }, { "cmd" => "3200", "ID" => "4", "Text" => "Sollwert", "Type" => "Button" , "set" => "desired-temperature:slider,4,0.5,30,1" }, { "cmd" => "3B1400", "ID" => "5", "Text" => "Tagwert aktivieren", "Type" => "Button" , "set" => "comfort:noArg" }, { "cmd" => "3B1E00", "ID" => "6", "Text" => "Nachtwert aktivieren", "Type" => "Button" , "set" => "eco:noArg" }, { "cmd" => "3B1500", "ID" => "7", "Text" => "Sollwert -> Tagwert", "Type" => "Button" , "set" => "setdesired2comfort:noArg" }, { "cmd" => "3B1F00", "ID" => "8", "Text" => "Sollwert -> Nachtwert","Type" => "Button" , "set" => "setdesired2eco:noArg" }, ]}, "95" => {"name" => "Ausgang, Jal, Motor AJ3", "OTcanSet" => "true", "OTcanReq" => "true", "fields" => [ { "cmd" => "350000", "ID" => "0", "text" => "Weiterschalten", "Type" => "Button" , "set" => "toggle:noArg" }, { "cmd" => "320000", "ID" => "1", "text" => "STOP", "Type" => "Button" , "set" => "stop:noArg" }, { "cmd" => "320300", "ID" => "2", "text" => "AUF", "Type" => "Button" , "set" => "up:noArg" }, { "cmd" => "320100", "ID" => "3", "text" => "AB", "Type" => "Button" , "set" => "down:noArg" }, { "cmd" => "428300", "ID" => "4", "text" => "Sperre OFFEN", "Type" => "Button" , "set" => "lockup:noArg" }, { "cmd" => "428100", "ID" => "5", "text" => "Sperre GESCHLOSSEN", "Type" => "Button" , "set" => "lockdown:noArg" }, { "cmd" => "420000", "ID" => "6", "text" => "Sperre freigeben", "Type" => "Button" , "set" => "unlock:noArg" }, ]}, "100" => {"name" => "Safety Basis", "OTcanSet" => "true", "OTcanReq" => "true", "fields" => [ { "cmd" => "320000", "ID" => "0", "text" => "AUS", "Type" => "Button" , "set" => "off:noArg" }, { "cmd" => "320100", "ID" => "1", "text" => "Intern scharf 1", "Type" => "Button" , "set" => "intern1:noArg" }, { "cmd" => "320200", "ID" => "2", "text" => "Intern scharf 1", "Type" => "Button" , "set" => "intern2:noArg" }, { "cmd" => "320300", "ID" => "3", "text" => "Extern scharf", "Type" => "Button" , "set" => "extern:noArg" }, ]}, "210" => {"name" => "UP-RF Absender", "OTcanSet" => "false", "OTcanReq" => "true", "fields" => [] }, "240" => {"name" => "Messwert,Wind", "OTcanSet" => "false", "OTcanReq" => "true", "fields" => [] }, "242" => {"name" => "Messwert,Luftdruck", "OTcanSet" => "false", "OTcanReq" => "true", "fields" => [] }, "245" => {"name" => "Messwert,Regenmenge", "OTcanSet" => "false", "OTcanReq" => "true", "fields" => [] }, "310" => {"name" => "Stromzähler", "OTcanSet" => "false", "OTcanReq" => "true", "fields" => [ { "cmd" => "330000", "ID" => "0", "Text" => "Inc", "Type" => "Button" , "set" => "inc" }, ]}, "348" => {"name" => "Zähler", "OTcanSet" => "false", "OTcanReq" => "true", "fields" => [ { "cmd" => "330000", "ID" => "0", "Text" => "+1", "Type" => "Button" , "set" => "inc"}, { "cmd" => "340000", "ID" => "1", "Text" => "-1", "Type" => "Button" , "set" => "dec" }, { "cmd" => "320000", "ID" => "3", "Text" => "Zählerwert reset", "Type" => "Button" , "set" => "reset" }, { "cmd" => "360000", "ID" => "4", "Text" => "Zählerwert lesen (nur LCD)", "Type" => "Button" }, ] }, "999" => {"name" => "Messwert,N56", "OTcanSet" => "false", "OTcanReq" => "true", "fields" => [] }, ); ################################################################################## sub N4HMODULE_Initialize($) { ################################################################################## my ($hash) = @_; my @otlist; foreach my $model (keys %OT_devices){ push @otlist,$OT_devices{$model}->{name}; } $hash->{DefFn} = "N4HMODULE_Define"; $hash->{UndefFn} = "N4HMODULE_Undefine"; $hash->{ParseFn} = "N4HMODULE_Parse"; $hash->{FingerprintFn} = "N4HMODULE_Fingerprint"; $hash->{SetFn} = "N4HMODULE_Set"; $hash->{GetFn} = "N4HMODULE_Get"; $hash->{AttrFn} = "N4HMODULE_Attr"; $hash->{AttrList} = "IoDev dummy:1,0 Interval sendack:on,off setList ". "$readingFnAttributes "; # "OT:" .join(",", sort @otlist); $hash->{DbLog_splitFn} = "N4HMODULE_DbLog_splitFn"; $hash->{Match} = "."; } ################################################################################## sub N4HMODULE_Define($$) { ################################################################################## my ($hash, $def) = @_; my @args = split("[ \t]+", $def); my ($name, $type, $n4hbus, $ot, $objadr) = @args; my ($readings) = ""; if(@args < 4) { my $msg = "Usage: define N4HMODULE "; Log3 $hash, 1, $msg; return $msg; } $hash->{VERSION} = $n4hmodule_Version; $hash->{Hardware} = "undefined"; $hash->{Software} = "undefined"; $hash->{STATE} = "Initializing"; $hash->{NOTIFYDEV} = "global"; $hash->{IODev} = $n4hbus; $hash->{OBJADR} = $objadr; $hash->{OT} = $ot; $hash->{DESC} = $OT_devices{$ot}{name}; $hash->{OTcanSet} = $OT_devices{$ot}{OTcanSet}; $hash->{OTcanReq} = $OT_devices{$ot}{OTcanReq}; $hash->{Interval} = 0; $modules{N4HMODULE}{defptr}{$objadr} = $hash; AssignIoPort($hash, $n4hbus); Log3 $hash, 3, "N4HMODULE Define -> $name ($ot) at device $n4hbus with objectadr $objadr"; $hash->{helper}{from} = ''; $hash->{helper}{value} = ''; $hash->{helper}{cmd} = ''; $hash->{helper}{ddata} = ''; $hash->{helper}{pct} = 0; if ($hash->{OTcanSet}eq"true") { $hash->{helper}{state} = 'undefined'; } $attr{$name}{room} = "net4home" if( !defined($attr{$name}{room}) ); my $state_format; my $icon; # Timer zum regelmäßigem aktualisieren auf dem Bus starten if (($ot == 24) or #Temperatur ($ot == 25) or #Licht ($ot == 26) or #Luftfeuchte ($ot == 240) or #Wind ($ot == 241) or #Sonne ($ot == 242) or #Luftdruck ($ot == 246) or #Luftdruck-Tendenz ($ot == 245)) { #Regenmenge l/h if( $ot == 24 ) { $state_format .= " " if( $state_format ); $state_format .= "T: temperature"; $icon = "temp_temperature"; } elsif( $ot == 25 ) { $state_format .= " " if( $state_format ); $state_format .= "L: brightness"; $icon = "weather_light_meter"; } elsif( $ot == 26 ) { $state_format .= " " if( $state_format ); $state_format .= "H: humidity"; $icon = "weather_humidity"; } elsif( $ot == 240 ) { $state_format .= " " if( $state_format ); $state_format .= "W: wind"; } elsif( $ot == 241 ) { $state_format .= " " if( $state_format ); $state_format .= "L: brightness"; } elsif( $ot == 242 ) { $state_format .= " " if( $state_format ); $state_format .= "P: pressure"; } elsif( $ot == 245 ) { $state_format .= " " if( $state_format ); $state_format .= "R: rain"; } $attr{$name}{stateFormat} = $state_format if( !defined($attr{$name}{stateFormat}) && defined($state_format) ); $attr{$name}{icon} = $icon if( !defined($attr{$name}{icon}) && defined($icon) ); RemoveInternalTimer($hash); $hash->{Interval} = 30; # Timer Zeitversetzt starten, damit nicht alles auf den Bus gleichzeit kommt 30 Sekunden + x Log3 $hash, 3, "N4HMODULE Define (set timer) -> $name ($ot)"; InternalTimer( gettimeofday() + 30 + int(rand(15)) , "N4HMODULE_Start", $hash, 0 ); } elsif (( $ot == 34 ) or # TLH ( $ot == 3 ) or # Aktor, Relais ( $ot == 5)) { # Aktor, Dimmer if( $ot == 5 ) { $icon = "light_control"; } elsif( $ot == 34 ) { $icon = "temp_control"; } $attr{$name}{icon} = $icon if( !defined($attr{$name}{icon}) && defined($icon) ); # get initial value from bus after first start Log3 $hash, 3, "N4HMODULE Define (get) -> $name ($ot)"; InternalTimer( gettimeofday() + int(rand(10)) , "N4HMODULE_Start", $hash, 0 ); } return undef; } ################################################################################## sub N4HMODULE_Start($) ################################################################################## { my ($hash) = @_; my $name = $hash->{NAME}; my $interval = $hash->{Interval}; my $ot = $hash->{OT}; my $webCmd; $webCmd = AttrVal($name,"webCmd",undef); $interval = $attr{$name}{Interval} if( defined($attr{$name}{Interval}) ); Log3 $hash, 5, "N4HMODULE (start): ($name)-> ".$interval." Sekunden"; # reset timer if interval is defined if (($interval >= 30) and ($interval <= 86400)) { Log3 $hash, 5, "N4HMODULE (restart timer): ($name)-> ".$interval." Sekunden"; RemoveInternalTimer( $hash ); InternalTimer(gettimeofday() + $interval, "N4HMODULE_Start", $hash, 1 ); N4HMODULE_Update( $hash ); } else { # get initial values/state after start if ( $ot == 3 ) { # ot 3 = "Ausgang, Binär, Relais" if(!defined $webCmd){ $webCmd="toggle:on:off";} Log3 $hash, 5, "N4HMODULE (first timer): ($name)-> status"; N4HMODULE_Get($hash, $hash->{NAME}, "status"); } elsif ( $ot == 5 ) { # ot 5 = "Ausgang, Dimmer" if(!defined $webCmd){ $webCmd="pct:toggle:on:off";} Log3 $hash, 5, "N4HMODULE (first timer): ($name)-> status"; N4HMODULE_Get($hash, $hash->{NAME}, "status"); } elsif ( $ot == 34 ) { # ot 34 = "TLH-Regler H,Sollwert,Temperatur" if(!defined $webCmd){ $webCmd="off:heat:cool:auto";} Log3 $hash, 5, "N4HMODULE (first timer): ($name)-> desired-temperature"; N4HMODULE_Get($hash, $hash->{NAME}, "desired-temperature"); } } } ################################################################################## sub N4HMODULE_Undefine($$) { ################################################################################## my ($hash,$arg) = @_; my $name = $hash->{NAME}; Log3 $hash, 3, "N4HMODULE Undefine -> $name"; RemoveInternalTimer( $hash ); delete($modules{N4HMODULE}{defptr}{$hash->{OBJADR}}); return undef; } ################################################################################## sub N4HMODULE_DbLog_splitFn($$) { ################################################################################## my ($event, $device) = @_; my ($reading, $value, $unit) = ""; my $hash = $defs{$device}; my @parts = split(/ /,$event); $value = $parts[1]; if ($event =~ m/temperature/) { $reading = 'temperature'; $unit = '°C'; } elsif ($event =~ m/humidity/) { $reading = 'humidity'; $unit = '%'; } elsif ($event =~ m/pressure/) { $reading = 'pressure'; $unit = 'hPas'; } elsif ($event =~ m/co2/) { $reading = 'co2'; $unit = 'ppm'; } elsif ($event =~ m/rain/) { $reading = 'rain'; $unit = 'l/h'; } elsif ($event =~ m/brightness/) { $reading = 'brightness'; $unit = ''; } return ($reading, $value, $unit); } ################################################################################## sub N4HMODULE_Parse($$) { ################################################################################## my ($iodev, $msg, $local) = @_; my $ioName = $iodev->{NAME}; my $object = ""; # Modul suchen my $type8 = hex(substr($msg,0,2)); my $ipsrc = substr($msg,4,2).substr($msg,2,2); my $ipdst = hex(substr($msg,8,2).substr($msg,6,2)); my $objsrc = hex(substr($msg,12,2).substr($msg,10,2)); my $datalen = int(hex(substr($msg,14,2))); my $ddata = substr($msg,16, ($datalen*2)); my $pos = $datalen*2+16; # 0c 6100 ff7f d817 05 65 09 05 01 67 4004000001ff4004000001f8403600c000000659 if ( length($msg) <= $pos ) { return undef; } my $csRX = substr($msg,$pos,2); my $csCalc = substr($msg,$pos+2,2); my $len = substr($msg,$pos+4,2); my $posb = substr($msg,$pos+6,2); if ($ipdst == 32767) { $object = $objsrc; } else { $object = $ipdst; } my $hash = $modules{N4HMODULE}{defptr}{$object}; if (!$hash) { $object = $objsrc; $hash = $modules{N4HMODULE}{defptr}{$objsrc}; } if(!$hash) { my $ret = "Undefined ObjectAddress (MI$ipsrc) ($object) ($ipdst) ($ddata) "; Log3 $hash, 5, "N4HMODULE (parse): $ret - $msg"; return undef; } my $devtype = $hash->{OT}; N4HMODULE_ParsePayload($hash, $devtype, $ipsrc, $objsrc, $ddata); my $ret = "(MI$ipsrc) ($object) ($ipdst) ($ddata) "; Log3 $hash, 5, "N4HMODULE (parse): $ret - $msg"; return $hash->{NAME}; } ################################################################################## sub N4HMODULE_Fingerprint($$) { ################################################################################## my ( $io_name, $msg ) = @_; # substr( $msg, 2, 2, "--" ); # entferne Empfangsadresse # substr( $msg, 4, 1, "-" ); # entferne Hop-Count return ( $io_name, $msg ); } ################################################################################## sub N4HMODULE_ParsePayload($@) { ################################################################################## my ($hash, $devtype, $ipsrc, $objsrc, $ddata) = @_; my $name = $hash->{NAME}; my $ot = $hash->{OT}; my $dev_funcion = hex(substr($ddata,0,2)); my $newState = ""; my $myval = ""; my $objadr = $hash->{OBJADR}; readingsBeginUpdate($hash); # +++++++ D0_GET_TYP if ($dev_funcion == hex("03")) { if (hex(substr($ddata,2,2)) == "1F") { $hash->{Hardware} = "UP-S4"; } elsif (hex(substr($ddata,2,2)) == "29") { $hash->{Hardware} = "HS-AD3"; } } # +++++++ D0_SET if ($dev_funcion == hex("32")) { Log3 $hash, 5, "N4HMODULE -> D0_SET ($name) (".$hash->{OT}." OBJ->$objsrc - IP->$ipsrc - ".$hash->{OBJADR}.")"; if ( $ot == 34 && $objsrc != $objadr) { # ot 34 = "TLH-Regler H,Sollwert,Temperatur" my ($lastval) = sprintf "%.1f", hex(substr($ddata,4,2))/10; readingsBulkUpdate($hash, "desired-temperature", $lastval); if( defined($objsrc)) {readingsBulkUpdate($hash,"from", $objsrc);} readingsBulkUpdate($hash,"ddata", $ddata); } elsif ( $ot == 5 ) { # ot 5 = "Ausgang, Dimmer" if( defined($objsrc)) {readingsBulkUpdate($hash,"from", $objsrc);} readingsBulkUpdate($hash,"ddata", $ddata); readingsBulkUpdate($hash,"cmd", "D0_SET"); my ($lastval) = sprintf "%.0f", hex(substr($ddata,2,2)); if ( $lastval == 0 ) { readingsBulkUpdate($hash, "state", "off"); $hash->{helper}{pct} = $hash->{READINGS}{pct}{VAL}; readingsBulkUpdate($hash, "pct", 0); } elsif ( $lastval == 101 ) { readingsBulkUpdate($hash, "state", "on"); readingsBulkUpdate($hash, "pct", $hash->{helper}{pct}); } else { readingsBulkUpdate($hash, "state", "on"); $hash->{helper}{pct} = $lastval; readingsBulkUpdate($hash, "pct", $lastval); } } elsif ( $ot == 34 ) { # do nothing - cmd only for ATe } elsif ( $ot == 95 ) { if( defined($objsrc)) {readingsBulkUpdate($hash,"from", $objsrc);} readingsBulkUpdate($hash,"ddata", $ddata); readingsBulkUpdate($hash,"cmd", "D0_SET"); if (hex(substr($ddata,2,2))== hex("00")) { readingsBulkUpdate($hash,"state", "stop"); $hash->{STATE} = "stop"; readingsBulkUpdate($hash, "position", 50); } elsif (hex(substr($ddata,2,2))== hex("01")) { readingsBulkUpdate($hash,"state", "down"); readingsBulkUpdate($hash, "position", 0); $hash->{STATE} = "down"; } elsif (hex(substr($ddata,2,2))== hex("03")) { readingsBulkUpdate($hash,"state", "up"); readingsBulkUpdate($hash, "position", 100); $hash->{STATE} = "up"; } } else { if( defined($objsrc)) {readingsBulkUpdate($hash,"from", $objsrc);} readingsBulkUpdate($hash,"ddata", $ddata); readingsBulkUpdate($hash,"cmd", "D0_SET"); if (hex(substr($ddata,2,2))== hex("00")) { readingsBulkUpdate($hash,"state", "off"); $hash->{STATE} = "off"; } else { readingsBulkUpdate($hash,"state", "on"); $hash->{STATE} = "on"; } } } # +++++++ D0_INC if ($dev_funcion == hex("33")) { if ( $ot == 5 ) { # ot 5 = "Ausgang, Dimmer" readingsBulkUpdate($hash, "state", "on"); $hash->{helper}{pct} = $hash->{helper}{pct}+2; readingsBulkUpdate($hash, "pct", $hash->{helper}{pct}); if( defined($objsrc)) {readingsBulkUpdate($hash,"from", $objsrc);} readingsBulkUpdate($hash,"ddata", $ddata); readingsBulkUpdate($hash,"cmd", "D0_INC"); } else { if( defined($objsrc)) {readingsBulkUpdate($hash,"from", $objsrc);} readingsBulkUpdate($hash,"ddata", $ddata); readingsBulkUpdate($hash,"cmd", "D0_INC"); } } # +++++++ D0_DEC if ($dev_funcion == hex("34")) { if ( $ot == 5 ) { # ot 5 = "Ausgang, Dimmer" readingsBulkUpdate($hash, "state", "on"); $hash->{helper}{pct} = $hash->{helper}{pct}-2; readingsBulkUpdate($hash, "pct", $hash->{helper}{pct}); if( defined($objsrc)) {readingsBulkUpdate($hash,"from", $objsrc);} readingsBulkUpdate($hash,"ddata", $ddata); readingsBulkUpdate($hash,"cmd", "D0_DEC"); } else { if( defined($objsrc)) {readingsBulkUpdate($hash,"from", $objsrc);} readingsBulkUpdate($hash,"ddata", $ddata); readingsBulkUpdate($hash,"cmd", "D0_DEC"); } } # +++++++ D0_TOGGLE if ($dev_funcion == hex("35")) { if( defined($objsrc)) {readingsBulkUpdate($hash,"from", $objsrc);} readingsBulkUpdate($hash,"ddata", $ddata); readingsBulkUpdate($hash,"cmd", "D0_TOGGLE"); if ( $ot == 5 ) { # ot 5 = "Ausgang, Dimmer" if (ReadingsVal($name, "state", "") eq "on") { readingsBulkUpdate($hash,"state", "off"); $hash->{helper}{pct} = $hash->{READINGS}{pct}{VAL}; readingsBulkUpdate($hash, "pct", 0); $hash->{STATE} = "off"; } elsif (ReadingsVal($name, "state", "") eq "off") { readingsBulkUpdate($hash,"state", "on"); readingsBulkUpdate($hash, "pct", $hash->{helper}{pct}); $hash->{STATE} = "on"; } } else { if (ReadingsVal($name, "state", "") eq "on") { readingsBulkUpdate($hash,"state", "off"); $hash->{STATE} = "off"; } elsif (ReadingsVal($name, "state", "") eq "off") { readingsBulkUpdate($hash,"state", "on"); $hash->{STATE} = "on"; } } } # +++++++ D0_REQ if ($dev_funcion == hex("36")) { if ( $ot == 3 ) { # ot 3 = "Ausgang, Binär, Relais" } elsif ( $ot == 34 ) { # ot 34 = "TLH-Regler H,Sollwert,Temperatur" } else { if( defined($objsrc)) {readingsBulkUpdate($hash,"from", $objsrc);} readingsBulkUpdate($hash,"ddata", $ddata); readingsBulkUpdate($hash,"cmd", "D0_REQ"); Log3 $hash, 5, "N4HMODULE -> D0_REQ ($name) (".$hash->{OT}.")"; N4HMODULE_Update( $hash ); } } # +++++++ D0_ACTOR_ACK if ($dev_funcion == hex("37")) { if( defined($objsrc)) {readingsBulkUpdate($hash,"from", $objsrc);} readingsBulkUpdate($hash,"ddata", $ddata); readingsBulkUpdate($hash,"cmd", "D0_ACTOR_ACK"); Log3 $hash, 5, "N4HMODULE -> D0_ACTOR_ACK ($name) (".$hash->{OT}.")"; if ( $ot == 3 ) { # ot 3 = "Ausgang, Binär, Relais" if ( (hex(substr($ddata,4,2))) == 0) { readingsBulkUpdate($hash, "state", "off"); $hash->{STATE} = "off"; } elsif ( (hex(substr($ddata,4,2))) == 1) { readingsBulkUpdate($hash, "state", "on"); $hash->{STATE} = "on"; } } elsif ( $ot == 5 ) { # ot 5 = "Ausgang, Dimmer" if ( hex(substr($ddata,4,2)) > 128) { my ($lastval) = sprintf "%.0f", hex(substr($ddata,4,2)); readingsBulkUpdate($hash, "pct", $lastval-128); $hash->{helper}{pct} = $lastval-128; readingsBulkUpdate($hash, "state", "on"); $hash->{STATE} = "on"; } else { my ($lastval) = sprintf "%.0f", hex(substr($ddata,4,2)); $hash->{helper}{pct} = $lastval; readingsBulkUpdate($hash, "state", "off"); $hash->{STATE} = "off"; } } elsif ( $ot == 34 ) { # ot 34 = "TLH-Regler H,Sollwert,Temperatur" my ($lastval) = sprintf "%.1f", hex(substr($ddata,4,2))/10; readingsBulkUpdate($hash, "desired-temperature", $lastval); if ( (hex(substr($ddata,6,2))) == 0) { readingsBulkUpdate($hash, "CurrentHeatingCoolingState", "0"); readingsBulkUpdate($hash, "state", "off"); $hash->{STATE} = "off"; } elsif ( (hex(substr($ddata,6,2))) == 1) { readingsBulkUpdate($hash, "CurrentHeatingCoolingState", "1"); readingsBulkUpdate($hash, "state", "heat"); $hash->{STATE} = "heat"; } elsif ( (hex(substr($ddata,6,2))) == 2) { readingsBulkUpdate($hash, "CurrentHeatingCoolingState", "2"); readingsBulkUpdate($hash, "state", "cool"); $hash->{STATE} = "cool"; } elsif ( (hex(substr($ddata,6,2))) == 3) { readingsBulkUpdate($hash, "CurrentHeatingCoolingState", "3"); readingsBulkUpdate($hash, "state", "auto"); $hash->{STATE} = "auto"; }; }; } # +++++++ D0_SENSOR_ACK if ($dev_funcion == hex("41")) { if( defined($objsrc)) {readingsBulkUpdate($hash,"from", $objsrc);} readingsBulkUpdate($hash,"ddata", $ddata); readingsBulkUpdate($hash,"cmd", "D0_SENSOR_ACK"); Log3 $hash, 5, "N4HMODULE -> D0_SENSOR_ACK ($name) (".$hash->{OT}.")"; N4HMODULE_Update( $hash ); } # +++++++ D0_LOCK_STATE_ACK if ($dev_funcion == hex("44")) { if( defined($objsrc)) {readingsBulkUpdate($hash,"from", $objsrc);} readingsBulkUpdate($hash,"ddata", $ddata); readingsBulkUpdate($hash,"cmd", "D0_LOCK_STATE_ACK"); Log3 $hash, 5, "N4HMODULE -> D0_LOCK_STATE_ACK ($name) (".$hash->{OT}.")"; } # +++++++ D0_VALUE_ACK (101) if ($dev_funcion == hex("65")) { if( defined($objsrc)) {readingsBulkUpdate($hash,"from", $objsrc);} readingsBulkUpdate($hash,"ddata", $ddata); readingsBulkUpdate($hash,"cmd", "D0_VALUE_ACK"); if ( (hex(substr($ddata,4,2))) == 0) { readingsBulkUpdate($hash, "state", "off"); $hash->{STATE} = "off"; } elsif ( (hex(substr($ddata,4,2))) == 1) { readingsBulkUpdate($hash, "state", "on"); $hash->{STATE} = "on"; } my ($valtype, $lastval) = N4HMODULE_paramToText($hash, $ddata); readingsBulkUpdate($hash, $valtype, $lastval); readingsBulkUpdate($hash, "state", $lastval); } # +++++++ D0_VALUE_REQ if ($dev_funcion == hex("66")) { if( defined($objsrc)) {readingsBulkUpdate($hash,"from", $objsrc);} readingsBulkUpdate($hash,"ddata", $ddata); readingsBulkUpdate($hash,"cmd", "D0_VALUE_REQ"); Log3 $hash, 5, "N4HMODULE -> D0_VALUE_REQ ($name) (".$hash->{OT}.")"; N4HMODULE_Update( $hash ); } # +++++++ SetN if ($dev_funcion == hex("3b")) { if( defined($objsrc)) {readingsBulkUpdate($hash,"from", $objsrc);} readingsBulkUpdate($hash,"ddata", $ddata); # ot 34 = "TLH-Regler H,Sollwert,Temperatur" if ($ot == 34) { readingsBulkUpdate($hash,"cmd", "SetN"); Log3 $hash, 5, "N4HMODULE -> SetN ($name) (".$hash->{OT}.")"; if ( (hex(substr($ddata,2,2))) == 0) { readingsBulkUpdate($hash, "CurrentHeatingCoolingState", "0"); readingsBulkUpdate($hash, "state", "off"); $hash->{STATE} = "off"; } elsif ( (hex(substr($ddata,2,2))) == 1) { readingsBulkUpdate($hash, "CurrentHeatingCoolingState", "1"); readingsBulkUpdate($hash, "state", "heat"); $hash->{STATE} = "heat"; } elsif ( (hex(substr($ddata,2,2))) == 2) { readingsBulkUpdate($hash, "CurrentHeatingCoolingState", "2"); readingsBulkUpdate($hash, "state", "cool"); $hash->{STATE} = "cool"; } elsif ( (hex(substr($ddata,2,2))) == 3) { readingsBulkUpdate($hash, "CurrentHeatingCoolingState", "3"); readingsBulkUpdate($hash, "state", "auto"); $hash->{STATE} = "auto"; }; } else { readingsBulkUpdate($hash,"cmd", "SetN"); } } readingsEndUpdate( $hash , 1); return undef; } ################################################################################## sub N4HMODULE_paramToText($@) { ################################################################################## my ($hash, $ddata) = @_; my $name = $hash->{NAME}; my $rettype = "unbekannte Formel / Parameter"; my $w = hex(substr($ddata,6,2))*256+hex(substr($ddata,8,2)); my $ret = $w; my $t; # +++++++++++++++++++ Licht analog - IN_HW_NR_IS_LICHT_ANALOG if (hex(substr($ddata,2,2)) == 5 ){ $rettype = "brightness"; } # +++++++++++++++++++ Uhrzeit - IN_HW_NR_IS_CLOCK elsif (hex(substr($ddata,2,2)) == 6 ){ $rettype = "Uhrzeit"; } # +++++++++++++++++++ RF-Tag Reader - IN_HW_NR_IS_RF_TAG_READER elsif (hex(substr($ddata,2,2)) == 7 ){ $ret = uc substr($ddata,6,10); if ( (hex(substr($ddata,18,2)) & 6) == 0) { $ret = $ret." vorgehalten"; } elsif ( (hex(substr($ddata,18,2)) & 6) == 2) { $ret = $ret." lang vorgehalten"; } elsif ( (hex(substr($ddata,18,2)) & 6) == 4) { $ret = $ret." weggezogen nach kurz"; } else { $ret = $ret." ".hex(substr($ddata,18,2)) } $rettype = "RF-Tag"; } # +++++++++++++++++++ Temperatur - IN_HW_NR_IS_TEMP elsif (hex(substr($ddata,2,2)) == 9 ){ if (hex(substr($ddata,4,2)) == 5 ){ # USE_FROMEL_TEMP_UI16 if (hex(substr($ddata,6,2)) == 0xff ) { $t = (hex(substr($ddata,8,2))-0xff)/16; } else { $t = $w/16; } $ret = sprintf "%.1f °C", $t; $rettype = "temperature"; } } # +++++++++++++++++++ Feuchte - IN_HW_NR_IS_HUMIDITY elsif (hex(substr($ddata,2,2)) == 11 ){ $ret = $w." %"; $rettype = "humidity"; } # +++++++++++++++++++ Wind - IN_HW_NR_IS_KMH elsif (hex(substr($ddata,2,2)) == 41 ){ if (hex(substr($ddata,4,2)) == 6 ){ # USE_FROMEL_RAW_16BIT $ret = $w." km/h"; } elsif (hex(substr($ddata,4,2)) == 7 ){ # USE_FROMEL_16BIT_X8 $t = $w/8; $ret = $t." km/h"; } $rettype = "wind"; } # +++++++++++++++++++ Luftdruck - IN_HW_NR_IS_PRESS_MBAR elsif (hex(substr($ddata,2,2)) == 48 ){ $t = $w/10; $ret = $t." hPas"; $rettype = "pressure"; } # +++++++++++++++++++ pressure Tendenz - IN_HW_NR_IS_PRESS_TENDENZ elsif (hex(substr($ddata,2,2)) == 49 ){ $rettype = "pressure (Tendenz)"; } # +++++++++++++++++++ Uhrzeit - Sonnenaufgang heute elsif (hex(substr($ddata,2,2)) == 50 ){ $rettype = "Sonnenaufgang"; } # +++++++++++++++++++ Uhrzeit - Sonnenuntergang heute elsif (hex(substr($ddata,2,2)) == 51 ){ $rettype = "Sonnenuntergang"; } # +++++++++++++++++++ Regenmenge (Liter/Stunde) - VAL_IS_MENGE_LITER elsif (hex(substr($ddata,2,2)) == 53 ){ if (hex(substr($ddata,4,2)) == 8 ){ # USE_FROMEL_16BIT_X10 $t = $w/10; $ret = $t." l/h"; } else { $ret = $w." l/h"; } $rettype = "rain"; } return ($rettype, $ret); } ################################################################################## sub N4HMODULE_Set($@) { ################################################################################## my ($hash, @a) = @_; return "\"set $a[0]\" needs at least two parameters" if(@a < 2); my $name = shift(@a); my $cmd = shift(@a); my $ext = shift(@a); Log3 $hash, 5, "N4HMODULE (set): (Name: $name) (CMD: $cmd) (ext: $ext)"; my $ot = $hash->{OT}; my $ipdst = $hash->{OBJADR}; my $ddata = ""; my @sets; my $fieldcmd = ""; my $fieldname; my $fieldset; my $setfield; my $devtype = $OT_devices{$ot}; if ($ot == 3 || $ot == 4 || $ot == 5 || $ot == 95 || $ot == 100) { for my $field (@{$devtype->{fields}}) { $setfield = $field->{set}; if (defined($setfield)) { push(@sets,$field->{set}); $setfield = ( split /:/, $setfield, 2 )[0]; if ($setfield eq $cmd) { $fieldname = $field->{text}; $fieldcmd = $field->{cmd}; $fieldset = $field->{set}; } } } if ($fieldcmd ne "" && $cmd ne "?") { if (defined($ext)) { if ($cmd eq "pct") { $fieldcmd = "$fieldcmd".sprintf ("%02x", ($ext))."00"; $ddata = sprintf ("%02x%s", ((length($fieldcmd))/2), $fieldcmd); readingsSingleUpdate($hash, "pct", "$ext", 1); Log3 $hash, 5, "N4HMODULE (set): $name to $cmd ($ext%) ($ddata)"; } else { $ddata = sprintf ("%02x%s", (length($fieldcmd)/2), $fieldcmd); Log3 $hash, 5, "N4HMODULE (set): $name to $cmd/$ext (".join(" ", @sets).") - $devtype - $fieldcmd - $ot)"; } } else { if ($cmd eq "toggle") { $ddata = sprintf ("%02x%s", (length($fieldcmd)/2), $fieldcmd); Log3 $hash, 5, "N4HMODULE (set): $name $cmd ($ddata)"; } else { Log3 $hash, 5, "N4HMODULE (set): $name to $cmd (".join(" ", @sets).") - $devtype - $fieldcmd - $ot)"; $ddata = sprintf ("%02x%s", (length($fieldcmd)/2), $fieldcmd); readingsSingleUpdate($hash, "state", "$cmd", 1); } } IOWrite($hash, $ipdst, $ddata, 0); return undef; } else { return SetExtensions($hash, join(" ", @sets), $name, $cmd, @a); } } elsif ($ot == 34) { for my $field (@{$devtype->{fields}}) { $setfield = $field->{set}; if (defined($setfield)) { push(@sets,$field->{set}); $setfield = ( split /:/, $setfield, 2 )[0]; if ($setfield eq $cmd) { $fieldname = $field->{text}; $fieldcmd = $field->{cmd}; $fieldset = $field->{set}; } } } if ($fieldcmd ne "" && $cmd ne "?") { Log3 $hash, 5, "N4HMODULE (set): $name to $cmd (".join(" ", @sets).") - $devtype - $fieldcmd - $ot)"; if ($cmd eq "desired-temperature") { $fieldcmd = "$fieldcmd".sprintf ("%02x", ($ext*10))."00"; $ddata = sprintf ("%02x%s", ((length($fieldcmd))/2), $fieldcmd); readingsSingleUpdate($hash, "desired-temperature", "$ext", 1); } else { $ddata = sprintf ("%02x%s", ((length($fieldcmd))/2), $fieldcmd); } IOWrite($hash, $ipdst, $ddata, 0); return undef; } else { return SetExtensions($hash, join(" ", @sets), $name, $cmd, @a); } } elsif ($ot == 24 || $ot == 25 || $ot == 26 || $ot == 240 || $ot == 242 || $ot == 245) { if ($cmd ne "?") { Log3 $hash, 5, "N4HMODULE (set): $name to $cmd"; N4HMODULE_Update($hash, $cmd); } return undef; } elsif ($ot == 999) { if ($cmd ne "?") { Log3 $hash, 5, "N4HMODULE (set n56): $name to $cmd"; N4HMODULE_Update($hash, $cmd); } return undef; } return undef; } ################################################################################## sub N4HMODULE_Get($$@) { ################################################################################## my ( $hash, $name, $opt, @args ) = @_; my $ot = $hash->{OT}; my $ipdst = $hash->{OBJADR}; my $ddata = ""; my $fieldcmd = ""; return "\"get $name\" needs at least one argument" unless(defined($opt)); if (( $ot == 3 ) or ( $ot == 5)) { if ($opt eq "status") { $fieldcmd = "360000"; $ddata = sprintf ("%02x%s", ((length($fieldcmd))/2), $fieldcmd); Log3 $hash, 5, "N4HMODULE (get): $opt from $name ($ddata)"; IOWrite($hash, $ipdst, $ddata, 0); } else { return "unknown argument $opt, choose one of gettype:noArg status:noArg"; } } elsif ( $ot == 34 ) { if ($opt eq "desired-temperature") { $fieldcmd = "360000"; $ddata = sprintf ("%02x%s", ((length($fieldcmd))/2), $fieldcmd); Log3 $hash, 5, "N4HMODULE (get): $opt from $name ($ddata)"; IOWrite($hash, $ipdst, $ddata, 0); } else { return "unknown argument $opt, choose one of gettype:noArg desired-temperature:noArg"; } } else { return "unknown argument, choose one of gettype:noArg"; } return undef; } ################################################################################## sub N4HMODULE_Update($@) { ################################################################################## my ($hash, @a) = @_; my $value = shift(@a); my $name = $hash->{NAME}; return unless (defined($hash->{NAME}) ); if (defined $value) { Log3 $hash, 5, "N4HMODULE (update): ($name) ($value)"; } else { Log3 $hash, 5, "N4HMODULE (update): ($name)"; } my $ot = $hash->{OT}; my $ipdst = $hash->{OBJADR}; my $ddata = ""; my $cs = ""; my $cmd = ""; if ($ot == 3) { # +++++++ 69 D0_STATUS_INFO (105) if ( (hex(substr($ddata,6,2))) == 0) { readingsBulkUpdate($hash, "state", "off"); $hash->{STATE} = "off"; } elsif ( (hex(substr($ddata,6,2))) == 1) { readingsBulkUpdate($hash, "state", "on"); $hash->{STATE} = "on"; } } elsif ($ot == 24) { # +++++++ 65 D0_VALUE_ACK (101) # +++++++ 09 Temperatur # +++++++ 05 USE_FROMEL_TEMP_UI16 my $ddata1 = "650905"; if (defined $value) { $cmd = $value; readingsSingleUpdate($hash, "temperature", "$cmd °C", 1); } else { ($cmd, undef) = split(/ /, ReadingsVal($name , "temperature", "")); } if (defined $cmd) { if ($cmd >= 0) { $cs = $cmd*16; $ddata1 = $ddata1.sprintf ("%02X", ($cs>>8) ); } elsif ($cmd < 0) { $cs = 0xff+($cmd*16); $ddata1 = $ddata1.sprintf ("%02X", 0xff ); } $ddata1 = $ddata1.sprintf ("%02X", ( ($cs>>0) & 255 ) ); $ddata = sprintf ("%02x%s", (length($ddata1)/2), $ddata1); Log3 $hash, 5, "N4HMODULE (set temperature): $name to $cmd - $ddata, $ddata1, $ipdst"; IOWrite($hash, 32767, $ddata, $ipdst); } return undef; } elsif ($ot == 25) { # Licht # +++++++ 65 D0_VALUE_ACK # +++++++ 05 Licht # +++++++ 01 USE_FROMEL my $ddata1 = "650506"; if (defined $value) { $cmd = $value; readingsSingleUpdate($hash, "Brightness", "$cmd", 1); } else { ($cmd, undef) = split(/ /, ReadingsVal($name , "Brightness","")); } if (defined $cmd) { my $cs = $cmd; $ddata1 = $ddata1.sprintf ("%02X", (0x00) ); $ddata1 = $ddata1.sprintf ("%02X", ( ($cs) ) ); $ddata = sprintf ("%02x%s", (length($ddata1)/2), $ddata1); Log3 $hash, 5, "N4HMODULE (set brightness): $name to $cmd - $ddata, $ddata1, $ipdst"; IOWrite($hash, 32767, $ddata, $ipdst); } return undef; } elsif ($ot == 26) { # Luftfeuchte # +++++++ 65 D0_VALUE_ACK # +++++++ 0B Luftfeuchte # +++++++ 01 USE_FROMEL my $ddata1 = "650B01"; if (defined $value) { $cmd = $value; readingsSingleUpdate($hash, "humidity", "$cmd %", 1); } else { ($cmd, undef) = split(/ /, ReadingsVal($name , "humidity","")); } if (defined $cmd) { my $cs = $cmd; $ddata1 = $ddata1.sprintf ("%02X", (0x00) ); $ddata1 = $ddata1.sprintf ("%02X", ( ($cs) ) ); $ddata = sprintf ("%02x%s", (length($ddata1)/2), $ddata1); Log3 $hash, 5, "N4HMODULE (set humidity): $name to $cmd - $ddata, $ddata1, $ipdst"; IOWrite($hash, 32767, $ddata, $ipdst); } return undef; } elsif ($ot == 34) { # TLH-Regler H,Sollwert,Temperatur # +++++++ 37 D0_ACTOR_ACK (101) Log3 $hash, 5, "N4HMODULE (xxx): $name to $cmd - $ddata, $ipdst"; my $ddata1 = "3700"; if (defined $value) { $cmd = $value; readingsSingleUpdate($hash, "desired-temp", "$cmd", 1); } else { ($cmd, undef) = split(/ /, ReadingsVal($name , "desired-temp", "")); } if (defined $cmd) { if ($cmd >= 0) { $cs = $cmd*16; $ddata1 = $ddata1.sprintf ("%02X", ($cs>>8) ); } elsif ($cmd < 0) { $cs = 0xff+($cmd*16); $ddata1 = $ddata1.sprintf ("%02X", 0xff ); } $ddata1 = $ddata1.sprintf ("%02X", ( ($cs>>0) & 255 ) ); $ddata = sprintf ("%02x%s", (length($ddata1)/2), $ddata1); Log3 $hash, 5, "N4HMODULE (set desired-temp): $name to $cmd - $ddata, $ddata1, $ipdst"; IOWrite($hash, 32767, $ddata, $ipdst); } return undef; } elsif ($ot == 240) { # Wind # +++++++ 65 D0_VALUE_ACK # +++++++ 29 Windgeschwindigkeit # +++++++ 01 USE_FROMEL my $ddata1 = "652907"; if (defined $value) { $cmd = $value; readingsSingleUpdate($hash, "wind", "$cmd km/h", 1); } else { ($cmd, undef) = split(/ /, ReadingsVal($name , "wind","")); } if (defined $cmd) { my $cs = $cmd*8; $ddata1 = $ddata1.sprintf ("%02X", ($cs>>8) ); $ddata1 = $ddata1.sprintf ("%02X", ( $cs & 0xff ) ); $ddata = sprintf ("%02x%s", (length($ddata1)/2), $ddata1); Log3 $hash, 5, "N4HMODULE (set wind): $name to $cmd - $ddata, $ddata1, $ipdst"; IOWrite($hash, 32767, $ddata, $ipdst); } return undef; } elsif ($ot == 242) { # Luftdruck # +++++++ 65 D0_VALUE_ACK # +++++++ 30 Luftdruck # +++++++ 01 USE_FROMEL my $ddata1 = "653001"; if (defined $value) { $cmd = $value; readingsSingleUpdate($hash, "pressure", "$cmd hPas", 1); } else { ($cmd, undef) = split(/ /, ReadingsVal($name , "pressure","")); } if (defined $cmd) { my $cs = $cmd*10; $ddata1 = $ddata1.sprintf ("%02X", ($cs>>8) ); $ddata1 = $ddata1.sprintf ("%02X", ( $cs & 0xff ) ); $ddata = sprintf ("%02x%s", (length($ddata1)/2), $ddata1); Log3 $hash, 5, "N4HMODULE (set pressure): $name to $cmd - $ddata, $ddata1, $ipdst"; IOWrite($hash, 32767, $ddata, $ipdst); } return undef; } elsif ($ot == 245) { # Regenmenge # +++++++ 65 D0_VALUE_ACK # +++++++ 35 Regenmenge # +++++++ 01 USE_FROMEL my $ddata1 = "653508"; if (defined $value) { $cmd = $value; readingsSingleUpdate($hash, "rain", "$cmd l/h", 1); } else { ($cmd, undef) = split(/ /, ReadingsVal($name , "rain","")); } if (defined $cmd) { my $cs = $cmd*10; $ddata1 = $ddata1.sprintf ("%02X", ($cs>>8) ); $ddata1 = $ddata1.sprintf ("%02X", ( $cs & 0xff ) ); $ddata = sprintf ("%02x%s", (length($ddata1)/2), $ddata1); Log3 $hash, 5, "N4HMODULE (set rain): $name to $cmd - $ddata, $ddata1, $ipdst"; IOWrite($hash, 32767, $ddata, $ipdst); } return undef; } elsif ($ot == 999) { # N56 # +++++++ 65 D0_SENSOR_ACK (41) my $ddata1 = "41"; if (defined $value) { $cmd = $value; # $cmd = N4HMODULE_ParseN56(substr($hash->{READINGS}{ddata},2,18)); readingsSingleUpdate($hash, "value", "$cmd", 1); } else{ return undef; } my $n56 = N4HMODULE_CreateN56($value,0,0,0,2); $ddata1 = $ddata1.$n56; $ddata = sprintf ("%02x%s", (length($ddata1)/2), $ddata1); $ipdst = 1; Log3 $hash, 5, "N4HMODULE (set N56): $name to $cmd - $ddata, $ddata1, $ipdst, $n56"; IOWrite($hash, 1, $ddata, $ipdst); return undef; } return undef; } ################################################################################## sub N4HMODULE_Attr(@) { ################################################################################## my ($cmd,$name,$attr_name,$attr_value) = @_; if($cmd eq "set") { if($attr_name eq "Interval") { if (($attr_value < 30) or ($attr_value > 86400)) { my $err = "Invalid time $attr_value to $attr_name. Must be > 30 and < 86400."; return $err; } } } return undef; } ################################################################################## sub N4HMODULE_CreateN56(@) { ################################################################################## my ($val32,$exp8,$expb,$findex,$decimal) = @_; my $ddata = ""; my $myformel = ""; # print "\n".$val32*($expb**$exp8)."\n"; my $unsigned = $val32 < 0 ? 2 ** 32 + $val32 : $val32; my $myddata = sprintf("%08X", $unsigned); my $ps = $exp8 < 0 ? 2 ** 8 + $exp8 : $exp8; $myformel = sprintf("%04X",$findex); $ddata = substr($myddata,6,2).substr($myddata,4,2).substr($myddata,2,2).substr($myddata,0,2).sprintf("%02X",$ps).substr($myformel,2,2).substr($myformel,0,2).sprintf("%02X",$decimal).sprintf("%02X",$expb); return ($ddata); } ################################################################################## sub N4HMODULE_ParseN56($) { ################################################################################## my ($n56) = @_; my $exp8 = hex(substr($n56,8,2)); my $val32 = hex(substr($n56,6,2).substr($n56,4,2).substr($n56,2,2).substr($n56,0,2)); my $expb = hex(substr($n56,16,2)); my $formel = hex(substr($n56,10,2)).hex(substr($n56,12,2));; my $deci = hex(substr($n56,14,2)); $exp8 = $exp8 >> 7 ? $exp8 - 2 ** 8 : $exp8; $val32 = $val32 >> 31 ? $val32 - 2 ** 32 : $val32; if ($expb eq 0) { $expb = 10; } elsif ($expb eq 1) { $expb = 2; } my $ddata = sprintf("Wert: %.".$deci."f", ($val32*($expb**$exp8)))." (Formel: $formel)"; return ($ddata); } ################################################################################## ################################################################################## 1; =pod =item device =item summary Module to emulate net4home Actors and Sensors via N4HBUS =item summary_DE Modul zum emulieren von net4home Aktoren und Sensoren ueber N4HBUS =begin html

N4HMODULE

fhem-Module to communicate with net4home modules via IP


    Define
      define <name> N4HMODULE <device> <type> <objectaddress>

      Defines a net4home device connected to a N4HBUS device

      Examples:
        define n4h_28204 N4HMODULE n4h 24 28204
      Currently the following values are supported:
      Measurement
      • 24 - Measurement,Temperature
      • 25 - Measurement,Brightness
      • 26 - Measurement,Humidity
      • 34 - TLH-Regler H,Sollwert,Temperatur
      • 95 - Ausgang, Jal, Motor AJ3
      • 210 - RF Reader
      • 240 - Measurement,Wind
      • 242 - Measurement,Pressure
      • 245 - Measurement,Rain

    Readings
    • The readings are dependent of the object of the net4home bus module.

    Attributes
    • interval
      the interval in seconds used to send values to bus.
=end html =begin html_DE

N4HMODULE

fhem-Modul zur Kommunikation mit dem net4home Bus über IP


    Define
      define <name> N4HMODULE <device> <type> <objectaddress>

      Erstellt ein net4home Modul-Device welches mit dem N4HBUS Device kommuniziert. Beispiel:
        define MyN4HMODULEice N4HMODULE 24 26004
      Derzeit werden folgende Typen unterstützt:
      Messwerte
      • 24 - Messwert,Temperatur
      • 25 - Messwert,Helligkeit
      • 26 - Messwert,Feuchte
      • 34 - TLH-Regler H,Sollwert,Temperatur
      • 95 - Ausgang, Jal, Motor AJ3
      • 210 - RF Reader
      • 240 - Messwert,Wind
      • 242 - Messwert,Luftdruck
      • 245 - Messwert,Regenmenge

    Readings
    • Die Readings werden Abhängig vom Modultyp angegeben.

    Attributes
    • Interval
      Das Interval bestimmt bei Messwerten die Zeit zwischen dem Senden der Daten auf den Bus. Ist kein Attribut definiert, so wird der Standardwert genutzt.
=end html_DE =cut