From a6387c8f13bcd2f6aa772f3510b7fdc06eeebd0d Mon Sep 17 00:00:00 2001
From: delmar <>
Date: Fri, 2 Nov 2018 07:46:34 +0000
Subject: [PATCH] 72_TA_CMI_JSON: introducing new module for querying the CMI's
JSON API
git-svn-id: https://svn.fhem.de/fhem/trunk/fhem@17661 2b470e98-0d58-463d-a4d8-8e2adae1ed80
---
CHANGED | 1 +
FHEM/72_TA_CMI_JSON.pm | 368 +++++++++++++++++++++++++++++++++++++++++
MAINTAINER.txt | 1 +
3 files changed, 370 insertions(+)
create mode 100755 FHEM/72_TA_CMI_JSON.pm
diff --git a/CHANGED b/CHANGED
index 47f3fe72d..30041db45 100644
--- a/CHANGED
+++ b/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.
+ - new: 72_TA_CMI_JSON: introduced JSON-API integration with CMI devices
- changed: 49_SSCam: usage of attribute \"livestreamprefix\" changed,
minor code changes, SSCamSTRM changed for compatibility
and new attributes with direct attribute help
diff --git a/FHEM/72_TA_CMI_JSON.pm b/FHEM/72_TA_CMI_JSON.pm
new file mode 100755
index 000000000..141a2da5c
--- /dev/null
+++ b/FHEM/72_TA_CMI_JSON.pm
@@ -0,0 +1,368 @@
+##############################################################################
+#
+# 72_TA_CMI_JSON.pm
+#
+# This file is part of Fhem.
+#
+# Fhem is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 2 of the License, or
+# (at your option) any later version.
+#
+# Fhem is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with Fhem. If not, see .
+#
+##############################################################################
+#
+# TA_CMI_JSON (c) Martin Gutenbrunner / https://github.com/delmar43/FHEM
+#
+# This module queries the CMI JSON API and allows to map values to readings.
+# Supported devices are UVR1611, UVR16x2, RSM610, CAN-I/O45, CAN-EZ2, CAN-MTx2,
+# and CAN-BC2 by Technische Alternative https://www.ta.co.at/
+#
+# Information in the Wiki: https://wiki.fhem.de/wiki/UVR16x2
+#
+# Discussed in FHEM Forum:
+# * https://forum.fhem.de/index.php/topic,41439.0.html
+# * https://forum.fhem.de/index.php/topic,13534.45.html
+#
+# $Id$
+#
+##############################################################################
+
+package main;
+use strict;
+use warnings;
+use HttpUtils;
+
+sub TA_CMI_JSON_Initialize;
+sub TA_CMI_JSON_Define;
+sub TA_CMI_JSON_GetStatus;
+sub TA_CMI_JSON_Undef;
+sub TA_CMI_JSON_PerformHttpRequest;
+sub TA_CMI_JSON_ParseHttpResponse;
+sub TA_CMI_JSON_Get;
+sub TA_CMI_JSON_extractDeviceName;
+sub TA_CMI_JSON_extractVersion;
+sub TA_CMI_JSON_extractReadings;
+
+sub TA_CMI_JSON_Initialize($) {
+ my ($hash) = @_;
+
+ $hash->{GetFn} = "TA_CMI_JSON_Get";
+ $hash->{DefFn} = "TA_CMI_JSON_Define";
+ $hash->{UndefFn} = "TA_CMI_JSON_Undef";
+
+ $hash->{AttrList} = "username password interval readingNamesInputs readingNamesOutputs readingNamesDL-Bus " . $readingFnAttributes;
+
+ Log3 '', 3, "TA_CMI_JSON - Initialize done ...";
+}
+
+sub TA_CMI_JSON_Define($$) {
+ my ( $hash, $def ) = @_;
+ my @a = split( "[ \t][ \t]*", $def );
+
+ my $name = $a[0];
+ my $module = $a[1];
+ my $cmiUrl = $a[2];
+ 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;
+ return $msg;
+ }
+
+ $hash->{NAME} = $name;
+ $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";
+
+ readingsSingleUpdate($hash, 'state', 'defined', 1);
+
+ TA_CMI_JSON_GetStatus( $hash, 2 );
+
+ return undef;
+}
+
+sub TA_CMI_JSON_GetStatus( $;$ ) {
+ my ( $hash, $delay ) = @_;
+ my $name = $hash->{NAME};
+
+ TA_CMI_JSON_PerformHttpRequest($hash);
+}
+
+sub TA_CMI_JSON_Undef($$) {
+ my ($hash, $arg) = @_;
+ my $name = $hash->{NAME};
+
+ HttpUtils_Close($hash);
+
+ return undef;
+}
+
+sub TA_CMI_JSON_PerformHttpRequest($) {
+ my ($hash, $def) = @_;
+ my $name = $hash->{NAME};
+ my $url = "http://$hash->{CMIURL}/INCLUDE/api.cgi?jsonnode=$hash->{NODEID}&jsonparam=$hash->{QUERYPARAM}";
+ 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 => \&TA_CMI_JSON_ParseHttpResponse
+ };
+
+ HttpUtils_NonblockingGet($param);
+}
+
+sub TA_CMI_JSON_ParseHttpResponse($) {
+ my ($param, $err, $data) = @_;
+ my $hash = $param->{hash};
+ my $name = $hash->{NAME};
+ my $return;
+
+ if($err ne "") {
+ Log3 $name, 0, "error while requesting ".$param->{url}." - $err"; # Eintrag fürs Log
+# readingsSingleUpdate($hash, "fullResponse", "ERROR", 0); # Readings erzeugen
+ readingsBeginUpdate($hash);
+ readingsBulkUpdate($hash, 'state', 'ERROR', 0);
+ readingsBulkUpdate($hash, 'error', $err, 0);
+ readingsEndUpdate($hash, 0);
+ } elsif($data ne "") {
+ my $keyValues = json2nameValue($data);
+
+ $hash->{STATE} = $keyValues->{Status};
+ $hash->{CAN_DEVICE} = TA_CMI_JSON_extractDeviceName($keyValues->{Header_Device});
+ $hash->{CMI_API_VERSION} = TA_CMI_JSON_extractVersion($keyValues->{Header_Version});
+ CommandDeleteReading(undef, "$name error");
+
+ readingsBeginUpdate($hash);
+ readingsBulkUpdateIfChanged($hash, 'state', $keyValues->{Status});
+ if ( $keyValues->{Status} eq 'OK' ) {
+ my $queryParams = $hash->{QUERYPARAM};
+ TA_CMI_JSON_extractReadings($hash, $keyValues, 'Inputs') if ($queryParams =~ /I/);
+ TA_CMI_JSON_extractReadings($hash, $keyValues, 'Outputs') if ($queryParams =~ /O/);
+ TA_CMI_JSON_extractReadings($hash, $keyValues, 'DL-Bus') if ($queryParams =~ /D/);
+ }
+
+ readingsEndUpdate($hash, 1);
+
+# Log3 $name, 3, "TA_CMI_JSON ($name) - Device: $keyValues->{Header_Device}";
+ }
+
+ my $functionName = "TA_CMI_JSON_GetStatus";
+ RemoveInternalTimer($hash, $functionName);
+ InternalTimer( gettimeofday() + $hash->{INTERVAL}, $functionName, $hash, 0 );
+
+ return undef;
+}
+
+sub TA_CMI_JSON_extractDeviceName($) {
+ my ($input) = @_;
+
+ my $result;
+ if ($input eq '80') {
+ $result = 'UVR1611';
+ } elsif ($input eq '87') {
+ $result = 'UVR16x2';
+ } elsif ($input eq '88') {
+ $result = 'RSM610';
+ } elsif ($input eq '89') {
+ $result = 'CAN-I/O45';
+ } elsif ($input eq '8B') {
+ $result = 'CAN-EZ2';
+ } elsif ($input eq '8C') {
+ $result = 'CAN-MTx2';
+ } elsif ($input eq '8D') {
+ $result = 'CAN-BC2';
+ } else {
+ $result = "Unknown: $input";
+ }
+
+ return $result;
+}
+
+sub TA_CMI_JSON_extractVersion($) {
+ my ($input) = @_;
+
+ my $result;
+ if ($input == 1) {
+ $result = '1.25.2 2016-12-12';
+ } elsif ($input == 2) {
+ $result = '1.26.1 2017-02-24';
+ } elsif ($input == 3) {
+ $result = '1.28.0 2017-11-09';
+ } else {
+ $result = "unknown: $input";
+ }
+
+ return $result;
+}
+
+sub TA_CMI_JSON_extractReadings($$$) {
+ my ( $hash, $keyValues, $id ) = @_;
+ my $name = $hash->{NAME};
+
+ my $readingNames = AttrVal($name, "readingNames$id", '');
+ Log3 $name, 5, 'readingNames'.$id.": $readingNames";
+ my @readingsArray = split(/ /, $readingNames); #1:T.Kollektor 5:T.Vorlauf
+
+ for my $i (0 .. (@readingsArray-1)) {
+ my ( $idx, $readingName ) = split(/\:/, $readingsArray[$i]);
+ $readingName = makeReadingName($readingName);
+
+ my $jsonKey = 'Data_'.$id.'_'.$idx.'_Value_Value';
+ my $readingValue = $keyValues->{$jsonKey};
+ Log3 $name, 5, "readingName: $readingName, key: $jsonKey, value: $readingValue";
+
+ readingsBulkUpdateIfChanged($hash, $readingName, $readingValue);
+ }
+
+ return undef;
+}
+
+sub TA_CMI_JSON_Get ($@) {
+ my ( $hash, $name, $opt, $args ) = @_;
+
+ if ("update" eq $opt) {
+ TA_CMI_JSON_PerformHttpRequest($hash);
+ return undef;
+ }
+
+# Log3 $name, 3, "ZoneMinder ($name) - Get done ...";
+ return "Unknown argument $opt, choose one of update";
+
+}
+
+# Eval-Rückgabewert für erfolgreiches
+# Laden des Moduls
+1;
+
+
+# Beginn der Commandref
+
+=pod
+=item [device]
+=item summary Reads values from the Technische Alternative CMI device
+=item summary_DE Werte vom CMI der Firma Technische Alternative auslesen.
+
+=begin html
+
+
+TA_CMI_JSON
+
+ Define
+
+ define <name> TA_CMI_JSON <IP> <CAN-Node-Id> <Query-Params>
+
+ Defines a device that receives values from the CMI at the given IP for the CAN-device with the given CAN-Node-Id.
+ Query-Param defines, which values you want to read. Allowed values are I,O,D.
+
+ Example:
+
+ 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.
+ Only mapped values will not be written to readings. (see Attributes for details)
+
+
+
+
+ Get
+
+ update
Triggers querying of values from the CMI. Please note that the request rate is limited to one query per minute.
+
+
+
+
+
+ Attributes
+
+
+ readingNamesDL-Bus {index:reading-name}
This maps received values from the DL-Bus to readings. eg 1:Flowrate_Solar 2:T.Solar_Backflow
+ readingNamesInputs {index:reading-name}
This maps received values from the Inputs to readings. eg 1:Flowrate_Solar 2:T.Solar_Backflow
+ readingNamesOutputs {index:reading-name}
This maps received values from the Outputs to readings. eg 1:Flowrate_Solar 2:T.Solar_Backflow
+ interval
Query interval in seconds. Minimum query interval is 60 seconds.
+ username
Username for querying the JSON-API. Needs to be either admin or user privilege.
+ password
Password for querying the JSON-API.
+
+
+
+
+
+ Readings
+
+ Readings will appear according to the mappings defined in Attributes.
+
+=end html
+
+=begin html_DE
+
+
+TA_CMI_JSON
+Weitere Informationen zu diesem Modul im FHEM-Wiki.
+
+ Define
+
+ define <name> TA_CMI_JSON <IP> <CAN-Node-Id> <Query-Params>
+
+ Liest Werte vom CMI mit der angegebenen IP für das CAN-Gerät mit der angegebenen Node-Id.
+ Query-Param definiert, welche Werte ausgelesen werden sollen. Erlaubt sind I,O,D.
+
+ Beispiel:
+
+ defmod cmi TA_CMI_JSON 192.168.4.250 1 I,O,D
+
+
+ Daneben muss auch noch das mapping angegeben werden, welche Werte in welches Reading geschrieben werden sollen.
+ Nur gemappte Werte werden in Readings geschrieben. (siehe Attributes)
+
+
+
+
+ Get
+
+ update
Hiermit kann sofort eine Abfrage der API ausgeführt werden. Das Limit von einer Anfrage pro Minute besteht trotzdem.
+
+
+
+
+
+ Attributes
+
+
+ readingNamesDL-Bus {index:reading-name}
Hiermit werden erhaltene Werte vom DL-Bus einem Reading zugewiesen. zB 1:Durchfluss_Solar 2:T.Solar_RL
+ readingNamesInput {index:reading-name}
Hiermit werden erhaltene Werte der Eingänge einem Reading zugewiesen. zB 1:Durchfluss_Solar 2:T.Solar_RL
+ readingNamesDL-Bus {index:reading-name}
Hiermit werden erhaltene Werte der Ausgänge einem Reading zugewiesen. zB 1:Durchfluss_Solar 2:T.Solar_RL
+ interval
Abfrage-Intervall in Sekunden. Muss mindestens 60 sein.
+ username
Username zur Abfrage der JSON-API. Muss die Berechtigungsstufe admin oder user haben.
+ password
Passwort zur Abfrage der JSON-API.
+
+
+
+
+
+ Readings
+
+ Readings werden entsprechend der Definition in den Attributen angelegt.
+=end html
+
+# Ende der Commandref
+=cut
diff --git a/MAINTAINER.txt b/MAINTAINER.txt
index 813b72c6f..1c19d30c1 100644
--- a/MAINTAINER.txt
+++ b/MAINTAINER.txt
@@ -339,6 +339,7 @@ FHEM/71_ZM_Monitor.pm delmar Sonstige Systeme
FHEM/72_FB_CALLMONITOR.pm markusbloch Unterstuetzende Dienste
FHEM/72_FB_CALLLIST.pm markusbloch Frontends
FHEM/72_FRITZBOX.pm tupol FRITZBOX (link als PM an tupol)
+FHEM/72_TA_CMI_JSON.pm delmar Sonstige Systeme
FHEM/72_XiaomiDevice.pm markus-m Sonstige Systeme
FHEM/73_ElectricityCalculator.pm Sailor http://forum.fhem.de/index.php/topic,57106.0.html
FHEM/73_GasCalculator Sailor http://forum.fhem.de/index.php/topic,47909.0.html