diff --git a/fhem/CHANGED b/fhem/CHANGED index b9fa5c967..8b8d445fe 100644 --- a/fhem/CHANGED +++ b/fhem/CHANGED @@ -1,5 +1,6 @@ # Add changes at the top of the list. Keep it in ASCII, and 80-char wide. # Do not insert empty lines here, update check depends on it. + - feature: 72_TA_CMI_JSON: added readOutputStates and according attributes - feature: 73_km200: Errorcodes implemented - change: 98_weekprofile: don't display '°C' by temperature keywords - change: 70_CanOverEthernet: changed indirect object notation (Perl 7 comp) diff --git a/fhem/FHEM/72_TA_CMI_JSON.pm b/fhem/FHEM/72_TA_CMI_JSON.pm index e836524d8..ceb9df8d3 100755 --- a/fhem/FHEM/72_TA_CMI_JSON.pm +++ b/fhem/FHEM/72_TA_CMI_JSON.pm @@ -43,11 +43,12 @@ use warnings; sub TA_CMI_JSON_Initialize { my ($hash) = @_; - $hash->{GetFn} = "TA_CMI_JSON::TA_CMI_JSON_Get"; - $hash->{DefFn} = "TA_CMI_JSON::TA_CMI_JSON_Define"; - $hash->{UndefFn} = "TA_CMI_JSON::TA_CMI_JSON_Undef"; + $hash->{GetFn} = "TA_CMI_JSON::Get"; + $hash->{DefFn} = "TA_CMI_JSON::Define"; + $hash->{UndefFn} = "TA_CMI_JSON::Undef"; + $hash->{NotifyFn} = "TA_CMI_JSON::Notify"; - $hash->{AttrList} = "username password interval readingNamesInputs readingNamesOutputs readingNamesDL-Bus readingNamesLoggingAnalog readingNamesLoggingDigital includePrettyReadings:0,1 includeUnitReadings:0,1 " . $readingFnAttributes; + $hash->{AttrList} = "username password outputStatesInterval interval readingNamesInputs readingNamesOutputs readingNamesDL-Bus readingNamesLoggingAnalog readingNamesLoggingDigital includePrettyReadings:0,1 includeUnitReadings:0,1 prettyOutputStates:0,1 " . $readingFnAttributes; Log3 '', 3, "TA_CMI_JSON - Initialize done ..."; } @@ -67,7 +68,12 @@ my %deviceNames = ( '89' => 'CAN-I/O45', '8B' => 'CAN-EZ2', '8C' => 'CAN-MTx2', - '8D' => 'CAN-BC2' + '8D' => 'CAN-BC2', + '8E' => 'UVR65', + '8F' => 'CAN-EZ3', + '91' => 'UVR610', + '92' => 'UVR67', + 'A3' => 'BL_NET' ); my %versions = ( @@ -75,7 +81,8 @@ my %versions = ( 2 => '1.26.1 2017-02-24', 3 => '1.28.0 2017-11-09', 4 => '1.34.2 2019-04-04', - 5 => '1.35.1 2019-07-02' + 5 => '1.35.1 2019-07-02', + 6 => '1.38.1 2021-02-04' ); my %units = ( @@ -97,6 +104,14 @@ my %rasStates = ( 3 => 'Standby/frost pr.' ); +my %outputStates = ( + 0 => 'n/a', + 1 => 'Auto-Off', + 3 => 'Auto-On', + 5 => 'Manual-Off', + 7 => 'Manual-On' +); + ## Import der FHEM Funktionen BEGIN { GP_Import(qw( @@ -118,7 +133,7 @@ BEGIN { )) }; -sub TA_CMI_JSON_Define { +sub Define { my ( $hash, $def ) = @_; my @a = split( "[ \t][ \t]*", $def ); @@ -128,9 +143,9 @@ sub TA_CMI_JSON_Define { my $nodeId = $a[3]; my $queryParams = $a[4]; - if(@a != 5) { - my $msg = "TA_CMI_JSON ($name) - Wrong syntax: define TA_CMI_JSON CMI-URL CAN-Node-ID QueryParameters"; - Log3 undef, 2, $msg; + if(@a != 4 && @a != 5) { + my $msg = "TA_CMI_JSON ($name) - Wrong syntax: define TA_CMI_JSON CMI-URL CAN-Node-ID "; + Log3 $name, 0, $msg; return $msg; } @@ -138,25 +153,58 @@ sub TA_CMI_JSON_Define { $hash->{CMIURL} = $cmiUrl; $hash->{NODEID} = $nodeId; $hash->{QUERYPARAM} = $queryParams; - $hash->{INTERVAL} = AttrVal( $name, "interval", "60" ); - - Log3 $name, 5, "TA_CMI_JSON ($name) - Define done ... module=$module, CMI-URL=$cmiUrl, nodeId=$nodeId, queryParams=$queryParams"; + Log3 $name, 0, "TA_CMI_JSON ($name) - Define ... module=$module, CMI-URL=$cmiUrl, nodeId=$nodeId"; readingsSingleUpdate($hash, 'state', 'defined', 1); - TA_CMI_JSON_GetStatus( $hash, 2 ); + SetupIntervals($hash) if ($main::init_done); return undef; } -sub TA_CMI_JSON_GetStatus { - my ( $hash, $delay ) = @_; - my $name = $hash->{NAME}; +sub Notify { + my ($own_hash, $dev_hash) = @_; + my $ownName = $own_hash->{NAME}; # own name / hash + + return "" if(main::IsDisabled($ownName)); # Return without any further action if the module is disabled + + my $devName = $dev_hash->{NAME}; # Device that created the events + my $events = main::deviceEvents($dev_hash, 1); + + if($devName eq "global" && grep(m/^INITIALIZED|REREADCFG$/, @{$events})) { + SetupIntervals($own_hash); + } - TA_CMI_JSON_PerformHttpRequest($hash); } -sub TA_CMI_JSON_Undef { +sub SetupIntervals { + my ($hash) = @_; + my $name = $hash->{NAME}; + + my $queryParams = $hash->{QUERYPARAM}; + if ( defined $queryParams ) { + $hash->{INTERVAL} = AttrVal( $name, "interval", "60" ); + Log3 $name, 0, "TA_CMI_JSON ($name) - queryParam interval: ".$hash->{INTERVAL}; + Log3 $name, 0, "TA_CMI_JSON ($name) - queryParams=$queryParams"; + GetStatus( $hash ); + } + + my $outputStatesInterval = AttrVal( $name, 'outputStatesInterval', undef); + Log3 $name, 0, "TA_CMI_JSON ($name) - Define::outputStatesInterval: $outputStatesInterval"; + if ( defined $outputStatesInterval ) { + RequestOutputStates ( $hash ); + } + +} + +sub GetStatus { + my ( $hash ) = @_; + my $name = $hash->{NAME}; + + PerformHttpRequest($hash); +} + +sub Undef { my ($hash, $arg) = @_; my $name = $hash->{NAME}; @@ -166,7 +214,7 @@ sub TA_CMI_JSON_Undef { return undef; } -sub TA_CMI_JSON_PerformHttpRequest { +sub PerformHttpRequest { my ($hash, $def) = @_; my $name = $hash->{NAME}; my $url = "http://$hash->{CMIURL}/INCLUDE/api.cgi?jsonnode=$hash->{NODEID}&jsonparam=$hash->{QUERYPARAM}"; @@ -181,13 +229,13 @@ sub TA_CMI_JSON_PerformHttpRequest { header => "User-Agent: FHEM\r\nAccept: application/json", user => $username, pwd => $password, - callback => \&TA_CMI_JSON_ParseHttpResponse + callback => \&ParseHttpResponse }; HttpUtils_NonblockingGet($param); } -sub TA_CMI_JSON_ParseHttpResponse { +sub ParseHttpResponse { my ($param, $err, $data) = @_; my $hash = $param->{hash}; my $name = $hash->{NAME}; @@ -202,10 +250,10 @@ sub TA_CMI_JSON_ParseHttpResponse { } elsif($data ne "") { my $keyValues = json2nameValue($data); - my $canDevice = TA_CMI_JSON_extractDeviceName($keyValues->{Header_Device}); + my $canDevice = extractDeviceName($keyValues->{Header_Device}); $hash->{CAN_DEVICE} = $canDevice; $hash->{model} = $canDevice; - $hash->{CMI_API_VERSION} = TA_CMI_JSON_extractVersion($keyValues->{Header_Version}); + $hash->{CMI_API_VERSION} = extractVersion($keyValues->{Header_Version}); CommandDeleteReading(undef, "$name error"); readingsBeginUpdate($hash); @@ -213,12 +261,12 @@ sub TA_CMI_JSON_ParseHttpResponse { if ( $keyValues->{Status} eq 'OK' ) { my $queryParams = $hash->{QUERYPARAM}; - TA_CMI_JSON_extractReadings($hash, $keyValues, 'Inputs', 'Inputs') if ($queryParams =~ /I/); - TA_CMI_JSON_extractReadings($hash, $keyValues, 'Outputs', 'Outputs') if ($queryParams =~ /O/); + extractReadings($hash, $keyValues, 'Inputs', 'Inputs') if ($queryParams =~ /I/); + extractReadings($hash, $keyValues, 'Outputs', 'Outputs') if ($queryParams =~ /O/); if ($queryParams =~ /D/) { if ($canDevice eq 'UVR16x2' or $canDevice eq 'RSM610' ) { - TA_CMI_JSON_extractReadings($hash, $keyValues, 'DL-Bus', 'DL-Bus'); + extractReadings($hash, $keyValues, 'DL-Bus', 'DL-Bus'); } else { Log3 $name, 0, "TA_CMI_JSON ($name) - Reading DL-Bus input is not supported on $canDevice"; } @@ -226,7 +274,7 @@ sub TA_CMI_JSON_ParseHttpResponse { if ($queryParams =~ /La/) { if ($canDevice eq 'UVR16x2') { - TA_CMI_JSON_extractReadings($hash, $keyValues, 'LoggingAnalog', 'Logging_Analog'); + extractReadings($hash, $keyValues, 'LoggingAnalog', 'Logging_Analog'); } else { Log3 $name, 0, "TA_CMI_JSON ($name) - Reading Logging Analog data is not supported on $canDevice"; } @@ -234,7 +282,7 @@ sub TA_CMI_JSON_ParseHttpResponse { if ($queryParams =~ /Ld/) { if ($canDevice eq 'UVR16x2') { - TA_CMI_JSON_extractReadings($hash, $keyValues, 'LoggingDigital', 'Logging_Digital'); + extractReadings($hash, $keyValues, 'LoggingDigital', 'Logging_Digital'); } else { Log3 $name, 0, "TA_CMI_JSON ($name) - Reading Logging Digital data is not supported on $canDevice"; } @@ -246,24 +294,24 @@ sub TA_CMI_JSON_ParseHttpResponse { # Log3 $name, 3, "TA_CMI_JSON ($name) - Device: $keyValues->{Header_Device}"; } - my $functionName = "TA_CMI_JSON::TA_CMI_JSON_GetStatus"; + my $functionName = "TA_CMI_JSON::GetStatus"; RemoveInternalTimer($hash, $functionName); InternalTimer( gettimeofday() + $hash->{INTERVAL}, $functionName, $hash, 0 ); return undef; } -sub TA_CMI_JSON_extractDeviceName { +sub extractDeviceName { my ($input) = @_; return (defined($deviceNames{$input}) ? $deviceNames{$input} : 'unknown: ' . $input); } -sub TA_CMI_JSON_extractVersion { +sub extractVersion { my ($input) = @_; return (defined($versions{$input}) ? $versions{$input} : 'unknown: ' . $input); } -sub TA_CMI_JSON_extractReadings { +sub extractReadings { my ( $hash, $keyValues, $id, $dataKey ) = @_; my $name = $hash->{NAME}; @@ -312,19 +360,106 @@ sub TA_CMI_JSON_extractReadings { return undef; } -sub TA_CMI_JSON_Get { +sub Get { my ( $hash, $name, $opt, $args ) = @_; if ("update" eq $opt) { - TA_CMI_JSON_PerformHttpRequest($hash); + PerformHttpRequest($hash); + return undef; + } + + if ("readOutputStates" eq $opt) { + RequestOutputStates($hash); #unless $hash->{CAN_DEVICE} ne 'UVR1611'; return undef; } # Log3 $name, 3, "ZoneMinder ($name) - Get done ..."; - return "Unknown argument $opt, choose one of update"; - + return "Unknown argument $opt, choose one of update readOutputStates"; } +sub RequestOutputStates { + my ($hash) = @_; + my $name = $hash->{NAME}; + my $nodeId = $hash->{NODEID}; + my $hexNodeId = sprintf('%1x',$nodeId); + $hexNodeId = "0$hexNodeId" unless length($hexNodeId) == 2; + + my $url = "http://$hash->{CMIURL}/INCLUDE/agx2.cgi?nodex2=$hexNodeId"; + my $username = AttrVal($name, 'username', 'admin'); + my $password = AttrVal($name, 'password', 'admin'); + + my $param = { + url => "$url", + timeout => 5, + hash => $hash, + method => "GET", + header => "User-Agent: FHEM\r\nAccept: application/json", + user => $username, + pwd => $password, + callback => \&ParseOutputStateResponse + }; + + Log3 $name, 4, "TA_CMI_JSON ($name) - RequestOutputStates $url"; + HttpUtils_NonblockingGet($param); +} + +sub ParseOutputStateResponse { + my ($param, $err, $data) = @_; + my $hash = $param->{hash}; + my $name = $hash->{NAME}; + my $return; + + if($err ne "") { + Log3 $name, 0, "error while requesting output data ".$param->{url}." - $err"; + readingsBeginUpdate($hash); + readingsBulkUpdate($hash, 'state', 'ERROR', 0); + readingsBulkUpdate($hash, 'error', $err, 0); + readingsEndUpdate($hash, 0); + } elsif($data ne "") { + + my @values = split(';', $data); + my $nrValues = @values -2; + + my $prettyOutputStates = AttrVal( $name, "prettyOutputStates", '0' ); + my $readingNames = AttrVal($name, "readingNamesOutputs", undef); + if ( ! defined $readingNames ) { + Log3 $name, 0, "TA_CMI_JSON ($name) - Unable to assign output states, please set attribute readingNamesOutputs"; + return undef; + } + my @readingsArray = split(/ /, $readingNames); #1:T.Kollektor 5:T.Vorlauf + + readingsBeginUpdate($hash); + foreach (@readingsArray) { + my ( $readingNameIndex, $readingName ) = split(/\:/, $_); + my $i = $readingNameIndex+1; + + $readingName = makeReadingName($readingName); + my $readingValue = $values[$i]; + readingsBulkUpdateIfChanged($hash, $readingName.'_State', $readingValue); + if ($prettyOutputStates eq '1') { + + my $prettyValue = (defined($outputStates{$readingValue}) ? $outputStates{$readingValue} : '?:'.$readingValue); + readingsBulkUpdateIfChanged($hash, $readingName.'_State_Pretty', $prettyValue); + } + Log3 $name, 5, "readingName: $readingName, readingNameIndex: $readingNameIndex, valueIndex: $i, value: $readingValue"; + } + readingsEndUpdate($hash, 0); + + Log3 $name, 5, "TA_CMI_JSON ($name) - Output Data $nrValues: $data"; + } + + my $outputStatesInterval = AttrVal($name, 'outputStatesInterval', undef); + if ( defined $outputStatesInterval ) { + Log3 $name, 5, "TA_CMI_JSON ($name) - outputStatesInterval $outputStatesInterval"; + my $functionName = "TA_CMI_JSON::RequestOutputStates"; + RemoveInternalTimer($hash, $functionName); + InternalTimer( gettimeofday() + $outputStatesInterval, $functionName, $hash, 0 ); + } + + return undef; +} + + # Eval-Rückgabewert für erfolgreiches # Laden des Moduls 1; @@ -351,7 +486,7 @@ sub TA_CMI_JSON_Get {
Example:
    - defmod cmi TA_CMI_JSON 192.168.4.250 1 I,O,D
    + defmod cmi TA_CMI_JSON 192.168.4.250 1 <I,O,D>

