All my modules: refactoring code

git-svn-id: https://svn.fhem.de/fhem/trunk/fhem@14012 2b470e98-0d58-463d-a4d8-8e2adae1ed80
This commit is contained in:
jpawlowski 2017-04-17 13:09:41 +00:00
parent 02d4e0de01
commit 63fd2bdd6b
12 changed files with 2901 additions and 3367 deletions

View File

@ -1,30 +1,7 @@
###############################################################################
# $Id$
##############################################################################
#
# 59_Wunderground.pm
#
# Copyright by Julian Pawlowski
# e-mail: julian.pawlowski at gmail.com
#
# 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 <http://www.gnu.org/licenses/>.
#
##############################################################################
# http://api.wunderground.com/weather/api
#
package main;
use strict;
@ -36,9 +13,7 @@ use Encode qw(encode_utf8 decode_utf8);
use Unit;
use Data::Dumper;
sub Wunderground_Hash2Readings($$;$);
###################################
# initialize ##################################################################
sub Wunderground_Initialize($) {
my ($hash) = @_;
@ -47,15 +22,15 @@ sub Wunderground_Initialize($) {
my $webhookFWinstance =
join( ",", devspec2array('TYPE=FHEMWEB:FILTER=TEMPORARY!=1') );
$hash->{SetFn} = "Wunderground_Set";
$hash->{DefFn} = "Wunderground_Define";
$hash->{AttrFn} = "Wunderground_Attr";
$hash->{UndefFn} = "Wunderground_Undefine";
$hash->{SetFn} = "Wunderground_Set";
$hash->{AttrFn} = "Wunderground_Attr";
$hash->{DbLog_splitFn} = "Unit_DbLog_split";
$hash->{parseParams} = 1;
$hash->{AttrList} =
"disable:0,1 timeout:1,2,3,4,5 pollInterval:300,450,600,750,900 wu_lang:en,de,at,ch,nl,fr,pl wu_pws:1,0 wu_bestfct:1,0 stateReadings stateReadingsFormat:0,1 "
"disable:0,1 disabledForIntervals do_not_notify:1,0 timeout:1,2,3,4,5 pollInterval:300,450,600,750,900 wu_lang:en,de,at,ch,nl,fr,pl wu_pws:1,0 wu_bestfct:1,0 stateReadings stateReadingsFormat:0,1 "
. "wu_features:multiple-strict,alerts,almanac,astronomy,conditions,currenthurricane,forecast,forecast10day,hourly,hourly10day "
. $readingFnAttributes;
@ -228,11 +203,116 @@ sub Wunderground_Initialize($) {
'wind_speed' => { rtype => 'kmph', formula_symbol => 'Ws' },
'wind_speed_mph' => { rtype => 'mph', formula_symbol => 'Ws' }
};
return;
}
#####################################
# regular Fn ##################################################################
sub Wunderground_Define($$$) {
my ( $hash, $a, $h ) = @_;
my $name = $hash->{NAME};
my $infix = "Wunderground";
Log3 $name, 5, "Wunderground $name: called function Wunderground_Define()";
eval {
require JSON;
import JSON qw( decode_json );
};
return "Please install Perl JSON to use module Wunderground"
if ($@);
if ( int(@$a) < 2 ) {
my $msg = "Wrong syntax: define <name> Wunderground <api-key> <pws-id>";
Log3 $name, 4, $msg;
return $msg;
}
$hash->{TYPE} = "Wunderground";
$hash->{API_KEY} = @$a[2];
$hash->{QUERY} = @$a[3];
$hash->{QUERY} = "pws:" . $hash->{QUERY}
if ( $hash->{QUERY} =~ /^[A-Z]{3,}\d{1,}$/ );
if ( $init_done && !defined( $hash->{OLDDEF} ) ) {
fhem 'attr ' . $name . ' stateReadings temp_c humidity';
fhem 'attr ' . $name . ' stateReadingsFormat 1';
fhem 'attr ' . $name . ' wu_features astronomy,conditions,forecast';
}
# start the status update timer
Wunderground_GetStatus( $hash, 2 );
return undef;
}
sub Wunderground_Undefine($$$) {
my ( $hash, $a, $h ) = @_;
my $name = $hash->{NAME};
if ( defined( $hash->{fhem}{infix} ) ) {
Wunderground_removeExtension( $hash->{fhem}{infix} );
}
Log3 $name, 5,
"Wunderground $name: called function Wunderground_Undefine()";
# Stop the internal GetStatus-Loop and exit
RemoveInternalTimer($hash);
# release reverse pointer
delete $modules{Wunderground}{defptr}{$name};
return undef;
}
sub Wunderground_Set($$$) {
my ( $hash, $a, $h ) = @_;
my $name = $hash->{NAME};
Log3 $name, 5, "Wunderground $name: called function Wunderground_Set()";
return "Argument is missing" if ( int(@$a) < 1 );
my $usage = "Unknown argument " . @$a[1] . ", choose one of update:noArg";
my $cmd = '';
my $result;
# update
if ( lc( @$a[1] ) eq "update" ) {
Log3 $name, 3, "Wunderground set $name " . @$a[1];
Wunderground_GetStatus($hash);
}
# return usage hint
else {
return $usage;
}
return $result;
}
sub Wunderground_Attr(@) {
my ( $cmd, $name, $attrName, $attrVal ) = @_;
my $hash = $defs{$name};
Log3 $name, 5, "Wunderground $name: called function Wunderground_Attr()";
return
"Invalid value for attribute $attrName: minimum value is 1 second, maximum 5 seconds"
if ( $attrVal
&& $attrName eq "timeout"
&& ( $attrVal < 1 || $attrVal > 5 ) );
return
"Invalid value for attribute $attrName: minimum value is 300 seconds"
if ( $attrVal && $attrName eq "pollInterval" && $attrVal < 300 );
return undef;
}
# module Fn ####################################################################
sub Wunderground_GetStatus($;$) {
my ( $hash, $delay ) = @_;
my $name = $hash->{NAME};
@ -286,102 +366,6 @@ sub Wunderground_GetStatus($;$) {
return;
}
###################################
sub Wunderground_Set($$$) {
my ( $hash, $a, $h ) = @_;
my $name = $hash->{NAME};
Log3 $name, 5, "Wunderground $name: called function Wunderground_Set()";
return "Argument is missing" if ( int(@$a) < 1 );
my $usage = "Unknown argument " . @$a[1] . ", choose one of update:noArg";
my $cmd = '';
my $result;
# update
if ( lc( @$a[1] ) eq "update" ) {
Log3 $name, 3, "Wunderground set $name " . @$a[1];
Wunderground_GetStatus($hash);
}
# return usage hint
else {
return $usage;
}
return $result;
}
###################################
sub Wunderground_Define($$$) {
my ( $hash, $a, $h ) = @_;
my $name = $hash->{NAME};
my $infix = "Wunderground";
Log3 $name, 5, "Wunderground $name: called function Wunderground_Define()";
eval {
require JSON;
import JSON qw( decode_json );
};
return "Please install Perl JSON to use module Wunderground"
if ($@);
if ( int(@$a) < 2 ) {
my $msg = "Wrong syntax: define <name> Wunderground <api-key> <pws-id>";
Log3 $name, 4, $msg;
return $msg;
}
$hash->{TYPE} = "Wunderground";
$hash->{API_KEY} = @$a[2];
$hash->{QUERY} = @$a[3];
$hash->{QUERY} = "pws:" . $hash->{QUERY}
if ( $hash->{QUERY} =~ /^[A-Z]{3,}\d{1,}$/ );
if ( $init_done && !defined( $hash->{OLDDEF} ) ) {
fhem 'attr ' . $name . ' stateReadings temp_c humidity';
fhem 'attr ' . $name . ' stateReadingsFormat 1';
fhem 'attr ' . $name . ' wu_features astronomy,conditions,forecast';
}
# start the status update timer
Wunderground_GetStatus( $hash, 2 );
return;
}
###################################
sub Wunderground_Attr(@) {
my ( $cmd, $name, $attrName, $attrVal ) = @_;
my $hash = $defs{$name};
Log3 $name, 5, "Wunderground $name: called function Wunderground_Attr()";
return
"Invalid value for attribute $attrName: minimum value is 1 second, maximum 5 seconds"
if ( $attrVal
&& $attrName eq "timeout"
&& ( $attrVal < 1 || $attrVal > 5 ) );
return
"Invalid value for attribute $attrName: minimum value is 300 seconds"
if ( $attrVal && $attrName eq "pollInterval" && $attrVal < 300 );
return undef;
}
############################################################################################################
#
# Begin of helper functions
#
############################################################################################################
###################################
sub Wunderground_SendCommand($$) {
my ( $hash, $features ) = @_;
my $name = $hash->{NAME};
@ -420,7 +404,6 @@ sub Wunderground_SendCommand($$) {
return;
}
###################################
sub Wunderground_ReceiveCommand($$$) {
my ( $param, $err, $data ) = @_;
my $hash = $param->{hash};
@ -495,7 +478,8 @@ sub Wunderground_ReceiveCommand($$$) {
return;
}
###################################
sub Wunderground_Hash2Readings($$;$);
sub Wunderground_Hash2Readings($$;$) {
my ( $hash, $h, $r ) = @_;
my $name = $hash->{NAME};
@ -991,27 +975,6 @@ sub Wunderground_Hash2Readings($$;$) {
return "ok" if ( !$loop );
}
###################################
sub Wunderground_Undefine($$$) {
my ( $hash, $a, $h ) = @_;
my $name = $hash->{NAME};
if ( defined( $hash->{fhem}{infix} ) ) {
Wunderground_removeExtension( $hash->{fhem}{infix} );
}
Log3 $name, 5,
"Wunderground $name: called function Wunderground_Undefine()";
# Stop the internal GetStatus-Loop and exit
RemoveInternalTimer($hash);
# release reverse pointer
delete $modules{Wunderground}{defptr}{$name};
return;
}
1;
=pod

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -1,68 +1,29 @@
###############################################################################
# $Id$
##############################################################################
#
# 70_PHTV.pm
# An FHEM Perl module for controlling Philips Televisons
# via network connection.
#
# Copyright by Julian Pawlowski
# e-mail: julian.pawlowski at gmail.com
#
# 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 <http://www.gnu.org/licenses/>.
#
##############################################################################
package main;
use 5.012;
use strict;
use warnings;
use Data::Dumper;
use Time::HiRes qw(gettimeofday);
use HttpUtils;
use Color;
use SetExtensions;
use Encode;
sub PHTV_Set($@);
sub PHTV_Get($@);
sub PHTV_GetStatus($;$);
sub PHTV_Define($$);
sub PHTV_Notify($$);
sub PHTV_Undefine($$);
#########################
# Forward declaration for remotecontrol module
#sub PHTV_RClayout_TV();
#sub PHTV_RCmakenotify($$);
###################################
# initialize ##################################################################
sub PHTV_Initialize($) {
my ($hash) = @_;
Log3 $hash, 5, "PHTV_Initialize: Entering";
$hash->{GetFn} = "PHTV_Get";
$hash->{SetFn} = "PHTV_Set";
$hash->{NotifyFn} = "PHTV_Notify";
$hash->{DefFn} = "PHTV_Define";
$hash->{UndefFn} = "PHTV_Undefine";
$hash->{SetFn} = "PHTV_Set";
$hash->{GetFn} = "PHTV_Get";
$hash->{NotifyFn} = "PHTV_Notify";
$hash->{AttrList} =
"disable:0,1 timeout sequentialQuery:0,1 drippyFactor:0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20 inputs ambiHueLeft ambiHueRight ambiHueTop ambiHueBottom ambiHueLatency:150,200,250,300,350,400,450,500,550,600,650,700,750,800,850,900,950,1000,1100,1200,1300,1400,1500,1600,1700,1800,1900,2000 jsversion:1,5,6 macaddr:textField model wakeupCmd:textField channelsMax:slider,30,1,200 httpLoglevel:1,2,3,4,5 sslVersion device_id auth_key "
"disable:0,1 disabledForIntervals do_not_notify:1,0 timeout sequentialQuery:0,1 drippyFactor:0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20 inputs ambiHueLeft ambiHueRight ambiHueTop ambiHueBottom ambiHueLatency:150,200,250,300,350,400,450,500,550,600,650,700,750,800,850,900,950,1000,1100,1200,1300,1400,1500,1600,1700,1800,1900,2000 jsversion:1,5,6 macaddr:textField model wakeupCmd:textField channelsMax:slider,30,1,200 httpLoglevel:1,2,3,4,5 sslVersion device_id auth_key "
. $readingFnAttributes;
$data{RC_layout}{PHTV_SVG} = "PHTV_RClayout_SVG";
@ -88,170 +49,73 @@ sub PHTV_Initialize($) {
};
FHEM_colorpickerInit();
return;
}
#####################################
sub PHTV_GetStatus($;$) {
my ( $hash, $update ) = @_;
my $name = $hash->{NAME};
my $interval = $hash->{INTERVAL};
my $presence = ReadingsVal( $name, "presence", "absent" );
my $sequential = AttrVal( $name, "sequentialQuery", 0 );
my $querySent = 0;
Log3 $name, 5, "PHTV $name: called function PHTV_GetStatus()";
$interval = $interval * 1.6
if ( ReadingsVal( $name, "ambiHue", "off" ) eq "on" );
RemoveInternalTimer($hash);
InternalTimer( gettimeofday() + $interval, "PHTV_GetStatus", $hash, 0 );
return
if ( IsDisabled($name) );
# try to fetch only some information to check device availability
if ( !$update ) {
PHTV_SendCommand( $hash, "audio/volume" ) if ( $presence eq "present" );
PHTV_SendCommand( $hash, "system" ) if ( $presence eq "absent" );
# in case we should query the device gently, mark we already sent a query
$querySent = 1 if $sequential;
$hash->{helper}{sequentialQueryCounter} = 1 if $sequential;
}
# fetch other info if device is on
if ( !$querySent
&& ( ReadingsVal( $name, "state", "off" ) eq "on" || $update ) )
{
# Read device info every 15 minutes only
if (
!$querySent
&& (
!defined( $hash->{helper}{lastFullUpdate} )
|| ( !$update
&& $hash->{helper}{lastFullUpdate} + 900 le time() )
)
)
{
PHTV_SendCommand( $hash, "system" );
PHTV_SendCommand( $hash, "ambilight/topology" );
$querySent = 1 if $sequential;
$hash->{helper}{sequentialQueryCounter}++ if $sequential;
# Update state
$hash->{helper}{lastFullUpdate} = time();
}
# read audio volume
if ( !$querySent && $update ) {
PHTV_SendCommand( $hash, "audio/volume" );
$querySent = 1 if $sequential;
$hash->{helper}{sequentialQueryCounter}++ if $sequential;
}
# read ambilight details
if ( !$querySent ) {
# read ambilight mode
PHTV_SendCommand( $hash, "ambilight/mode" );
$querySent = 1 if $sequential;
$hash->{helper}{sequentialQueryCounter}++ if $sequential;
# read ambilight RGB value
PHTV_SendCommand( $hash, "ambilight/cached" )
if ( ReadingsVal( $name, "ambiMode", "internal" ) ne "internal" );
}
# read all sources if not existing
if (
!$querySent
&& ( !defined( $hash->{helper}{device}{sourceName} )
|| !defined( $hash->{helper}{device}{sourceID} ) )
)
{
PHTV_SendCommand( $hash, "sources" );
$querySent = 1 if $sequential;
$hash->{helper}{sequentialQueryCounter}++ if $sequential;
}
# otherwise read current source
elsif ( !$querySent ) {
PHTV_SendCommand( $hash, "sources/current" );
$querySent = 1 if $sequential;
$hash->{helper}{sequentialQueryCounter}++ if $sequential;
}
# read all channels if not existing
if (
!$querySent
&& ( !defined( $hash->{helper}{device}{channelName} )
|| !defined( $hash->{helper}{device}{channelID} ) )
)
{
PHTV_SendCommand( $hash, "channels" );
$querySent = 1 if $sequential;
$hash->{helper}{sequentialQueryCounter}++ if $sequential;
}
# otherwise read current channel
elsif ( !$querySent ) {
PHTV_SendCommand( $hash, "channels/current" );
$querySent = 1 if $sequential;
$hash->{helper}{sequentialQueryCounter}++ if $sequential;
}
}
# Input alias handling
#
if ( AttrVal( $name, "inputs", "" ) ne "" ) {
my @inputs = split( ':', AttrVal( $name, "inputs", ":" ) );
if (@inputs) {
foreach (@inputs) {
if (m/[^,\s]+(,[^,\s]+)+/) {
my @input_names = split( ',', $_ );
$input_names[1] =~ s/\s/_/g;
$hash->{helper}{device}{inputAliases}{ $input_names[0] } =
$input_names[1];
$hash->{helper}{device}{inputNames}{ $input_names[1] } =
$input_names[0];
}
}
}
}
return;
}
###################################
sub PHTV_Get($@) {
my ( $hash, @a ) = @_;
# regular Fn ##################################################################
sub PHTV_Define($$) {
my ( $hash, $def ) = @_;
my @a = split( "[ \t][ \t]*", $def );
my $name = $hash->{NAME};
my $state = ReadingsVal( $name, "state", "Initialized" );
my $what;
Log3 $name, 5, "PHTV $name: called function PHTV_Get()";
Log3 $name, 5, "PHTV $name: called function PHTV_Define()";
return "argument is missing" if ( int(@a) < 2 );
return if ( $state =~ /^(pairing.*|initialized)$/i );
eval {
require JSON;
import JSON qw( decode_json encode_json );
};
return "Please install Perl JSON to use module PHTV"
if ($@);
$what = $a[1];
if ( $what =~ /^(power|input|volume|mute|rgb)$/ ) {
return ReadingsVal( $name, $what, "no such reading: $what" );
if ( int(@a) < 3 ) {
my $msg =
"Wrong syntax: define <name> PHTV <ip-or-hostname> [<poll-interval>]";
Log3 $name, 4, $msg;
return $msg;
}
else {
return
"Unknown argument $what, choose one of power:noArg input:noArg volume:noArg mute:noArg rgb:noArg ";
$hash->{TYPE} = "PHTV";
my $address = $a[2];
$hash->{helper}{ADDRESS} = $address;
# use interval of 45sec if not defined
my $interval = $a[3] || 45;
$hash->{INTERVAL} = $interval;
readingsSingleUpdate( $hash, "ambiHue", "off", 0 )
if ( ReadingsVal( $name, "ambiHue", "" ) ne "off" );
$hash->{model} = ReadingsVal( $name, "model", undef )
if ( ReadingsVal( $name, "model", undef ) );
$hash->{swversion} = ReadingsVal( $name, "softwareversion", undef )
if ( ReadingsVal( $name, "softwareversion", undef ) );
# set default settings on first define
if ( $init_done && !defined( $hash->{OLDDEF} ) ) {
fhem 'attr ' . $name . ' webCmd volume:input:rgb';
fhem 'attr ' . $name
. ' devStateIcon on:rc_GREEN:off off:rc_YELLOW:on absent:rc_STOP:on';
fhem 'attr ' . $name . ' icon it_television';
PHTV_GetStatus($hash);
}
return;
}
sub PHTV_Undefine($$) {
my ( $hash, $arg ) = @_;
my $name = $hash->{NAME};
Log3 $name, 5, "PHTV $name: called function PHTV_Undefine()";
# Stop the internal GetStatus-Loop and exit
RemoveInternalTimer($hash);
return;
}
###################################
sub PHTV_Set($@) {
my ( $hash, @a ) = @_;
my $name = $hash->{NAME};
@ -1396,57 +1260,26 @@ sub PHTV_Set($@) {
return;
}
###################################
sub PHTV_Define($$) {
my ( $hash, $def ) = @_;
my @a = split( "[ \t][ \t]*", $def );
sub PHTV_Get($@) {
my ( $hash, @a ) = @_;
my $name = $hash->{NAME};
my $state = ReadingsVal( $name, "state", "Initialized" );
my $what;
Log3 $name, 5, "PHTV $name: called function PHTV_Define()";
Log3 $name, 5, "PHTV $name: called function PHTV_Get()";
eval {
require JSON;
import JSON qw( decode_json encode_json );
};
return "Please install Perl JSON to use module PHTV"
if ($@);
return "argument is missing" if ( int(@a) < 2 );
return if ( $state =~ /^(pairing.*|initialized)$/i );
if ( int(@a) < 3 ) {
my $msg =
"Wrong syntax: define <name> PHTV <ip-or-hostname> [<poll-interval>]";
Log3 $name, 4, $msg;
return $msg;
$what = $a[1];
if ( $what =~ /^(power|input|volume|mute|rgb)$/ ) {
return ReadingsVal( $name, $what, "no such reading: $what" );
}
$hash->{TYPE} = "PHTV";
my $address = $a[2];
$hash->{helper}{ADDRESS} = $address;
# use interval of 45sec if not defined
my $interval = $a[3] || 45;
$hash->{INTERVAL} = $interval;
readingsSingleUpdate( $hash, "ambiHue", "off", 0 )
if ( ReadingsVal( $name, "ambiHue", "" ) ne "off" );
$hash->{model} = ReadingsVal( $name, "model", undef )
if ( ReadingsVal( $name, "model", undef ) );
$hash->{swversion} = ReadingsVal( $name, "softwareversion", undef )
if ( ReadingsVal( $name, "softwareversion", undef ) );
# set default settings on first define
if ( $init_done && !defined( $hash->{OLDDEF} ) ) {
fhem 'attr ' . $name . ' webCmd volume:input:rgb';
fhem 'attr ' . $name
. ' devStateIcon on:rc_GREEN:off off:rc_YELLOW:on absent:rc_STOP:on';
fhem 'attr ' . $name . ' icon it_television';
PHTV_GetStatus($hash);
else {
return
"Unknown argument $what, choose one of power:noArg input:noArg volume:noArg mute:noArg rgb:noArg ";
}
return;
}
sub PHTV_Notify($$) {
@ -1488,13 +1321,142 @@ sub PHTV_Notify($$) {
return undef;
}
############################################################################################################
#
# Begin of helper functions
#
############################################################################################################
# module Fn ####################################################################
sub PHTV_GetStatus($;$) {
my ( $hash, $update ) = @_;
my $name = $hash->{NAME};
my $interval = $hash->{INTERVAL};
my $presence = ReadingsVal( $name, "presence", "absent" );
my $sequential = AttrVal( $name, "sequentialQuery", 0 );
my $querySent = 0;
Log3 $name, 5, "PHTV $name: called function PHTV_GetStatus()";
$interval = $interval * 1.6
if ( ReadingsVal( $name, "ambiHue", "off" ) eq "on" );
RemoveInternalTimer($hash);
InternalTimer( gettimeofday() + $interval, "PHTV_GetStatus", $hash, 0 );
return
if ( IsDisabled($name) );
# try to fetch only some information to check device availability
if ( !$update ) {
PHTV_SendCommand( $hash, "audio/volume" ) if ( $presence eq "present" );
PHTV_SendCommand( $hash, "system" ) if ( $presence eq "absent" );
# in case we should query the device gently, mark we already sent a query
$querySent = 1 if $sequential;
$hash->{helper}{sequentialQueryCounter} = 1 if $sequential;
}
# fetch other info if device is on
if ( !$querySent
&& ( ReadingsVal( $name, "state", "off" ) eq "on" || $update ) )
{
# Read device info every 15 minutes only
if (
!$querySent
&& (
!defined( $hash->{helper}{lastFullUpdate} )
|| ( !$update
&& $hash->{helper}{lastFullUpdate} + 900 le time() )
)
)
{
PHTV_SendCommand( $hash, "system" );
PHTV_SendCommand( $hash, "ambilight/topology" );
$querySent = 1 if $sequential;
$hash->{helper}{sequentialQueryCounter}++ if $sequential;
# Update state
$hash->{helper}{lastFullUpdate} = time();
}
# read audio volume
if ( !$querySent && $update ) {
PHTV_SendCommand( $hash, "audio/volume" );
$querySent = 1 if $sequential;
$hash->{helper}{sequentialQueryCounter}++ if $sequential;
}
# read ambilight details
if ( !$querySent ) {
# read ambilight mode
PHTV_SendCommand( $hash, "ambilight/mode" );
$querySent = 1 if $sequential;
$hash->{helper}{sequentialQueryCounter}++ if $sequential;
# read ambilight RGB value
PHTV_SendCommand( $hash, "ambilight/cached" )
if ( ReadingsVal( $name, "ambiMode", "internal" ) ne "internal" );
}
# read all sources if not existing
if (
!$querySent
&& ( !defined( $hash->{helper}{device}{sourceName} )
|| !defined( $hash->{helper}{device}{sourceID} ) )
)
{
PHTV_SendCommand( $hash, "sources" );
$querySent = 1 if $sequential;
$hash->{helper}{sequentialQueryCounter}++ if $sequential;
}
# otherwise read current source
elsif ( !$querySent ) {
PHTV_SendCommand( $hash, "sources/current" );
$querySent = 1 if $sequential;
$hash->{helper}{sequentialQueryCounter}++ if $sequential;
}
# read all channels if not existing
if (
!$querySent
&& ( !defined( $hash->{helper}{device}{channelName} )
|| !defined( $hash->{helper}{device}{channelID} ) )
)
{
PHTV_SendCommand( $hash, "channels" );
$querySent = 1 if $sequential;
$hash->{helper}{sequentialQueryCounter}++ if $sequential;
}
# otherwise read current channel
elsif ( !$querySent ) {
PHTV_SendCommand( $hash, "channels/current" );
$querySent = 1 if $sequential;
$hash->{helper}{sequentialQueryCounter}++ if $sequential;
}
}
# Input alias handling
#
if ( AttrVal( $name, "inputs", "" ) ne "" ) {
my @inputs = split( ':', AttrVal( $name, "inputs", ":" ) );
if (@inputs) {
foreach (@inputs) {
if (m/[^,\s]+(,[^,\s]+)+/) {
my @input_names = split( ',', $_ );
$input_names[1] =~ s/\s/_/g;
$hash->{helper}{device}{inputAliases}{ $input_names[0] } =
$input_names[1];
$hash->{helper}{device}{inputNames}{ $input_names[1] } =
$input_names[0];
}
}
}
}
return;
}
###################################
sub PHTV_SendCommandDelayed($) {
my ($par) = @_;
@ -1507,7 +1469,6 @@ sub PHTV_SendCommandDelayed($) {
$par->{type} );
}
###################################
sub PHTV_SendCommand($$;$$$) {
my ( $hash, $service, $cmd, $type, $delay ) = @_;
my $name = $hash->{NAME};
@ -1613,7 +1574,7 @@ sub PHTV_SendCommand($$;$$$) {
'Accept-Charset' => 'UTF-8',
},
sslargs => {
SSL_verify_mode => 0,
SSL_verify_mode => 'SSL_VERIFY_NONE',
},
}
);
@ -1621,7 +1582,6 @@ sub PHTV_SendCommand($$;$$$) {
return;
}
###################################
sub PHTV_ReceiveCommand($$$) {
my ( $param, $err, $data ) = @_;
my $hash = $param->{hash};
@ -3079,20 +3039,6 @@ m/^\s*(([{\[][\s\S]+[}\]])|(<html>\s*<head>\s*<title>\s*Ok\s*<\/title>\s*<\/head
return;
}
###################################
sub PHTV_Undefine($$) {
my ( $hash, $arg ) = @_;
my $name = $hash->{NAME};
Log3 $name, 5, "PHTV $name: called function PHTV_Undefine()";
# Stop the internal GetStatus-Loop and exit
RemoveInternalTimer($hash);
return;
}
###################################
sub PHTV_GetStateAV($) {
my ($hash) = @_;
my $name = $hash->{NAME};
@ -3114,7 +3060,6 @@ sub PHTV_GetStateAV($) {
}
}
###################################
sub PHTV_wake ($) {
my ($hash) = @_;
my $name = $hash->{NAME};
@ -3153,8 +3098,6 @@ sub PHTV_wake ($) {
return 1;
}
#####################################
# Callback from 95_remotecontrol for command makenotify.
sub PHTV_RCmakenotify($$) {
my ( $nam, $ndev ) = @_;
my $nname = "notify_$nam";
@ -3164,10 +3107,6 @@ sub PHTV_RCmakenotify($$) {
return "Notify created by PHTV: $nname";
}
#####################################
# RC layouts
# Philips TV with SVG
sub PHTV_RClayout_SVG() {
my @row;
@ -3202,7 +3141,6 @@ sub PHTV_RClayout_SVG() {
return @row;
}
# Philips TV with PNG
sub PHTV_RClayout() {
my @row;
@ -3233,7 +3171,6 @@ sub PHTV_RClayout() {
return @row;
}
###################################
sub PHTV_GetRemotecontrolCommand($) {
my ($command) = @_;
my $commands = {
@ -3305,26 +3242,22 @@ sub PHTV_GetRemotecontrolCommand($) {
}
}
###################################
sub PHTV_isinteger {
defined $_[0] && $_[0] =~ /^[+-]?\d+$/;
}
###################################
sub PHTV_bri2pct($) {
my ($bri) = @_;
return 0 if ( $bri <= 0 );
return int( $bri / 255 * 100 + 0.5 );
}
###################################
sub PHTV_pct2bri($) {
my ($pct) = @_;
return 0 if ( $pct <= 0 );
return int( $pct / 100 * 255 + 0.5 );
}
###################################
sub PHTV_hex2rgb($) {
my ($hex) = @_;
if ( uc($hex) =~ /^(..)(..)(..)$/ ) {
@ -3339,7 +3272,6 @@ sub PHTV_hex2rgb($) {
}
}
###################################
sub PHTV_rgb2hex($$$) {
my ( $r, $g, $b ) = @_;
my $return = sprintf( "%2.2X%2.2X%2.2X", $r, $g, $b );
@ -3347,7 +3279,6 @@ sub PHTV_rgb2hex($$$) {
return uc($return);
}
###################################
sub PHTV_hex2hsb($;$) {
my ( $hex, $type ) = @_;
$type = lc($type) if ( defined( ($type) && $type ne "" ) );
@ -3364,14 +3295,12 @@ sub PHTV_hex2hsb($;$) {
}
}
###################################
sub PHTV_hsb2hex($$$) {
my ( $h, $s, $b ) = @_;
my $rgb = PHTV_hsb2rgb( $h, $s, $b );
return PHTV_rgb2hex( $rgb->{r}, $rgb->{g}, $rgb->{b} );
}
###################################
sub PHTV_rgb2hsb ($$$) {
my ( $r, $g, $b ) = @_;
@ -3393,7 +3322,6 @@ sub PHTV_rgb2hsb ($$$) {
};
}
###################################
sub PHTV_hsb2rgb ($$$) {
my ( $h, $s, $bri ) = @_;
@ -3415,7 +3343,6 @@ sub PHTV_hsb2rgb ($$$) {
};
}
###################################
sub PHTV_rgb2hsv($$$) {
my ( $r, $g, $b ) = @_;
my ( $M, $m, $c, $h, $s, $v );
@ -3457,7 +3384,6 @@ sub PHTV_rgb2hsv($$$) {
};
}
###################################
sub PHTV_hsv2rgb($$$) {
my ( $h, $s, $v ) = @_;
my $r = 0.0;
@ -3518,7 +3444,6 @@ sub PHTV_hsv2rgb($$$) {
};
}
###################################
sub PHTV_max {
my ( $max, @vars ) = @_;
for (@vars) {
@ -3528,7 +3453,6 @@ sub PHTV_max {
return $max;
}
###################################
sub PHTV_min {
my ( $min, @vars ) = @_;
for (@vars) {
@ -3537,7 +3461,6 @@ sub PHTV_min {
return $min;
}
###################################
sub PHTV_createDeviceId() {
my $deviceid;
my @chars = ( "A" .. "Z", "a" .. "z", 0 .. 9 );
@ -3545,7 +3468,6 @@ sub PHTV_createDeviceId() {
return $deviceid;
}
###################################
sub PHTV_createAuthSignature($$$) {
my ( $timestamp, $pin, $secretkey ) = @_;
my $base64 = 0;

View File

@ -1,24 +1,22 @@
# $Id$
###############################################################################
#
# Also see API documentation:
# $Id$
# https://pushover.net/api
#
package main;
use HttpUtils;
use utf8;
use Data::Dumper;
use HttpUtils;
use SetExtensions;
use Encode;
#------------------------------------------------------------------------------
# initialize ##################################################################
sub Pushover_Initialize($$) {
my ($hash) = @_;
$hash->{DefFn} = "Pushover_Define";
$hash->{UndefFn} = "Pushover_Undefine";
$hash->{SetFn} = "Pushover_Set";
$hash->{AttrList} =
"disable:0,1 disabledForIntervals do_not_notify:0,1 timestamp:0,1 title sound:pushover,bike,bugle,cashregister,classical,cosmic,falling,gamelan,incoming,intermission,magic,mechanical,pianobar,siren,spacealarm,tugboat,alien,climb,persistent,echo,updown,none device priority:0,1,2,-1,-2 callbackUrl retry expire "
. $readingFnAttributes;
@ -27,39 +25,7 @@ sub Pushover_Initialize($$) {
$hash->{'.msgParams'} = { parseParams => 1, };
}
#------------------------------------------------------------------------------
sub Pushover_addExtension($$$) {
my ( $name, $func, $link ) = @_;
my $url = "/$link";
return 0
if ( defined( $data{FWEXT}{$url} )
&& $data{FWEXT}{$url}{deviceName} ne $name );
Log3 $name, 2,
"Pushover $name: Registering Pushover for webhook URI $url ...";
$data{FWEXT}{$url}{deviceName} = $name;
$data{FWEXT}{$url}{FUNC} = $func;
$data{FWEXT}{$url}{LINK} = $link;
$name->{HASH}{FHEMWEB_URI} = $url;
return 1;
}
#------------------------------------------------------------------------------
sub Pushover_removeExtension($) {
my ($link) = @_;
my $url = "/$link";
my $name = $data{FWEXT}{$url}{deviceName};
Log3 $name, 2,
"Pushover $name: Unregistering Pushover for webhook URI $url...";
delete $data{FWEXT}{$url};
delete $name->{HASH}{FHEMWEB_URI};
}
#------------------------------------------------------------------------------
# regular Fn ##################################################################
sub Pushover_Define($$) {
my ( $hash, $def ) = @_;
@ -67,8 +33,8 @@ sub Pushover_Define($$) {
my $name = shift @a;
my $type = shift @a;
return
"Invalid number of arguments: define <name> Pushover <token> <user> [<infix>]"
return "Invalid number of arguments: "
. "define <name> Pushover <token> <user> [<infix>]"
if ( int(@a) < 2 );
my ( $token, $user, $infix ) = @a;
@ -107,9 +73,10 @@ sub Pushover_Define($$) {
else {
return "App or user/group token missing.";
}
return undef;
}
#------------------------------------------------------------------------------
sub Pushover_Undefine($$) {
my ( $hash, $name ) = @_;
@ -122,7 +89,6 @@ sub Pushover_Undefine($$) {
return undef;
}
#------------------------------------------------------------------------------
sub Pushover_Set($@) {
my ( $hash, $name, $cmd, @args ) = @_;
my ( $a, $h ) = parseParams( join " ", @args );
@ -169,9 +135,199 @@ sub Pushover_Set($@) {
return Pushover_SetMessage( $hash, @args )
if ( $cmd eq 'msg' );
return undef;
}
# module Fn ####################################################################
sub Pushover_addExtension($$$) {
my ( $name, $func, $link ) = @_;
my $url = "/$link";
return 0
if ( defined( $data{FWEXT}{$url} )
&& $data{FWEXT}{$url}{deviceName} ne $name );
Log3 $name, 2,
"Pushover $name: Registering Pushover for webhook URI $url ...";
$data{FWEXT}{$url}{deviceName} = $name;
$data{FWEXT}{$url}{FUNC} = $func;
$data{FWEXT}{$url}{LINK} = $link;
$name->{HASH}{FHEMWEB_URI} = $url;
return 1;
}
sub Pushover_removeExtension($) {
my ($link) = @_;
my $url = "/$link";
my $name = $data{FWEXT}{$url}{deviceName};
Log3 $name, 2,
"Pushover $name: Unregistering Pushover for webhook URI $url...";
delete $data{FWEXT}{$url};
delete $name->{HASH}{FHEMWEB_URI};
}
sub Pushover_CGI() {
my ($request) = @_;
my $hash;
my $name = "";
my $link = "";
my $URI = "";
# data received
if ( $request =~ m,^(/[^/]+?)(?:\&|\?)(.*)?$, ) {
$link = $1;
$URI = $2;
# get device name
$name = $data{FWEXT}{$link}{deviceName} if ( $data{FWEXT}{$link} );
$hash = $defs{$name};
# return error if no such device
return ( "text/plain; charset=utf-8",
"NOK No Pushover device for callback $link" )
unless ($name);
Log3 $name, 4, "Pushover $name callback: link='$link' URI='$URI'";
my $webArgs;
my $receipt = "";
my %revReadings;
# extract values from URI
foreach my $pv ( split( "&", $URI ) ) {
next if ( $pv eq "" );
$pv =~ s/\+/ /g;
$pv =~ s/%([\dA-F][\dA-F])/chr(hex($1))/ige;
my ( $p, $v ) = split( "=", $pv, 2 );
$webArgs->{$p} = $v;
}
if ( defined( $webArgs->{receipt} ) ) {
$receipt = $webArgs->{receipt};
}
elsif ( defined( $webArgs->{FhemCallbackId} ) ) {
$receipt = $webArgs->{FhemCallbackId};
}
else {
return ( "text/plain; charset=utf-8",
"NOK missing argument receipt or FhemCallbackId" );
}
# search for existing receipt
keys %{ $hash->{READINGS} };
while ( my ( $key, $value ) = each %{ $hash->{READINGS} } ) {
$revReadings{ $value->{VAL} } = $1
if ( defined( $value->{VAL} ) && $key =~ /^cb_(\d+)$/ );
}
if ( defined( $revReadings{$receipt} ) ) {
my $rAct = "cbAct_" . $revReadings{$receipt};
my $rAck = "cbAck_" . $revReadings{$receipt};
my $rAckAt = "cbAckAt_" . $revReadings{$receipt};
my $rAckBy = "cbAckBy_" . $revReadings{$receipt};
my $rCancelId = "cbCancelId_" . $revReadings{$receipt};
my $rDev = "cbDev_" . $revReadings{$receipt};
return ( "text/plain; charset=utf-8",
"NOK " . $receipt . ": invalid argument 'acknowledged'" )
if ( !defined( $webArgs->{acknowledged} )
|| $webArgs->{acknowledged} ne "1" );
return ( "text/plain; charset=utf-8",
"NOK " . $receipt . ": invalid argument 'acknowledged_by'" )
if ( !defined( $webArgs->{acknowledged_by} )
|| $webArgs->{acknowledged_by} ne $hash->{USER_KEY} );
if ( ReadingsVal( $name, $rAck, "1" ) eq "0"
&& $revReadings{$receipt} > int( time() ) )
{
delete $hash->{READINGS}{$rCancelId}
if ( defined( $hash->{READINGS}{$rCancelId} ) );
readingsBeginUpdate($hash);
readingsBulkUpdate( $hash, $rAck, "1" );
readingsBulkUpdate( $hash, $rAckBy,
$webArgs->{acknowledged_by} );
if ( defined( $webArgs->{acknowledged_at} )
&& $webArgs->{acknowledged_at} ne "" )
{
readingsBulkUpdate( $hash, $rAckAt,
$webArgs->{acknowledged_at} );
}
else {
readingsBulkUpdate( $hash, $rAckAt, int( time() ) );
}
my $redirect = "";
# run FHEM command if desired
if ( ReadingsVal( $name, $rAct, "pushover://" ) !~
/^[\w-]+:\/\/.*$/ )
{
$redirect = "pushover://";
fhem ReadingsVal( $name, $rAct, "" );
readingsBulkUpdate( $hash, $rAct,
"executed: " . ReadingsVal( $name, $rAct, "" ) );
}
# redirect to presented URL
if ( ReadingsVal( $name, $rAct, "none" ) =~ /^[\w-]+:\/\/.*$/ )
{
$redirect = ReadingsVal( $name, $rAct, "" );
}
readingsEndUpdate( $hash, 1 );
return (
"text/html; charset=utf-8",
"<html><head><meta http-equiv=\"refresh\" content=\"0;url="
. $redirect
. "\"></head><body><a href=\""
. $redirect
. "\">Click here to get redirected to your destination"
. "</a></body></html>"
) if ( $redirect ne "" );
}
else {
Log3 $name, 4,
"Pushover $name callback: " . $receipt . " has expired";
return (
"text/plain; charset=utf-8",
"NOK " . $receipt . " has expired"
);
}
}
else {
Log3 $name, 4,
"Pushover $name callback: unable to find existing receipt "
. $receipt;
return ( "text/plain; charset=utf-8",
"NOK unable to find existing receipt " . $receipt );
}
}
# no data received
else {
Log3 $name, 5,
"Pushover $name callback: received malformed request\n$request";
return ( "text/plain; charset=utf-8", "NOK malformed request" );
}
return ( "text/plain; charset=utf-8", "OK" );
}
#------------------------------------------------------------------------------
sub Pushover_SendCommand($$;$\%) {
my ( $hash, $service, $cmd, $type ) = @_;
my $name = $hash->{NAME};
@ -267,6 +423,10 @@ sub Pushover_SendCommand($$;$\%) {
Accept => 'application/json;charset=UTF-8',
'Accept-Charset' => 'UTF-8',
},
# sslargs => {
# SSL_verify_mode => 'SSL_verify_PEER',
# },
}
);
@ -316,7 +476,6 @@ sub Pushover_SendCommand($$;$\%) {
return;
}
#------------------------------------------------------------------------------
sub Pushover_ReceiveCommand($$$) {
my ( $param, $err, $data ) = @_;
my $hash = $param->{hash};
@ -711,7 +870,6 @@ sub Pushover_ReceiveCommand($$$) {
return;
}
#------------------------------------------------------------------------------
sub Pushover_ValidateUser ($;$) {
my ( $hash, $update ) = @_;
my $name = $hash->{NAME};
@ -739,7 +897,6 @@ sub Pushover_ValidateUser ($;$) {
}
}
#------------------------------------------------------------------------------
sub Pushover_SetMessage {
my $hash = shift;
my $name = $hash->{NAME};
@ -843,7 +1000,6 @@ sub Pushover_SetMessage {
return Pushover_SetMessage2( $hash, "msg", undef, \%values );
}
#------------------------------------------------------------------------------
sub Pushover_SetMessage2 ($$$$) {
my ( $hash, $cmd, $a, $h ) = @_;
my $name = $hash->{NAME};
@ -1236,165 +1392,6 @@ sub Pushover_CancelMessage ($$$$) {
return $return;
}
#------------------------------------------------------------------------------
sub Pushover_CGI() {
my ($request) = @_;
my $hash;
my $name = "";
my $link = "";
my $URI = "";
# data received
if ( $request =~ m,^(/[^/]+?)(?:\&|\?)(.*)?$, ) {
$link = $1;
$URI = $2;
# get device name
$name = $data{FWEXT}{$link}{deviceName} if ( $data{FWEXT}{$link} );
$hash = $defs{$name};
# return error if no such device
return ( "text/plain; charset=utf-8",
"NOK No Pushover device for callback $link" )
unless ($name);
Log3 $name, 4, "Pushover $name callback: link='$link' URI='$URI'";
my $webArgs;
my $receipt = "";
my %revReadings;
# extract values from URI
foreach my $pv ( split( "&", $URI ) ) {
next if ( $pv eq "" );
$pv =~ s/\+/ /g;
$pv =~ s/%([\dA-F][\dA-F])/chr(hex($1))/ige;
my ( $p, $v ) = split( "=", $pv, 2 );
$webArgs->{$p} = $v;
}
if ( defined( $webArgs->{receipt} ) ) {
$receipt = $webArgs->{receipt};
}
elsif ( defined( $webArgs->{FhemCallbackId} ) ) {
$receipt = $webArgs->{FhemCallbackId};
}
else {
return ( "text/plain; charset=utf-8",
"NOK missing argument receipt or FhemCallbackId" );
}
# search for existing receipt
keys %{ $hash->{READINGS} };
while ( my ( $key, $value ) = each %{ $hash->{READINGS} } ) {
$revReadings{ $value->{VAL} } = $1
if ( defined( $value->{VAL} ) && $key =~ /^cb_(\d+)$/ );
}
if ( defined( $revReadings{$receipt} ) ) {
my $rAct = "cbAct_" . $revReadings{$receipt};
my $rAck = "cbAck_" . $revReadings{$receipt};
my $rAckAt = "cbAckAt_" . $revReadings{$receipt};
my $rAckBy = "cbAckBy_" . $revReadings{$receipt};
my $rCancelId = "cbCancelId_" . $revReadings{$receipt};
my $rDev = "cbDev_" . $revReadings{$receipt};
return ( "text/plain; charset=utf-8",
"NOK " . $receipt . ": invalid argument 'acknowledged'" )
if ( !defined( $webArgs->{acknowledged} )
|| $webArgs->{acknowledged} ne "1" );
return ( "text/plain; charset=utf-8",
"NOK " . $receipt . ": invalid argument 'acknowledged_by'" )
if ( !defined( $webArgs->{acknowledged_by} )
|| $webArgs->{acknowledged_by} ne $hash->{USER_KEY} );
if ( ReadingsVal( $name, $rAck, "1" ) eq "0"
&& $revReadings{$receipt} > int( time() ) )
{
delete $hash->{READINGS}{$rCancelId}
if ( defined( $hash->{READINGS}{$rCancelId} ) );
readingsBeginUpdate($hash);
readingsBulkUpdate( $hash, $rAck, "1" );
readingsBulkUpdate( $hash, $rAckBy,
$webArgs->{acknowledged_by} );
if ( defined( $webArgs->{acknowledged_at} )
&& $webArgs->{acknowledged_at} ne "" )
{
readingsBulkUpdate( $hash, $rAckAt,
$webArgs->{acknowledged_at} );
}
else {
readingsBulkUpdate( $hash, $rAckAt, int( time() ) );
}
my $redirect = "";
# run FHEM command if desired
if ( ReadingsVal( $name, $rAct, "pushover://" ) !~
/^[\w-]+:\/\/.*$/ )
{
$redirect = "pushover://";
fhem ReadingsVal( $name, $rAct, "" );
readingsBulkUpdate( $hash, $rAct,
"executed: " . ReadingsVal( $name, $rAct, "" ) );
}
# redirect to presented URL
if ( ReadingsVal( $name, $rAct, "none" ) =~ /^[\w-]+:\/\/.*$/ )
{
$redirect = ReadingsVal( $name, $rAct, "" );
}
readingsEndUpdate( $hash, 1 );
return (
"text/html; charset=utf-8",
"<html><head><meta http-equiv=\"refresh\" content=\"0;url="
. $redirect
. "\"></head><body><a href=\""
. $redirect
. "\">Click here to get redirected to your destination"
. "</a></body></html>"
) if ( $redirect ne "" );
}
else {
Log3 $name, 4,
"Pushover $name callback: " . $receipt . " has expired";
return (
"text/plain; charset=utf-8",
"NOK " . $receipt . " has expired"
);
}
}
else {
Log3 $name, 4,
"Pushover $name callback: unable to find existing receipt "
. $receipt;
return ( "text/plain; charset=utf-8",
"NOK unable to find existing receipt " . $receipt );
}
}
# no data received
else {
Log3 $name, 5,
"Pushover $name callback: received malformed request\n$request";
return ( "text/plain; charset=utf-8", "NOK malformed request" );
}
return ( "text/plain; charset=utf-8", "OK" );
}
1;
###############################################################################

View File

@ -1,50 +1,13 @@
###############################################################################
# $Id$
##############################################################################
#
# 70_ONKYO_AVR_ZONE.pm
# An FHEM Perl module for controlling ONKYO A/V receivers
# via network connection.
#
# Copyright by Julian Pawlowski
# e-mail: julian.pawlowski at gmail.com
#
# 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 <http://www.gnu.org/licenses/>.
#
##############################################################################
package main;
use strict;
use warnings;
use Time::HiRes qw(usleep);
use Symbol qw<qualify_to_ref>;
use Data::Dumper;
use Symbol qw<qualify_to_ref>;
$Data::Dumper::Sortkeys = 1;
sub ONKYO_AVR_ZONE_Set($$$);
sub ONKYO_AVR_ZONE_Get($$$);
sub ONKYO_AVR_ZONE_Define($$$);
sub ONKYO_AVR_ZONE_Undefine($$);
#########################
# Forward declaration for remotecontrol module
sub ONKYO_AVR_ZONE_RClayout_TV();
sub ONKYO_AVR_ZONE_RCmakenotify($$);
###################################
# initialize ##################################################################
sub ONKYO_AVR_ZONE_Initialize($) {
my ($hash) = @_;
@ -52,22 +15,17 @@ sub ONKYO_AVR_ZONE_Initialize($) {
require "$attr{global}{modpath}/FHEM/ONKYOdb.pm";
$hash->{Match} = ".+";
$hash->{DefFn} = "ONKYO_AVR_ZONE_Define";
$hash->{UndefFn} = "ONKYO_AVR_ZONE_Undefine";
# $hash->{DeleteFn} = "ONKYO_AVR_ZONE_Delete";
$hash->{SetFn} = "ONKYO_AVR_ZONE_Set";
$hash->{GetFn} = "ONKYO_AVR_ZONE_Get";
# $hash->{AttrFn} = "ONKYO_AVR_ZONE_Attr";
# $hash->{NotifyFn} = "ONKYO_AVR_ZONE_Notify";
$hash->{SetFn} = "ONKYO_AVR_ZONE_Set";
$hash->{GetFn} = "ONKYO_AVR_ZONE_Get";
$hash->{ParseFn} = "ONKYO_AVR_ZONE_Parse";
$hash->{Match} = ".+";
$hash->{AttrList} =
"IODev do_not_notify:1,0 "
. "volumeSteps:1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20 inputs disable:0,1 model wakeupCmd:textField "
"IODev disable:0,1 disabledForIntervals do_not_notify:1,0 "
. "volumeSteps:1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20 inputs model wakeupCmd:textField "
. $readingFnAttributes;
# $data{RC_layout}{ONKYO_AVR_ZONE_SVG} = "ONKYO_AVR_ZONE_RClayout_SVG";
@ -90,7 +48,7 @@ sub ONKYO_AVR_ZONE_Initialize($) {
$hash->{parseParams} = 1;
}
###################################
# regular Fn ##################################################################
sub ONKYO_AVR_ZONE_Define($$$) {
my ( $hash, $a, $h ) = @_;
my $name = $hash->{NAME};
@ -125,8 +83,8 @@ sub ONKYO_AVR_ZONE_Define($$$) {
. $modules{ONKYO_AVR_ZONE}{defptr}{$IOname}{$zone}{NAME};
}
elsif ( !defined($IOhash) ) {
return
"No matching I/O device found, please define a ONKYO_AVR device first";
return "No matching I/O device found, "
. "please define a ONKYO_AVR device first";
}
elsif ( !defined( $IOhash->{TYPE} ) || !defined( $IOhash->{NAME} ) ) {
return "IODev does not seem to be existing";
@ -182,10 +140,9 @@ sub ONKYO_AVR_ZONE_Define($$$) {
ONKYO_AVR_ZONE_SendCommand( $hash, "mute", "query" );
ONKYO_AVR_ZONE_SendCommand( $hash, "volume", "query" );
return;
return undef;
}
###################################
sub ONKYO_AVR_ZONE_Undefine($$) {
my ( $hash, $name ) = @_;
my $zone = $hash->{ZONE};
@ -207,300 +164,6 @@ sub ONKYO_AVR_ZONE_Undefine($$) {
return undef;
}
#############################
sub ONKYO_AVR_ZONE_Parse($$) {
my ( $IOhash, $msg ) = @_;
my @matches;
my $IOname = $IOhash->{NAME};
my $zone = $msg->{zone} || "";
delete $msg->{zone} if ( defined( $msg->{zone} ) );
Log3 $IOname, 5,
"ONKYO_AVR $IOname: called function ONKYO_AVR_ZONE_Parse()";
foreach my $d ( keys %defs ) {
my $hash = $defs{$d};
my $name = $hash->{NAME};
my $state = ReadingsVal( $name, "power", "off" );
if ( $hash->{TYPE} eq "ONKYO_AVR_ZONE"
&& $hash->{IODev} eq $IOhash
&& ( $zone eq "" || $hash->{ZONE} eq $zone ) )
{
push @matches, $d;
# Update readings
readingsBeginUpdate($hash);
foreach my $cmd ( keys %{$msg} ) {
my $value = $msg->{$cmd};
$hash->{INPUT} = $value and next if ( $cmd eq "INPUT_RAW" );
$hash->{CHANNEL} = $value and next if ( $cmd eq "CHANNEL_RAW" );
Log3 $name, 4, "ONKYO_AVR_ZONE $name: rcv $cmd = $value";
# presence
if ( $cmd eq "presence" && $value eq "present" ) {
ONKYO_AVR_ZONE_SendCommand( $hash, "power", "query" );
ONKYO_AVR_ZONE_SendCommand( $hash, "input", "query" );
ONKYO_AVR_ZONE_SendCommand( $hash, "mute", "query" );
ONKYO_AVR_ZONE_SendCommand( $hash, "volume", "query" );
}
# input
elsif ( $cmd eq "input" ) {
# Input alias handling
if (
defined(
$hash->{helper}{receiver}{input_aliases}{$value}
)
)
{
Log3 $name, 4,
"ONKYO_AVR_AVR $name: Input aliasing '$value' to '"
. $hash->{helper}{receiver}{input_aliases}{$value}
. "'";
$value =
$hash->{helper}{receiver}{input_aliases}{$value};
}
}
# power
elsif ( $cmd eq "power" ) {
readingsBulkUpdate( $hash, "presence", "present" )
if ( ReadingsVal( $name, "presence", "-" ) ne "present" );
}
# balance
elsif ( $cmd eq "balance" ) {
my $prefix = "";
$prefix = "-" if ( $value =~ /^\-.*/ );
$value = substr( $value, 1 ) if ( $value =~ /^[\+|\-].*/ );
$value = $prefix . ONKYO_AVR_hex2dec($value);
}
# preset
elsif ( $cmd eq "preset" ) {
if ( defined( $IOhash->{helper}{receiver}{preset} ) ) {
foreach my $id (
sort keys %{ $IOhash->{helper}{receiver}{preset} } )
{
my $presetName =
$IOhash->{helper}{receiver}{preset}{$id};
next if ( !$presetName || $presetName eq "" );
$presetName =~ s/\s/_/g;
if ( $id eq ONKYO_AVR_dec2hex($value) ) {
$value = $presetName;
last;
}
}
}
$value = "" if ( $value eq "0" );
}
# tone
if ( $cmd =~ /^tone/ ) {
if ( $value =~ /^B(..)T(..)$/ ) {
my $bass = $1;
my $treble = $2;
my $bassName = $cmd . "-bass";
my $trebleName = $cmd . "-treble";
my $prefixBass = "";
my $prefixTreble = "";
# tone-bass
$prefixBass = "-" if ( $bass =~ /^\-.*/ );
$bass = substr( $bass, 1 ) if ( $bass =~ /^[\+|\-].*/ );
$bass = $prefixBass . ONKYO_AVR_hex2dec($bass);
readingsBulkUpdate( $hash, $bassName, $bass )
if ( ReadingsVal( $name, $bassName, "-" ) ne $bass );
# tone-treble
$prefixTreble = "-" if ( $treble =~ /^\-.*/ );
$treble = substr( $treble, 1 )
if ( $treble =~ /^[\+|\-].*/ );
$treble = $prefixTreble . ONKYO_AVR_hex2dec($treble);
readingsBulkUpdate( $hash, $trebleName, $treble )
if (
ReadingsVal( $name, $trebleName, "-" ) ne $treble );
}
}
# all other commands
else {
readingsBulkUpdate( $hash, $cmd, $value )
if ( ReadingsVal( $name, $cmd, "-" ) ne $value
|| $cmd =~ /^currentAlbumArt.*/ );
}
}
# stateAV
my $stateAV = ONKYO_AVR_ZONE_GetStateAV($hash);
readingsBulkUpdate( $hash, "stateAV", $stateAV )
if ( ReadingsVal( $name, "stateAV", "-" ) ne $stateAV );
readingsEndUpdate( $hash, 1 );
last;
}
}
return @matches if (@matches);
return "UNDEFINED ONKYO_AVR_ZONE";
}
###################################
sub ONKYO_AVR_ZONE_Get($$$) {
my ( $hash, $a, $h ) = @_;
my $name = $hash->{NAME};
my $zone = $hash->{ZONE};
my $IOhash = $hash->{IODev};
my $IOname = $IOhash->{NAME};
my $state = ReadingsVal( $name, "power", "off" );
my $presence = ReadingsVal( $name, "presence", "absent" );
my $commands = ONKYOdb::ONKYO_GetRemotecontrolCommand($zone);
my $commands_details = ONKYOdb::ONKYO_GetRemotecontrolCommandDetails($zone);
my $return;
Log3 $name, 5, "ONKYO_AVR_ZONE $name: called function ONKYO_AVR_ZONE_Get()";
return "Argument is missing" if ( int(@$a) < 1 );
# readings
return $hash->{READINGS}{ @$a[1] }{VAL}
if ( defined( $hash->{READINGS}{ @$a[1] } ) );
return "Device is offline and cannot be controlled at that stage."
if ( $presence eq "absent" );
# statusRequest
if ( lc( @$a[1] ) eq "statusrequest" ) {
Log3 $name, 3, "ONKYO_AVR_ZONE get $name " . @$a[1];
ONKYO_AVR_ZONE_SendCommand( $hash, "power", "query" );
ONKYO_AVR_ZONE_SendCommand( $hash, "input", "query" );
ONKYO_AVR_ZONE_SendCommand( $hash, "mute", "query" );
ONKYO_AVR_ZONE_SendCommand( $hash, "volume", "query" );
}
# remoteControl
elsif ( lc( @$a[1] ) eq "remotecontrol" ) {
# Output help for commands
if ( !defined( @$a[2] ) || @$a[2] eq "help" || @$a[2] eq "?" ) {
my $valid_commands =
"Usage: <command> <value>\n\nValid commands in zone$zone:\n\n\n"
. "COMMAND\t\t\tDESCRIPTION\n\n";
# For each valid command
foreach my $command ( sort keys %{$commands} ) {
my $command_raw = $commands->{$command};
# add command including description if found
if ( defined( $commands_details->{$command_raw}{description} ) )
{
$valid_commands .=
$command
. "\t\t\t"
. $commands_details->{$command_raw}{description} . "\n";
}
# add command only
else {
$valid_commands .= $command . "\n";
}
}
$valid_commands .=
"\nTry '&lt;command&gt; help' to find out well known values.\n\n\n";
$return = $valid_commands;
}
else {
# Reading values for command from HASH table
my $values =
ONKYOdb::ONKYO_GetRemotecontrolValue( $zone,
$commands->{ @$a[2] } );
@$a[3] = "query"
if ( !defined( @$a[3] ) && defined( $values->{query} ) );
# Output help for values
if ( !defined( @$a[3] ) || @$a[3] eq "help" || @$a[3] eq "?" ) {
# Get all details for command
my $command_details =
ONKYOdb::ONKYO_GetRemotecontrolCommandDetails( $zone,
$commands->{ @$a[2] } );
my $valid_values =
"Usage: "
. @$a[2]
. " <value>\n\nWell known values:\n\n\n"
. "VALUE\t\t\tDESCRIPTION\n\n";
# For each valid value
foreach my $value ( sort keys %{$values} ) {
# add value including description if found
if ( defined( $command_details->{description} ) ) {
$valid_values .=
$value
. "\t\t\t"
. $command_details->{description} . "\n";
}
# add value only
else {
$valid_values .= $value . "\n";
}
}
$valid_values .= "\n\n\n";
$return = $valid_values;
}
# normal processing
else {
Log3 $name, 3,
"ONKYO_AVR_ZONE get $name "
. @$a[1] . " "
. @$a[2] . " "
. @$a[3]
if ( !@$a[4] || @$a[4] ne "quiet" );
ONKYO_AVR_ZONE_SendCommand( $hash, @$a[2], @$a[3] );
$return = "Sent command: " . @$a[2] . " " . @$a[3]
if ( !@$a[4] || @$a[4] ne "quiet" );
}
}
}
else {
$return =
"Unknown argument " . @$a[1] . ", choose one of statusRequest:noArg";
# remoteControl
$return .= " remoteControl:";
foreach my $command ( sort keys %{$commands} ) {
$return .= "," . $command;
}
}
return $return if ($return);
}
###################################
sub ONKYO_AVR_ZONE_Set($$$) {
my ( $hash, $a, $h ) = @_;
my $IOhash = $hash->{IODev};
@ -1443,13 +1106,298 @@ sub ONKYO_AVR_ZONE_Set($$$) {
return $return;
}
############################################################################################################
#
# Begin of helper functions
#
############################################################################################################
sub ONKYO_AVR_ZONE_Get($$$) {
my ( $hash, $a, $h ) = @_;
my $name = $hash->{NAME};
my $zone = $hash->{ZONE};
my $IOhash = $hash->{IODev};
my $IOname = $IOhash->{NAME};
my $state = ReadingsVal( $name, "power", "off" );
my $presence = ReadingsVal( $name, "presence", "absent" );
my $commands = ONKYOdb::ONKYO_GetRemotecontrolCommand($zone);
my $commands_details = ONKYOdb::ONKYO_GetRemotecontrolCommandDetails($zone);
my $return;
###################################
Log3 $name, 5, "ONKYO_AVR_ZONE $name: called function ONKYO_AVR_ZONE_Get()";
return "Argument is missing" if ( int(@$a) < 1 );
# readings
return $hash->{READINGS}{ @$a[1] }{VAL}
if ( defined( $hash->{READINGS}{ @$a[1] } ) );
return "Device is offline and cannot be controlled at that stage."
if ( $presence eq "absent" );
# statusRequest
if ( lc( @$a[1] ) eq "statusrequest" ) {
Log3 $name, 3, "ONKYO_AVR_ZONE get $name " . @$a[1];
ONKYO_AVR_ZONE_SendCommand( $hash, "power", "query" );
ONKYO_AVR_ZONE_SendCommand( $hash, "input", "query" );
ONKYO_AVR_ZONE_SendCommand( $hash, "mute", "query" );
ONKYO_AVR_ZONE_SendCommand( $hash, "volume", "query" );
}
# remoteControl
elsif ( lc( @$a[1] ) eq "remotecontrol" ) {
# Output help for commands
if ( !defined( @$a[2] ) || @$a[2] eq "help" || @$a[2] eq "?" ) {
my $valid_commands =
"Usage: <command> <value>\n\nValid commands in zone$zone:\n\n\n"
. "COMMAND\t\t\tDESCRIPTION\n\n";
# For each valid command
foreach my $command ( sort keys %{$commands} ) {
my $command_raw = $commands->{$command};
# add command including description if found
if ( defined( $commands_details->{$command_raw}{description} ) )
{
$valid_commands .=
$command
. "\t\t\t"
. $commands_details->{$command_raw}{description} . "\n";
}
# add command only
else {
$valid_commands .= $command . "\n";
}
}
$valid_commands .=
"\nTry '&lt;command&gt; help' to find out well known values.\n\n\n";
$return = $valid_commands;
}
else {
# Reading values for command from HASH table
my $values =
ONKYOdb::ONKYO_GetRemotecontrolValue( $zone,
$commands->{ @$a[2] } );
@$a[3] = "query"
if ( !defined( @$a[3] ) && defined( $values->{query} ) );
# Output help for values
if ( !defined( @$a[3] ) || @$a[3] eq "help" || @$a[3] eq "?" ) {
# Get all details for command
my $command_details =
ONKYOdb::ONKYO_GetRemotecontrolCommandDetails( $zone,
$commands->{ @$a[2] } );
my $valid_values =
"Usage: "
. @$a[2]
. " <value>\n\nWell known values:\n\n\n"
. "VALUE\t\t\tDESCRIPTION\n\n";
# For each valid value
foreach my $value ( sort keys %{$values} ) {
# add value including description if found
if ( defined( $command_details->{description} ) ) {
$valid_values .=
$value
. "\t\t\t"
. $command_details->{description} . "\n";
}
# add value only
else {
$valid_values .= $value . "\n";
}
}
$valid_values .= "\n\n\n";
$return = $valid_values;
}
# normal processing
else {
Log3 $name, 3,
"ONKYO_AVR_ZONE get $name "
. @$a[1] . " "
. @$a[2] . " "
. @$a[3]
if ( !@$a[4] || @$a[4] ne "quiet" );
ONKYO_AVR_ZONE_SendCommand( $hash, @$a[2], @$a[3] );
$return = "Sent command: " . @$a[2] . " " . @$a[3]
if ( !@$a[4] || @$a[4] ne "quiet" );
}
}
}
else {
$return =
"Unknown argument " . @$a[1] . ", choose one of statusRequest:noArg";
# remoteControl
$return .= " remoteControl:";
foreach my $command ( sort keys %{$commands} ) {
$return .= "," . $command;
}
}
return $return;
}
sub ONKYO_AVR_ZONE_Parse($$) {
my ( $IOhash, $msg ) = @_;
my @matches;
my $IOname = $IOhash->{NAME};
my $zone = $msg->{zone} || "";
delete $msg->{zone} if ( defined( $msg->{zone} ) );
Log3 $IOname, 5,
"ONKYO_AVR $IOname: called function ONKYO_AVR_ZONE_Parse()";
foreach my $d ( keys %defs ) {
my $hash = $defs{$d};
my $name = $hash->{NAME};
my $state = ReadingsVal( $name, "power", "off" );
if ( $hash->{TYPE} eq "ONKYO_AVR_ZONE"
&& $hash->{IODev} eq $IOhash
&& ( $zone eq "" || $hash->{ZONE} eq $zone ) )
{
push @matches, $d;
# Update readings
readingsBeginUpdate($hash);
foreach my $cmd ( keys %{$msg} ) {
my $value = $msg->{$cmd};
$hash->{INPUT} = $value and next if ( $cmd eq "INPUT_RAW" );
$hash->{CHANNEL} = $value and next if ( $cmd eq "CHANNEL_RAW" );
Log3 $name, 4, "ONKYO_AVR_ZONE $name: rcv $cmd = $value";
# presence
if ( $cmd eq "presence" && $value eq "present" ) {
ONKYO_AVR_ZONE_SendCommand( $hash, "power", "query" );
ONKYO_AVR_ZONE_SendCommand( $hash, "input", "query" );
ONKYO_AVR_ZONE_SendCommand( $hash, "mute", "query" );
ONKYO_AVR_ZONE_SendCommand( $hash, "volume", "query" );
}
# input
elsif ( $cmd eq "input" ) {
# Input alias handling
if (
defined(
$hash->{helper}{receiver}{input_aliases}{$value}
)
)
{
Log3 $name, 4,
"ONKYO_AVR_AVR $name: Input aliasing '$value' to '"
. $hash->{helper}{receiver}{input_aliases}{$value}
. "'";
$value =
$hash->{helper}{receiver}{input_aliases}{$value};
}
}
# power
elsif ( $cmd eq "power" ) {
readingsBulkUpdate( $hash, "presence", "present" )
if ( ReadingsVal( $name, "presence", "-" ) ne "present" );
}
# balance
elsif ( $cmd eq "balance" ) {
my $prefix = "";
$prefix = "-" if ( $value =~ /^\-.*/ );
$value = substr( $value, 1 ) if ( $value =~ /^[\+|\-].*/ );
$value = $prefix . ONKYO_AVR_hex2dec($value);
}
# preset
elsif ( $cmd eq "preset" ) {
if ( defined( $IOhash->{helper}{receiver}{preset} ) ) {
foreach my $id (
sort keys %{ $IOhash->{helper}{receiver}{preset} } )
{
my $presetName =
$IOhash->{helper}{receiver}{preset}{$id};
next if ( !$presetName || $presetName eq "" );
$presetName =~ s/\s/_/g;
if ( $id eq ONKYO_AVR_dec2hex($value) ) {
$value = $presetName;
last;
}
}
}
$value = "" if ( $value eq "0" );
}
# tone
if ( $cmd =~ /^tone/ ) {
if ( $value =~ /^B(..)T(..)$/ ) {
my $bass = $1;
my $treble = $2;
my $bassName = $cmd . "-bass";
my $trebleName = $cmd . "-treble";
my $prefixBass = "";
my $prefixTreble = "";
# tone-bass
$prefixBass = "-" if ( $bass =~ /^\-.*/ );
$bass = substr( $bass, 1 ) if ( $bass =~ /^[\+|\-].*/ );
$bass = $prefixBass . ONKYO_AVR_hex2dec($bass);
readingsBulkUpdate( $hash, $bassName, $bass )
if ( ReadingsVal( $name, $bassName, "-" ) ne $bass );
# tone-treble
$prefixTreble = "-" if ( $treble =~ /^\-.*/ );
$treble = substr( $treble, 1 )
if ( $treble =~ /^[\+|\-].*/ );
$treble = $prefixTreble . ONKYO_AVR_hex2dec($treble);
readingsBulkUpdate( $hash, $trebleName, $treble )
if (
ReadingsVal( $name, $trebleName, "-" ) ne $treble );
}
}
# all other commands
else {
readingsBulkUpdate( $hash, $cmd, $value )
if ( ReadingsVal( $name, $cmd, "-" ) ne $value
|| $cmd =~ /^currentAlbumArt.*/ );
}
}
# stateAV
my $stateAV = ONKYO_AVR_ZONE_GetStateAV($hash);
readingsBulkUpdate( $hash, "stateAV", $stateAV )
if ( ReadingsVal( $name, "stateAV", "-" ) ne $stateAV );
readingsEndUpdate( $hash, 1 );
last;
}
}
return @matches if (@matches);
return "UNDEFINED ONKYO_AVR_ZONE";
}
# module Fn ####################################################################
sub ONKYO_AVR_ZONE_SendCommand($$$) {
my ( $hash, $cmd, $value ) = @_;
my $IOhash = $hash->{IODev};
@ -1523,7 +1471,6 @@ sub ONKYO_AVR_ZONE_SendCommand($$$) {
return;
}
###################################
sub ONKYO_AVR_ZONE_GetStateAV($) {
my ($hash) = @_;
my $name = $hash->{NAME};

View File

@ -1,30 +1,5 @@
###############################################################################
# $Id$
##############################################################################
#
# 74_THINKINGCLEANER.pm
# An FHEM Perl module for controlling ThinkingCleaner connected
# Roomba vacuum cleaning robot.
#
# Copyright by Julian Pawlowski
# e-mail: julian.pawlowski at gmail.com
#
# 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 <http://www.gnu.org/licenses/>.
#
##############################################################################
package main;
use strict;
@ -34,15 +9,7 @@ use HttpUtils;
use Encode;
use Data::Dumper;
no warnings "all";
sub THINKINGCLEANER_Set($@);
sub THINKINGCLEANER_GetStatus($;$);
sub THINKINGCLEANER_Attr($@);
sub THINKINGCLEANER_Define($$);
sub THINKINGCLEANER_Undefine($$);
###################################
# initialize ##################################################################
sub THINKINGCLEANER_Initialize($) {
my ($hash) = @_;
@ -51,14 +18,14 @@ sub THINKINGCLEANER_Initialize($) {
my $webhookFWinstance =
join( ",", devspec2array('TYPE=FHEMWEB:FILTER=TEMPORARY!=1') );
$hash->{SetFn} = "THINKINGCLEANER_Set";
$hash->{DefFn} = "THINKINGCLEANER_Define";
$hash->{AttrFn} = "THINKINGCLEANER_Attr";
$hash->{UndefFn} = "THINKINGCLEANER_Undefine";
$hash->{SetFn} = "THINKINGCLEANER_Set";
$hash->{AttrFn} = "THINKINGCLEANER_Attr";
$hash->{parseParams} = 1;
$hash->{AttrList} =
"disable:0,1 timeout:1,2,3,4,5 pollInterval:30,45,60,75,90 pollMultiplierWebhook pollMultiplierCleaning model webhookHttpHostname webhookPort webhookFWinstance:$webhookFWinstance restart:noArg "
"disable:0,1 disabledForIntervals timeout:1,2,3,4,5 pollInterval:30,45,60,75,90 pollMultiplierWebhook pollMultiplierCleaning model webhookHttpHostname webhookPort webhookFWinstance:$webhookFWinstance restart:noArg "
. $readingFnAttributes;
# 98_powerMap.pm support
@ -89,50 +56,82 @@ sub THINKINGCLEANER_Initialize($) {
},
},
};
return;
}
#####################################
sub THINKINGCLEANER_GetStatus($;$) {
my ( $hash, $delay ) = @_;
my $name = $hash->{NAME};
$hash->{INTERVAL_MULTIPLIER} = (
ReadingsVal( $name, "state", "off" ) ne "off"
&& ReadingsVal( $name, "state", "absent" ) ne "absent"
&& ReadingsVal( $name, "state", "standby" ) ne "standby"
? AttrVal( $name, "pollMultiplierCleaning", "0.5" )
: (
$hash->{WEBHOOK_REGISTER} eq "success"
? AttrVal( $name, "pollMultiplierWebhook", "2" )
: "1"
)
);
$hash->{INTERVAL} =
AttrVal( $name, "pollInterval", "45" ) * $hash->{INTERVAL_MULTIPLIER};
my $interval = (
$delay
? $delay
: $hash->{INTERVAL}
);
# regular Fn ##################################################################
sub THINKINGCLEANER_Define($$$) {
my ( $hash, $a, $h ) = @_;
my $name = $hash->{NAME};
my $infix = "THINKINGCLEANER";
Log3 $name, 5,
"THINKINGCLEANER $name: called function THINKINGCLEANER_GetStatus()";
"THINKINGCLEANER $name: called function THINKINGCLEANER_Define()";
RemoveInternalTimer($hash);
InternalTimer( gettimeofday() + $interval,
"THINKINGCLEANER_GetStatus", $hash, 0 );
eval {
require JSON;
import JSON qw( decode_json );
};
return "Please install Perl JSON to use module THINKINGCLEANER"
if ($@);
return
if ( $delay || AttrVal( $name, "disable", 0 ) == 1 );
if ( int(@$a) < 2 ) {
my $msg =
"Wrong syntax: define <name> THINKINGCLEANER <ip-or-hostname>";
Log3 $name, 4, $msg;
return $msg;
}
THINKINGCLEANER_SendCommand( $hash, "full_status.json" );
$hash->{TYPE} = "THINKINGCLEANER";
return;
my $address = @$a[2];
$hash->{DeviceName} = $address;
# set reverse pointer
$modules{THINKINGCLEANER}{defptr}{$name} = \$hash;
# set default settings on first define
if ( $init_done && !defined( $hash->{OLDDEF} ) ) {
$attr{$name}{cmdIcon} =
'on-max:text_max on-spot:refresh on-delayed:time_timer dock:measure_battery_50 locate:rc_SEARCH';
$attr{$name}{devStateIcon} =
'on-delayed:rc_STOP@green:off on-max:rc_BLUE@green:off on-spot:rc_GREEN@red:off on.*:rc_GREEN@green:off dock:rc_GREEN@orange:off off:rc_STOP:on standby|remote:rc_YELLOW:on locate:rc_YELLOW .*:rc_RED';
$attr{$name}{icon} = 'scene_cleaning';
$attr{$name}{webCmd} = 'on-max:on-spot:on-delayed:dock:locate';
}
if ( THINKINGCLEANER_addExtension( $name, "THINKINGCLEANER_CGI", $infix ) )
{
$hash->{fhem}{infix} = $infix;
}
$hash->{WEBHOOK_REGISTER} = "unregistered";
# start the status update timer
THINKINGCLEANER_GetStatus( $hash, 2 );
return undef;
}
sub THINKINGCLEANER_Undefine($$$) {
my ( $hash, $a, $h ) = @_;
my $name = $hash->{NAME};
if ( defined( $hash->{fhem}{infix} ) ) {
THINKINGCLEANER_removeExtension( $hash->{fhem}{infix} );
}
Log3 $name, 5,
"THINKINGCLEANER $name: called function THINKINGCLEANER_Undefine()";
# Stop the internal GetStatus-Loop and exit
RemoveInternalTimer($hash);
# release reverse pointer
delete $modules{THINKINGCLEANER}{defptr}{$name};
return undef;
}
###################################
sub THINKINGCLEANER_Set($$$) {
my ( $hash, $a, $h ) = @_;
my $name = $hash->{NAME};
@ -842,64 +841,9 @@ sub THINKINGCLEANER_Set($$$) {
return $usage;
}
return;
return undef;
}
###################################
sub THINKINGCLEANER_Define($$$) {
my ( $hash, $a, $h ) = @_;
my $name = $hash->{NAME};
my $infix = "THINKINGCLEANER";
Log3 $name, 5,
"THINKINGCLEANER $name: called function THINKINGCLEANER_Define()";
eval {
require JSON;
import JSON qw( decode_json );
};
return "Please install Perl JSON to use module THINKINGCLEANER"
if ($@);
if ( int(@$a) < 2 ) {
my $msg =
"Wrong syntax: define <name> THINKINGCLEANER <ip-or-hostname>";
Log3 $name, 4, $msg;
return $msg;
}
$hash->{TYPE} = "THINKINGCLEANER";
my $address = @$a[2];
$hash->{DeviceName} = $address;
# set reverse pointer
$modules{THINKINGCLEANER}{defptr}{$name} = \$hash;
# set default settings on first define
if ( $init_done && !defined( $hash->{OLDDEF} ) ) {
$attr{$name}{cmdIcon} =
'on-max:text_max on-spot:refresh on-delayed:time_timer dock:measure_battery_50 locate:rc_SEARCH';
$attr{$name}{devStateIcon} =
'on-delayed:rc_STOP@green:off on-max:rc_BLUE@green:off on-spot:rc_GREEN@red:off on.*:rc_GREEN@green:off dock:rc_GREEN@orange:off off:rc_STOP:on standby|remote:rc_YELLOW:on locate:rc_YELLOW .*:rc_RED';
$attr{$name}{icon} = 'scene_cleaning';
$attr{$name}{webCmd} = 'on-max:on-spot:on-delayed:dock:locate';
}
if ( THINKINGCLEANER_addExtension( $name, "THINKINGCLEANER_CGI", $infix ) )
{
$hash->{fhem}{infix} = $infix;
}
$hash->{WEBHOOK_REGISTER} = "unregistered";
# start the status update timer
THINKINGCLEANER_GetStatus( $hash, 2 );
return;
}
###################################
sub THINKINGCLEANER_Attr(@) {
my ( $cmd, $name, $attrName, $attrVal ) = @_;
my $hash = $defs{$name};
@ -1004,7 +948,7 @@ sub THINKINGCLEANER_Attr(@) {
return undef;
}
###################################
# module Fn ####################################################################
sub THINKINGCLEANER_addExtension($$$) {
my ( $name, $func, $link ) = @_;
@ -1023,7 +967,6 @@ sub THINKINGCLEANER_addExtension($$$) {
return 1;
}
###################################
sub THINKINGCLEANER_removeExtension($) {
my ($link) = @_;
@ -1034,13 +977,88 @@ sub THINKINGCLEANER_removeExtension($) {
delete $data{FWEXT}{$url};
}
############################################################################################################
#
# Begin of helper functions
#
############################################################################################################
sub THINKINGCLEANER_CGI() {
my ($request) = @_;
# data received
if ( defined( $FW_httpheader{UUID} ) ) {
if ( defined( $modules{THINKINGCLEANER}{defptr} ) ) {
while ( my ( $key, $value ) =
each %{ $modules{THINKINGCLEANER}{defptr} } )
{
my $uuid = ReadingsVal( $key, "uuid", undef );
next if ( !$uuid || $uuid ne $FW_httpheader{UUID} );
$defs{$key}{WEBHOOK_COUNTER}++;
$defs{$key}{WEBHOOK_LAST} = TimeNow();
Log3 $key, 4,
"THINKINGCLEANER $key: Received webhook for matching UUID at device $key";
my $delay = undef;
# we need some delay as to the Robo seems to send webhooks but it's status does
# not really reflect the change we'd expect to get here already so give 'em some
# more time to think about it...
$delay = "2"
if ( defined( $defs{$key}{LAST_COMMAND} )
&& time() - time_str2num( $defs{$key}{LAST_COMMAND} ) < 3 );
THINKINGCLEANER_GetStatus( $defs{$key}, $delay );
last;
}
}
return ( undef, undef );
}
# no data received
else {
Log3 undef, 5, "THINKINGCLEANER: received malformed request\n$request";
}
return ( "text/plain; charset=utf-8", "Call failure: " . $request );
}
sub THINKINGCLEANER_GetStatus($;$) {
my ( $hash, $delay ) = @_;
my $name = $hash->{NAME};
$hash->{INTERVAL_MULTIPLIER} = (
ReadingsVal( $name, "state", "off" ) ne "off"
&& ReadingsVal( $name, "state", "absent" ) ne "absent"
&& ReadingsVal( $name, "state", "standby" ) ne "standby"
? AttrVal( $name, "pollMultiplierCleaning", "0.5" )
: (
$hash->{WEBHOOK_REGISTER} eq "success"
? AttrVal( $name, "pollMultiplierWebhook", "2" )
: "1"
)
);
$hash->{INTERVAL} =
AttrVal( $name, "pollInterval", "45" ) * $hash->{INTERVAL_MULTIPLIER};
my $interval = (
$delay
? $delay
: $hash->{INTERVAL}
);
Log3 $name, 5,
"THINKINGCLEANER $name: called function THINKINGCLEANER_GetStatus()";
RemoveInternalTimer($hash);
InternalTimer( gettimeofday() + $interval,
"THINKINGCLEANER_GetStatus", $hash, 0 );
return
if ( $delay || AttrVal( $name, "disable", 0 ) == 1 );
THINKINGCLEANER_SendCommand( $hash, "full_status.json" );
return;
}
###################################
sub THINKINGCLEANER_SendCommand($$;$$) {
my ( $hash, $service, $cmd, $type ) = @_;
my $name = $hash->{NAME};
@ -1166,7 +1184,6 @@ sub THINKINGCLEANER_SendCommand($$;$$) {
return;
}
###################################
sub THINKINGCLEANER_ReceiveCommand($$$) {
my ( $param, $err, $data ) = @_;
my $hash = $param->{hash};
@ -1728,73 +1745,6 @@ sub THINKINGCLEANER_ReceiveCommand($$$) {
return;
}
###################################
sub THINKINGCLEANER_CGI() {
my ($request) = @_;
# data received
if ( defined( $FW_httpheader{UUID} ) ) {
if ( defined( $modules{THINKINGCLEANER}{defptr} ) ) {
while ( my ( $key, $value ) =
each %{ $modules{THINKINGCLEANER}{defptr} } )
{
my $uuid = ReadingsVal( $key, "uuid", undef );
next if ( !$uuid || $uuid ne $FW_httpheader{UUID} );
$defs{$key}{WEBHOOK_COUNTER}++;
$defs{$key}{WEBHOOK_LAST} = TimeNow();
Log3 $key, 4,
"THINKINGCLEANER $key: Received webhook for matching UUID at device $key";
my $delay = undef;
# we need some delay as to the Robo seems to send webhooks but it's status does
# not really reflect the change we'd expect to get here already so give 'em some
# more time to think about it...
$delay = "2"
if ( defined( $defs{$key}{LAST_COMMAND} )
&& time() - time_str2num( $defs{$key}{LAST_COMMAND} ) < 3 );
THINKINGCLEANER_GetStatus( $defs{$key}, $delay );
last;
}
}
return ( undef, undef );
}
# no data received
else {
Log3 undef, 5, "THINKINGCLEANER: received malformed request\n$request";
}
return ( "text/plain; charset=utf-8", "Call failure: " . $request );
}
###################################
sub THINKINGCLEANER_Undefine($$$) {
my ( $hash, $a, $h ) = @_;
my $name = $hash->{NAME};
if ( defined( $hash->{fhem}{infix} ) ) {
THINKINGCLEANER_removeExtension( $hash->{fhem}{infix} );
}
Log3 $name, 5,
"THINKINGCLEANER $name: called function THINKINGCLEANER_Undefine()";
# Stop the internal GetStatus-Loop and exit
RemoveInternalTimer($hash);
# release reverse pointer
delete $modules{THINKINGCLEANER}{defptr}{$name};
return;
}
###################################
sub THINKINGCLEANER_time2sec($) {
my ($timeString) = @_;
my @time = split /:/, $timeString;
@ -1802,7 +1752,6 @@ sub THINKINGCLEANER_time2sec($) {
return $time[0] * 3600 + $time[1] * 60;
}
###################################
sub THINKINGCLEANER_sec2time($) {
my ($sec) = @_;
@ -1818,6 +1767,7 @@ sub THINKINGCLEANER_sec2time($) {
return "$hours:$minutes:$seconds";
}
1;
=pod

View File

@ -1,41 +1,11 @@
###############################################################################
# $Id$
##############################################################################
#
# 97_msgConfig.pm
# Global configuration settings for FHEM msg command.
#
# Copyright by Julian Pawlowski
# e-mail: julian.pawlowski at gmail.com
#
# 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 <http://www.gnu.org/licenses/>.
#
##############################################################################
package main;
use strict;
use warnings;
use Data::Dumper;
sub msgConfig_Set($@);
sub msgConfig_Get($@);
sub msgConfig_Define($$);
sub msgConfig_Undefine($$);
###################################
# initialize ##################################################################
sub msgConfig_Initialize($) {
my ($hash) = @_;
@ -173,7 +143,7 @@ sub msgConfig_Initialize($) {
}
}
###################################
# regular Fn ##################################################################
sub msgConfig_Define($$) {
my ( $hash, $def ) = @_;
@ -212,7 +182,6 @@ sub msgConfig_Define($$) {
return undef;
}
###################################
sub msgConfig_Undefine($$) {
my ( $hash, $name ) = @_;
@ -223,7 +192,6 @@ sub msgConfig_Undefine($$) {
return undef;
}
###################################
sub msgConfig_Set($@) {
my ( $hash, @a ) = @_;
my $name = $hash->{NAME};
@ -275,7 +243,7 @@ sub msgConfig_Set($@) {
$attr{$device}{userattr} .= " msgLocationName"
if ( defined( $attr{$device}{userattr} )
&& $attr{$device}{userattr} !~
/^msgLocationName$|^msgLocationName\s|\smsgLocationName\s|\smsgLocationName$/
m/^msgLocationName$|^msgLocationName\s|\smsgLocationName\s|\smsgLocationName$/
);
$attr{$device}{userattr} = "msgLocationName"
if ( !defined( $attr{$device}{userattr} ) );
@ -399,9 +367,10 @@ sub msgConfig_Set($@) {
return
"Unknown argument $what, choose one of cleanReadings addLocation createSwitcherDev:de,en createResidentsDev:de,en";
}
return undef;
}
###################################
sub msgConfig_Get($@) {
my ( $hash, @a ) = @_;
my $name = $hash->{NAME};
@ -578,9 +547,11 @@ sub msgConfig_Get($@) {
return
"Unknown argument $what, choose one of routeCmd:,audio,light,mail,push,screen,queue";
}
return undef;
}
########################################
# module Fn ####################################################################
sub MSG_FindAttrVal($$$$) {
my ( $d, $n, $msgType, $default ) = @_;
$msgType = "" unless ($msgType);
@ -636,7 +607,6 @@ sub MSG_FindAttrVal($$$$) {
);
}
########################################
sub msgConfig_FindReadingsVal($$$$) {
my ( $d, $n, $msgType, $default ) = @_;
$msgType = ucfirst($msgType) if ($msgType);
@ -664,7 +634,6 @@ sub msgConfig_FindReadingsVal($$$$) {
);
}
########################################
sub msgConfig_QueueAdd(@) {
my (
$msgA, $params, $datetime, $msgID,
@ -703,7 +672,6 @@ sub msgConfig_QueueAdd(@) {
return 1;
}
########################################
sub msgConfig_QueueReleaseMsgId($$) {
my ( $recipient, $msgID ) = @_;

View File

@ -1,59 +1,18 @@
################################################################################
###############################################################################
# $Id$
##############################################################################
#
# 98_powerMap.pm
# Original version by igami
#
# Copyright by Julian Pawlowski
# e-mail: julian.pawlowski at gmail.com
#
# 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 <http://www.gnu.org/licenses/>.
#
################################################################################
# TODO
# - document how to include powerMap for other module maintainers
# (see 50_HP1000)
#
package main;
use strict;
use warnings;
use Data::Dumper;
use Unit;
# forward declarations #########################################################
sub powerMap_Initialize($);
sub powerMap_Define($$);
sub powerMap_Undefine($$);
sub powerMap_Set($@);
sub powerMap_Get($@);
sub powerMap_Attr(@);
sub powerMap_Notify($$);
sub powerMap_AttrVal($$$$);
sub powerMap_load($$;$$);
sub powerMap_unload($$);
sub powerMap_findPowerMaps($;$);
sub powerMap_verifyEventChain($$$);
sub powerMap_power($$$;$);
sub powerMap_energy($$;$);
sub powerMap_update($;$);
# module hashes ################################################################
# module hashes ###############################################################
my %powerMap_tmpl = (
# Format example for devices w/ model support:
@ -540,7 +499,7 @@ my %powerMap_tmpl = (
},
);
# initialize ###################################################################
# initialize ##################################################################
sub powerMap_Initialize($) {
my ($hash) = @_;
my $TYPE = "powerMap";
@ -553,7 +512,7 @@ sub powerMap_Initialize($) {
$hash->{NotifyFn} = $TYPE . "_Notify";
$hash->{AttrList} =
"disable:1,0 "
"disable:1,0 disabledForIntervals do_not_notify:1,0 "
. $TYPE
. "_gridV:230,110 "
. $TYPE
@ -568,7 +527,7 @@ sub powerMap_Initialize($) {
addToAttrList( $TYPE . ":textField-long" );
}
# regular Fn ###################################################################
# regular Fn ##################################################################
sub powerMap_Define($$) {
my ( $hash, $def ) = @_;
my ( $name, $type, $rest ) = split( /[\s]+/, $def, 3 );
@ -747,7 +706,7 @@ sub powerMap_Notify($$) {
next unless ( defined($event) );
# initialize or terminate powerMap for each device
if ( $event =~ /^(INITIALIZED|SHUTDOWN)$/ ) {
if ( $event =~ /^(INITIALIZED|REREADCFG|SHUTDOWN)$/ ) {
foreach ( keys %{ powerMap_findPowerMaps( $name, ":PM_$1" ) } )
{
next
@ -1337,6 +1296,8 @@ sub powerMap_verifyEventChain($$$) {
return 1;
}
sub powerMap_power($$$;$);
sub powerMap_power($$$;$) {
my ( $name, $dev, $event, $loop ) = @_;
my $hash = $defs{$name};
@ -1579,7 +1540,7 @@ sub powerMap_update($;$) {
1;
# commandref ###################################################################
# commandref ##################################################################
=pod
=item helper

View File

@ -1,34 +1,9 @@
###############################################################################
# $Id$
##############################################################################
#
# ONKYOdb.pm
# ONKYO command database for ONKYO AVR module to split DB from code
#
# Copyright by Julian Pawlowski
# e-mail: julian.pawlowski at gmail.com
#
# 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 <http://www.gnu.org/licenses/>.
#
##############################################################################
sub ONKYOdb_Initialize() {
}
package main;
sub ONKYOdb_Initialize() { }
package ONKYOdb;
use strict;
use warnings;

View File

@ -541,10 +541,10 @@ return;;\
Log3 $NAME, 3,
"RESIDENTStk $NAME: "
. "new notify macro device $macroRNameGotosleep created";
fhem
"define $macroRNameGotosleep notify $macroRNameGotosleep $templateGotosleep";
fhem
"attr $macroRNameGotosleep comment Auto-created by RESIDENTS Toolkit: FHEM commands to run when all residents are gettin' ready for bed";
fhem "define $macroRNameGotosleep "
. "notify $macroRNameGotosleep $templateGotosleep";
fhem "attr $macroRNameGotosleep "
. "comment Auto-created by RESIDENTS Toolkit: FHEM commands to run when all residents are gettin' ready for bed";
fhem "attr $macroRNameGotosleep room $room"
if ($room);
}
@ -2061,15 +2061,19 @@ sub RESIDENTStk_RG_Attr(@) {
if ( $lang eq "DE" ) {
$attr{$name}{devStateIcon} =
'.*zuhause:user_available:absent .*anwesend:user_available:absent .*abwesend:user_away:home .*verreist:user_ext_away:home .*bettfertig:scene_toilet:asleep .*schlaeft:scene_sleeping:awoken .*schläft:scene_sleeping:awoken .*aufgestanden:scene_sleeping_alternat:home .*:user_unknown:home';
'.*zuhause:user_available:absent '
. '.*anwesend:user_available:absent .*abwesend:user_away:home .*verreist:user_ext_away:home .*bettfertig:scene_toilet:asleep .*schlaeft:scene_sleeping:awoken .*schläft:scene_sleeping:awoken .*aufgestanden:scene_sleeping_alternat:home .*:user_unknown:home';
$attr{$name}{eventMap} =
"home:zuhause absent:abwesend gone:verreist gotosleep:bettfertig asleep:schläft awoken:aufgestanden";
"home:zuhause absent:abwesend gone:verreist "
. "gotosleep:bettfertig asleep:schläft awoken:aufgestanden";
$attr{$name}{widgetOverride} =
"state:zuhause,bettfertig,schläft,aufgestanden,abwesend,verreist";
"state:zuhause,bettfertig,schläft,"
. "aufgestanden,abwesend,verreist";
}
elsif ( $lang eq "EN" ) {
$attr{$name}{devStateIcon} =
'.*home:user_available:absent .*absent:user_away:home .*gone:user_ext_away:home .*gotosleep:scene_toilet:asleep .*asleep:scene_sleeping:awoken .*awoken:scene_sleeping_alternat:home .*:user_unknown:home';
'.*home:user_available:absent .*absent:user_away:home '
. '.*gone:user_ext_away:home .*gotosleep:scene_toilet:asleep .*asleep:scene_sleeping:awoken .*awoken:scene_sleeping_alternat:home .*:user_unknown:home';
delete $attr{$name}{eventMap}
if ( defined( $attr{$name}{eventMap} ) );
delete $attr{$name}{widgetOverride}

View File

@ -1,38 +1,9 @@
###############################################################################
# $Id$
##############################################################################
#
# msgSchema.pm
# Schema database for FHEM modules and their messaging options.
# These commands are being used as default setting for FHEM command 'msg'
# unless there is an explicit msgCmd* attribute.
#
# FHEM module authors may request to extend this file
#
# Copyright by Julian Pawlowski
# e-mail: julian.pawlowski at gmail.com
#
# 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 <http://www.gnu.org/licenses/>.
#
##############################################################################
sub msgSchema_Initialize() {
}
package main;
sub msgSchema_Initialize() { }
package msgSchema;
use strict;
use warnings;