";
Log3 $name, 0, $msg;
return $msg;
}
$hash->{NAME} = $name;
$hash->{CMIURL} = $cmiUrl;
$hash->{NODEID} = $nodeId;
$hash->{QUERYPARAM} = $queryParams;
Log3 $name, 3, "TA_CMI_JSON ($name) - Define ... module=$module, CMI-URL=$cmiUrl, nodeId=$nodeId";
readingsSingleUpdate($hash, 'state', 'defined', 1);
SetupIntervals($hash) if ($main::init_done);
return undef;
}
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);
}
}
sub SetupIntervals {
my ($hash) = @_;
my $name = $hash->{NAME};
my $queryParams = $hash->{QUERYPARAM};
if ( defined $queryParams ) {
$hash->{INTERVAL} = AttrVal( $name, "interval", "60" );
Log3 $name, 3, "TA_CMI_JSON ($name) - queryParam interval: ".$hash->{INTERVAL};
Log3 $name, 3, "TA_CMI_JSON ($name) - queryParams=$queryParams";
}
#make initial call in any case, even if queryParams not defined. because that brings us model and number of outputs
GetStatus( $hash );
my $outputStatesInterval = AttrVal( $name, 'outputStatesInterval', undef);
if ( defined $outputStatesInterval ) {
Log3 $name, 3, "TA_CMI_JSON ($name) - Define::outputStatesInterval: $outputStatesInterval";
RequestOutputStates ( $hash );
}
}
sub GetStatus {
my ( $hash ) = @_;
my $name = $hash->{NAME};
PerformHttpRequest($hash);
}
sub Undef {
my ($hash, $arg) = @_;
my $name = $hash->{NAME};
HttpUtils_Close($hash);
RemoveInternalTimer($hash);
return undef;
}
sub PerformHttpRequest {
my ($hash, $def) = @_;
my $name = $hash->{NAME};
my $queryParam = $hash->{QUERYPARAM};
$queryParam = '' unless defined $queryParam;
my $url = "http://$hash->{CMIURL}/INCLUDE/api.cgi?jsonnode=$hash->{NODEID}&jsonparam=$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 => \&ParseHttpResponse
};
HttpUtils_NonblockingGet($param);
}
sub ParseHttpResponse {
my ($param, $err, $data) = @_;
my $hash = $param->{hash};
my $name = $hash->{NAME};
my $return;
if($err ne "") {
Log3 $name, 0, "TA_CMI_JSON ($name) - error while requesting ".$param->{url}." - $err";
readingsBeginUpdate($hash);
readingsBulkUpdate($hash, 'state', 'ERROR', 1);
readingsBulkUpdate($hash, 'error', $err, 1);
readingsEndUpdate($hash, 1);
} elsif($data ne "") {
my $keyValues = json2nameValue($data);
my $canDevice = extractDeviceName($keyValues->{Header_Device});
$hash->{CAN_DEVICE} = $canDevice;
$hash->{model} = $canDevice;
$hash->{CMI_API_VERSION} = extractVersion($keyValues->{Header_Version});
readingsDelete($hash, "error");
readingsDelete($hash, "state");
readingsBeginUpdate($hash);
readingsBulkUpdateIfChanged($hash, 'state', $keyValues->{Status}, 1);
if ( $keyValues->{Status} eq 'OK' ) {
my $queryParams = $hash->{QUERYPARAM};
$queryParams = '' unless defined $queryParams;
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' ) {
extractReadings($hash, $keyValues, 'DL-Bus', 'DL-Bus');
} else {
Log3 $name, 1, "TA_CMI_JSON ($name) - Reading DL-Bus input is not supported on $canDevice";
}
}
if ($queryParams =~ /La/) {
if ($canDevice eq 'UVR16x2') {
extractReadings($hash, $keyValues, 'LoggingAnalog', 'Logging_Analog');
} else {
Log3 $name, 1, "TA_CMI_JSON ($name) - Reading Logging Analog data is not supported on $canDevice";
}
}
if ($queryParams =~ /Ld/) {
if ($canDevice eq 'UVR16x2') {
extractReadings($hash, $keyValues, 'LoggingDigital', 'Logging_Digital');
} else {
Log3 $name, 1, "TA_CMI_JSON ($name) - Reading Logging Digital data is not supported on $canDevice";
}
}
}
readingsEndUpdate($hash, 1);
# Log3 $name, 3, "TA_CMI_JSON ($name) - Device: $keyValues->{Header_Device}";
}
if ( defined $hash->{QUERYPARAM} ) {
my $functionName = "TA_CMI_JSON::GetStatus";
RemoveInternalTimer($hash, $functionName);
InternalTimer( gettimeofday() + $hash->{INTERVAL}, $functionName, $hash, 0 );
}
return undef;
}
sub extractDeviceName {
my ($input) = @_;
return (defined($deviceNames{$input}) ? $deviceNames{$input} : 'unknown: ' . $input);
}
sub extractVersion {
my ($input) = @_;
return (defined($versions{$input}) ? $versions{$input} : 'unknown: ' . $input);
}
sub extractReadings {
my ( $hash, $keyValues, $id, $dataKey ) = @_;
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
my $inclUnitReadings = AttrVal( $name, "includeUnitReadings", 0 );
my $inclPrettyReadings = AttrVal( $name, "includePrettyReadings", 0 );
for my $i (0 .. (@readingsArray-1)) {
my ( $idx, $readingName ) = split(/\:/, $readingsArray[$i]);
$readingName = makeReadingName($readingName);
my $jsonKey = 'Data_'.$dataKey.'_'.$idx.'_Value_Value';
my $readingValue = $keyValues->{$jsonKey};
Log3 $name, 5, "TA_CMI_JSON ($name) - readingName: $readingName, key: $jsonKey, value: $readingValue";
readingsBulkUpdateIfChanged($hash, $readingName, $readingValue, 1);
$jsonKey = 'Data_'.$dataKey.'_'.$idx.'_Value_RAS';
my $readingRas = $keyValues->{$jsonKey};
if (defined($readingRas)) {
readingsBulkUpdateIfChanged($hash, $readingName . '_RAS', $readingRas, 1);
if ($inclPrettyReadings) {
my $ras = (defined($rasStates{$readingRas}) ? $rasStates{$readingRas} : undef);
readingsBulkUpdateIfChanged($hash, $readingName . '_RAS_Pretty', $ras, 1) if ($ras);
}
}
my $unit;
if ($inclUnitReadings || $inclPrettyReadings) {
$jsonKey = 'Data_'.$dataKey.'_'.$idx.'_Value_Unit';
my $readingUnit = $keyValues->{$jsonKey};
$unit = (defined($units{$readingUnit}) ? $units{$readingUnit} : 'unknown: ' . $readingUnit);
Log3 $name, 5, "TA_CMI_JSON ($name) - readingName: $readingName . '_Unit', key: $jsonKey, value: $readingUnit, unit: $unit";
readingsBulkUpdateIfChanged($hash, $readingName . '_Unit', $unit, 1) if ($inclUnitReadings);
}
if ($inclPrettyReadings) {
readingsBulkUpdateIfChanged($hash, $readingName . '_Pretty', $readingValue . ' ' . $unit, 1);
}
}
return undef;
}
sub Get {
my ( $hash, $name, $opt, $args ) = @_;
if ("update" eq $opt) {
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 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, "TA_CMI_JSON ($name) - error while requesting output data ".$param->{url}." - $err";
readingsBeginUpdate($hash);
readingsBulkUpdate($hash, 'state', 'ERROR', 0);
readingsBulkUpdate($hash, 'error', $err, 0);
readingsEndUpdate($hash, 1);
} elsif($data ne "") {
readingsDelete($hash, "error");
readingsDelete($hash, "state");
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, 1, "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, 1);
if ($prettyOutputStates eq '1') {
my $prettyValue = (defined($outputStates{$readingValue}) ? $outputStates{$readingValue} : '?:'.$readingValue);
readingsBulkUpdateIfChanged($hash, $readingName.'_State_Pretty', $prettyValue, 1);
}
Log3 $name, 5, "TA_CMI_JSON ($name) - readingName: $readingName, readingNameIndex: $readingNameIndex, valueIndex: $i, value: $readingValue";
}
readingsEndUpdate($hash, 1);
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;
}
my $counter = 0;
sub DetailFn {
my $css = '';
my $html = '
1
2
3
'.$counter.'
5
6
7
8
9
10
11
12
13
14
15
16
';
$counter++;
#InternalTimer( gettimeofday() + $hash->{INTERVAL}, $functionName, $hash, 0 );
return $css.$html;
}
#sub DirectNotify {
# my ($filter,$fhemweb_instance,
#}
# 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.
readOutputStates
Reads Output states (eg Manual-On, Auto-Off) per defined output. Can be automated by setting outputStatesInterval
.
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
readingNamesLoggingAnalog {index:reading-name}
This maps received values from Analog Logging to readings. zB eg 1:Flowrate_Solar 2:T.Solar_Backflow
readingNamesLoggingDigital {index:reading-name}
This maps received values from Digital Logging to readings. zB eg 1:Flowrate_Solar 2:T.Solar_Backflow
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.
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.
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.
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
readingNamesLoggingAnalog {index:reading-name}
Hiermit werden erhaltene Werte vom Analog Logging einem Reading zugewiesen. zB 1:Durchfluss_Solar 2:T.Solar_RL
readingNamesLoggingDigital {index:reading-name}
Hiermit werden erhaltene Werte vom Digital Logging einem Reading zugewiesen. zB 1:Durchfluss_Solar 2:T.Solar_RL
includeUnitReadings [0:1]
Definiert, ob zu jedem Reading ein zusätzliches Reading _Name geschrieben werden soll, welches die Einheit enthält.
includePrettyReadings [0:1]
Definiert, ob zu jedem Reading zusätzlich ein Reading, welches Wert und Einheit enthält, geschrieben werden soll.
interval
Abfrage-Intervall in Sekunden. Muss mindestens 60 sein.
outputStatesInterval
Abfrage-Intervall für Ausgangs-Stati in Sekunden.
prettyOutputStates [0:1]
Definiert, ob zu einem Ausgangs-Status ein zusätzliches Reading _State_Pretty geschrieben werden soll (liefert zB Auto-On, Manual-Off)
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_DE
# Ende der Commandref
=cut