It's mandatory to define which values should be mapped to readings.
@@ -364,6 +499,8 @@ sub TA_CMI_JSON_Get {
  • update
    Triggers querying of values from the CMI. Please note that the request rate is limited to one query per minute.
  • +
  • readOutputStates
    Reads Output states (eg Manual-On, Auto-Off) per defined output. Can be automated by setting outputStatesInterval. +


@@ -379,6 +516,8 @@ sub TA_CMI_JSON_Get {
  • includeUnitReadings [0:1]
    Adds another reading per value, which just contains the according unit of that reading.
  • includePrettyReadings [0:1]
    Adds another reading per value, which contains value plus unit of that reading.
  • interval
    Query interval in seconds. Minimum query interval is 60 seconds.
  • +
  • outputStatesInterval
    Request interval in seconds for getting output states.
  • +
  • prettyOutputStates [0:1]
    If set, generates a reading _State_Pretty for output states (eg Auto-On, Manual-Off)
  • username
    Username for querying the JSON-API. Needs to be either admin or user privilege.
  • password
    Password for querying the JSON-API.
  • @@ -407,7 +546,7 @@ Weitere Informationen zu diesem Modul im
  • update
    Hiermit kann sofort eine Abfrage der API ausgeführt werden. Das Limit von einer Anfrage pro Minute besteht trotzdem.
  • +
  • readOutputStates
    Liest Ausgangs-Stati, zB Auto-Ein, Hand-Aus. Kann mittels outputStatesInterval automatisch ausgeführt werden. Das Zeitlimit von 60 Sekunden trifft hier nicht zu. +


  • @@ -435,9 +576,10 @@ Weitere Informationen zu diesem Modul im