fhem-mirror/FHEM/46_SmartPi.pm
LeonGaultier 4194020a62 46_SmartPi: exchange commandref, add NotifyFn
git-svn-id: https://svn.fhem.de/fhem/trunk/fhem@17119 2b470e98-0d58-463d-a4d8-8e2adae1ed80
2018-08-10 14:56:05 +00:00

535 lines
17 KiB
Perl
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

###############################################################################
#
# Developed with Kate
#
# (c) 2017 Copyright: Marko Oldenburg (leongaultier at gmail dot com)
# All rights reserved
#
# This script 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
# any later version.
#
# The GNU General Public License can be found at
# http://www.gnu.org/copyleft/gpl.html.
# A copy is found in the textfile GPL.txt and important notices to the license
# from the author is found in LICENSE.txt distributed with these scripts.
#
# 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. See the
# GNU General Public License for more details.
#
#
# $Id$
#
###############################################################################
##
##
## Das JSON Modul immer in einem eval aufrufen
# $data = eval{decode_json($data)};
#
# if($@){
# Log3($SELF, 2, "$TYPE ($SELF) - error while request: $@");
#
# readingsSingleUpdate($hash, "state", "error", 1);
#
# return;
# }
#
#
###### Möglicher Aufbau eines JSON Strings für die SmartPi
#
# Recieve JSON data: {"serial":"smartpi160812345","name":"B1.1_House","lat":52.3667,"lng":9.7167,"time":"2017-06-17 10:19:04","softwareversion":"","ipaddress":"169.254.3.10","datasets":[{"time":"2017-06-17 10:19:02","phases":[{"phase":1,"name":"phase 1","values":[{"type":"current","unity":"A","info":"","data":1.0003561},{"type":"voltage","unity":"V","info":"","data":230},{"type":"power","unity":"W","info":"","data":230.0819},{"type":"cosphi","unity":"","info":"","data":-0.72846437},{"type":"frequency","unity":"Hz","info":"","data":49.306625}]},{"phase":2,"name":"phase 2","values":[{"type":"current","unity":"A","info":"","data":0.45092472},{"type":"voltage","unity":"V","info":"","data":230},{"type":"power","unity":"W","info":"","data":103.712685},{"type":"cosphi","unity":"","info":"","data":-0.82941854},{"type":"frequency","unity":"Hz","info":"","data":48.192772}]},{"phase":3,"name":"phase 3","values":[{"type":"current","unity":"A","info":"","data":0.4813663},{"type":"voltage","unity":"V","info":"","data":230},{"type":"power","unity":"W","info":"","data":110.71425},{"type":"cosphi","unity":"","info":"","data":-0.2584238},{"type":"frequency","unity":"Hz","info":"","data":50.354053}]},{"phase":4,"name":"phase 4","values":[{"type":"current","unity":"A","info":"","data":0.7937981}]}]}]}
#
#
##
##
package main;
my $missingModul = "";
use strict;
use warnings;
use HttpUtils;
eval "use JSON;1" or $missingModul .= "JSON ";
my $version = "1.4.0";
# Declare functions
sub SmartPi_Attr(@);
sub SmartPi_Define($$);
sub SmartPi_Initialize($);
sub SmartPi_Notify($$);
sub SmartPi_Get($@);
sub SmartPi_GetData($@);
sub SmartPi_Undef($$);
sub SmartPi_ResponseProcessing($$);
sub SmartPi_ErrorHandling($$$);
sub SmartPi_WriteReadings($$);
sub SmartPi_Timer_GetData($);
sub SmartPi_Initialize($) {
my ($hash) = @_;
# Consumer
$hash->{GetFn} = "SmartPi_Get";
$hash->{DefFn} = "SmartPi_Define";
$hash->{UndefFn} = "SmartPi_Undef";
$hash->{NotifyFn} = "SmartPi_Notify";
$hash->{AttrFn} = "SmartPi_Attr";
$hash->{AttrList} = "interval ".
"disable:1 ".
"decimalPlace:0,1,2,3,4,5 ".
"disabledForIntervals ".
$readingFnAttributes;
foreach my $d(sort keys %{$modules{SmartPi}{defptr}}) {
my $hash = $modules{SmartPi}{defptr}{$d};
$hash->{VERSION} = $version;
}
}
sub SmartPi_Define($$) {
my ( $hash, $def ) = @_;
my @a = split( "[ \t][ \t]*", $def );
return "too few parameters: define <name> SmartPi <HOST>" if( @a != 3);
return "Cannot define a HEOS device. Perl modul $missingModul is missing." if ( $missingModul );
my $name = $a[0];
my $host = $a[2];
$hash->{HOST} = $host;
$hash->{NOTIFYDEV} = "global";
$hash->{INTERVAL} = 300;
$hash->{PORT} = 1080;
$hash->{VERSION} = $version;
$attr{$name}{room} = "SmartPi" if( !defined( $attr{$name}{room} ) );
Log3 $name, 3, "SmartPi ($name) - defined SmartPi Device with Host $host, Port $hash->{PORT} and Interval $hash->{INTERVAL}";
$modules{SmartPi}{defptr}{HOST} = $hash;
return undef;
}
sub SmartPi_Undef($$) {
my ( $hash, $arg ) = @_;
my $name = $hash->{NAME};
Log3 $name, 3, "SmartPi ($name) - Device $name deleted";
delete $modules{SmartPi}{defptr}{HOST} if( defined($modules{SmartPi}{defptr}{HOST}) and $hash->{HOST} );
return undef;
}
sub SmartPi_Attr(@) {
my ( $cmd, $name, $attrName, $attrVal ) = @_;
my $hash = $defs{$name};
my $orig = $attrVal;
if( $attrName eq "disable" ) {
if( $cmd eq "set" ) {
if( $attrVal eq "0" ) {
readingsSingleUpdate ( $hash, "state", "enabled", 1 );
Log3 $name, 3, "SmartPi ($name) - enabled";
} else {
RemoveInternalTimer($hash);
readingsSingleUpdate ( $hash, "state", "disabled", 1 );
Log3 $name, 3, "SmartPi ($name) - disabled";
}
} else {
readingsSingleUpdate ( $hash, "state", "enabled", 1 );
Log3 $name, 3, "SmartPi ($name) - enabled";
}
} elsif( $attrName eq "disabledForIntervals" ) {
if( $cmd eq "set" ) {
return "check disabledForIntervals Syntax HH:MM-HH:MM or 'HH:MM-HH:MM HH:MM-HH:MM ...'"
unless($attrVal =~ /^((\d{2}:\d{2})-(\d{2}:\d{2})\s?)+$/);
Log3 $name, 3, "SmartPi ($name) - disabledForIntervals";
}
elsif( $cmd eq "del" ) {
Log3 $name, 3, "SmartPi ($name) - enabled";
readingsSingleUpdate ( $hash, "state", "active", 1 );
}
} elsif( $attrName eq "interval" ) {
if( $cmd eq "set" ) {
$hash->{INTERVAL} = $attrVal;
Log3 $name, 3, "SmartPi ($name) - set interval: $attrVal";
} else {
$hash->{INTERVAL} = 300;
Log3 $name, 3, "SmartPi ($name) - delete User interval and set default: 300";
}
}
return undef;
}
sub SmartPi_Notify($$) {
my ($hash,$dev) = @_;
my $name = $hash->{NAME};
return if (IsDisabled($name));
my $devname = $dev->{NAME};
my $devtype = $dev->{TYPE};
my $events = deviceEvents($dev,1);
return if (!$events);
SmartPi_Timer_GetData($hash) if( (grep /^DEFINED.$name$/,@{$events}
or grep /^INITIALIZED$/,@{$events}
or grep /^MODIFIED.$name$/,@{$events}
or grep /^DELETEATTR.$name.interval$/,@{$events}
or grep /^ATTR.$name.interval.[0-9]+/,@{$events}
or grep /^DELETEATTR.$name.disable$/,@{$events}
or grep /^ATTR.$name.disable.0$/,@{$events}
or grep /^DELETEATTR.$name.decimalPlace$/,@{$events}
or grep /^ATTR.$name.decimalPlace.[0-9]+/,@{$events}) and $init_done );
return;
}
sub SmartPi_Get($@) {
my ($hash, $name, $cmd, @args) = @_;
my ($arg, @params) = @args;
my @phaseId = ('phase1','phase2','phase3','all');
my @valueId = ('all','current','voltage','power','cosphi','frequency');
my $phaseId;
my $valueId;
if( $cmd eq 'phase1' ) {
$phaseId = 1;
$valueId = $arg;
} elsif( $cmd eq 'phase2' ) {
$phaseId = 2;
$valueId = $arg;
} elsif( $cmd eq 'phase3' ) {
$phaseId = 3;
$valueId = $arg;
} elsif( $cmd eq 'phase4' ) {
$phaseId = 4;
$valueId = $arg;
} elsif( $cmd eq 'all' ) {
$phaseId = 'all';
$valueId = $arg;
} else {
my $list = '';
foreach(@phaseId) {
$list .= $_ . ':' . join(',',@valueId) . ' ';
}
$list .= 'phase4:current';
return "Unknown argument $cmd, choose one of $list";
}
if( not IsDisabled($name) ) {
SmartPi_GetData($hash,$phaseId,$valueId);
} else {
readingsSingleUpdate($hash,'state','disabled',1);
}
return undef;
}
sub SmartPi_Timer_GetData($) {
my $hash = shift;
my $name = $hash->{NAME};
RemoveInternalTimer($hash);
if( not IsDisabled($name) ) {
SmartPi_GetData($hash,'all','all');
} else {
readingsSingleUpdate($hash,'state','disabled',1);
}
InternalTimer( gettimeofday()+$hash->{INTERVAL}, 'SmartPi_Timer_GetData', $hash, 1 );
Log3 $name, 4, "SmartPi ($name) - Call InternalTimer SmartPi_Timer_GetData";
}
sub SmartPi_GetData($@) {
my ($hash,$phaseId,$valueId) = @_;
my $name = $hash->{NAME};
my $host = $hash->{HOST};
my $port = $hash->{PORT};
my $uri = $host . ':' . $port . '/api/' . $phaseId . '/' . $valueId . '/now';
readingsSingleUpdate($hash,'state','fetch data',1);
HttpUtils_NonblockingGet(
{
url => "http://" . $uri,
timeout => 5,
method => 'GET',
hash => $hash,
doTrigger => 1,
callback => \&SmartPi_ErrorHandling,
}
);
Log3 $name, 5, "SmartPi ($name) - Send with URI: $uri";
}
sub SmartPi_ErrorHandling($$$) {
my ($param,$err,$data) = @_;
my $hash = $param->{hash};
my $name = $hash->{NAME};
### Begin Error Handling
if( defined( $err ) ) {
if( $err ne "" ) {
readingsBeginUpdate( $hash );
readingsBulkUpdateIfChanged ( $hash, 'state', $err, 1);
readingsBulkUpdateIfChanged( $hash, 'lastRequestError', $err, 1 );
readingsEndUpdate( $hash, 1 );
Log3 $name, 3, "SmartPi ($name) - RequestERROR: $err";
return;
}
}
if( $data eq "" and exists( $param->{code} ) && $param->{code} ne 200 ) {
readingsBeginUpdate( $hash );
readingsBulkUpdateIfChanged ( $hash, 'state', $param->{code}, 1 );
readingsBulkUpdateIfChanged( $hash, 'lastRequestError', $param->{code}, 1 );
Log3 $name, 3, "SmartPi ($name) - RequestERROR: ".$param->{code};
readingsEndUpdate( $hash, 1 );
Log3 $name, 5, "SmartPi ($name) - RequestERROR: received http code ".$param->{code}." without any data after requesting";
return;
}
if( ( $data =~ /Error/i ) and exists( $param->{code} ) ) {
readingsBeginUpdate( $hash );
readingsBulkUpdateIfChanged( $hash, 'state', $param->{code}, 1 );
readingsBulkUpdateIfChanged( $hash, "lastRequestError", $param->{code}, 1 );
readingsEndUpdate( $hash, 1 );
Log3 $name, 3, "SmartPi ($name) - statusRequestERROR: http error ".$param->{code};
return;
### End Error Handling
}
Log3 $name, 4, "SmartPi ($name) - Recieve JSON data: $data";
SmartPi_ResponseProcessing($hash,$data);
}
sub SmartPi_ResponseProcessing($$) {
my ($hash,$json) = @_;
my $name = $hash->{NAME};
my $decode_json;
#$json = '{"serial":"smartpi160812345","name":"House","lat":52.3667,"lng":9.7167,"time":"2017-05-30 19:52:11","softwareversion":"","ipaddress":"169.254.3.10","datasets":[{"time":"2017-05-30 19:52:08","phases":[{"phase":1,"name":"phase 1","values":[{"type":"current","unity":"A","info":"","data":0.24830514},{"type":"voltage","unity":"V","info":"","data":230},{"type":"power","unity":"W","info":"","data":57.110184},{"type":"cosphi","unity":"","info":"","data":0.70275474},{"type":"frequency","unity":"Hz","info":"","data":120.413925}]},{"phase":2,"name":"phase 2","values":[{"type":"current","unity":"A","info":"","data":0.86874366},{"type":"voltage","unity":"V","info":"","data":230},{"type":"power","unity":"W","info":"","data":199.81104},{"type":"cosphi","unity":"","info":"","data":0.99155134},{"type":"frequency","unity":"Hz","info":"","data":386.1237}]},{"phase":3,"name":"phase 3","values":[{"type":"current","unity":"A","info":"","data":1.3195294},{"type":"voltage","unity":"V","info":"","data":230},{"type":"power","unity":"W","info":"","data":303.49176},{"type":"cosphi","unity":"","info":"","data":-0.25960922},{"type":"frequency","unity":"Hz","info":"","data":153.38525}]},{"phase":4,"name":"phase 4","values":[{"type":"current","unity":"A","info":"","data":1.0668689}]}]}]}';
$decode_json = eval{decode_json($json)};
if($@){
Log3 $name, 4, "SmartPi ($name) - error while request: $@";
readingsSingleUpdate($hash, "state", "error", 1);
return;
}
SmartPi_WriteReadings($hash,$decode_json);
}
sub SmartPi_WriteReadings($$) {
my ($hash,$decode_json) = @_;
my $name = $hash->{NAME};
Log3 $name, 4, "SmartPi ($name) - Write Readings";
readingsBeginUpdate($hash);
readingsBulkUpdateIfChanged($hash,'serialNumber',$decode_json->{serial},1);
readingsBulkUpdateIfChanged($hash,'smartPiName',$decode_json->{name},1);
readingsBulkUpdateIfChanged($hash,'latitude',$decode_json->{lat},1);
readingsBulkUpdateIfChanged($hash,'longitude',$decode_json->{lng},1);
readingsBulkUpdateIfChanged($hash,'lastfetchTime',$decode_json->{time},1);
readingsBulkUpdateIfChanged($hash,'serialNumber',$decode_json->{softwareversion},1);
if( ref($decode_json->{datasets}) eq "ARRAY" and scalar(@{$decode_json->{datasets}}) > 0 ) {
my $dataset;
my $phase;
my $value;
my $decimal = AttrVal($name,'decimalPlace',2);
foreach $dataset (@{$decode_json->{datasets}}) {
readingsBulkUpdateIfChanged($hash,'datasetsTime',$dataset->{time},1);
if( ref($dataset->{phases}) eq "ARRAY" and scalar(@{$dataset->{phases}}) > 0 ) {
foreach $phase (@{$dataset->{phases}}) {
if( ref($phase->{values}) eq "ARRAY" and scalar(@{$phase->{values}}) > 0 ) {
foreach $value (@{$phase->{values}}) {
readingsBulkUpdateIfChanged( $hash, "phase$phase->{phase}_Current", sprintf("%.${decimal}f",$value->{data}), 1 ) if( $value->{type} eq 'current' );
readingsBulkUpdateIfChanged( $hash, "phase$phase->{phase}_Voltage", sprintf("%.${decimal}f",$value->{data}), 1 ) if( $value->{type} eq 'voltage' );
readingsBulkUpdateIfChanged( $hash, "phase$phase->{phase}_Power", sprintf("%.${decimal}f",$value->{data}), 1 ) if( $value->{type} eq 'power' );
readingsBulkUpdateIfChanged( $hash, "phase$phase->{phase}_Cosphi", sprintf("%.${decimal}f",$value->{data}), 1 ) if( $value->{type} eq 'cosphi' );
readingsBulkUpdateIfChanged( $hash, "phase$phase->{phase}_Frequency", sprintf("%.${decimal}f",$value->{data}), 1 ) if( $value->{type} eq 'frequency' );
}
}
}
}
}
}
readingsBulkUpdateIfChanged($hash,'state','done',1);
readingsEndUpdate($hash,1);
}
1;
=pod
=item device
=item summary Support read data from Smart Pi expansion module
=item summary_DE Liest die Daten vom Smart Pi Aufsteckmodul aus
=begin html
<a name="SmartPi"></a>
<h3>SmartPi</h3>
<a name="GardenaSmartBridgedefine"></a>
<b>Define</b>
<ul><br>
<code>define &lt;name&gt; SmartPi &lt;HOST&gt;</code>
<br><br>
Beispiel:
<ul><br>
<code>define Gardena_Bridge SmartPi 192.168.1.34</code><br>
</ul>
<br><br>
</ul>
<ul>
<a name="SmartPireadings"></a>
<b>Readings</b>
<ul>
<li>phaseX_Current - Current [A] (available for phase 1,2,3, neutral conductor)</li>
<li>phaseX_Voltage - Voltage [V] (available for phase 1,2,3)</li>
<li>phaseX_Power - Power [W] (available for phase 1,2,3)</li>
<li>phaseX_Cosphi - cos φ (available for phase 1,2,3 it is important to measure the voltage)</li>
<li>phaseX_Frequency - Frequency [Hz] (available for phase 1,2,3)</li>
</ul>
<a name="SmartPiget"></a>
<b>get</b>
<ul>
<li>phaseX Y - get new Y (Voltage or Current or so)data about phaseX</li>
</ul>
<a name="SmartPiattribut"></a>
<b>Attribute</b>
<ul>
<li>disable - disables the device</li>
<li>disabledForIntervals - disable device for interval time (13:00-18:30 or 13:00-18:30 22:00-23:00)</li>
<li>interval - interval in seconds for statusRequest</li>
<li>decimalPlace - set decimal Place</li>
</ul>
</ul>
=end html
=begin html_DE
<a name="SmartPi"></a>
<h3>SmartPi</h3>
=end html_DE
=cut