mirror of
https://github.com/fhem/fhem-mirror.git
synced 2025-01-31 12:49:34 +00:00
98_expandJSON.pm: Initial check in
git-svn-id: https://svn.fhem.de/fhem/trunk@13708 2b470e98-0d58-463d-a4d8-8e2adae1ed80
This commit is contained in:
parent
6ca8476b5f
commit
264256bc18
@ -1,5 +1,6 @@
|
|||||||
# Add changes at the top of the list. Keep it in ASCII, and 80-char wide.
|
# 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.
|
# Do not insert empty lines here, update check depends on it.
|
||||||
|
- new: 98_expandJSON: initial check in
|
||||||
- update: 98_DOIFtools: added hints to the derived operands
|
- update: 98_DOIFtools: added hints to the derived operands
|
||||||
- update: 98_Text2Speech: some small improvements
|
- update: 98_Text2Speech: some small improvements
|
||||||
special Audiodevice "none" is now "default due
|
special Audiodevice "none" is now "default due
|
||||||
|
354
fhem/FHEM/98_expandJSON.pm
Normal file
354
fhem/FHEM/98_expandJSON.pm
Normal file
@ -0,0 +1,354 @@
|
|||||||
|
# $Id$
|
||||||
|
################################################################################
|
||||||
|
#
|
||||||
|
# Copyright (c) 2017 dev0
|
||||||
|
#
|
||||||
|
# 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
|
||||||
|
# (at your option) 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.
|
||||||
|
#
|
||||||
|
# This copyright notice MUST APPEAR in all copies of the script!
|
||||||
|
#
|
||||||
|
################################################################################
|
||||||
|
|
||||||
|
my $module_version = "1.10";
|
||||||
|
|
||||||
|
package main;
|
||||||
|
|
||||||
|
use strict;
|
||||||
|
use warnings;
|
||||||
|
use POSIX;
|
||||||
|
|
||||||
|
sub expandJSON_expand($$$$;$$); # Forum #66761
|
||||||
|
|
||||||
|
sub expandJSON_Initialize($$) {
|
||||||
|
my ($hash) = @_;
|
||||||
|
$hash->{DefFn} = "expandJSON_Define";
|
||||||
|
$hash->{NotifyFn} = "expandJSON_Notify";
|
||||||
|
$hash->{AttrFn} = "expandJSON_Attr";
|
||||||
|
$hash->{AttrList} = "addStateEvent:1,0 "
|
||||||
|
. "addReadingsPrefix:1,0 "
|
||||||
|
. "disable:1,0 "
|
||||||
|
. "disabledForIntervals "
|
||||||
|
. "do_not_notify:1,0";
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
sub expandJSON_Define(@) {
|
||||||
|
my ($hash, $def) = @_;
|
||||||
|
my @a = split("[ \t][ \t]*", $def);
|
||||||
|
my $usg = "\nUse 'define <name> expandJSON <event regexp>"
|
||||||
|
. " [<target reading regexp>]";
|
||||||
|
return "Wrong syntax: $usg" if(int(@a) < 3);
|
||||||
|
return "ERROR: Perl module JSON is not installed"
|
||||||
|
if (expandJSON_isPmInstalled($hash,"JSON"));
|
||||||
|
|
||||||
|
my $name = $a[0];
|
||||||
|
my $type = $a[1];
|
||||||
|
|
||||||
|
# source regexp
|
||||||
|
my $re = $a[2];
|
||||||
|
return "Bad regexp: starting with *" if($re =~ m/^\*/);
|
||||||
|
eval { "test" =~ m/^$re$/ };
|
||||||
|
return "Bad regexp $re: $@" if($@);
|
||||||
|
|
||||||
|
$hash->{s_regexp} = $re;
|
||||||
|
InternalTimer(gettimeofday(), sub(){notifyRegexpChanged($hash, $re);}, $hash);
|
||||||
|
|
||||||
|
# dest regexp
|
||||||
|
if (defined $a[3]) {
|
||||||
|
$re = $a[3];
|
||||||
|
return "Bad regexp: starting with *" if($re =~ m/^\*/);
|
||||||
|
eval { "test" =~ m/^$re$/ };
|
||||||
|
return "Bad regexp $re: $@" if($@);
|
||||||
|
$hash->{t_regexp} = $re;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
$hash->{t_regexp} = ".*";
|
||||||
|
}
|
||||||
|
|
||||||
|
my $doTrigger = ($name !~ m/^$re$/); # Forum #34516
|
||||||
|
readingsSingleUpdate($hash, "state", "active", $doTrigger);
|
||||||
|
$hash->{version} = $module_version;
|
||||||
|
|
||||||
|
return undef;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
sub expandJSON_Attr($$) {
|
||||||
|
my ($cmd,$name,$aName,$aVal) = @_;
|
||||||
|
my $hash = $defs{$name};
|
||||||
|
my $type = $hash->{TYPE};
|
||||||
|
my $ret;
|
||||||
|
|
||||||
|
if ($cmd eq "set" && !defined $aVal) {
|
||||||
|
$ret = "not empty"
|
||||||
|
}
|
||||||
|
|
||||||
|
elsif ($aName eq "addReadingsPrefix") {
|
||||||
|
$cmd eq "set"
|
||||||
|
? $aVal =~ m/^[01]$/ ? ($hash->{helper}{$aName} = $aVal) : ($ret = "0|1")
|
||||||
|
: delete $hash->{helper}{$aName}
|
||||||
|
}
|
||||||
|
|
||||||
|
elsif ($aName eq "do_not_notify") {
|
||||||
|
$ret = "0|1" if $cmd eq "set" && $aVal !~ m/^[01]$/;
|
||||||
|
}
|
||||||
|
|
||||||
|
elsif ($aName eq "disable") {
|
||||||
|
if ($cmd eq "set") {
|
||||||
|
if ($aVal !~ m/^[01]$/) { $ret = "0|1" }
|
||||||
|
else { readingsSingleUpdate($hash, "state", $aVal?"disabled":"active", 1) }
|
||||||
|
}
|
||||||
|
elsif ($cmd eq "del") { readingsSingleUpdate($hash, "state", "active", 1) }
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($ret) {
|
||||||
|
my $v = defined $aVal ? $aVal : "";
|
||||||
|
my $msg = "$type: attr $name $aName $v: value must be: ";
|
||||||
|
Log3 $name, 2, $msg.$ret;
|
||||||
|
return $msg.$ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
return undef;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
sub expandJSON_Notify($$) {
|
||||||
|
my ($hash, $dhash) = @_;
|
||||||
|
my $name = $hash->{NAME};
|
||||||
|
return "" if(IsDisabled($name));
|
||||||
|
|
||||||
|
my $events = deviceEvents($dhash, AttrVal($name, "addStateEvent", 0));
|
||||||
|
return if( !grep { m/{.*}\s*$/s } @{ $events } ); #there is no JSON content
|
||||||
|
|
||||||
|
for (my $i = 0; $i < int(@{$events}); $i++) {
|
||||||
|
my $event = $events->[$i];
|
||||||
|
$event = "" if(!defined($event));
|
||||||
|
|
||||||
|
my $re = $hash->{s_regexp};
|
||||||
|
my $devName = $dhash->{NAME};
|
||||||
|
my $found = ($devName =~ m/^$re$/ || "$devName:$event" =~ m/^$re$/s);
|
||||||
|
if ($found) {
|
||||||
|
my $type = $hash->{TYPE};
|
||||||
|
Log3 $name, 5, "$type $name: Found $devName:$event";
|
||||||
|
|
||||||
|
my ($reading,$value) = $event =~ m/^\s*{.*}\s*$/s
|
||||||
|
? ("state", $event)
|
||||||
|
: split(": ", $event, 2);
|
||||||
|
|
||||||
|
$hash->{STATE} = $dhash->{NTFY_TRIGGERTIME};
|
||||||
|
|
||||||
|
if ($value !~ m/^\s*{.*}\s*$/s) { # eg. state with an invalid json
|
||||||
|
Log3 $name, 5, "$type $name: Invalid JSON: $value";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Log3 $name, 5, "$type $name: Yield decode: $hash | $devName | $reading "
|
||||||
|
. "| $value";
|
||||||
|
|
||||||
|
InternalTimer(
|
||||||
|
gettimeofday(),
|
||||||
|
sub(){ expandJSON_decode($hash,$devName,$reading,$value) },
|
||||||
|
$hash);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return undef;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
sub expandJSON_decode($$$$) {
|
||||||
|
my ($p) = @_;
|
||||||
|
my ($hash,$dname,$dreading,$dvalue) = @_;
|
||||||
|
my ($name,$type) = ($hash->{NAME},$hash->{TYPE});
|
||||||
|
my $dhash = $defs{$dname};
|
||||||
|
my $h;
|
||||||
|
eval { $h = decode_json($dvalue); 1; };
|
||||||
|
if ( $@ ) {
|
||||||
|
Log3 $name, 2, "$type $name: Bad JSON: $dname $dreading: $dvalue";
|
||||||
|
Log3 $name, 2, "$type $name: $@";
|
||||||
|
return undef;
|
||||||
|
}
|
||||||
|
|
||||||
|
my $sPrefix = $hash->{helper}{addReadingsPrefix} ? $dreading."_" : "";
|
||||||
|
readingsBeginUpdate($dhash);
|
||||||
|
expandJSON_expand($hash,$dhash,$sPrefix,$h);
|
||||||
|
readingsEndUpdate($dhash, AttrVal($name,"do_not_notify",0) ? 0 : 1);
|
||||||
|
|
||||||
|
return undef;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
sub expandJSON_expand($$$$;$$) {
|
||||||
|
# thanx to bgewehr for the root position of this recursive snippet
|
||||||
|
# https://github.com/bgewehr/fhem
|
||||||
|
my ($hash,$dhash,$sPrefix,$ref,$prefix,$suffix) = @_;
|
||||||
|
$prefix = "" if( !$prefix );
|
||||||
|
$suffix = "" if( !$suffix );
|
||||||
|
$suffix = "_$suffix" if( $suffix );
|
||||||
|
|
||||||
|
if( ref( $ref ) eq "ARRAY" ) {
|
||||||
|
while( my ($key,$value) = each @{ $ref } ) {
|
||||||
|
expandJSON_expand($hash,$dhash,$sPrefix,$value,
|
||||||
|
$prefix.sprintf("%02i",$key+1)."_");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
elsif( ref( $ref ) eq "HASH" ) {
|
||||||
|
while( my ($key,$value) = each %{ $ref } ) {
|
||||||
|
if( ref( $value ) ) {
|
||||||
|
expandJSON_expand($hash,$dhash,$sPrefix,$value,$prefix.$key.$suffix."_");
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
# replace illegal characters in reading names
|
||||||
|
(my $reading = $sPrefix.$prefix.$key.$suffix) =~ s/[^A-Za-z\d_\.\-\/]/_/g;
|
||||||
|
readingsBulkUpdate($dhash, $reading, $value)
|
||||||
|
if $reading =~ m/^$hash->{t_regexp}$/;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
sub expandJSON_isPmInstalled($$)
|
||||||
|
{
|
||||||
|
my ($hash,$pm) = @_;
|
||||||
|
my ($name,$type) = ($hash->{NAME},$hash->{TYPE});
|
||||||
|
if (not eval "use $pm;1")
|
||||||
|
{
|
||||||
|
Log3 $name, 1, "$type $name: perl modul missing: $pm. Install it, please.";
|
||||||
|
$hash->{MISSING_MODULES} .= "$pm ";
|
||||||
|
return "failed: $pm";
|
||||||
|
}
|
||||||
|
|
||||||
|
return undef;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
1;
|
||||||
|
|
||||||
|
|
||||||
|
=pod
|
||||||
|
=item helper
|
||||||
|
=item summary Expand a JSON string from a reading into individual readings
|
||||||
|
=item summary_DE Expandiert eine JSON Zeichenkette in individuelle Readings
|
||||||
|
=begin html
|
||||||
|
|
||||||
|
<a name="expandJSON"></a>
|
||||||
|
<h3>expandJSON</h3>
|
||||||
|
|
||||||
|
<ul>
|
||||||
|
<p>Expand a JSON string from a reading into individual readings</p>
|
||||||
|
|
||||||
|
<ul>
|
||||||
|
<li>Requirement: perl module JSON<br>
|
||||||
|
Use "cpan install JSON" or operating system's package manager to install
|
||||||
|
Perl JSON Modul. Depending on your os the required package is named:
|
||||||
|
libjson-perl or perl-JSON.
|
||||||
|
</li>
|
||||||
|
</ul><br>
|
||||||
|
|
||||||
|
<a name="expandJSONdefine"></a>
|
||||||
|
<b>Define</b><br><br>
|
||||||
|
|
||||||
|
<ul>
|
||||||
|
<code>define <name> expandJSON <source_regex>
|
||||||
|
[<target_regex>]</code><br><br>
|
||||||
|
|
||||||
|
<li>
|
||||||
|
<a name=""><name></a><br>
|
||||||
|
A name of your choice.</li><br>
|
||||||
|
|
||||||
|
<li>
|
||||||
|
<a name=""><source_regex></a><br>
|
||||||
|
Regexp that must match your devices, readings and values that contain
|
||||||
|
the JSON strings. Regexp syntax is the same as used by notify and must not
|
||||||
|
contain spaces.<br>
|
||||||
|
</li><br>
|
||||||
|
|
||||||
|
<li>
|
||||||
|
<a name=""><target_regex></a><br>
|
||||||
|
Optional: This regexp is used to determine whether the target reading is
|
||||||
|
converted or not at all. If not set then all readings will be used. If set
|
||||||
|
then only matching readings will be used. Regexp syntax is the same as
|
||||||
|
used by notify and must not contain spaces.<br>
|
||||||
|
</li><br>
|
||||||
|
|
||||||
|
<li>
|
||||||
|
Examples:<br>
|
||||||
|
<br>
|
||||||
|
<u>Source reading:</u><br>
|
||||||
|
<code>
|
||||||
|
device:{.*} #state without attribute addStateEvent<br>
|
||||||
|
device:state:.{.*} #state with attribute addStateEvent<br>
|
||||||
|
device:reading:.{.*}<br>
|
||||||
|
Sonoff.*:ENERGY.*:.{.*}<br>
|
||||||
|
.*wifiIOT.*:.*sensor.*:.{.*}<br>
|
||||||
|
(?i)dev.*:(sensor1|sensor2|teleme.*):.{.*}<br>
|
||||||
|
(devX:{.*}|devY.*:jsonY:.{.*Wifi.*{.*SSID.*}.*})
|
||||||
|
</code><br>
|
||||||
|
<br>
|
||||||
|
|
||||||
|
<u>Target reading:</u><br>
|
||||||
|
<code>.*power.*</code><br>
|
||||||
|
<code>(Power|Current|Voltage|.*day)</code><br><br>
|
||||||
|
|
||||||
|
<u>Complete definitions:</u><br>
|
||||||
|
<code>define ej1 expandJSON device:sourceReading:.{.*} targetReading
|
||||||
|
</code><br>
|
||||||
|
<code>define ej2 expandJSON Sonoff.*:ENERGY.*:.{.*} (Power|.*day)
|
||||||
|
</code><br>
|
||||||
|
<code>define ej3 expandJSON (?i).*_sensordev_.*:.*:.{.*}
|
||||||
|
</code><br><br>
|
||||||
|
|
||||||
|
</li><br>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<a name="expandJSONset"></a>
|
||||||
|
<b>Set</b><br><br>
|
||||||
|
<ul>
|
||||||
|
N/A<br><br>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<a name="expandJSONget"></a>
|
||||||
|
<b>Get</b><br><br>
|
||||||
|
<ul>
|
||||||
|
N/A<br><br>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<a name="expandJSON_attr"></a>
|
||||||
|
<b>Attributes</b><br><br>
|
||||||
|
<ul>
|
||||||
|
<li><a name="expandJSON_attr_addReadingsPrefix">addReadingsPrefix</a><br>
|
||||||
|
Add source reading as prefix to new generated readings. Useful if you have
|
||||||
|
more than one reading with a JSON string that should be converted.
|
||||||
|
</li><br>
|
||||||
|
|
||||||
|
<li><a name="expandJSON_attr_disable">disable</a><br>
|
||||||
|
Used to disable this device.
|
||||||
|
</li><br>
|
||||||
|
|
||||||
|
<li><a name="expandJSON_attr_do_not_notify">do_not_notify</a><br>
|
||||||
|
Do not generate events for converted readings at all. Think twice before
|
||||||
|
using this attribute. In most cases, it is more appropriate to use
|
||||||
|
event-on-change-reading in target devices.
|
||||||
|
</li><br>
|
||||||
|
|
||||||
|
<li><a href="#disabledForIntervals">disabledForIntervals</a></li>
|
||||||
|
<li><a href="#addStateEvent">addStateEvent</a></li>
|
||||||
|
</ul>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
=end html
|
||||||
|
=cut
|
@ -404,6 +404,7 @@ FHEM/98_CustomReadings.pm HCS http://forum.fhem.de Unterstue
|
|||||||
FHEM/98_dewpoint.pm Joachim http://forum.fhem.de Automatisierung
|
FHEM/98_dewpoint.pm Joachim http://forum.fhem.de Automatisierung
|
||||||
FHEM/98_Dooya.pm Jarnsen/ralf9/darkmission http://forum.fhem.de Sonstige Systeme
|
FHEM/98_Dooya.pm Jarnsen/ralf9/darkmission http://forum.fhem.de Sonstige Systeme
|
||||||
FHEM/98_dummy.pm rudolfkoenig http://forum.fhem.de Automatisierung
|
FHEM/98_dummy.pm rudolfkoenig http://forum.fhem.de Automatisierung
|
||||||
|
FHEM/98_expandJSON.pm dev0 http://forum.fhem.de Unterstuetzende Dienste
|
||||||
FHEM/98_fheminfo.pm mfr69bs/rudolfkoenig http://forum.fhem.de Sonstiges
|
FHEM/98_fheminfo.pm mfr69bs/rudolfkoenig http://forum.fhem.de Sonstiges
|
||||||
FHEM/98_fhemdebug.pm rudolfkoenig http://forum.fhem.de Sonstiges
|
FHEM/98_fhemdebug.pm rudolfkoenig http://forum.fhem.de Sonstiges
|
||||||
FHEM/98_GoogleAuth.pm betateilchen http://forum.fhem.de Unterstuetzende Dienste
|
FHEM/98_GoogleAuth.pm betateilchen http://forum.fhem.de Unterstuetzende Dienste
|
||||||
|
Loading…
Reference in New Issue
Block a user