diff --git a/fhem/FHEM/59_Wunderground.pm b/fhem/FHEM/59_Wunderground.pm index 54dd0589e..c3f77d541 100644 --- a/fhem/FHEM/59_Wunderground.pm +++ b/fhem/FHEM/59_Wunderground.pm @@ -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://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 Wunderground "; + 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 Wunderground "; - 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 diff --git a/fhem/FHEM/70_ENIGMA2.pm b/fhem/FHEM/70_ENIGMA2.pm index 804cb0d57..cdce7fb74 100644 --- a/fhem/FHEM/70_ENIGMA2.pm +++ b/fhem/FHEM/70_ENIGMA2.pm @@ -1,68 +1,28 @@ +############################################################################### # $Id$ -############################################################################## -# -# 70_ENIGMA2.pm -# An FHEM Perl module for controlling ENIGMA2 based TV 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 . -# -############################################################################## - package main; - -#use 5.012; use strict; use warnings; use Data::Dumper; +use Time::Local; +use Encode qw(encode_utf8 decode_utf8); + use HttpUtils; -use Encode; -no if $] >= 5.017011, warnings => 'experimental'; - -no warnings "all"; - -sub ENIGMA2_Set($@); -sub ENIGMA2_Get($@); -sub ENIGMA2_GetStatus($;$); -sub ENIGMA2_Define($$); -sub ENIGMA2_Undefine($$); - -######################### -# Forward declaration for remotecontrol module -sub ENIGMA2_RClayout_TV(); -sub ENIGMA2_RCmakenotify($$); - -################################### +# initialize ################################################################## sub ENIGMA2_Initialize($) { my ($hash) = @_; Log3 $hash, 5, "ENIGMA2_Initialize: Entering"; - $hash->{parseParams} = 1; - $hash->{GetFn} = "ENIGMA2_Get"; - $hash->{SetFn} = "ENIGMA2_Set"; $hash->{DefFn} = "ENIGMA2_Define"; $hash->{UndefFn} = "ENIGMA2_Undefine"; + $hash->{SetFn} = "ENIGMA2_Set"; + $hash->{GetFn} = "ENIGMA2_Get"; + $hash->{parseParams} = 1; $hash->{AttrList} = -"https:0,1 http-method:GET,POST http-noshutdown:1,0 disable:0,1 bouquet-tv bouquet-radio timeout remotecontrol:standard,advanced,keyboard lightMode:0,1 ignoreState:0,1 macaddr:textField model wakeupCmd:textField WOL_useUdpBroadcast WOL_port WOL_mode:EW,UDP,BOTH " +"disable:1,0 disabledForIntervals do_not_notify:1,0 https:0,1 http-method:GET,POST http-noshutdown:1,0 disable:0,1 bouquet-tv bouquet-radio timeout remotecontrol:standard,advanced,keyboard lightMode:0,1 ignoreState:0,1 macaddr:textField model wakeupCmd:textField WOL_useUdpBroadcast WOL_port WOL_mode:EW,UDP,BOTH " . $readingFnAttributes; $data{RC_layout}{ENIGMA2_DreamMultimedia_DM500_DM800_SVG} = @@ -76,16 +36,10 @@ sub ENIGMA2_Initialize($) { $data{RC_layout}{ENIGMA2_DreamMultimedia_RC10_SVG} = "ENIGMA2_RClayout_RC10_SVG"; $data{RC_layout}{ENIGMA2_DreamMultimedia_RC10} = "ENIGMA2_RClayout_RC10"; - -# $data{RC_layout}{ENIGMA2_VUplus_Solo2_SVG} = "ENIGMA2_RClayout_VUplusSolo2_SVG"; -# $data{RC_layout}{ENIGMA2_VUplus_Solo2} = "ENIGMA2_RClayout_VUplusSolo2"; $data{RC_layout}{ENIGMA2_VUplus_Duo2_SVG} = "ENIGMA2_RClayout_VUplusDuo2_SVG"; $data{RC_layout}{ENIGMA2_VUplus_Duo2} = "ENIGMA2_RClayout_VUplusDuo2"; - -# $data{RC_layout}{ENIGMA2_VUplus_Ultimo_SVG} = "ENIGMA2_RClayout_VUplusUltimo_SVG"; -# $data{RC_layout}{ENIGMA2_VUplus_Ultimo} = "ENIGMA2_RClayout_VUplusUltimo"; - $data{RC_makenotify}{ENIGMA2} = "ENIGMA2_RCmakenotify"; + $data{RC_makenotify}{ENIGMA2} = "ENIGMA2_RCmakenotify"; # 98_powerMap.pm support $hash->{powerMap} = { @@ -104,76 +58,105 @@ sub ENIGMA2_Initialize($) { }, }, }; - - return; } -##################################### -sub ENIGMA2_GetStatus($;$) { - my ( $hash, $update ) = @_; - my $name = $hash->{NAME}; - my $interval = $hash->{INTERVAL}; - - Log3 $name, 5, "ENIGMA2 $name: called function ENIGMA2_GetStatus()"; - - RemoveInternalTimer($hash); - InternalTimer( gettimeofday() + $interval, "ENIGMA2_GetStatus", $hash, 0 ); - - return - if ( AttrVal( $name, "disable", 0 ) == 1 ); - - if ( !$update ) { - ENIGMA2_SendCommand( $hash, "powerstate" ); - } - else { - ENIGMA2_SendCommand( $hash, "getcurrent" ); - } - - return; -} - -################################### -sub ENIGMA2_Get($@) { +# regular Fn ################################################################## +sub ENIGMA2_Define($$) { my ( $hash, $a, $h ) = @_; my $name = shift @$a; - my $what; + my $type = shift @$a; - Log3 $name, 5, "ENIGMA2 $name: called function ENIGMA2_Get()"; + Log3 $name, 5, "ENIGMA2 $name: called function ENIGMA2_Define()"; - return "argument is missing" if ( int(@$a) < 1 ); + eval { require XML::Simple; }; + return "Please install Perl XML::Simple to use module ENIGMA2" + if ($@); - $what = shift @$a; + if ( int(@$a) < 1 ) { + my $msg = + "Wrong syntax: " + . "define ENIGMA2 [[[[] []] []]"; + Log3 $name, 4, $msg; + return $msg; + } - if ( $what =~ -/^(power|input|volume|mute|channel|currentMedia|currentTitle|nextTitle|providername|servicevideosize)$/ - ) - { - if ( ReadingsVal( $name, $what, "" ) ne "" ) { - return ReadingsVal( $name, $what, "" ); + $hash->{URL} = shift @$a; + + # use port 80 if not defined + my $port = shift @$a || 80; + return "Port parameter needs to be of type integer" + unless ( $port =~ /^\d+$/ ); + + # use interval of 45sec if not defined + my $interval = shift @$a || 45; + return "Interval parameter needs to be of type integer" + unless ( $interval =~ /^\d+$/ ); + $hash->{INTERVAL} = $interval; + + my $http_user = shift @$a; + my $http_passwd = shift @$a; + $hash->{URL} = "$http_user:$http_passwd@" . $hash->{URL} + if ( $hash->{URL} !~ /^https?:\/\// + && $hash->{URL} !~ /^\w+(:\w+)?\@/ + && $http_user + && $http_passwd ); + $hash->{URL} = "$http_user@" . $hash->{URL} + if ( $hash->{URL} !~ /^https?:\/\// + && $hash->{URL} !~ /^\w+(:\w+)?\@/ + && $http_user + && !$http_passwd ); + $hash->{URL} = "http://" . $hash->{URL} + unless ( $hash->{URL} =~ /^https?:\/\// || $port eq "443" ); + $hash->{URL} = "https://" . $hash->{URL} + if ( $hash->{URL} !~ /^https?:\/\// && $port eq "443" ); + $hash->{URL} .= ":$port" + unless ( $hash->{URL} =~ /:\d+$/ || $port eq "80" || $port eq "443" ); + $hash->{URL} .= "/" unless ( $hash->{URL} =~ /\/$/ ); + + # set default settings on first define + if ( $init_done && !defined( $hash->{OLDDEF} ) ) { + + # use http-method POST for FritzBox environment as GET does not seem to + # work properly. Might restrict use to newer + # ENIGMA2 Webif versions or use of OWIF only. + if ( exists $ENV{CONFIG_PRODUKT_NAME} + && defined $ENV{CONFIG_PRODUKT_NAME} ) + { + $attr{$name}{"http-method"} = 'POST'; } + + # default method is GET and should be compatible to most + # ENIGMA2 Webif versions else { - return "no such reading: $what"; + $attr{$name}{"http-method"} = 'GET'; } + $attr{$name}{webCmd} = 'channel:input'; + $attr{$name}{devStateIcon} = + 'on:rc_GREEN:off off:rc_YELLOW:on absent:rc_STOP:on'; + $attr{$name}{icon} = 'dreambox'; } - # streamUrl - elsif ( $what eq "streamUrl" ) { - my $device = "etc"; - $device = "phone" if ( defined $a->[0] ); - return - $hash->{URL} - . "/web/stream.m3u?ref=" - . urlEncode( ReadingsVal( $name, "servicereference", "-" ) ) - . "&device=$device"; - } + # start the status update timer + RemoveInternalTimer($hash); + InternalTimer( gettimeofday() + 2, "ENIGMA2_GetStatus", $hash, 1 ); - else { - return -"Unknown argument $what, choose one of power:noArg input:noArg volume:noArg mute:noArg channel:noArg currentMedia:noArg currentTitle:noArg nextTitle:noArg providername:noArg servicevideosize:noArg streamUrl:,mobile "; - } + return undef; } -################################### +sub ENIGMA2_Undefine($$) { + my ( $hash, $arg ) = @_; + my $name = $hash->{NAME}; + + Log3 $name, 5, "ENIGMA2 $name: called function ENIGMA2_Undefine()"; + + # Stop the internal GetStatus-Loop and exit + RemoveInternalTimer($hash); + + return undef; +} + +sub ENIGMA2_Set($@); + sub ENIGMA2_Set($@) { my ( $hash, $a, $h ) = @_; my $name = shift @$a; @@ -359,8 +342,8 @@ sub ENIGMA2_Set($@) { return "wake-up command sent to MAC $macAddr"; } else { - return -"Device MAC address unknown. Please turn on the device manually once or set attribute macaddr."; + return "Device MAC address unknown. " + . "Please turn on the device manually once or set attribute macaddr."; } } else { @@ -423,8 +406,8 @@ sub ENIGMA2_Set($@) { $cmd = "set=set" . $a->[0]; } else { - return -"Argument does not seem to be a valid integer between 0 and 100"; + return "Argument does not seem to be a " + . "valid integer between 0 and 100"; } $result = ENIGMA2_SendCommand( $hash, "vol", $cmd ); } @@ -507,8 +490,8 @@ sub ENIGMA2_Set($@) { $timeout = $timeout2; } - return -"No type argument given, choose one of yesno info message attention" + return "No type argument given, " + . "choose one of yesno info message attention" unless ( defined($type) ); return "No timeout argument given" @@ -605,8 +588,8 @@ sub ENIGMA2_Set($@) { # channel elsif ( lc($set) eq "channel" ) { - return -"No argument given, choose one of channel channelNumber servicereference " + return "No argument given, " + . "choose one of channel channelNumber servicereference " if ( !defined( $a->[0] ) ); if ( defined( $a->[0] ) @@ -773,98 +756,52 @@ sub ENIGMA2_Set($@) { return $usage; } - return; + return undef; } -################################### -sub ENIGMA2_Define($$) { +sub ENIGMA2_Get($@) { my ( $hash, $a, $h ) = @_; my $name = shift @$a; - my $type = shift @$a; + my $what; - Log3 $name, 5, "ENIGMA2 $name: called function ENIGMA2_Define()"; + Log3 $name, 5, "ENIGMA2 $name: called function ENIGMA2_Get()"; - eval { require XML::Simple; }; - return "Please install Perl XML::Simple to use module ENIGMA2" - if ($@); + return "argument is missing" if ( int(@$a) < 1 ); - if ( int(@$a) < 1 ) { - my $msg = -"Wrong syntax: define ENIGMA2 [[[[] []] []]"; - Log3 $name, 4, $msg; - return $msg; - } + $what = shift @$a; - $hash->{URL} = shift @$a; - - # use port 80 if not defined - my $port = shift @$a || 80; - return "Port parameter needs to be of type integer" - unless ( $port =~ /^\d+$/ ); - - # use interval of 45sec if not defined - my $interval = shift @$a || 45; - return "Interval parameter needs to be of type integer" - unless ( $interval =~ /^\d+$/ ); - $hash->{INTERVAL} = $interval; - - my $http_user = shift @$a; - my $http_passwd = shift @$a; - $hash->{URL} = "$http_user:$http_passwd@" . $hash->{URL} - if ( $hash->{URL} !~ /^https?:\/\// - && $hash->{URL} !~ /^\w+(:\w+)?\@/ - && $http_user - && $http_passwd ); - $hash->{URL} = "$http_user@" . $hash->{URL} - if ( $hash->{URL} !~ /^https?:\/\// - && $hash->{URL} !~ /^\w+(:\w+)?\@/ - && $http_user - && !$http_passwd ); - $hash->{URL} = "http://" . $hash->{URL} - unless ( $hash->{URL} =~ /^https?:\/\// || $port eq "443" ); - $hash->{URL} = "https://" . $hash->{URL} - if ( $hash->{URL} !~ /^https?:\/\// && $port eq "443" ); - $hash->{URL} .= ":$port" - unless ( $hash->{URL} =~ /:\d+$/ || $port eq "80" || $port eq "443" ); - $hash->{URL} .= "/" unless ( $hash->{URL} =~ /\/$/ ); - - # set default settings on first define - if ( $init_done && !defined( $hash->{OLDDEF} ) ) { - - # use http-method POST for FritzBox environment as GET does not seem to - # work properly. Might restrict use to newer - # ENIGMA2 Webif versions or use of OWIF only. - if ( exists $ENV{CONFIG_PRODUKT_NAME} - && defined $ENV{CONFIG_PRODUKT_NAME} ) - { - $attr{$name}{"http-method"} = 'POST'; + if ( $what =~ +/^(power|input|volume|mute|channel|currentMedia|currentTitle|nextTitle|providername|servicevideosize)$/ + ) + { + if ( ReadingsVal( $name, $what, "" ) ne "" ) { + return ReadingsVal( $name, $what, "" ); } - - # default method is GET and should be compatible to most - # ENIGMA2 Webif versions else { - $attr{$name}{"http-method"} = 'GET'; + return "no such reading: $what"; } - $attr{$name}{webCmd} = 'channel:input'; - $attr{$name}{devStateIcon} = - 'on:rc_GREEN:off off:rc_YELLOW:on absent:rc_STOP:on'; - $attr{$name}{icon} = 'dreambox'; } - # start the status update timer - RemoveInternalTimer($hash); - InternalTimer( gettimeofday() + 2, "ENIGMA2_GetStatus", $hash, 1 ); + # streamUrl + elsif ( $what eq "streamUrl" ) { + my $device = "etc"; + $device = "phone" if ( defined $a->[0] ); + return + $hash->{URL} + . "/web/stream.m3u?ref=" + . urlEncode( ReadingsVal( $name, "servicereference", "-" ) ) + . "&device=$device"; + } - return; + else { + return "Unknown argument $what, " + . "choose one of power:noArg input:noArg volume:noArg mute:noArg channel:noArg currentMedia:noArg currentTitle:noArg nextTitle:noArg providername:noArg servicevideosize:noArg streamUrl:,mobile "; + } + + return undef; } -############################################################################################################ -# -# Begin of helper functions -# -############################################################################################################ - -################################### +# module Fn #################################################################### sub ENIGMA2_SendCommand($$;$$) { my ( $hash, $service, $cmd, $type ) = @_; my $name = $hash->{NAME}; @@ -930,7 +867,7 @@ sub ENIGMA2_SendCommand($$;$$) { 'Accept-Charset' => 'UTF-8', }, sslargs => { - SSL_verify_mode => 0, + SSL_verify_mode => 'SSL_VERIFY_NONE', }, } ); @@ -967,7 +904,7 @@ sub ENIGMA2_SendCommand($$;$$) { 'Accept-Charset' => 'UTF-8', }, sslargs => { - SSL_verify_mode => 0, + SSL_verify_mode => 'SSL_VERIFY_NONE', }, } ); @@ -984,7 +921,6 @@ sub ENIGMA2_SendCommand($$;$$) { return; } -################################### sub ENIGMA2_ReceiveCommand($$$) { my ( $param, $err, $data ) = @_; my $hash = $param->{hash}; @@ -1059,7 +995,8 @@ sub ENIGMA2_ReceiveCommand($$$) { if ( !defined($cmd) || $cmd eq "" ) { Log3 $name, 5, -"ENIGMA2 $name: RES ERROR $service - unable to parse malformed XML: $@\n" + "ENIGMA2 $name: " + . "RES ERROR $service - unable to parse malformed XML: $@\n" . $data; } else { @@ -1547,7 +1484,8 @@ sub ENIGMA2_ReceiveCommand($$$) { } else { Log3 $name, 2, -"ENIGMA2 $name: ERROR: boxinfo could not be read - /about sent malformed response"; + "ENIGMA2 $name: " + . "ERROR: boxinfo could not be read - /about sent malformed response"; } } @@ -1580,7 +1518,8 @@ sub ENIGMA2_ReceiveCommand($$$) { || $return->{e2service}{$e2reading} eq "True" ) { Log3 $name, 5, -"ENIGMA2 $name: transforming value of $reading to lower case"; + "ENIGMA2 $name: " + . "transforming value of $reading to lower case"; $return->{e2service}{$e2reading} = lc( $return->{e2service}{$e2reading} ); @@ -1615,7 +1554,8 @@ sub ENIGMA2_ReceiveCommand($$$) { && $servicetype[2] ne "10" ) { Log3 $name, 5, -"ENIGMA2 $name: detected servicereference type: tv"; + "ENIGMA2 $name: " + . "detected servicereference type: tv"; readingsBulkUpdate( $hash, "input", "tv" ) if ( ReadingsVal( $name, "input", "" ) ne "tv" ); @@ -1628,19 +1568,22 @@ sub ENIGMA2_ReceiveCommand($$$) { ) { Log3 $name, 5, -"ENIGMA2 $name: detected servicereference type: radio"; + "ENIGMA2 $name: " + . "detected servicereference type: radio"; readingsBulkUpdateIfChanged( $hash, "input", "radio" ); } else { Log3 $name, 2, -"ENIGMA2 $name: ERROR: servicereference type could not be detected (neither 'tv' nor 'radio')"; + "ENIGMA2 $name: " + . "ERROR: servicereference type could not be detected (neither 'tv' nor 'radio')"; } } } else { Log3 $name, 5, -"ENIGMA2 $name: received no value for reading $reading"; + "ENIGMA2 $name: " + . "received no value for reading $reading"; if ( ReadingsVal( $name, $reading, "" ) ne "-" ) { readingsBulkUpdate( $hash, $reading, "-" ); @@ -1692,7 +1635,8 @@ sub ENIGMA2_ReceiveCommand($$$) { && $eventNow->{$e2reading} ne "" ) { Log3 $name, 5, -"ENIGMA2 $name: detected valid reading $e2reading for current event"; + "ENIGMA2 $name: " + . "detected valid reading $e2reading for current event"; if ( ReadingsVal( $name, $reading, "" ) ne $eventNow->{$e2reading} ) @@ -1708,7 +1652,8 @@ sub ENIGMA2_ReceiveCommand($$$) { } else { Log3 $name, 5, -"ENIGMA2 $name: no valid reading $e2reading found for current event"; + "ENIGMA2 $name: " + . "no valid reading $e2reading found for current event"; if ( ReadingsVal( $name, $reading, "" ) ne "-" ) { readingsBulkUpdate( $hash, $reading, "-" ); @@ -1727,7 +1672,8 @@ sub ENIGMA2_ReceiveCommand($$$) { && $eventNext->{$e2reading} ne "" ) { Log3 $name, 5, -"ENIGMA2 $name: detected valid reading $e2reading for next event"; + "ENIGMA2 $name: " + . "detected valid reading $e2reading for next event"; if ( ReadingsVal( $name, $readingN, "" ) ne $eventNext->{$e2reading} ) @@ -1743,7 +1689,8 @@ sub ENIGMA2_ReceiveCommand($$$) { } else { Log3 $name, 5, -"ENIGMA2 $name: no valid reading $e2reading found for next event"; + "ENIGMA2 $name: " + . "no valid reading $e2reading found for next event"; if ( ReadingsVal( $name, $readingN, "" ) ne "-" ) { readingsBulkUpdate( $hash, $readingN, "-" ); @@ -1823,7 +1770,8 @@ sub ENIGMA2_ReceiveCommand($$$) { } else { Log3 $name, 2, -"ENIGMA2 $name: ERROR: current service info could not be read - /getcurrent sent malformed response"; + "ENIGMA2 $name: " + . "ERROR: current service info could not be read - /getcurrent sent malformed response"; } } @@ -2018,13 +1966,13 @@ sub ENIGMA2_ReceiveCommand($$$) { $readingname = "recordings" . $ri . "_servicename"; readingsBulkUpdateIfChanged( $hash, $readingname, $2 ) if ( $recordings{$ri}{servicename} =~ -/^(\s*[\[\(\{].*[\]\)\}]\s*)?([\s\w\(\)_-]+)(\s*[\[\(\{].*[\]\)\}]\s*)?$/ +m/^(\s*[\[\(\{].*[\]\)\}]\s*)?([\s\w\(\)_-]+)(\s*[\[\(\{].*[\]\)\}]\s*)?$/ ); $readingname = "recordings" . $ri . "_name"; readingsBulkUpdateIfChanged( $hash, $readingname, $2 ) if ( $recordings{$ri}{name} =~ -/^(\s*[\[\(\{].*[\]\)\}]\s*)?([\s\w\(\)_-]+)(\s*[\[\(\{].*[\]\)\}]\s*)?$/ +m/^(\s*[\[\(\{].*[\]\)\}]\s*)?([\s\w\(\)_-]+)(\s*[\[\(\{].*[\]\)\}]\s*)?$/ ); } } @@ -2192,20 +2140,29 @@ sub ENIGMA2_ReceiveCommand($$$) { return; } -################################### -sub ENIGMA2_Undefine($$) { - my ( $hash, $arg ) = @_; - my $name = $hash->{NAME}; +sub ENIGMA2_GetStatus($;$) { + my ( $hash, $update ) = @_; + my $name = $hash->{NAME}; + my $interval = $hash->{INTERVAL}; - Log3 $name, 5, "ENIGMA2 $name: called function ENIGMA2_Undefine()"; + Log3 $name, 5, "ENIGMA2 $name: called function ENIGMA2_GetStatus()"; - # Stop the internal GetStatus-Loop and exit RemoveInternalTimer($hash); + InternalTimer( gettimeofday() + $interval, "ENIGMA2_GetStatus", $hash, 0 ); + + return + if ( AttrVal( $name, "disable", 0 ) == 1 ); + + if ( !$update ) { + ENIGMA2_SendCommand( $hash, "powerstate" ); + } + else { + ENIGMA2_SendCommand( $hash, "getcurrent" ); + } return; } -################################### sub ENIGMA2_GetStateAV($) { my ($hash) = @_; my $name = $hash->{NAME}; @@ -2227,316 +2184,6 @@ sub ENIGMA2_GetStateAV($) { } } -################################### -sub ENIGMA2_wake ($$) { - if ( !$modules{WOL}{LOADED} - && -f "$attr{global}{modpath}/FHEM/98_WOL.pm" ) - { - my $ret = CommandReload( undef, "98_WOL" ); - return $ret if ($ret); - } - elsif ( !-f "$attr{global}{modpath}/FHEM/98_WOL.pm" ) { - return "Missing module: $attr{global}{modpath}/FHEM/98_WOL.pm"; - } - - my ( $name, $mac ) = @_; - my $hash = $defs{$name}; - my $host = - AttrVal( $name, "WOL_useUdpBroadcast", - AttrVal( $name, "useUdpBroadcast", "255.255.255.255" ) ); - my $port = AttrVal( $name, "WOL_port", "9" ); - my $mode = lc( AttrVal( $name, "WOL_mode", "UDP" ) ); - - Log3 $name, 4, - "ENIGMA2 $name: Waking up by sending Wake-On-Lan magic package to " - . $mac; - - if ( $mode eq "both" || $mode eq "ew" ) { - WOL_by_ew( $hash, $mac ); - } - if ( $mode eq "both" || $mode eq "udp" ) { - WOL_by_udp( $hash, $mac, $host, $port ); - } -} - -##################################### -# Callback from 95_remotecontrol for command makenotify. -sub ENIGMA2_RCmakenotify($$) { - my ( $nam, $ndev ) = @_; - my $nname = "notify_$nam"; - - fhem( "define $nname notify $nam set $ndev remoteControl " . '$EVENT', 1 ); - Log3 undef, 2, "[remotecontrol:ENIGMA2] Notify created: $nname"; - return "Notify created by ENIGMA2: $nname"; -} - -##################################### -# RC layouts - -# Dreambox DM500 + DM800 with SVG -sub ENIGMA2_RClayout_DM800_SVG() { - my @row; - - $row[0] = ":rc_BLANK.svg,:rc_BLANK.svg,POWER:rc_POWER.svg"; - $row[1] = ":rc_BLANK.svg,:rc_BLANK.svg,:rc_BLANK.svg"; - - $row[2] = "1:rc_1.svg,2:rc_2.svg,3:rc_3.svg"; - $row[3] = "4:rc_4.svg,5:rc_5.svg,6:rc_6.svg"; - $row[4] = "7:rc_7.svg,8:rc_8.svg,9:rc_9.svg"; - $row[5] = "LEFTBRACE:rc_PREVIOUS.svg,0:rc_0.svg,RIGHTBRACE:rc_NEXT.svg"; - $row[6] = ":rc_BLANK.svg,:rc_BLANK.svg,:rc_BLANK.svg"; - - $row[7] = "VOLUMEUP:rc_VOLPLUS.svg,MUTE:rc_MUTE.svg,CHANNELUP:rc_UP.svg"; - $row[8] = - "VOLUMEDOWN:rc_VOLMINUS.svg,EXIT:rc_EXIT.svg,CHANNELDOWN:rc_DOWN.svg"; - $row[9] = ":rc_BLANK.svg,:rc_BLANK.svg,:rc_BLANK.svg"; - - $row[10] = "INFO:rc_INFO.svg,UP:rc_UP.svg,MENU:rc_MENU.svg"; - $row[11] = "LEFT:rc_LEFT.svg,OK:rc_OK.svg,RIGHT:rc_RIGHT.svg"; - $row[12] = "AUDIO:rc_AUDIO.svg,DOWN:rc_DOWN.svg,VIDEO:rc_VIDEO.svg"; - $row[13] = ":rc_BLANK.svg,EXIT:rc_EXIT.svg,:rc_BLANK.svg"; - - $row[14] = -"RED:rc_REWred.svg,GREEN:rc_PLAYgreen.svg,YELLOW:rc_PAUSEyellow.svg,BLUE:rc_FFblue.svg"; - $row[15] = -"TV:rc_TVstop.svg,RADIO:rc_RADIOred.svg,TEXT:rc_TEXT.svg,HELP:rc_HELP.svg"; - - $row[16] = "attr rc_iconpath icons/remotecontrol"; - $row[17] = "attr rc_iconprefix black_btn_"; - return @row; -} - -# Dreambox DM500 + DM800 with PNG -sub ENIGMA2_RClayout_DM800() { - my @row; - - $row[0] = ":blank,:blank,POWER:POWEROFF"; - $row[1] = ":blank,:blank,:blank"; - - $row[2] = "1,2,3"; - $row[3] = "4,5,6"; - $row[4] = "7,8,9"; - $row[5] = "LEFTBRACE:LEFT2,0:0,RIGHTBRACE:RIGHT2"; - $row[6] = ":blank,:blank,:blank"; - - $row[7] = "VOLUMEUP:VOLUP,MUTE,CHANNELUP:CHUP2"; - $row[8] = "VOLUMEDOWN:VOLDOWN,EXIT,CHANNELDOWN:CHDOWN2"; - $row[9] = ":blank,:blank,:blank"; - - $row[10] = "INFO,UP,MENU"; - $row[11] = "LEFT,OK,RIGHT"; - $row[12] = "AUDIO,DOWN,VIDEO"; - $row[13] = ":blank,:blank,:blank"; - - $row[14] = "RED:REWINDred,GREEN:PLAYgreen,YELLOW:PAUSEyellow,BLUE:FFblue"; - $row[15] = "TV:TVstop,RADIO:RADIOred,TEXT,HELP"; - - $row[16] = "attr rc_iconpath icons/remotecontrol"; - $row[17] = "attr rc_iconprefix black_btn_"; - return @row; -} - -# Dreambox DM800se + DM8000 with SVG -sub ENIGMA2_RClayout_DM8000_SVG() { - my @row; - - $row[0] = ":rc_BLANK.svg,:rc_BLANK.svg,POWER:rc_POWER.svg"; - $row[1] = ":rc_BLANK.svg,:rc_BLANK.svg,:rc_BLANK.svg"; - - $row[2] = "1:rc_1.svg,2:rc_2.svg,3:rc_3.svg"; - $row[3] = "4:rc_4.svg,5:rc_5.svg,6:rc_6.svg"; - $row[4] = "7:rc_7.svg,8:rc_8.svg,9:rc_9.svg"; - $row[5] = "LEFTBRACE:rc_PREVIOUS.svg,0:rc_0.svg,RIGHTBRACE:rc_NEXT.svg"; - $row[6] = ":rc_BLANK.svg,:rc_BLANK.svg,:rc_BLANK.svg"; - - $row[7] = "VOLUMEUP:rc_VOLPLUS.svg,MUTE:rc_MUTE.svg,CHANNELUP:rc_UP.svg"; - $row[8] = - "VOLUMEDOWN:rc_VOLMINUS.svg,EXIT:rc_EXIT.svg,CHANNELDOWN:rc_DOWN.svg"; - $row[9] = ":rc_BLANK.svg,:rc_BLANK.svg,:rc_BLANK.svg"; - - $row[10] = "INFO:rc_INFO.svg,UP:rc_UP.svg,MENU:rc_MENU.svg"; - $row[11] = "LEFT:rc_LEFT.svg,OK:rc_OK.svg,RIGHT:rc_RIGHT.svg"; - $row[12] = "AUDIO:rc_AUDIO.svg,DOWN:rc_DOWN.svg,VIDEO:rc_VIDEO.svg"; - $row[13] = ":rc_BLANK.svg,EXIT:rc_EXIT.svg,:rc_BLANK.svg"; - - $row[14] = - "RED:rc_RED.svg,GREEN:rc_GREEN.svg,YELLOW:rc_YELLOW.svg,BLUE:rc_BLUE.svg"; - $row[15] = -"REWIND:rc_REW.svg,PLAY:rc_PLAY.svg,STOP:rc_STOP.svg,FASTFORWARD:rc_FF.svg"; - $row[16] = - "TV:rc_TV.svg,RADIO:rc_RADIO.svg,TEXT:rc_TEXT.svg,RECORD:rc_REC.svg"; - - $row[17] = "attr rc_iconpath icons/remotecontrol"; - $row[18] = "attr rc_iconprefix black_btn_"; - return @row; -} - -# Dreambox DM800se + DM8000 with PNG -sub ENIGMA2_RClayout_DM8000() { - my @row; - - $row[0] = ":blank,:blank,POWER:POWEROFF"; - $row[1] = ":blank,:blank,:blank"; - - $row[2] = "1,2,3"; - $row[3] = "4,5,6"; - $row[4] = "7,8,9"; - $row[5] = "LEFTBRACE:LEFT2,0:0,RIGHTBRACE:RIGHT2"; - $row[6] = ":blank,:blank,:blank"; - - $row[7] = "VOLUMEUP:VOLUP,MUTE,CHANNELUP:CHUP2"; - $row[8] = "VOLUMEDOWN:VOLDOWN,EXIT,CHANNELDOWN:CHDOWN2"; - $row[9] = ":blank,:blank,:blank"; - - $row[10] = "INFO,UP,MENU"; - $row[11] = "LEFT,OK,RIGHT"; - $row[12] = "AUDIO,DOWN,VIDEO"; - $row[13] = ":blank,:blank,:blank"; - - $row[14] = "RED,GREEN,YELLOW,BLUE"; - $row[15] = "REWIND,PLAY,STOP,FASTFORWARD:FF"; - $row[16] = "TV,RADIO,TEXT,RECORD:REC"; - - $row[17] = "attr rc_iconpath icons/remotecontrol"; - $row[18] = "attr rc_iconprefix black_btn_"; - return @row; -} - -# Dreambox RC10 with SVG -sub ENIGMA2_RClayout_RC10_SVG() { - my @row; - - $row[0] = ":rc_BLANK.svg,:rc_BLANK.svg,POWER:rc_POWER.svg"; - $row[1] = ":rc_BLANK.svg,:rc_BLANK.svg,:rc_BLANK.svg"; - - $row[2] = "1:rc_1.svg,2:rc_2.svg,3:rc_3.svg"; - $row[3] = "4:rc_4.svg,5:rc_5.svg,6:rc_6.svg"; - $row[4] = "7:rc_7.svg,8:rc_8.svg,9:rc_9.svg"; - $row[5] = "LEFTBRACE:rc_PREVIOUS.svg,0:rc_0.svg,RIGHTBRACE:rc_NEXT.svg"; - $row[6] = - "RED:rc_RED.svg,GREEN:rc_GREEN.svg,YELLOW:rc_YELLOW.svg,BLUE:rc_BLUE.svg"; - $row[7] = ":rc_BLANK.svg,:rc_BLANK.svg,:rc_BLANK.svg"; - - $row[8] = "INFO:rc_INFO.svg,UP:rc_UP.svg,MENU:rc_MENU.svg"; - $row[9] = "LEFT:rc_LEFT.svg,OK:rc_OK.svg,RIGHT:rc_RIGHT.svg"; - $row[10] = "AUDIO:rc_AUDIO.svg,DOWN:rc_DOWN.svg,VIDEO:rc_VIDEO.svg"; - $row[11] = ":rc_BLANK.svg,EXIT:rc_EXIT.svg,:rc_BLANK.svg"; - - $row[12] = "VOLUMEUP:rc_VOLPLUS.svg,:rc_BLANK.svg,CHANNELUP:rc_UP.svg"; - $row[13] = - "VOLUMEDOWN:rc_VOLMINUS.svg,MUTE:rc_MUTE.svg,CHANNELDOWN:rc_DOWN.svg"; - $row[14] = ":rc_BLANK.svg,:rc_BLANK.svg,:rc_BLANK.svg"; - - $row[15] = -"REWIND:rc_REW.svg,PLAY:rc_PLAY.svg,STOP:rc_STOP.svg,FASTFORWARD:rc_FF.svg"; - $row[16] = - "TV:rc_TV.svg,RADIO:rc_RADIO.svg,TEXT:rc_TEXT.svg,RECORD:rc_REC.svg"; - - $row[17] = "attr rc_iconpath icons"; - $row[18] = "attr rc_iconprefix rc_"; - return @row; -} - -# Dreambox RC10 with PNG -sub ENIGMA2_RClayout_RC10() { - my @row; - - $row[0] = ":blank,:blank,POWER:POWEROFF"; - $row[1] = ":blank,:blank,:blank"; - - $row[2] = "1,2,3"; - $row[3] = "4,5,6"; - $row[4] = "7,8,9"; - $row[5] = "LEFTBRACE:LEFT2,0:0,RIGHTBRACE:RIGHT2"; - $row[6] = "RED,GREEN,YELLOW,BLUE"; - $row[7] = ":blank,:blank,:blank"; - - $row[8] = "INFO,UP,MENU"; - $row[9] = "LEFT,OK,RIGHT"; - $row[10] = "AUDIO,DOWN,VIDEO"; - $row[11] = ":blank,EXIT,:blank"; - - $row[12] = "VOLUMEUP:VOLUP,:blank,CHANNELUP:CHUP2"; - $row[13] = "VOLUMEDOWN:VOLDOWN,MUTE,CHANNELDOWN:CHDOWN2"; - $row[14] = ":blank,:blank,:blank"; - - $row[15] = "REWIND,PLAY,STOP,FASTFORWARD:FF"; - $row[16] = "TV,RADIO,TEXT,RECORD:REC"; - - $row[17] = "attr rc_iconpath icons/remotecontrol"; - $row[18] = "attr rc_iconprefix black_btn_"; - return @row; -} - -# VU+ Duo2 with SVG -sub ENIGMA2_RClayout_VUplusDuo2_SVG() { - my @row; - - $row[0] = ":rc_BLANK.svg,MUTE:rc_MUTE.svg,POWER:rc_POWER.svg"; - $row[1] = ":rc_BLANK.svg,:rc_BLANK.svg,:rc_BLANK.svg"; - - $row[2] = "REWIND:rc_REW.svg,PLAY:rc_PLAY.svg,FASTFORWARD:rc_FF.svg"; - $row[3] = "RECORD:rc_REC.svg,STOP:rc_STOP.svg,VIDEO:rc_VIDEO.svg"; - $row[4] = ":rc_BLANK.svg,:rc_BLANK.svg,:rc_BLANK.svg"; - - $row[5] = "TV:rc_TV.svg,AUDIO:rc_AUDIO.svg,RADIO:rc_RADIO.svg"; - $row[6] = "TEXT:rc_TEXT.svg,HELP:rc_HELP.svg,AV:rc_AV.svg"; - $row[7] = "INFO:rc_EPG.svg,MENU:rc_MENU.svg,EXIT:rc_EXIT.svg"; - $row[8] = ":rc_BLANK.svg,:rc_BLANK.svg,:rc_BLANK.svg"; - - $row[9] = "VOLUMEUP:rc_VOLPLUS.svg,UP:rc_UP.svg,CHANNELUP:rc_PLUS.svg"; - $row[10] = "LEFT:rc_LEFT.svg,OK:rc_OK.svg,RIGHT:rc_RIGHT.svg"; - $row[11] = - "VOLUMEDOWN:rc_VOLMINUS.svg,DOWN:rc_DOWN.svg,CHANNELDOWN:rc_MINUS.svg"; - - $row[12] = ":rc_BLANK.svg,:rc_BLANK.svg,:rc_BLANK.svg"; - - $row[13] = - "RED:rc_RED.svg,GREEN:rc_GREEN.svg,YELLOW:rc_YELLOW.svg,BLUE:rc_BLUE.svg"; - $row[14] = "1:rc_1.svg,2:rc_2.svg,3:rc_3.svg"; - $row[15] = "4:rc_4.svg,5:rc_5.svg,6:rc_6.svg"; - $row[16] = "7:rc_7.svg,8:rc_8.svg,9:rc_9.svg"; - $row[17] = "LEFTBRACE:rc_PREVIOUS.svg,0:rc_0.svg,RIGHTBRACE:rc_NEXT.svg"; - - $row[18] = "attr rc_iconpath icons"; - $row[19] = "attr rc_iconprefix rc_"; - return @row; -} - -# VU+ Duo2 with PNG -sub ENIGMA2_RClayout_VUplusDuo2() { - my @row; - - $row[0] = ":blank,MUTE,POWER:POWEROFF"; - $row[1] = ":blank,:blank,:blank"; - - $row[2] = "REWIND,PLAY,FASTFORWARD:FF"; - $row[3] = "RECORD:REC,STOP,VIDEO"; - $row[4] = ":blank,:blank,:blank"; - - $row[5] = "TV,AUDIO,RADIO:RADIO"; - $row[6] = "TEXT,HELP,AV"; - $row[7] = "INFO,MENU,EXIT"; - $row[8] = ":blank,:blank,:blank"; - - $row[9] = "VOLUMEUP:VOLUP,UP,CHANNELUP:CHUP2"; - $row[10] = "LEFT,OK,RIGHT"; - $row[11] = "VOLUMEDOWN:VOLDOWN,DOWN,CHANNELDOWN:CHDOWN2"; - - $row[12] = ":blank,:blank,:blank"; - - $row[13] = "RED,GREEN,YELLOW,BLUE"; - $row[14] = "1,2,3"; - $row[15] = "4,5,6"; - $row[16] = "7,8,9"; - $row[17] = "LEFTBRACE:LEFT2,0:0,RIGHTBRACE:RIGHT2"; - - $row[18] = "attr rc_iconpath icons/remotecontrol"; - $row[19] = "attr rc_iconprefix black_btn_"; - return @row; -} - -################################### sub ENIGMA2_GetRemotecontrolCommand($) { my ($command) = @_; my $commands = { @@ -2855,6 +2502,306 @@ sub ENIGMA2_GetRemotecontrolCommand($) { } } +sub ENIGMA2_wake ($$) { + if ( !$modules{WOL}{LOADED} + && -f "$attr{global}{modpath}/FHEM/98_WOL.pm" ) + { + my $ret = CommandReload( undef, "98_WOL" ); + return $ret if ($ret); + } + elsif ( !-f "$attr{global}{modpath}/FHEM/98_WOL.pm" ) { + return "Missing module: $attr{global}{modpath}/FHEM/98_WOL.pm"; + } + + my ( $name, $mac ) = @_; + my $hash = $defs{$name}; + my $host = + AttrVal( $name, "WOL_useUdpBroadcast", + AttrVal( $name, "useUdpBroadcast", "255.255.255.255" ) ); + my $port = AttrVal( $name, "WOL_port", "9" ); + my $mode = lc( AttrVal( $name, "WOL_mode", "UDP" ) ); + + Log3 $name, 4, + "ENIGMA2 $name: Waking up by sending Wake-On-Lan magic package to " + . $mac; + + if ( $mode eq "both" || $mode eq "ew" ) { + WOL_by_ew( $hash, $mac ); + } + if ( $mode eq "both" || $mode eq "udp" ) { + WOL_by_udp( $hash, $mac, $host, $port ); + } +} + +sub ENIGMA2_RCmakenotify($$) { + my ( $nam, $ndev ) = @_; + my $nname = "notify_$nam"; + + fhem( "define $nname notify $nam set $ndev remoteControl " . '$EVENT', 1 ); + Log3 undef, 2, "[remotecontrol:ENIGMA2] Notify created: $nname"; + return "Notify created by ENIGMA2: $nname"; +} + +sub ENIGMA2_RClayout_DM800_SVG() { + my @row; + + $row[0] = ":rc_BLANK.svg,:rc_BLANK.svg,POWER:rc_POWER.svg"; + $row[1] = ":rc_BLANK.svg,:rc_BLANK.svg,:rc_BLANK.svg"; + + $row[2] = "1:rc_1.svg,2:rc_2.svg,3:rc_3.svg"; + $row[3] = "4:rc_4.svg,5:rc_5.svg,6:rc_6.svg"; + $row[4] = "7:rc_7.svg,8:rc_8.svg,9:rc_9.svg"; + $row[5] = "LEFTBRACE:rc_PREVIOUS.svg,0:rc_0.svg,RIGHTBRACE:rc_NEXT.svg"; + $row[6] = ":rc_BLANK.svg,:rc_BLANK.svg,:rc_BLANK.svg"; + + $row[7] = "VOLUMEUP:rc_VOLPLUS.svg,MUTE:rc_MUTE.svg,CHANNELUP:rc_UP.svg"; + $row[8] = + "VOLUMEDOWN:rc_VOLMINUS.svg,EXIT:rc_EXIT.svg,CHANNELDOWN:rc_DOWN.svg"; + $row[9] = ":rc_BLANK.svg,:rc_BLANK.svg,:rc_BLANK.svg"; + + $row[10] = "INFO:rc_INFO.svg,UP:rc_UP.svg,MENU:rc_MENU.svg"; + $row[11] = "LEFT:rc_LEFT.svg,OK:rc_OK.svg,RIGHT:rc_RIGHT.svg"; + $row[12] = "AUDIO:rc_AUDIO.svg,DOWN:rc_DOWN.svg,VIDEO:rc_VIDEO.svg"; + $row[13] = ":rc_BLANK.svg,EXIT:rc_EXIT.svg,:rc_BLANK.svg"; + + $row[14] = + "RED:rc_REWred.svg,GREEN:rc_PLAYgreen.svg," + . "YELLOW:rc_PAUSEyellow.svg,BLUE:rc_FFblue.svg"; + $row[15] = + "TV:rc_TVstop.svg,RADIO:rc_RADIOred.svg," + . "TEXT:rc_TEXT.svg,HELP:rc_HELP.svg"; + + $row[16] = "attr rc_iconpath icons/remotecontrol"; + $row[17] = "attr rc_iconprefix black_btn_"; + return @row; +} + +sub ENIGMA2_RClayout_DM800() { + my @row; + + $row[0] = ":blank,:blank,POWER:POWEROFF"; + $row[1] = ":blank,:blank,:blank"; + + $row[2] = "1,2,3"; + $row[3] = "4,5,6"; + $row[4] = "7,8,9"; + $row[5] = "LEFTBRACE:LEFT2,0:0,RIGHTBRACE:RIGHT2"; + $row[6] = ":blank,:blank,:blank"; + + $row[7] = "VOLUMEUP:VOLUP,MUTE,CHANNELUP:CHUP2"; + $row[8] = "VOLUMEDOWN:VOLDOWN,EXIT,CHANNELDOWN:CHDOWN2"; + $row[9] = ":blank,:blank,:blank"; + + $row[10] = "INFO,UP,MENU"; + $row[11] = "LEFT,OK,RIGHT"; + $row[12] = "AUDIO,DOWN,VIDEO"; + $row[13] = ":blank,:blank,:blank"; + + $row[14] = "RED:REWINDred,GREEN:PLAYgreen,YELLOW:PAUSEyellow,BLUE:FFblue"; + $row[15] = "TV:TVstop,RADIO:RADIOred,TEXT,HELP"; + + $row[16] = "attr rc_iconpath icons/remotecontrol"; + $row[17] = "attr rc_iconprefix black_btn_"; + return @row; +} + +sub ENIGMA2_RClayout_DM8000_SVG() { + my @row; + + $row[0] = ":rc_BLANK.svg,:rc_BLANK.svg,POWER:rc_POWER.svg"; + $row[1] = ":rc_BLANK.svg,:rc_BLANK.svg,:rc_BLANK.svg"; + + $row[2] = "1:rc_1.svg,2:rc_2.svg,3:rc_3.svg"; + $row[3] = "4:rc_4.svg,5:rc_5.svg,6:rc_6.svg"; + $row[4] = "7:rc_7.svg,8:rc_8.svg,9:rc_9.svg"; + $row[5] = "LEFTBRACE:rc_PREVIOUS.svg,0:rc_0.svg,RIGHTBRACE:rc_NEXT.svg"; + $row[6] = ":rc_BLANK.svg,:rc_BLANK.svg,:rc_BLANK.svg"; + + $row[7] = "VOLUMEUP:rc_VOLPLUS.svg,MUTE:rc_MUTE.svg,CHANNELUP:rc_UP.svg"; + $row[8] = + "VOLUMEDOWN:rc_VOLMINUS.svg,EXIT:rc_EXIT.svg,CHANNELDOWN:rc_DOWN.svg"; + $row[9] = ":rc_BLANK.svg,:rc_BLANK.svg,:rc_BLANK.svg"; + + $row[10] = "INFO:rc_INFO.svg,UP:rc_UP.svg,MENU:rc_MENU.svg"; + $row[11] = "LEFT:rc_LEFT.svg,OK:rc_OK.svg,RIGHT:rc_RIGHT.svg"; + $row[12] = "AUDIO:rc_AUDIO.svg,DOWN:rc_DOWN.svg,VIDEO:rc_VIDEO.svg"; + $row[13] = ":rc_BLANK.svg,EXIT:rc_EXIT.svg,:rc_BLANK.svg"; + + $row[14] = + "RED:rc_RED.svg,GREEN:rc_GREEN.svg," + . "YELLOW:rc_YELLOW.svg,BLUE:rc_BLUE.svg"; + $row[15] = + "REWIND:rc_REW.svg,PLAY:rc_PLAY.svg," + . "STOP:rc_STOP.svg,FASTFORWARD:rc_FF.svg"; + $row[16] = + "TV:rc_TV.svg,RADIO:rc_RADIO.svg,TEXT:rc_TEXT.svg,RECORD:rc_REC.svg"; + + $row[17] = "attr rc_iconpath icons/remotecontrol"; + $row[18] = "attr rc_iconprefix black_btn_"; + return @row; +} + +sub ENIGMA2_RClayout_DM8000() { + my @row; + + $row[0] = ":blank,:blank,POWER:POWEROFF"; + $row[1] = ":blank,:blank,:blank"; + + $row[2] = "1,2,3"; + $row[3] = "4,5,6"; + $row[4] = "7,8,9"; + $row[5] = "LEFTBRACE:LEFT2,0:0,RIGHTBRACE:RIGHT2"; + $row[6] = ":blank,:blank,:blank"; + + $row[7] = "VOLUMEUP:VOLUP,MUTE,CHANNELUP:CHUP2"; + $row[8] = "VOLUMEDOWN:VOLDOWN,EXIT,CHANNELDOWN:CHDOWN2"; + $row[9] = ":blank,:blank,:blank"; + + $row[10] = "INFO,UP,MENU"; + $row[11] = "LEFT,OK,RIGHT"; + $row[12] = "AUDIO,DOWN,VIDEO"; + $row[13] = ":blank,:blank,:blank"; + + $row[14] = "RED,GREEN,YELLOW,BLUE"; + $row[15] = "REWIND,PLAY,STOP,FASTFORWARD:FF"; + $row[16] = "TV,RADIO,TEXT,RECORD:REC"; + + $row[17] = "attr rc_iconpath icons/remotecontrol"; + $row[18] = "attr rc_iconprefix black_btn_"; + return @row; +} + +sub ENIGMA2_RClayout_RC10_SVG() { + my @row; + + $row[0] = ":rc_BLANK.svg,:rc_BLANK.svg,POWER:rc_POWER.svg"; + $row[1] = ":rc_BLANK.svg,:rc_BLANK.svg,:rc_BLANK.svg"; + + $row[2] = "1:rc_1.svg,2:rc_2.svg,3:rc_3.svg"; + $row[3] = "4:rc_4.svg,5:rc_5.svg,6:rc_6.svg"; + $row[4] = "7:rc_7.svg,8:rc_8.svg,9:rc_9.svg"; + $row[5] = "LEFTBRACE:rc_PREVIOUS.svg,0:rc_0.svg,RIGHTBRACE:rc_NEXT.svg"; + $row[6] = + "RED:rc_RED.svg,GREEN:rc_GREEN.svg,YELLOW:rc_YELLOW.svg,BLUE:rc_BLUE.svg"; + $row[7] = ":rc_BLANK.svg,:rc_BLANK.svg,:rc_BLANK.svg"; + + $row[8] = "INFO:rc_INFO.svg,UP:rc_UP.svg,MENU:rc_MENU.svg"; + $row[9] = "LEFT:rc_LEFT.svg,OK:rc_OK.svg,RIGHT:rc_RIGHT.svg"; + $row[10] = "AUDIO:rc_AUDIO.svg,DOWN:rc_DOWN.svg,VIDEO:rc_VIDEO.svg"; + $row[11] = ":rc_BLANK.svg,EXIT:rc_EXIT.svg,:rc_BLANK.svg"; + + $row[12] = "VOLUMEUP:rc_VOLPLUS.svg,:rc_BLANK.svg,CHANNELUP:rc_UP.svg"; + $row[13] = + "VOLUMEDOWN:rc_VOLMINUS.svg,MUTE:rc_MUTE.svg,CHANNELDOWN:rc_DOWN.svg"; + $row[14] = ":rc_BLANK.svg,:rc_BLANK.svg,:rc_BLANK.svg"; + + $row[15] = + "REWIND:rc_REW.svg,PLAY:rc_PLAY.svg," + . "STOP:rc_STOP.svg,FASTFORWARD:rc_FF.svg"; + $row[16] = + "TV:rc_TV.svg,RADIO:rc_RADIO.svg,TEXT:rc_TEXT.svg,RECORD:rc_REC.svg"; + + $row[17] = "attr rc_iconpath icons"; + $row[18] = "attr rc_iconprefix rc_"; + return @row; +} + +sub ENIGMA2_RClayout_RC10() { + my @row; + + $row[0] = ":blank,:blank,POWER:POWEROFF"; + $row[1] = ":blank,:blank,:blank"; + + $row[2] = "1,2,3"; + $row[3] = "4,5,6"; + $row[4] = "7,8,9"; + $row[5] = "LEFTBRACE:LEFT2,0:0,RIGHTBRACE:RIGHT2"; + $row[6] = "RED,GREEN,YELLOW,BLUE"; + $row[7] = ":blank,:blank,:blank"; + + $row[8] = "INFO,UP,MENU"; + $row[9] = "LEFT,OK,RIGHT"; + $row[10] = "AUDIO,DOWN,VIDEO"; + $row[11] = ":blank,EXIT,:blank"; + + $row[12] = "VOLUMEUP:VOLUP,:blank,CHANNELUP:CHUP2"; + $row[13] = "VOLUMEDOWN:VOLDOWN,MUTE,CHANNELDOWN:CHDOWN2"; + $row[14] = ":blank,:blank,:blank"; + + $row[15] = "REWIND,PLAY,STOP,FASTFORWARD:FF"; + $row[16] = "TV,RADIO,TEXT,RECORD:REC"; + + $row[17] = "attr rc_iconpath icons/remotecontrol"; + $row[18] = "attr rc_iconprefix black_btn_"; + return @row; +} + +sub ENIGMA2_RClayout_VUplusDuo2_SVG() { + my @row; + + $row[0] = ":rc_BLANK.svg,MUTE:rc_MUTE.svg,POWER:rc_POWER.svg"; + $row[1] = ":rc_BLANK.svg,:rc_BLANK.svg,:rc_BLANK.svg"; + + $row[2] = "REWIND:rc_REW.svg,PLAY:rc_PLAY.svg,FASTFORWARD:rc_FF.svg"; + $row[3] = "RECORD:rc_REC.svg,STOP:rc_STOP.svg,VIDEO:rc_VIDEO.svg"; + $row[4] = ":rc_BLANK.svg,:rc_BLANK.svg,:rc_BLANK.svg"; + + $row[5] = "TV:rc_TV.svg,AUDIO:rc_AUDIO.svg,RADIO:rc_RADIO.svg"; + $row[6] = "TEXT:rc_TEXT.svg,HELP:rc_HELP.svg,AV:rc_AV.svg"; + $row[7] = "INFO:rc_EPG.svg,MENU:rc_MENU.svg,EXIT:rc_EXIT.svg"; + $row[8] = ":rc_BLANK.svg,:rc_BLANK.svg,:rc_BLANK.svg"; + + $row[9] = "VOLUMEUP:rc_VOLPLUS.svg,UP:rc_UP.svg,CHANNELUP:rc_PLUS.svg"; + $row[10] = "LEFT:rc_LEFT.svg,OK:rc_OK.svg,RIGHT:rc_RIGHT.svg"; + $row[11] = + "VOLUMEDOWN:rc_VOLMINUS.svg,DOWN:rc_DOWN.svg,CHANNELDOWN:rc_MINUS.svg"; + + $row[12] = ":rc_BLANK.svg,:rc_BLANK.svg,:rc_BLANK.svg"; + + $row[13] = + "RED:rc_RED.svg,GREEN:rc_GREEN.svg,YELLOW:rc_YELLOW.svg,BLUE:rc_BLUE.svg"; + $row[14] = "1:rc_1.svg,2:rc_2.svg,3:rc_3.svg"; + $row[15] = "4:rc_4.svg,5:rc_5.svg,6:rc_6.svg"; + $row[16] = "7:rc_7.svg,8:rc_8.svg,9:rc_9.svg"; + $row[17] = "LEFTBRACE:rc_PREVIOUS.svg,0:rc_0.svg,RIGHTBRACE:rc_NEXT.svg"; + + $row[18] = "attr rc_iconpath icons"; + $row[19] = "attr rc_iconprefix rc_"; + return @row; +} + +sub ENIGMA2_RClayout_VUplusDuo2() { + my @row; + + $row[0] = ":blank,MUTE,POWER:POWEROFF"; + $row[1] = ":blank,:blank,:blank"; + + $row[2] = "REWIND,PLAY,FASTFORWARD:FF"; + $row[3] = "RECORD:REC,STOP,VIDEO"; + $row[4] = ":blank,:blank,:blank"; + + $row[5] = "TV,AUDIO,RADIO:RADIO"; + $row[6] = "TEXT,HELP,AV"; + $row[7] = "INFO,MENU,EXIT"; + $row[8] = ":blank,:blank,:blank"; + + $row[9] = "VOLUMEUP:VOLUP,UP,CHANNELUP:CHUP2"; + $row[10] = "LEFT,OK,RIGHT"; + $row[11] = "VOLUMEDOWN:VOLDOWN,DOWN,CHANNELDOWN:CHDOWN2"; + + $row[12] = ":blank,:blank,:blank"; + + $row[13] = "RED,GREEN,YELLOW,BLUE"; + $row[14] = "1,2,3"; + $row[15] = "4,5,6"; + $row[16] = "7,8,9"; + $row[17] = "LEFTBRACE:LEFT2,0:0,RIGHTBRACE:RIGHT2"; + + $row[18] = "attr rc_iconpath icons/remotecontrol"; + $row[19] = "attr rc_iconprefix black_btn_"; + return @row; +} + 1; =pod diff --git a/fhem/FHEM/70_ONKYO_AVR.pm b/fhem/FHEM/70_ONKYO_AVR.pm index e415c5472..f12c000cb 100644 --- a/fhem/FHEM/70_ONKYO_AVR.pm +++ b/fhem/FHEM/70_ONKYO_AVR.pm @@ -1,57 +1,16 @@ +############################################################################### # $Id$ -############################################################################## -# -# 70_ONKYO_AVR.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 . -# -############################################################################## - package main; - use strict; use warnings; -use Time::HiRes qw(usleep); +use Data::Dumper; use Symbol qw; use File::Path; use File::stat; use File::Temp; use File::Copy; -use Data::Dumper; -$Data::Dumper::Sortkeys = 1; - -sub ONKYO_AVR_Set($$$); -sub ONKYO_AVR_Get($$$); -sub ONKYO_AVR_Define($$$); -sub ONKYO_AVR_Undefine($$); -sub ONKYO_AVR_Notify($$); -sub ONKYO_AVR_Shutdown($); - -######################### -# Forward declaration for remotecontrol module -sub ONKYO_AVR_RClayout_TV(); -sub ONKYO_AVR_RCmakenotify($$); - -################################### +# initialize ################################################################## sub ONKYO_AVR_Initialize($) { my ($hash) = @_; @@ -60,16 +19,13 @@ sub ONKYO_AVR_Initialize($) { require "$attr{global}{modpath}/FHEM/DevIo.pm"; require "$attr{global}{modpath}/FHEM/ONKYOdb.pm"; - # Provider - $hash->{ReadFn} = "ONKYO_AVR_Read"; - $hash->{WriteFn} = "ONKYO_AVR_Write"; - $hash->{ReadyFn} = "ONKYO_AVR_Ready"; - - # Normal devices $hash->{DefFn} = "ONKYO_AVR_Define"; $hash->{UndefFn} = "ONKYO_AVR_Undefine"; - $hash->{GetFn} = "ONKYO_AVR_Get"; $hash->{SetFn} = "ONKYO_AVR_Set"; + $hash->{GetFn} = "ONKYO_AVR_Get"; + $hash->{ReadFn} = "ONKYO_AVR_Read"; + $hash->{WriteFn} = "ONKYO_AVR_Write"; + $hash->{ReadyFn} = "ONKYO_AVR_Ready"; $hash->{NotifyFn} = "ONKYO_AVR_Notify"; $hash->{ShutdownFn} = "ONKYO_AVR_Shutdown"; $hash->{parseParams} = 1; @@ -77,6 +33,7 @@ sub ONKYO_AVR_Initialize($) { no warnings 'qw'; my @attrList = qw( do_not_notify:1,0 + disabledForIntervals 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 @@ -111,36 +68,7 @@ sub ONKYO_AVR_Initialize($) { }; } -sub ONKYO_AVR_addExtension($$$) { - my ( $name, $func, $link ) = @_; - - my $url = "?/$link"; - - return 0 - if ( defined( $data{FWEXT}{$url} ) - && $data{FWEXT}{$url}{deviceName} ne $name ); - - Log3 $name, 2, - "ONKYO_AVR $name: Registering ONKYO_AVR for webhook URI $url ..."; - $data{FWEXT}{$url}{deviceName} = $name; - $data{FWEXT}{$url}{FUNC} = $func; - $data{FWEXT}{$url}{LINK} = $link; - - return 1; -} - -################################### -sub ONKYO_AVR_removeExtension($) { - my ($link) = @_; - - my $url = "?/$link"; - my $name = $data{FWEXT}{$url}{deviceName}; - Log3 $name, 2, - "ONKYO_AVR $name: Unregistering ONKYO_AVR for webhook URI $url..."; - delete $data{FWEXT}{$url}; -} - -################################### +# regular Fn ################################################################## sub ONKYO_AVR_Define($$$) { my ( $hash, $a, $h ) = @_; my $name = $hash->{NAME}; @@ -228,195 +156,10 @@ sub ONKYO_AVR_Define($$$) { } ); } + + return undef; } -##################################### -sub ONKYO_AVR_DevInit($) { - my ($hash) = @_; - my $name = $hash->{NAME}; - - if ( lc( ReadingsVal( $name, "state", "?" ) ) eq "opened" ) { - DoTrigger( $name, "CONNECTED" ); - } - else { - DoTrigger( $name, "DISCONNECTED" ); - } -} - -#------------------------------------------------------------------------------ -sub ONKYO_AVR_CGI() { - my ($request) = @_; - - # data received - if ( $request =~ m,^\?\/ONKYO_AVR\/cover\/(.+)\.(.+)$, ) { - - Log3 undef, 5, "ONKYO_AVR: sending cover $1.$2"; - - if ( $1 eq "empty" && $2 eq "jpg" ) { - FW_serveSpecial( 'sonos_empty', 'jpg', - AttrVal( "global", "modpath", "." ) . '/FHEM/lib/UPnP', 1 ); - } - else { - FW_serveSpecial( - $1, - $2, - AttrVal( "global", "modpath", "." ) - . '/www/images/default/ONKYO_AVR', - 1 - ); - } - - return ( undef, undef ); - } - - # no data received - else { - Log3 undef, 5, "ONKYO_AVR: received malformed request\n$request"; - } - - return ( "text/plain; charset=utf-8", "Call failure: " . $request ); -} - -##################################### -sub ONKYO_AVR_Notify($$) { - my ( $hash, $dev ) = @_; - my $name = $hash->{NAME}; - my $devName = $dev->{NAME}; - my $definedZones = scalar keys %{ $modules{ONKYO_AVR_ZONE}{defptr}{$name} }; - my $presence = ReadingsVal( $name, "presence", "-" ); - - return - if ( !$dev->{CHANGED} ); # Some previous notify deleted the array. - - # work on global events related to us - if ( $devName eq "global" ) { - foreach my $change ( @{ $dev->{CHANGED} } ) { - if ( $change !~ /^(\w+)\s(\w+)\s?(\w*)\s?(.*)$/ - || $2 ne $name ) - { - return; - } - - # DEFINED - # MODIFIED - elsif ( $1 eq "DEFINED" || $1 eq "MODIFIED" ) { - Log3 $hash, 5, - "ONKYO_AVR " - . $name - . ": processing my global event $1: $3 -> $4"; - - if ( lc( ReadingsVal( $name, "state", "?" ) ) eq "opened" ) { - DoTrigger( $name, "CONNECTED" ); - } - else { - DoTrigger( $name, "DISCONNECTED" ); - } - } - - # unknown event - else { - Log3 $hash, 5, - "ONKYO_AVR " - . $name - . ": WONT BE processing my global event $1: $3 -> $4"; - } - } - - return; - } - - # do nothing for any other device - elsif ( $devName ne $name ) { - return; - } - - readingsBeginUpdate($hash); - - foreach my $change ( @{ $dev->{CHANGED} } ) { - - # DISCONNECTED - if ( $change eq "DISCONNECTED" ) { - Log3 $hash, 5, "ONKYO_AVR " . $name . ": processing change $change"; - - # disable connectionCheck and wait - # until DevIo reopened the connection - RemoveInternalTimer($hash); - - readingsBulkUpdate( $hash, "presence", "absent" ) - if ( $presence ne "absent" ); - - readingsBulkUpdate( $hash, "power", "off" ) - if ( ReadingsVal( $name, "power", "on" ) ne "off" ); - - # stateAV - my $stateAV = ONKYO_AVR_GetStateAV($hash); - readingsBulkUpdate( $hash, "stateAV", $stateAV ) - if ( ReadingsVal( $name, "stateAV", "-" ) ne $stateAV ); - - # send to slaves - if ( $definedZones > 1 ) { - Log3 $name, 5, - "ONKYO_AVR $name: Dispatching state change to slaves"; - Dispatch( - $hash, - { - "presence" => "absent", - "power" => "off", - }, - undef - ); - } - } - - # CONNECTED - elsif ( $change eq "CONNECTED" ) { - Log3 $hash, 5, "ONKYO_AVR " . $name . ": processing change $change"; - - readingsBulkUpdate( $hash, "presence", "present" ) - if ( $presence ne "present" ); - - # stateAV - my $stateAV = ONKYO_AVR_GetStateAV($hash); - readingsBulkUpdate( $hash, "stateAV", $stateAV ) - if ( ReadingsVal( $name, "stateAV", "-" ) ne $stateAV ); - - ONKYO_AVR_SendCommand( $hash, "power", "query" ); - ONKYO_AVR_SendCommand( $hash, "network-standby", "query" ); - ONKYO_AVR_SendCommand( $hash, "input", "query" ); - ONKYO_AVR_SendCommand( $hash, "mute", "query" ); - ONKYO_AVR_SendCommand( $hash, "volume", "query" ); - ONKYO_AVR_SendCommand( $hash, "sleep", "query" ); - ONKYO_AVR_SendCommand( $hash, "audio-information", "query" ); - ONKYO_AVR_SendCommand( $hash, "video-information", "query" ); - ONKYO_AVR_SendCommand( $hash, "listening-mode", "query" ); - ONKYO_AVR_SendCommand( $hash, "video-picture-mode", "query" ); - ONKYO_AVR_SendCommand( $hash, "phase-matching-bass", "query" ); - ONKYO_AVR_SendCommand( $hash, "center-temporary-level", "query" ); - ONKYO_AVR_SendCommand( $hash, "subwoofer-temporary-level", - "query" ); - fhem -"sleep 1 quiet;get $name remoteControl net-receiver-information query quiet"; - - # send to slaves - if ( $definedZones > 1 ) { - Log3 $name, 5, - "ONKYO_AVR $name: Dispatching state change to slaves"; - Dispatch( - $hash, - { - "presence" => "present", - }, - undef - ); - } - - } - } - - readingsEndUpdate( $hash, 1 ); -} - -################################### sub ONKYO_AVR_Undefine($$) { my ( $hash, $name ) = @_; @@ -443,17 +186,1187 @@ sub ONKYO_AVR_Undefine($$) { return undef; } -################################### -sub ONKYO_AVR_Shutdown($) { - my ($hash) = @_; - my $name = $hash->{NAME}; - Log3 $name, 5, "ONKYO_AVR $name: called function ONKYO_AVR_Shutdown()"; +sub ONKYO_AVR_Set($$$) { + my ( $hash, $a, $h ) = @_; + my $name = $hash->{NAME}; + my $zone = $hash->{ZONE}; + my $state = ReadingsVal( $name, "power", "off" ); + my $presence = ReadingsVal( $name, "presence", "absent" ); + my $return; + my $reading; + my $inputs_txt = ""; + my $channels_txt = ""; + my @implicit_cmds; + my $implicit_txt = ""; - DevIo_CloseDev($hash); - return undef; + Log3 $name, 5, "ONKYO_AVR $name: called function ONKYO_AVR_Set()"; + + return "Argument is missing" if ( int(@$a) < 1 ); + + # Input alias handling + if ( defined( $attr{$name}{inputs} ) && $attr{$name}{inputs} ne "" ) { + my @inputs = split( ':', $attr{$name}{inputs} ); + + if (@inputs) { + foreach (@inputs) { + if (m/[^,\s]+(,[^,\s]+)+/) { + my @input_names = split( ',', $_ ); + $inputs_txt .= $input_names[1] . ","; + $input_names[1] =~ s/\s/_/g; + $hash->{helper}{receiver}{input_aliases}{ $input_names[0] } + = $input_names[1]; + $hash->{helper}{receiver}{input_names}{ $input_names[1] } = + $input_names[0]; + } + else { + $inputs_txt .= $_ . ","; + } + } + } + + $inputs_txt =~ s/\s/_/g; + $inputs_txt = substr( $inputs_txt, 0, -1 ); + } + + # if we could read the actual available inputs from the receiver, use them + elsif (defined( $hash->{helper}{receiver} ) + && ref( $hash->{helper}{receiver} ) eq "HASH" + && defined( $hash->{helper}{receiver}{device}{selectorlist}{count} ) + && $hash->{helper}{receiver}{device}{selectorlist}{count} > 0 ) + { + + foreach my $input ( + @{ $hash->{helper}{receiver}{device}{selectorlist}{selector} } ) + { + if ( $input->{value} eq "1" + && $input->{zone} ne "00" + && $input->{id} ne "80" ) + { + my $id = $input->{id}; + my $name = trim( $input->{name} ); + $inputs_txt .= $name . ","; + } + } + + $inputs_txt =~ s/\s/_/g; + $inputs_txt = substr( $inputs_txt, 0, -1 ); + } + + # use general list of possible inputs + else { + # Find out valid inputs + my $inputs = + ONKYOdb::ONKYO_GetRemotecontrolValue( $zone, + ONKYOdb::ONKYO_GetRemotecontrolCommand( $zone, "input" ) ); + + foreach my $input ( sort keys %{$inputs} ) { + $inputs_txt .= $input . "," + if ( !( $input =~ /^(07|08|09|up|down|query)$/ ) ); + } + $inputs_txt = substr( $inputs_txt, 0, -1 ); + } + + # list of network channels/services + my $channels_src = "internal"; + if ( defined( $hash->{helper}{receiver} ) + && ref( $hash->{helper}{receiver} ) eq "HASH" + && defined( $hash->{helper}{receiver}{device}{netservicelist}{count} ) + && $hash->{helper}{receiver}{device}{netservicelist}{count} > 0 ) + { + + foreach my $id ( + sort keys + %{ $hash->{helper}{receiver}{device}{netservicelist}{netservice} } ) + { + if ( + defined( + $hash->{helper}{receiver}{device}{netservicelist} + {netservice}{$id}{value} + ) + && $hash->{helper}{receiver}{device}{netservicelist} + {netservice}{$id}{value} eq "1" + ) + { + $channels_txt .= + trim( $hash->{helper}{receiver}{device}{netservicelist} + {netservice}{$id}{name} ) + . ","; + } + } + + $channels_txt =~ s/\s/_/g; + $channels_txt = substr( $channels_txt, 0, -1 ); + $channels_src = "receiver"; + } + + # use general list of possible channels + else { + # Find out valid channels + my $channels = + ONKYOdb::ONKYO_GetRemotecontrolValue( "1", + ONKYOdb::ONKYO_GetRemotecontrolCommand( "1", "net-service" ) ); + + foreach my $channel ( sort keys %{$channels} ) { + $channels_txt .= $channel . "," + if ( !( $channel =~ /^(up|down|query)$/ ) ); + } + $channels_txt = substr( $channels_txt, 0, -1 ); + } + + # for each reading, check if there is a known command for it + # and allow to set values if there are any available + if ( defined( $hash->{READINGS} ) ) { + + foreach my $reading ( keys %{ $hash->{READINGS} } ) { + my $cmd_raw = + ONKYOdb::ONKYO_GetRemotecontrolCommand( $zone, $reading ); + my @readingExceptions = ( + "volume", "input", "mute", "sleep", "center-temporary-level", + "subwoofer-temporary-level", "balance", "preset", + ); + + if ( $cmd_raw && !( grep $_ eq $reading, @readingExceptions ) ) { + my $cmd_details = + ONKYOdb::ONKYO_GetRemotecontrolCommandDetails( $zone, + $cmd_raw ); + + my $value_list = ""; + my $debuglist; + foreach my $value ( keys %{ $cmd_details->{values} } ) { + next + if ( $value eq "QSTN" ); + + if ( defined( $cmd_details->{values}{$value}{name} ) ) { + $value_list .= "," if ( $value_list ne "" ); + + $value_list .= $cmd_details->{values}{$value}{name} + if ( + ref( $cmd_details->{values}{$value}{name} ) eq "" ); + + $value_list .= $cmd_details->{values}{$value}{name}[0] + if ( + ref( $cmd_details->{values}{$value}{name} ) eq + "ARRAY" ); + } + } + + if ( $value_list ne "" ) { + push @implicit_cmds, $reading; + $implicit_txt .= " $reading:$value_list"; + } + } + + # tone-* + elsif ( $reading =~ /^tone.*-([a-zA-Z]+)$/ ) { + $implicit_txt .= " $reading:slider,-10,1,10"; + } + + # center-temporary-level + elsif ( $reading eq "center-temporary-level" ) { + $implicit_txt .= " $reading:slider,-12,1,12"; + } + + # subwoofer*-temporary-level + elsif ( $reading =~ /^subwoofer.*-temporary-level$/ ) { + $implicit_txt .= " $reading:slider,-15,1,12"; + } + } + } + + my $preset_txt = ""; + if ( defined( $hash->{helper}{receiver}{preset} ) ) { + + foreach my $id ( + sort + keys %{ $hash->{helper}{receiver}{preset} } + ) + { + my $presetName = + $hash->{helper}{receiver}{preset}{$id}; + next if ( !$presetName || $presetName eq "" ); + + $preset_txt = "preset:" if ( $preset_txt eq "" ); + $preset_txt .= "," + if ( $preset_txt eq "preset:" + && ReadingsVal( $name, "preset", "-" ) eq "" ); + + $presetName =~ s/\s/_/g; + $preset_txt .= $presetName . ","; + } + } + $preset_txt = substr( $preset_txt, 0, -1 ) if ( $preset_txt ne "" ); + + if ( $preset_txt eq "" ) { + $preset_txt = "preset:"; + $preset_txt .= "," if ( ReadingsVal( $name, "preset", "-" ) eq "" ); + $preset_txt .= +"1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40"; + } + + my $shuffle_txt = "shuffle:"; + $shuffle_txt .= "," if ( ReadingsVal( $name, "shuffle", "-" ) eq "-" ); + $shuffle_txt .= "off,on,on-album,on-folder"; + + my $repeat_txt = "repeat:"; + $repeat_txt .= "," if ( ReadingsVal( $name, "repeat", "-" ) eq "-" ); + $repeat_txt .= "off,all,all-folder,one"; + + my $usage = + "Unknown argument '" + . @$a[1] + . "', choose one of toggle:noArg on:noArg off:noArg volume:slider,0,1,100 volumeDown:noArg volumeUp:noArg mute:off,on muteT:noArg play:noArg pause:noArg stop:noArg previous:noArg next:noArg shuffleT:noArg repeatT:noArg remoteControl:play,pause,repeat,stop,top,down,up,right,delete,display,ff,left,mode,return,rew,select,setup,0,1,2,3,4,5,6,7,8,9,prev,next,shuffle,menu channelDown:noArg channelUp:noArg inputDown:noArg inputUp:noArg internet-radio-preset:1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40 input:" + . $inputs_txt; + $usage .= " channel:$channels_txt"; + $usage .= " presetDown:noArg presetUp:noArg $preset_txt"; + $usage .= " $shuffle_txt"; + $usage .= " $repeat_txt"; + $usage .= $implicit_txt if ( $implicit_txt ne "" ); + $usage .= " sleep:off,5,10,15,30,60,90"; + + if ( ReadingsVal( $name, "currentTrackPosition", "--:--" ) ne "--:--" ) { + $usage .= " currentTrackPosition"; + } + + my $cmd = ''; + + return "Device is offline and cannot be controlled at that stage." + if ( $presence eq "absent" + && lc( @$a[1] ) ne "on" + && lc( @$a[1] ) ne "?" + && lc( @$a[1] ) ne "help" ); + + readingsBeginUpdate($hash); + + # create inputList reading for frontends + readingsBulkUpdate( $hash, "inputList", $inputs_txt ) + if ( ReadingsVal( $name, "inputList", "-" ) ne $inputs_txt ); + + # create channelList reading for frontends + readingsBulkUpdate( $hash, "channelList", $channels_txt ) + if ( + ( + $channels_src eq "internal" + && ReadingsVal( $name, "channelList", "-" ) eq "-" + ) + || ( $channels_src eq "receiver" + && ReadingsVal( $name, "channelList", "-" ) ne $channels_txt ) + ); + + # channel + if ( lc( @$a[1] ) eq "channel" ) { + if ( !defined( @$a[2] ) ) { + $return = "Syntax: CHANNELNAME [USERNAME PASSWORD]"; + } + else { + if ( $state eq "off" ) { + $return = ONKYO_AVR_SendCommand( $hash, "power", "on" ); + $return .= fhem "sleep 5;set $name channel " . @$a[2]; + } + elsif ( $hash->{INPUT} ne "2B" ) { + $return = ONKYO_AVR_SendCommand( $hash, "input", "2B" ); + $return .= fhem "sleep 1;set $name channel " . @$a[2]; + } + elsif ( ReadingsVal( $name, "channel", "" ) ne @$a[2] + || ( defined( @$a[3] ) && defined( @$a[4] ) ) ) + { + + my $servicename = ""; + my $channelname = @$a[2]; + + if ( + defined( $hash->{helper}{receiver} ) + && ref( $hash->{helper}{receiver} ) eq "HASH" + && defined( + $hash->{helper}{receiver}{device}{netservicelist}{count} + ) + && $hash->{helper}{receiver}{device}{netservicelist}{count} + > 0 + ) + { + + $channelname =~ s/_/ /g; + + foreach my $id ( + sort keys %{ + $hash->{helper}{receiver}{device}{netservicelist} + {netservice} + } + ) + { + if ( + defined( + $hash->{helper}{receiver}{device} + {netservicelist}{netservice}{$id}{value} + ) + && $hash->{helper}{receiver}{device} + {netservicelist}{netservice}{$id}{value} eq "1" + && $hash->{helper}{receiver}{device} + {netservicelist}{netservice}{$id}{name} eq + $channelname + ) + { + $servicename .= uc($id); + last; + } + } + } + else { + my $channels = ONKYOdb::ONKYO_GetRemotecontrolValue( + "1", + ONKYOdb::ONKYO_GetRemotecontrolCommand( + "1", "net-service" + ) + ); + + $servicename = $channels->{$channelname} + if ( defined( $channels->{$channelname} ) ); + } + + Log3 $name, 3, "ONKYO_AVR set $name " . @$a[1] . " " . @$a[2]; + + $servicename = uc($channelname) + if ( $servicename eq "" ); + + $servicename .= "0" if ( !defined( @$a[3] ) ); + $servicename .= "1" . @$a[3] if ( defined( @$a[3] ) ); + $servicename .= @$a[4] if ( defined( @$a[4] ) ); + + $return = + ONKYO_AVR_SendCommand( $hash, "net-service", $servicename ); + } + } + } + + # channelDown + elsif ( lc( @$a[1] ) eq "channeldown" ) { + if ( $state eq "off" ) { + $return = ONKYO_AVR_SendCommand( $hash, "power", "on" ); + $return .= fhem "sleep 5;set $name channelDown"; + } + elsif ( $hash->{INPUT} ne "2B" ) { + $return = ONKYO_AVR_SendCommand( $hash, "input", "2B" ); + $return .= fhem "sleep 1;set $name channelDown"; + } + else { + Log3 $name, 3, "ONKYO_AVR set $name " . @$a[1]; + $return = ONKYO_AVR_SendCommand( $hash, "net-usb", "chdn" ); + } + } + + # channelUp + elsif ( lc( @$a[1] ) eq "channelup" ) { + if ( $state eq "off" ) { + $return = ONKYO_AVR_SendCommand( $hash, "power", "on" ); + $return .= fhem "sleep 5;set $name channelUp"; + } + elsif ( $hash->{INPUT} ne "2B" ) { + $return = ONKYO_AVR_SendCommand( $hash, "input", "2B" ); + $return .= fhem "sleep 1;set $name channelUp"; + } + else { + Log3 $name, 3, "ONKYO_AVR set $name " . @$a[1]; + $return = ONKYO_AVR_SendCommand( $hash, "net-usb", "chup" ); + } + } + + # currentTrackPosition + elsif ( lc( @$a[1] ) eq "currenttrackposition" ) { + Log3 $name, 3, "ONKYO_AVR set $name " . @$a[1] . " " . @$a[2]; + + if ( !defined( @$a[2] ) ) { + $return = "No argument given"; + } + else { + + if ( @$a[2] !~ /^[0-9][0-9]:[0-5][0-9]$/ ) { + $return = + "Time needs to have format mm:ss and between 00:00 and 99:59"; + } + else { + $return = + ONKYO_AVR_SendCommand( $hash, "net-usb-time-seek", @$a[2] ); + } + } + } + + # internet-radio-preset + elsif ( lc( @$a[1] ) eq "internet-radio-preset" ) { + if ( !defined( @$a[2] ) ) { + $return = "No argument given"; + } + else { + if ( $state eq "off" ) { + $return = ONKYO_AVR_SendCommand( $hash, "power", "on" ); + $return .= fhem "sleep 5;set $name " . @$a[1] . " " . @$a[2]; + } + elsif ( $hash->{INPUT} ne "2B" ) { + $return = ONKYO_AVR_SendCommand( $hash, "input", "2B" ); + $return .= fhem "sleep 5;set $name " . @$a[1] . " " . @$a[2]; + } + elsif ( @$a[2] =~ /^\d*$/ ) { + Log3 $name, 3, "ONKYO_AVR set $name " . @$a[1] . " " . @$a[2]; + $return = ONKYO_AVR_SendCommand( + $hash, + lc( @$a[1] ), + ONKYO_AVR_dec2hex( @$a[2] ) + ); + } + else { + $return = "Invalid argument format"; + } + } + } + + # preset + elsif ( lc( @$a[1] ) eq "preset" ) { + if ( !defined( @$a[2] ) ) { + $return = "No argument given"; + } + else { + if ( $state eq "off" ) { + $return = ONKYO_AVR_SendCommand( $hash, "power", "on" ); + $return .= fhem "sleep 5;set $name preset " . @$a[2]; + } + elsif ( $hash->{INPUT} ne "24" && $hash->{INPUT} ne "25" ) { + $return = ONKYO_AVR_SendCommand( $hash, "input", "24" ); + $return .= fhem "sleep 1;set $name preset " . @$a[2]; + } + elsif ( lc( @$a[2] ) eq "up" ) { + Log3 $name, 3, "ONKYO_AVR set $name " . @$a[1] . " " . @$a[2]; + $return = ONKYO_AVR_SendCommand( $hash, lc( @$a[1] ), "UP" ); + } + elsif ( lc( @$a[2] ) eq "down" ) { + Log3 $name, 3, "ONKYO_AVR set $name " . @$a[1] . " " . @$a[2]; + $return = + ONKYO_AVR_SendCommand( $hash, lc( @$a[1] ), "DOWN" ); + } + elsif ( @$a[2] =~ /^\d*$/ ) { + Log3 $name, 3, "ONKYO_AVR set $name " . @$a[1] . " " . @$a[2]; + $return = ONKYO_AVR_SendCommand( + $hash, + lc( @$a[1] ), + ONKYO_AVR_dec2hex( @$a[2] ) + ); + } + elsif ( defined( $hash->{helper}{receiver}{preset} ) ) { + + foreach + my $id ( sort keys %{ $hash->{helper}{receiver}{preset} } ) + { + my $presetName = + $hash->{helper}{receiver}{preset}{$id}; + next if ( !$presetName || $presetName eq "" ); + + $presetName =~ s/\s/_/g; + + if ( $presetName eq @$a[2] ) { + Log3 $name, 3, + "ONKYO_AVR set $name " . @$a[1] . " " . @$a[2]; + + $return = + ONKYO_AVR_SendCommand( $hash, lc( @$a[1] ), uc($id) ); + + last; + } + } + } + } + } + + # presetDown + elsif ( lc( @$a[1] ) eq "presetdown" ) { + if ( $state eq "off" ) { + $return = ONKYO_AVR_SendCommand( $hash, "power", "on" ); + $return .= fhem "sleep 5;set $name presetDown"; + } + elsif ( $hash->{INPUT} ne "24" && $hash->{INPUT} ne "25" ) { + $return = ONKYO_AVR_SendCommand( $hash, "input", "24" ); + $return .= fhem "sleep 1;set $name presetDown"; + } + else { + Log3 $name, 3, "ONKYO_AVR set $name " . @$a[1]; + $return = ONKYO_AVR_SendCommand( $hash, "preset", "down" ); + } + } + + # presetUp + elsif ( lc( @$a[1] ) eq "presetup" ) { + if ( $state eq "off" ) { + $return = ONKYO_AVR_SendCommand( $hash, "power", "on" ); + $return .= fhem "sleep 5;set $name presetUp"; + } + elsif ( $hash->{INPUT} ne "24" && $hash->{INPUT} ne "25" ) { + $return = ONKYO_AVR_SendCommand( $hash, "input", "24" ); + $return .= fhem "sleep 1;set $name presetUp"; + } + else { + Log3 $name, 3, "ONKYO_AVR set $name " . @$a[1]; + $return = ONKYO_AVR_SendCommand( $hash, "preset", "up" ); + } + } + + # tone-* + elsif ( lc( @$a[1] ) =~ /^(tone.*)-(bass|treble)$/ ) { + Log3 $name, 3, "ONKYO_AVR set $name " . @$a[1] . " " . @$a[2]; + + if ( !defined( @$a[2] ) ) { + $return = "No argument given"; + } + else { + if ( $state eq "off" ) { + $return = +"Device power is turned off, this function is unavailable at that stage."; + } + elsif ( lc( @$a[2] ) eq "up" ) { + my $setVal = ""; + $setVal = "B" if ( $2 eq "bass" ); + $setVal = "T" if ( $2 eq "treble" ); + $return = + ONKYO_AVR_SendCommand( $hash, lc($1), $setVal . "UP" ); + } + elsif ( lc( @$a[2] ) eq "down" ) { + my $setVal = ""; + $setVal = "B" if ( $2 eq "bass" ); + $setVal = "T" if ( $2 eq "treble" ); + $return = + ONKYO_AVR_SendCommand( $hash, lc($1), $setVal . "DOWN" ); + } + elsif ( @$a[2] =~ /^-*\d+$/ ) { + my $setVal = ""; + $setVal = "B" if ( $2 eq "bass" ); + $setVal = "T" if ( $2 eq "treble" ); + $setVal .= "+" if ( @$a[2] > 0 ); + $setVal .= "-" if ( @$a[2] < 0 ); + + my $setVal2 = @$a[2]; + $setVal2 = substr( $setVal2, 1 ) if ( $setVal2 < 0 ); + $setVal2 = ONKYO_AVR_dec2hex($setVal2); + $setVal2 = substr( $setVal2, 1 ) if ( $setVal2 ne "00" ); + + $return = + ONKYO_AVR_SendCommand( $hash, lc($1), $setVal . $setVal2 ); + } + } + } + + # center-temporary-level + # subwoofer-temporary-level + elsif (lc( @$a[1] ) eq "center-temporary-level" + || lc( @$a[1] ) eq "subwoofer-temporary-level" ) + { + Log3 $name, 3, "ONKYO_AVR set $name " . @$a[1] . " " . @$a[2]; + + if ( !defined( @$a[2] ) ) { + $return = "No argument given"; + } + else { + if ( $state eq "off" ) { + $return = +"Device power is turned off, this function is unavailable at that stage."; + } + elsif ( lc( @$a[2] ) eq "up" ) { + $return = ONKYO_AVR_SendCommand( $hash, lc($1), "UP" ); + } + elsif ( lc( @$a[2] ) eq "down" ) { + $return = ONKYO_AVR_SendCommand( $hash, lc($1), "DOWN" ); + } + elsif ( @$a[2] =~ /^-*\d+$/ ) { + my $setVal = ""; + $setVal = "+" if ( @$a[2] > 0 ); + $setVal = "-" if ( @$a[2] < 0 ); + + my $setVal2 = @$a[2]; + $setVal2 = substr( $setVal2, 1 ) if ( $setVal2 < 0 ); + $setVal2 = ONKYO_AVR_dec2hex($setVal2); + $setVal2 = substr( $setVal2, 1 ) if ( $setVal2 ne "00" ); + + $return = ONKYO_AVR_SendCommand( + $hash, + lc( @$a[1] ), + $setVal . $setVal2 + ); + } + } + } + + # toggle + elsif ( lc( @$a[1] ) eq "toggle" ) { + if ( $state eq "off" ) { + $return = fhem "set $name on"; + } + else { + $return = fhem "set $name off"; + } + } + + # on + elsif ( lc( @$a[1] ) eq "on" ) { + if ( $presence eq "absent" ) { + Log3 $name, 3, "ONKYO_AVR set $name " . @$a[1] . " (wakeup)"; + my $wakeupCmd = AttrVal( $name, "wakeupCmd", "" ); + + if ( $wakeupCmd ne "" ) { + $wakeupCmd =~ s/\$DEVICE/$name/g; + + if ( $wakeupCmd =~ s/^[ \t]*\{|\}[ \t]*$//g ) { + Log3 $name, 4, + "ONKYO_AVR executing wake-up command (Perl): $wakeupCmd"; + $return = eval $wakeupCmd; + } + else { + Log3 $name, 4, + "ONKYO_AVR executing wake-up command (fhem): $wakeupCmd"; + $return = fhem $wakeupCmd; + } + } + else { + $return = + "Device is offline and cannot be controlled at that stage."; + $return .= +"\nYou may enable network-standby to allow a permanent connection to the device by the following command:\nget $name remoteControl network-standby on" + if ( ReadingsVal( $name, "network-standby", "off" ) ne "on" ); + } + } + else { + Log3 $name, 3, "ONKYO_AVR set $name " . @$a[1]; + $return = ONKYO_AVR_SendCommand( $hash, "power", "on" ); + + # don't wait for receiver to confirm power on + # + + readingsBeginUpdate($hash); + + # power + readingsBulkUpdate( $hash, "power", "on" ) + if ( ReadingsVal( $name, "power", "-" ) ne "on" ); + + # stateAV + my $stateAV = ONKYO_AVR_GetStateAV($hash); + readingsBulkUpdate( $hash, "stateAV", $stateAV ) + if ( ReadingsVal( $name, "stateAV", "-" ) ne $stateAV ); + + readingsEndUpdate( $hash, 1 ); + } + } + + # off + elsif ( lc( @$a[1] ) eq "off" ) { + Log3 $name, 3, "ONKYO_AVR set $name " . @$a[1]; + $return = ONKYO_AVR_SendCommand( $hash, "power", "off" ); + } + + # remoteControl + elsif ( lc( @$a[1] ) eq "remotecontrol" ) { + if ( !defined( @$a[2] ) ) { + $return = "No argument given, choose one of minutes off"; + } + else { + Log3 $name, 3, "ONKYO_AVR set $name " . @$a[1] . " " . @$a[2]; + + if ( lc( @$a[2] ) eq "play" + || lc( @$a[2] ) eq "pause" + || lc( @$a[2] ) eq "repeat" + || lc( @$a[2] ) eq "stop" + || lc( @$a[2] ) eq "top" + || lc( @$a[2] ) eq "down" + || lc( @$a[2] ) eq "up" + || lc( @$a[2] ) eq "right" + || lc( @$a[2] ) eq "delete" + || lc( @$a[2] ) eq "display" + || lc( @$a[2] ) eq "ff" + || lc( @$a[2] ) eq "left" + || lc( @$a[2] ) eq "mode" + || lc( @$a[2] ) eq "return" + || lc( @$a[2] ) eq "rew" + || lc( @$a[2] ) eq "select" + || lc( @$a[2] ) eq "setup" + || lc( @$a[2] ) eq "0" + || lc( @$a[2] ) eq "1" + || lc( @$a[2] ) eq "2" + || lc( @$a[2] ) eq "3" + || lc( @$a[2] ) eq "4" + || lc( @$a[2] ) eq "5" + || lc( @$a[2] ) eq "6" + || lc( @$a[2] ) eq "7" + || lc( @$a[2] ) eq "8" + || lc( @$a[2] ) eq "9" ) + { + $return = + ONKYO_AVR_SendCommand( $hash, "net-usb", lc( @$a[2] ) ); + } + elsif ( lc( @$a[2] ) eq "prev" ) { + $return = ONKYO_AVR_SendCommand( $hash, "net-usb", "trdown" ); + } + elsif ( lc( @$a[2] ) eq "next" ) { + $return = ONKYO_AVR_SendCommand( $hash, "net-usb", "trup" ); + } + elsif ( lc( @$a[2] ) eq "shuffle" ) { + $return = ONKYO_AVR_SendCommand( $hash, "net-usb", "random" ); + } + elsif ( lc( @$a[2] ) eq "menu" ) { + $return = ONKYO_AVR_SendCommand( $hash, "net-usb", "men" ); + } + else { + $return = "Unsupported remoteControl command: " . @$a[2]; + } + + } + } + + # play + elsif ( lc( @$a[1] ) eq "play" ) { + Log3 $name, 3, "ONKYO_AVR set $name " . @$a[1]; + + if ( $state ne "on" ) { + $return = +"Device power is turned off, this function is unavailable at that stage."; + } + else { + $return = ONKYO_AVR_SendCommand( $hash, "net-usb", "play" ); + } + } + + # pause + elsif ( lc( @$a[1] ) eq "pause" ) { + Log3 $name, 3, "ONKYO_AVR set $name " . @$a[1]; + + if ( $state ne "on" ) { + $return = +"Device power is turned off, this function is unavailable at that stage."; + } + else { + $return = ONKYO_AVR_SendCommand( $hash, "net-usb", "pause" ); + } + } + + # stop + elsif ( lc( @$a[1] ) eq "stop" ) { + Log3 $name, 3, "ONKYO_AVR set $name " . @$a[1]; + + if ( $state ne "on" ) { + $return = +"Device power is turned off, this function is unavailable at that stage."; + } + else { + $return = ONKYO_AVR_SendCommand( $hash, "net-usb", "stop" ); + } + } + + # shuffle + elsif ( lc( @$a[1] ) eq "shuffle" || lc( @$a[1] ) eq "shufflet" ) { + Log3 $name, 3, "ONKYO_AVR set $name " . @$a[1]; + + if ( $state ne "on" ) { + $return = +"Device power is turned off, this function is unavailable at that stage."; + } + else { + $return = ONKYO_AVR_SendCommand( $hash, "net-usb", "random" ); + } + } + + # repeat + elsif ( lc( @$a[1] ) eq "repeat" || lc( @$a[1] ) eq "repeatt" ) { + Log3 $name, 3, "ONKYO_AVR set $name " . @$a[1]; + + if ( $state ne "on" ) { + $return = +"Device power is turned off, this function is unavailable at that stage."; + } + else { + $return = ONKYO_AVR_SendCommand( $hash, "net-usb", "repeat" ); + } + } + + # previous + elsif ( lc( @$a[1] ) eq "previous" ) { + Log3 $name, 3, "ONKYO_AVR set $name " . @$a[1]; + + if ( $state ne "on" ) { + $return = +"Device power is turned off, this function is unavailable at that stage."; + } + else { + $return = ONKYO_AVR_SendCommand( $hash, "net-usb", "trdown" ); + } + } + + # next + elsif ( lc( @$a[1] ) eq "next" ) { + Log3 $name, 3, "ONKYO_AVR set $name " . @$a[1]; + + if ( $state ne "on" ) { + $return = +"Device power is turned off, this function is unavailable at that stage."; + } + else { + $return = ONKYO_AVR_SendCommand( $hash, "net-usb", "trup" ); + } + } + + # sleep + elsif ( lc( @$a[1] ) eq "sleep" ) { + if ( !defined( @$a[2] ) ) { + $return = "No argument given, choose one of minutes off"; + } + else { + Log3 $name, 3, "ONKYO_AVR set $name " . @$a[1] . " " . @$a[2]; + + if ( @$a[2] eq "off" ) { + $return = ONKYO_AVR_SendCommand( $hash, "sleep", "off" ); + } + elsif ( @$a[2] =~ m/^\d+$/ && @$a[2] > 0 && @$a[2] <= 90 ) { + $return = + ONKYO_AVR_SendCommand( $hash, "sleep", + ONKYO_AVR_dec2hex( @$a[2] ) ); + } + else { + $return = +"Argument does not seem to be a valid integer between 0 and 90"; + } + } + } + + # mute + elsif ( lc( @$a[1] ) eq "mute" || lc( @$a[1] ) eq "mutet" ) { + if ( defined( @$a[2] ) ) { + Log3 $name, 3, "ONKYO_AVR set $name " . @$a[1] . " " . @$a[2]; + } + else { + Log3 $name, 3, "ONKYO_AVR set $name " . @$a[1]; + } + + if ( $state eq "on" ) { + if ( !defined( @$a[2] ) || @$a[2] eq "toggle" ) { + $return = ONKYO_AVR_SendCommand( $hash, "mute", "toggle" ); + } + elsif ( lc( @$a[2] ) eq "off" ) { + $return = ONKYO_AVR_SendCommand( $hash, "mute", "off" ); + } + elsif ( lc( @$a[2] ) eq "on" ) { + $return = ONKYO_AVR_SendCommand( $hash, "mute", "on" ); + } + else { + $return = "Argument does not seem to be one of on off toogle"; + } + } + else { + $return = "Device needs to be ON to mute/unmute audio."; + } + } + + # volume + elsif ( lc( @$a[1] ) eq "volume" ) { + if ( !defined( @$a[2] ) ) { + $return = "No argument given"; + } + else { + Log3 $name, 3, "ONKYO_AVR set $name " . @$a[1] . " " . @$a[2]; + + if ( $state eq "on" ) { + if ( @$a[2] =~ m/^\d+$/ && @$a[2] >= 0 && @$a[2] <= 100 ) { + $return = + ONKYO_AVR_SendCommand( $hash, "volume", + ONKYO_AVR_dec2hex( @$a[2] ) ); + } + else { + $return = +"Argument does not seem to be a valid integer between 0 and 100"; + } + } + else { + $return = "Device needs to be ON to adjust volume."; + } + } + } + + # volumeUp/volumeDown + elsif ( lc( @$a[1] ) =~ /^(volumeup|volumedown)$/ ) { + Log3 $name, 3, "ONKYO_AVR set $name " . @$a[1]; + my $volumeSteps = AttrVal( $name, "volumeSteps", "1" ); + my $volume = ReadingsVal( $name, "volume", "0" ); + + if ( $state eq "on" ) { + if ( lc( @$a[1] ) eq "volumeup" ) { + if ( $volumeSteps > 1 ) { + $return = + ONKYO_AVR_SendCommand( $hash, "volume", + ONKYO_AVR_dec2hex( $volume + $volumeSteps ) ); + } + else { + $return = + ONKYO_AVR_SendCommand( $hash, "volume", "level-up" ); + } + } + else { + if ( $volumeSteps > 1 ) { + $return = + ONKYO_AVR_SendCommand( $hash, "volume", + ONKYO_AVR_dec2hex( $volume - $volumeSteps ) ); + } + else { + $return = + ONKYO_AVR_SendCommand( $hash, "volume", "level-down" ); + } + } + } + else { + $return = "Device needs to be ON to adjust volume."; + } + } + + # input + elsif ( lc( @$a[1] ) eq "input" ) { + if ( !defined( @$a[2] ) ) { + $return = "No input given"; + } + else { + if ( $state eq "off" ) { + $return = ONKYO_AVR_SendCommand( $hash, "power", "on" ); + $return .= fhem "sleep 2;set $name input " . @$a[2]; + } + else { + Log3 $name, 3, "ONKYO_AVR set $name " . @$a[1] . " " . @$a[2]; + $return = ONKYO_AVR_SendCommand( $hash, "input", @$a[2] ); + } + } + } + + # inputUp + elsif ( lc( @$a[1] ) eq "inputup" ) { + if ( $state eq "off" ) { + $return = ONKYO_AVR_SendCommand( $hash, "power", "on" ); + $return .= fhem "sleep 2;set $name inputUp"; + } + else { + Log3 $name, 3, "ONKYO_AVR set $name " . @$a[1]; + $return = ONKYO_AVR_SendCommand( $hash, "input", "up" ); + } + } + + # inputDown + elsif ( lc( @$a[1] ) eq "inputdown" ) { + if ( $state eq "off" ) { + $return = ONKYO_AVR_SendCommand( $hash, "power", "on" ); + $return .= fhem "sleep 2;set $name inputDown"; + } + else { + Log3 $name, 3, "ONKYO_AVR set $name " . @$a[1]; + $return = ONKYO_AVR_SendCommand( $hash, "input", "down" ); + } + } + + # implicit commands through available readings + elsif ( grep $_ eq lc( @$a[1] ), @implicit_cmds ) { + Log3 $name, 3, "ONKYO_AVR set $name " . @$a[1] . " " . @$a[2]; + + if ( !defined( @$a[2] ) ) { + $return = "No argument given"; + } + else { + $return = ONKYO_AVR_SendCommand( $hash, @$a[1], @$a[2] ); + } + } + + # return usage hint + else { + $return = $usage; + } + + readingsEndUpdate( $hash, 1 ); + + # return result + return $return; +} + +sub ONKYO_AVR_Get($$$) { + my ( $hash, $a, $h ) = @_; + my $name = $hash->{NAME}; + my $zone = $hash->{ZONE}; + 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 $name: called function ONKYO_AVR_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" ); + + # createZone + if ( lc( @$a[1] ) eq "createzone" ) { + + if ( !defined( @$a[2] ) ) { + $return = "Syntax: ZONE ID or NAME"; + } + else { + $return = + fhem "define " + . $name . "_" + . @$a[2] + . " ONKYO_AVR_ZONE " + . @$a[2]; + $return = $name . "_" . @$a[2] . " created" + if ( !$return || $return eq "" ); + } + } + + # statusRequest + elsif ( lc( @$a[1] ) eq "statusrequest" ) { + Log3 $name, 3, "ONKYO_AVR get $name " . @$a[1]; + + ONKYO_AVR_SendCommand( $hash, "power", "query" ); + ONKYO_AVR_SendCommand( $hash, "input", "query" ); + ONKYO_AVR_SendCommand( $hash, "mute", "query" ); + ONKYO_AVR_SendCommand( $hash, "volume", "query" ); + ONKYO_AVR_SendCommand( $hash, "sleep", "query" ); + ONKYO_AVR_SendCommand( $hash, "audio-information", "query" ); + ONKYO_AVR_SendCommand( $hash, "video-information", "query" ); + ONKYO_AVR_SendCommand( $hash, "listening-mode", "query" ); + ONKYO_AVR_SendCommand( $hash, "video-picture-mode", "query" ); + ONKYO_AVR_SendCommand( $hash, "phase-matching-bass", "query" ); + ONKYO_AVR_SendCommand( $hash, "center-temporary-level", "query" ); + ONKYO_AVR_SendCommand( $hash, "subwoofer-temporary-level", "query" ); + fhem +"sleep 1 quiet;get $name remoteControl net-receiver-information query quiet"; + } + + # 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: \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 '<command> 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] + . " \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 get $name " . @$a[1] . " " . @$a[2] . " " . @$a[3] + if ( !@$a[4] || @$a[4] ne "quiet" ); + + ONKYO_AVR_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"; + + # createZone + my $zones = ""; + if ( defined( $hash->{helper}{receiver}{device}{zonelist}{zone} ) ) { + foreach my $zoneID ( + keys %{ $hash->{helper}{receiver}{device}{zonelist}{zone} } ) + { + next + if ( + !defined( + $hash->{helper}{receiver}{device}{zonelist}{zone} + {$zoneID}{value} + ) + || $hash->{helper}{receiver}{device}{zonelist}{zone} + {$zoneID}{value} ne "1" + || $zoneID eq "1" + ); + $zones .= "," if ( $zones ne "" ); + $zones .= $zoneID; + } + } + $return .= " createZone:$zones" if ( $zones ne "" ); + $return .= " createZone:2,3,4" if ( $zones eq "" ); + + # remoteControl + $return .= " remoteControl:"; + foreach my $command ( sort keys %{$commands} ) { + $return .= "," . $command; + } + } + + return $return; } -################################### sub ONKYO_AVR_Read($) { my ($hash) = @_; my $name = $hash->{NAME}; @@ -1744,7 +2657,33 @@ sub ONKYO_AVR_Read($) { return; } -################################### +sub ONKYO_AVR_Write($$) { + my ( $hash, $cmd ) = @_; + my $name = $hash->{NAME}; + my $str = ONKYO_AVR_Pack( $cmd, $hash->{PROTOCOLVERSION} ); + + Log3 $name, 1, +"ONKYO_AVR $name: $hash->{DeviceName} snd ERROR - could not transcode $cmd to HEX command" + and return + if ( !$str ); + + # Log3 $name, 5, + # "ONKYO_AVR $name: $hash->{DeviceName} snd " . ONKYO_AVR_hexdump($str); + Log3 $name, 5, "ONKYO_AVR $name: $hash->{DeviceName} snd $str"; + + DevIo_SimpleWrite( $hash, "$str", 0 ); + + # do connection check latest after TIMEOUT + my $next = gettimeofday() + $hash->{TIMEOUT}; + if ( !defined( $hash->{helper}{nextConnectionCheck} ) + || $hash->{helper}{nextConnectionCheck} > $next ) + { + $hash->{helper}{nextConnectionCheck} = $next; + RemoveInternalTimer($hash); + InternalTimer( $next, "ONKYO_AVR_connectionCheck", $hash, 0 ); + } +} + sub ONKYO_AVR_Ready($) { my ($hash) = @_; my $name = $hash->{NAME}; @@ -1771,1196 +2710,227 @@ sub ONKYO_AVR_Ready($) { return ( $InBytes && $InBytes > 0 ); } -################################### -sub ONKYO_AVR_Get($$$) { - my ( $hash, $a, $h ) = @_; - my $name = $hash->{NAME}; - my $zone = $hash->{ZONE}; - 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; +sub ONKYO_AVR_Notify($$) { + my ( $hash, $dev ) = @_; + my $name = $hash->{NAME}; + my $devName = $dev->{NAME}; + my $definedZones = scalar keys %{ $modules{ONKYO_AVR_ZONE}{defptr}{$name} }; + my $presence = ReadingsVal( $name, "presence", "-" ); - Log3 $name, 5, "ONKYO_AVR $name: called function ONKYO_AVR_Get()"; + return + if ( !$dev->{CHANGED} ); # Some previous notify deleted the array. - return "Argument is missing" if ( int(@$a) < 1 ); + # work on global events related to us + if ( $devName eq "global" ) { + foreach my $change ( @{ $dev->{CHANGED} } ) { + if ( $change !~ /^(\w+)\s(\w+)\s?(\w*)\s?(.*)$/ + || $2 ne $name ) + { + return; + } - # readings - return $hash->{READINGS}{ @$a[1] }{VAL} - if ( defined( $hash->{READINGS}{ @$a[1] } ) ); + # DEFINED + # MODIFIED + elsif ( $1 eq "DEFINED" || $1 eq "MODIFIED" ) { + Log3 $hash, 5, + "ONKYO_AVR " + . $name + . ": processing my global event $1: $3 -> $4"; - return "Device is offline and cannot be controlled at that stage." - if ( $presence eq "absent" ); - - # createZone - if ( lc( @$a[1] ) eq "createzone" ) { - - if ( !defined( @$a[2] ) ) { - $return = "Syntax: ZONE ID or NAME"; - } - else { - $return = - fhem "define " - . $name . "_" - . @$a[2] - . " ONKYO_AVR_ZONE " - . @$a[2]; - $return = $name . "_" . @$a[2] . " created" - if ( !$return || $return eq "" ); - } - } - - # statusRequest - elsif ( lc( @$a[1] ) eq "statusrequest" ) { - Log3 $name, 3, "ONKYO_AVR get $name " . @$a[1]; - - ONKYO_AVR_SendCommand( $hash, "power", "query" ); - ONKYO_AVR_SendCommand( $hash, "input", "query" ); - ONKYO_AVR_SendCommand( $hash, "mute", "query" ); - ONKYO_AVR_SendCommand( $hash, "volume", "query" ); - ONKYO_AVR_SendCommand( $hash, "sleep", "query" ); - ONKYO_AVR_SendCommand( $hash, "audio-information", "query" ); - ONKYO_AVR_SendCommand( $hash, "video-information", "query" ); - ONKYO_AVR_SendCommand( $hash, "listening-mode", "query" ); - ONKYO_AVR_SendCommand( $hash, "video-picture-mode", "query" ); - ONKYO_AVR_SendCommand( $hash, "phase-matching-bass", "query" ); - ONKYO_AVR_SendCommand( $hash, "center-temporary-level", "query" ); - ONKYO_AVR_SendCommand( $hash, "subwoofer-temporary-level", "query" ); - fhem -"sleep 1 quiet;get $name remoteControl net-receiver-information query quiet"; - } - - # 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: \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"; + if ( lc( ReadingsVal( $name, "state", "?" ) ) eq "opened" ) { + DoTrigger( $name, "CONNECTED" ); } - - # add command only else { - $valid_commands .= $command . "\n"; + DoTrigger( $name, "DISCONNECTED" ); } } - $valid_commands .= -"\nTry '<command> 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] - . " \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 + # unknown event else { - Log3 $name, 3, - "ONKYO_AVR get $name " . @$a[1] . " " . @$a[2] . " " . @$a[3] - if ( !@$a[4] || @$a[4] ne "quiet" ); - - ONKYO_AVR_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"; - - # createZone - my $zones = ""; - if ( defined( $hash->{helper}{receiver}{device}{zonelist}{zone} ) ) { - foreach my $zoneID ( - keys %{ $hash->{helper}{receiver}{device}{zonelist}{zone} } ) - { - next - if ( - !defined( - $hash->{helper}{receiver}{device}{zonelist}{zone} - {$zoneID}{value} - ) - || $hash->{helper}{receiver}{device}{zonelist}{zone} - {$zoneID}{value} ne "1" - || $zoneID eq "1" - ); - $zones .= "," if ( $zones ne "" ); - $zones .= $zoneID; - } - } - $return .= " createZone:$zones" if ( $zones ne "" ); - $return .= " createZone:2,3,4" if ( $zones eq "" ); - - # remoteControl - $return .= " remoteControl:"; - foreach my $command ( sort keys %{$commands} ) { - $return .= "," . $command; - } - } - - return $return if ($return); -} - -################################### -sub ONKYO_AVR_Set($$$) { - my ( $hash, $a, $h ) = @_; - my $name = $hash->{NAME}; - my $zone = $hash->{ZONE}; - my $state = ReadingsVal( $name, "power", "off" ); - my $presence = ReadingsVal( $name, "presence", "absent" ); - my $return; - my $reading; - my $inputs_txt = ""; - my $channels_txt = ""; - my @implicit_cmds; - my $implicit_txt = ""; - - Log3 $name, 5, "ONKYO_AVR $name: called function ONKYO_AVR_Set()"; - - return "Argument is missing" if ( int(@$a) < 1 ); - - # Input alias handling - if ( defined( $attr{$name}{inputs} ) && $attr{$name}{inputs} ne "" ) { - my @inputs = split( ':', $attr{$name}{inputs} ); - - if (@inputs) { - foreach (@inputs) { - if (m/[^,\s]+(,[^,\s]+)+/) { - my @input_names = split( ',', $_ ); - $inputs_txt .= $input_names[1] . ","; - $input_names[1] =~ s/\s/_/g; - $hash->{helper}{receiver}{input_aliases}{ $input_names[0] } - = $input_names[1]; - $hash->{helper}{receiver}{input_names}{ $input_names[1] } = - $input_names[0]; - } - else { - $inputs_txt .= $_ . ","; - } + Log3 $hash, 5, + "ONKYO_AVR " + . $name + . ": WONT BE processing my global event $1: $3 -> $4"; } } - $inputs_txt =~ s/\s/_/g; - $inputs_txt = substr( $inputs_txt, 0, -1 ); + return; } - # if we could read the actual available inputs from the receiver, use them - elsif (defined( $hash->{helper}{receiver} ) - && ref( $hash->{helper}{receiver} ) eq "HASH" - && defined( $hash->{helper}{receiver}{device}{selectorlist}{count} ) - && $hash->{helper}{receiver}{device}{selectorlist}{count} > 0 ) - { - - foreach my $input ( - @{ $hash->{helper}{receiver}{device}{selectorlist}{selector} } ) - { - if ( $input->{value} eq "1" - && $input->{zone} ne "00" - && $input->{id} ne "80" ) - { - my $id = $input->{id}; - my $name = trim( $input->{name} ); - $inputs_txt .= $name . ","; - } - } - - $inputs_txt =~ s/\s/_/g; - $inputs_txt = substr( $inputs_txt, 0, -1 ); + # do nothing for any other device + elsif ( $devName ne $name ) { + return; } - # use general list of possible inputs - else { - # Find out valid inputs - my $inputs = - ONKYOdb::ONKYO_GetRemotecontrolValue( $zone, - ONKYOdb::ONKYO_GetRemotecontrolCommand( $zone, "input" ) ); - - foreach my $input ( sort keys %{$inputs} ) { - $inputs_txt .= $input . "," - if ( !( $input =~ /^(07|08|09|up|down|query)$/ ) ); - } - $inputs_txt = substr( $inputs_txt, 0, -1 ); - } - - # list of network channels/services - my $channels_src = "internal"; - if ( defined( $hash->{helper}{receiver} ) - && ref( $hash->{helper}{receiver} ) eq "HASH" - && defined( $hash->{helper}{receiver}{device}{netservicelist}{count} ) - && $hash->{helper}{receiver}{device}{netservicelist}{count} > 0 ) - { - - foreach my $id ( - sort keys - %{ $hash->{helper}{receiver}{device}{netservicelist}{netservice} } ) - { - if ( - defined( - $hash->{helper}{receiver}{device}{netservicelist} - {netservice}{$id}{value} - ) - && $hash->{helper}{receiver}{device}{netservicelist} - {netservice}{$id}{value} eq "1" - ) - { - $channels_txt .= - trim( $hash->{helper}{receiver}{device}{netservicelist} - {netservice}{$id}{name} ) - . ","; - } - } - - $channels_txt =~ s/\s/_/g; - $channels_txt = substr( $channels_txt, 0, -1 ); - $channels_src = "receiver"; - } - - # use general list of possible channels - else { - # Find out valid channels - my $channels = - ONKYOdb::ONKYO_GetRemotecontrolValue( "1", - ONKYOdb::ONKYO_GetRemotecontrolCommand( "1", "net-service" ) ); - - foreach my $channel ( sort keys %{$channels} ) { - $channels_txt .= $channel . "," - if ( !( $channel =~ /^(up|down|query)$/ ) ); - } - $channels_txt = substr( $channels_txt, 0, -1 ); - } - - # for each reading, check if there is a known command for it - # and allow to set values if there are any available - if ( defined( $hash->{READINGS} ) ) { - - foreach my $reading ( keys %{ $hash->{READINGS} } ) { - my $cmd_raw = - ONKYOdb::ONKYO_GetRemotecontrolCommand( $zone, $reading ); - my @readingExceptions = ( - "volume", "input", "mute", "sleep", "center-temporary-level", - "subwoofer-temporary-level", "balance", "preset", - ); - - if ( $cmd_raw && !( grep $_ eq $reading, @readingExceptions ) ) { - my $cmd_details = - ONKYOdb::ONKYO_GetRemotecontrolCommandDetails( $zone, - $cmd_raw ); - - my $value_list = ""; - my $debuglist; - foreach my $value ( keys %{ $cmd_details->{values} } ) { - next - if ( $value eq "QSTN" ); - - if ( defined( $cmd_details->{values}{$value}{name} ) ) { - $value_list .= "," if ( $value_list ne "" ); - - $value_list .= $cmd_details->{values}{$value}{name} - if ( - ref( $cmd_details->{values}{$value}{name} ) eq "" ); - - $value_list .= $cmd_details->{values}{$value}{name}[0] - if ( - ref( $cmd_details->{values}{$value}{name} ) eq - "ARRAY" ); - } - } - - if ( $value_list ne "" ) { - push @implicit_cmds, $reading; - $implicit_txt .= " $reading:$value_list"; - } - } - - # tone-* - elsif ( $reading =~ /^tone.*-([a-zA-Z]+)$/ ) { - $implicit_txt .= " $reading:slider,-10,1,10"; - } - - # center-temporary-level - elsif ( $reading eq "center-temporary-level" ) { - $implicit_txt .= " $reading:slider,-12,1,12"; - } - - # subwoofer*-temporary-level - elsif ( $reading =~ /^subwoofer.*-temporary-level$/ ) { - $implicit_txt .= " $reading:slider,-15,1,12"; - } - } - } - - my $preset_txt = ""; - if ( defined( $hash->{helper}{receiver}{preset} ) ) { - - foreach my $id ( - sort - keys %{ $hash->{helper}{receiver}{preset} } - ) - { - my $presetName = - $hash->{helper}{receiver}{preset}{$id}; - next if ( !$presetName || $presetName eq "" ); - - $preset_txt = "preset:" if ( $preset_txt eq "" ); - $preset_txt .= "," - if ( $preset_txt eq "preset:" - && ReadingsVal( $name, "preset", "-" ) eq "" ); - - $presetName =~ s/\s/_/g; - $preset_txt .= $presetName . ","; - } - } - $preset_txt = substr( $preset_txt, 0, -1 ) if ( $preset_txt ne "" ); - - if ( $preset_txt eq "" ) { - $preset_txt = "preset:"; - $preset_txt .= "," if ( ReadingsVal( $name, "preset", "-" ) eq "" ); - $preset_txt .= -"1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40"; - } - - my $shuffle_txt = "shuffle:"; - $shuffle_txt .= "," if ( ReadingsVal( $name, "shuffle", "-" ) eq "-" ); - $shuffle_txt .= "off,on,on-album,on-folder"; - - my $repeat_txt = "repeat:"; - $repeat_txt .= "," if ( ReadingsVal( $name, "repeat", "-" ) eq "-" ); - $repeat_txt .= "off,all,all-folder,one"; - - my $usage = - "Unknown argument '" - . @$a[1] - . "', choose one of toggle:noArg on:noArg off:noArg volume:slider,0,1,100 volumeDown:noArg volumeUp:noArg mute:off,on muteT:noArg play:noArg pause:noArg stop:noArg previous:noArg next:noArg shuffleT:noArg repeatT:noArg remoteControl:play,pause,repeat,stop,top,down,up,right,delete,display,ff,left,mode,return,rew,select,setup,0,1,2,3,4,5,6,7,8,9,prev,next,shuffle,menu channelDown:noArg channelUp:noArg inputDown:noArg inputUp:noArg internet-radio-preset:1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40 input:" - . $inputs_txt; - $usage .= " channel:$channels_txt"; - $usage .= " presetDown:noArg presetUp:noArg $preset_txt"; - $usage .= " $shuffle_txt"; - $usage .= " $repeat_txt"; - $usage .= $implicit_txt if ( $implicit_txt ne "" ); - $usage .= " sleep:off,5,10,15,30,60,90"; - - if ( ReadingsVal( $name, "currentTrackPosition", "--:--" ) ne "--:--" ) { - $usage .= " currentTrackPosition"; - } - - my $cmd = ''; - - return "Device is offline and cannot be controlled at that stage." - if ( $presence eq "absent" - && lc( @$a[1] ) ne "on" - && lc( @$a[1] ) ne "?" - && lc( @$a[1] ) ne "help" ); - readingsBeginUpdate($hash); - # create inputList reading for frontends - readingsBulkUpdate( $hash, "inputList", $inputs_txt ) - if ( ReadingsVal( $name, "inputList", "-" ) ne $inputs_txt ); + foreach my $change ( @{ $dev->{CHANGED} } ) { - # create channelList reading for frontends - readingsBulkUpdate( $hash, "channelList", $channels_txt ) - if ( - ( - $channels_src eq "internal" - && ReadingsVal( $name, "channelList", "-" ) eq "-" - ) - || ( $channels_src eq "receiver" - && ReadingsVal( $name, "channelList", "-" ) ne $channels_txt ) - ); + # DISCONNECTED + if ( $change eq "DISCONNECTED" ) { + Log3 $hash, 5, "ONKYO_AVR " . $name . ": processing change $change"; - # channel - if ( lc( @$a[1] ) eq "channel" ) { - if ( !defined( @$a[2] ) ) { - $return = "Syntax: CHANNELNAME [USERNAME PASSWORD]"; - } - else { - if ( $state eq "off" ) { - $return = ONKYO_AVR_SendCommand( $hash, "power", "on" ); - $return .= fhem "sleep 5;set $name channel " . @$a[2]; - } - elsif ( $hash->{INPUT} ne "2B" ) { - $return = ONKYO_AVR_SendCommand( $hash, "input", "2B" ); - $return .= fhem "sleep 1;set $name channel " . @$a[2]; - } - elsif ( ReadingsVal( $name, "channel", "" ) ne @$a[2] - || ( defined( @$a[3] ) && defined( @$a[4] ) ) ) - { + # disable connectionCheck and wait + # until DevIo reopened the connection + RemoveInternalTimer($hash); - my $servicename = ""; - my $channelname = @$a[2]; + readingsBulkUpdate( $hash, "presence", "absent" ) + if ( $presence ne "absent" ); - if ( - defined( $hash->{helper}{receiver} ) - && ref( $hash->{helper}{receiver} ) eq "HASH" - && defined( - $hash->{helper}{receiver}{device}{netservicelist}{count} - ) - && $hash->{helper}{receiver}{device}{netservicelist}{count} - > 0 - ) - { - - $channelname =~ s/_/ /g; - - foreach my $id ( - sort keys %{ - $hash->{helper}{receiver}{device}{netservicelist} - {netservice} - } - ) - { - if ( - defined( - $hash->{helper}{receiver}{device} - {netservicelist}{netservice}{$id}{value} - ) - && $hash->{helper}{receiver}{device} - {netservicelist}{netservice}{$id}{value} eq "1" - && $hash->{helper}{receiver}{device} - {netservicelist}{netservice}{$id}{name} eq - $channelname - ) - { - $servicename .= uc($id); - last; - } - } - } - else { - my $channels = ONKYOdb::ONKYO_GetRemotecontrolValue( - "1", - ONKYOdb::ONKYO_GetRemotecontrolCommand( - "1", "net-service" - ) - ); - - $servicename = $channels->{$channelname} - if ( defined( $channels->{$channelname} ) ); - } - - Log3 $name, 3, "ONKYO_AVR set $name " . @$a[1] . " " . @$a[2]; - - $servicename = uc($channelname) - if ( $servicename eq "" ); - - $servicename .= "0" if ( !defined( @$a[3] ) ); - $servicename .= "1" . @$a[3] if ( defined( @$a[3] ) ); - $servicename .= @$a[4] if ( defined( @$a[4] ) ); - - $return = - ONKYO_AVR_SendCommand( $hash, "net-service", $servicename ); - } - } - } - - # channelDown - elsif ( lc( @$a[1] ) eq "channeldown" ) { - if ( $state eq "off" ) { - $return = ONKYO_AVR_SendCommand( $hash, "power", "on" ); - $return .= fhem "sleep 5;set $name channelDown"; - } - elsif ( $hash->{INPUT} ne "2B" ) { - $return = ONKYO_AVR_SendCommand( $hash, "input", "2B" ); - $return .= fhem "sleep 1;set $name channelDown"; - } - else { - Log3 $name, 3, "ONKYO_AVR set $name " . @$a[1]; - $return = ONKYO_AVR_SendCommand( $hash, "net-usb", "chdn" ); - } - } - - # channelUp - elsif ( lc( @$a[1] ) eq "channelup" ) { - if ( $state eq "off" ) { - $return = ONKYO_AVR_SendCommand( $hash, "power", "on" ); - $return .= fhem "sleep 5;set $name channelUp"; - } - elsif ( $hash->{INPUT} ne "2B" ) { - $return = ONKYO_AVR_SendCommand( $hash, "input", "2B" ); - $return .= fhem "sleep 1;set $name channelUp"; - } - else { - Log3 $name, 3, "ONKYO_AVR set $name " . @$a[1]; - $return = ONKYO_AVR_SendCommand( $hash, "net-usb", "chup" ); - } - } - - # currentTrackPosition - elsif ( lc( @$a[1] ) eq "currenttrackposition" ) { - Log3 $name, 3, "ONKYO_AVR set $name " . @$a[1] . " " . @$a[2]; - - if ( !defined( @$a[2] ) ) { - $return = "No argument given"; - } - else { - - if ( @$a[2] !~ /^[0-9][0-9]:[0-5][0-9]$/ ) { - $return = - "Time needs to have format mm:ss and between 00:00 and 99:59"; - } - else { - $return = - ONKYO_AVR_SendCommand( $hash, "net-usb-time-seek", @$a[2] ); - } - } - } - - # internet-radio-preset - elsif ( lc( @$a[1] ) eq "internet-radio-preset" ) { - if ( !defined( @$a[2] ) ) { - $return = "No argument given"; - } - else { - if ( $state eq "off" ) { - $return = ONKYO_AVR_SendCommand( $hash, "power", "on" ); - $return .= fhem "sleep 5;set $name " . @$a[1] . " " . @$a[2]; - } - elsif ( $hash->{INPUT} ne "2B" ) { - $return = ONKYO_AVR_SendCommand( $hash, "input", "2B" ); - $return .= fhem "sleep 5;set $name " . @$a[1] . " " . @$a[2]; - } - elsif ( @$a[2] =~ /^\d*$/ ) { - Log3 $name, 3, "ONKYO_AVR set $name " . @$a[1] . " " . @$a[2]; - $return = ONKYO_AVR_SendCommand( - $hash, - lc( @$a[1] ), - ONKYO_AVR_dec2hex( @$a[2] ) - ); - } - else { - $return = "Invalid argument format"; - } - } - } - - # preset - elsif ( lc( @$a[1] ) eq "preset" ) { - if ( !defined( @$a[2] ) ) { - $return = "No argument given"; - } - else { - if ( $state eq "off" ) { - $return = ONKYO_AVR_SendCommand( $hash, "power", "on" ); - $return .= fhem "sleep 5;set $name preset " . @$a[2]; - } - elsif ( $hash->{INPUT} ne "24" && $hash->{INPUT} ne "25" ) { - $return = ONKYO_AVR_SendCommand( $hash, "input", "24" ); - $return .= fhem "sleep 1;set $name preset " . @$a[2]; - } - elsif ( lc( @$a[2] ) eq "up" ) { - Log3 $name, 3, "ONKYO_AVR set $name " . @$a[1] . " " . @$a[2]; - $return = ONKYO_AVR_SendCommand( $hash, lc( @$a[1] ), "UP" ); - } - elsif ( lc( @$a[2] ) eq "down" ) { - Log3 $name, 3, "ONKYO_AVR set $name " . @$a[1] . " " . @$a[2]; - $return = - ONKYO_AVR_SendCommand( $hash, lc( @$a[1] ), "DOWN" ); - } - elsif ( @$a[2] =~ /^\d*$/ ) { - Log3 $name, 3, "ONKYO_AVR set $name " . @$a[1] . " " . @$a[2]; - $return = ONKYO_AVR_SendCommand( - $hash, - lc( @$a[1] ), - ONKYO_AVR_dec2hex( @$a[2] ) - ); - } - elsif ( defined( $hash->{helper}{receiver}{preset} ) ) { - - foreach - my $id ( sort keys %{ $hash->{helper}{receiver}{preset} } ) - { - my $presetName = - $hash->{helper}{receiver}{preset}{$id}; - next if ( !$presetName || $presetName eq "" ); - - $presetName =~ s/\s/_/g; - - if ( $presetName eq @$a[2] ) { - Log3 $name, 3, - "ONKYO_AVR set $name " . @$a[1] . " " . @$a[2]; - - $return = - ONKYO_AVR_SendCommand( $hash, lc( @$a[1] ), uc($id) ); - - last; - } - } - } - } - } - - # presetDown - elsif ( lc( @$a[1] ) eq "presetdown" ) { - if ( $state eq "off" ) { - $return = ONKYO_AVR_SendCommand( $hash, "power", "on" ); - $return .= fhem "sleep 5;set $name presetDown"; - } - elsif ( $hash->{INPUT} ne "24" && $hash->{INPUT} ne "25" ) { - $return = ONKYO_AVR_SendCommand( $hash, "input", "24" ); - $return .= fhem "sleep 1;set $name presetDown"; - } - else { - Log3 $name, 3, "ONKYO_AVR set $name " . @$a[1]; - $return = ONKYO_AVR_SendCommand( $hash, "preset", "down" ); - } - } - - # presetUp - elsif ( lc( @$a[1] ) eq "presetup" ) { - if ( $state eq "off" ) { - $return = ONKYO_AVR_SendCommand( $hash, "power", "on" ); - $return .= fhem "sleep 5;set $name presetUp"; - } - elsif ( $hash->{INPUT} ne "24" && $hash->{INPUT} ne "25" ) { - $return = ONKYO_AVR_SendCommand( $hash, "input", "24" ); - $return .= fhem "sleep 1;set $name presetUp"; - } - else { - Log3 $name, 3, "ONKYO_AVR set $name " . @$a[1]; - $return = ONKYO_AVR_SendCommand( $hash, "preset", "up" ); - } - } - - # tone-* - elsif ( lc( @$a[1] ) =~ /^(tone.*)-(bass|treble)$/ ) { - Log3 $name, 3, "ONKYO_AVR set $name " . @$a[1] . " " . @$a[2]; - - if ( !defined( @$a[2] ) ) { - $return = "No argument given"; - } - else { - if ( $state eq "off" ) { - $return = -"Device power is turned off, this function is unavailable at that stage."; - } - elsif ( lc( @$a[2] ) eq "up" ) { - my $setVal = ""; - $setVal = "B" if ( $2 eq "bass" ); - $setVal = "T" if ( $2 eq "treble" ); - $return = - ONKYO_AVR_SendCommand( $hash, lc($1), $setVal . "UP" ); - } - elsif ( lc( @$a[2] ) eq "down" ) { - my $setVal = ""; - $setVal = "B" if ( $2 eq "bass" ); - $setVal = "T" if ( $2 eq "treble" ); - $return = - ONKYO_AVR_SendCommand( $hash, lc($1), $setVal . "DOWN" ); - } - elsif ( @$a[2] =~ /^-*\d+$/ ) { - my $setVal = ""; - $setVal = "B" if ( $2 eq "bass" ); - $setVal = "T" if ( $2 eq "treble" ); - $setVal .= "+" if ( @$a[2] > 0 ); - $setVal .= "-" if ( @$a[2] < 0 ); - - my $setVal2 = @$a[2]; - $setVal2 = substr( $setVal2, 1 ) if ( $setVal2 < 0 ); - $setVal2 = ONKYO_AVR_dec2hex($setVal2); - $setVal2 = substr( $setVal2, 1 ) if ( $setVal2 ne "00" ); - - $return = - ONKYO_AVR_SendCommand( $hash, lc($1), $setVal . $setVal2 ); - } - } - } - - # center-temporary-level - # subwoofer-temporary-level - elsif (lc( @$a[1] ) eq "center-temporary-level" - || lc( @$a[1] ) eq "subwoofer-temporary-level" ) - { - Log3 $name, 3, "ONKYO_AVR set $name " . @$a[1] . " " . @$a[2]; - - if ( !defined( @$a[2] ) ) { - $return = "No argument given"; - } - else { - if ( $state eq "off" ) { - $return = -"Device power is turned off, this function is unavailable at that stage."; - } - elsif ( lc( @$a[2] ) eq "up" ) { - $return = ONKYO_AVR_SendCommand( $hash, lc($1), "UP" ); - } - elsif ( lc( @$a[2] ) eq "down" ) { - $return = ONKYO_AVR_SendCommand( $hash, lc($1), "DOWN" ); - } - elsif ( @$a[2] =~ /^-*\d+$/ ) { - my $setVal = ""; - $setVal = "+" if ( @$a[2] > 0 ); - $setVal = "-" if ( @$a[2] < 0 ); - - my $setVal2 = @$a[2]; - $setVal2 = substr( $setVal2, 1 ) if ( $setVal2 < 0 ); - $setVal2 = ONKYO_AVR_dec2hex($setVal2); - $setVal2 = substr( $setVal2, 1 ) if ( $setVal2 ne "00" ); - - $return = ONKYO_AVR_SendCommand( - $hash, - lc( @$a[1] ), - $setVal . $setVal2 - ); - } - } - } - - # toggle - elsif ( lc( @$a[1] ) eq "toggle" ) { - if ( $state eq "off" ) { - $return = fhem "set $name on"; - } - else { - $return = fhem "set $name off"; - } - } - - # on - elsif ( lc( @$a[1] ) eq "on" ) { - if ( $presence eq "absent" ) { - Log3 $name, 3, "ONKYO_AVR set $name " . @$a[1] . " (wakeup)"; - my $wakeupCmd = AttrVal( $name, "wakeupCmd", "" ); - - if ( $wakeupCmd ne "" ) { - $wakeupCmd =~ s/\$DEVICE/$name/g; - - if ( $wakeupCmd =~ s/^[ \t]*\{|\}[ \t]*$//g ) { - Log3 $name, 4, - "ONKYO_AVR executing wake-up command (Perl): $wakeupCmd"; - $return = eval $wakeupCmd; - } - else { - Log3 $name, 4, - "ONKYO_AVR executing wake-up command (fhem): $wakeupCmd"; - $return = fhem $wakeupCmd; - } - } - else { - $return = - "Device is offline and cannot be controlled at that stage."; - $return .= -"\nYou may enable network-standby to allow a permanent connection to the device by the following command:\nget $name remoteControl network-standby on" - if ( ReadingsVal( $name, "network-standby", "off" ) ne "on" ); - } - } - else { - Log3 $name, 3, "ONKYO_AVR set $name " . @$a[1]; - $return = ONKYO_AVR_SendCommand( $hash, "power", "on" ); - - # don't wait for receiver to confirm power on - # - - readingsBeginUpdate($hash); - - # power - readingsBulkUpdate( $hash, "power", "on" ) - if ( ReadingsVal( $name, "power", "-" ) ne "on" ); + readingsBulkUpdate( $hash, "power", "off" ) + if ( ReadingsVal( $name, "power", "on" ) ne "off" ); # stateAV my $stateAV = ONKYO_AVR_GetStateAV($hash); readingsBulkUpdate( $hash, "stateAV", $stateAV ) if ( ReadingsVal( $name, "stateAV", "-" ) ne $stateAV ); - readingsEndUpdate( $hash, 1 ); + # send to slaves + if ( $definedZones > 1 ) { + Log3 $name, 5, + "ONKYO_AVR $name: Dispatching state change to slaves"; + Dispatch( + $hash, + { + "presence" => "absent", + "power" => "off", + }, + undef + ); + } } - } - # off - elsif ( lc( @$a[1] ) eq "off" ) { - Log3 $name, 3, "ONKYO_AVR set $name " . @$a[1]; - $return = ONKYO_AVR_SendCommand( $hash, "power", "off" ); - } + # CONNECTED + elsif ( $change eq "CONNECTED" ) { + Log3 $hash, 5, "ONKYO_AVR " . $name . ": processing change $change"; - # remoteControl - elsif ( lc( @$a[1] ) eq "remotecontrol" ) { - if ( !defined( @$a[2] ) ) { - $return = "No argument given, choose one of minutes off"; - } - else { - Log3 $name, 3, "ONKYO_AVR set $name " . @$a[1] . " " . @$a[2]; + readingsBulkUpdate( $hash, "presence", "present" ) + if ( $presence ne "present" ); - if ( lc( @$a[2] ) eq "play" - || lc( @$a[2] ) eq "pause" - || lc( @$a[2] ) eq "repeat" - || lc( @$a[2] ) eq "stop" - || lc( @$a[2] ) eq "top" - || lc( @$a[2] ) eq "down" - || lc( @$a[2] ) eq "up" - || lc( @$a[2] ) eq "right" - || lc( @$a[2] ) eq "delete" - || lc( @$a[2] ) eq "display" - || lc( @$a[2] ) eq "ff" - || lc( @$a[2] ) eq "left" - || lc( @$a[2] ) eq "mode" - || lc( @$a[2] ) eq "return" - || lc( @$a[2] ) eq "rew" - || lc( @$a[2] ) eq "select" - || lc( @$a[2] ) eq "setup" - || lc( @$a[2] ) eq "0" - || lc( @$a[2] ) eq "1" - || lc( @$a[2] ) eq "2" - || lc( @$a[2] ) eq "3" - || lc( @$a[2] ) eq "4" - || lc( @$a[2] ) eq "5" - || lc( @$a[2] ) eq "6" - || lc( @$a[2] ) eq "7" - || lc( @$a[2] ) eq "8" - || lc( @$a[2] ) eq "9" ) - { - $return = - ONKYO_AVR_SendCommand( $hash, "net-usb", lc( @$a[2] ) ); - } - elsif ( lc( @$a[2] ) eq "prev" ) { - $return = ONKYO_AVR_SendCommand( $hash, "net-usb", "trdown" ); - } - elsif ( lc( @$a[2] ) eq "next" ) { - $return = ONKYO_AVR_SendCommand( $hash, "net-usb", "trup" ); - } - elsif ( lc( @$a[2] ) eq "shuffle" ) { - $return = ONKYO_AVR_SendCommand( $hash, "net-usb", "random" ); - } - elsif ( lc( @$a[2] ) eq "menu" ) { - $return = ONKYO_AVR_SendCommand( $hash, "net-usb", "men" ); - } - else { - $return = "Unsupported remoteControl command: " . @$a[2]; + # stateAV + my $stateAV = ONKYO_AVR_GetStateAV($hash); + readingsBulkUpdate( $hash, "stateAV", $stateAV ) + if ( ReadingsVal( $name, "stateAV", "-" ) ne $stateAV ); + + ONKYO_AVR_SendCommand( $hash, "power", "query" ); + ONKYO_AVR_SendCommand( $hash, "network-standby", "query" ); + ONKYO_AVR_SendCommand( $hash, "input", "query" ); + ONKYO_AVR_SendCommand( $hash, "mute", "query" ); + ONKYO_AVR_SendCommand( $hash, "volume", "query" ); + ONKYO_AVR_SendCommand( $hash, "sleep", "query" ); + ONKYO_AVR_SendCommand( $hash, "audio-information", "query" ); + ONKYO_AVR_SendCommand( $hash, "video-information", "query" ); + ONKYO_AVR_SendCommand( $hash, "listening-mode", "query" ); + ONKYO_AVR_SendCommand( $hash, "video-picture-mode", "query" ); + ONKYO_AVR_SendCommand( $hash, "phase-matching-bass", "query" ); + ONKYO_AVR_SendCommand( $hash, "center-temporary-level", "query" ); + ONKYO_AVR_SendCommand( $hash, "subwoofer-temporary-level", + "query" ); + fhem +"sleep 1 quiet;get $name remoteControl net-receiver-information query quiet"; + + # send to slaves + if ( $definedZones > 1 ) { + Log3 $name, 5, + "ONKYO_AVR $name: Dispatching state change to slaves"; + Dispatch( + $hash, + { + "presence" => "present", + }, + undef + ); } } } - # play - elsif ( lc( @$a[1] ) eq "play" ) { - Log3 $name, 3, "ONKYO_AVR set $name " . @$a[1]; - - if ( $state ne "on" ) { - $return = -"Device power is turned off, this function is unavailable at that stage."; - } - else { - $return = ONKYO_AVR_SendCommand( $hash, "net-usb", "play" ); - } - } - - # pause - elsif ( lc( @$a[1] ) eq "pause" ) { - Log3 $name, 3, "ONKYO_AVR set $name " . @$a[1]; - - if ( $state ne "on" ) { - $return = -"Device power is turned off, this function is unavailable at that stage."; - } - else { - $return = ONKYO_AVR_SendCommand( $hash, "net-usb", "pause" ); - } - } - - # stop - elsif ( lc( @$a[1] ) eq "stop" ) { - Log3 $name, 3, "ONKYO_AVR set $name " . @$a[1]; - - if ( $state ne "on" ) { - $return = -"Device power is turned off, this function is unavailable at that stage."; - } - else { - $return = ONKYO_AVR_SendCommand( $hash, "net-usb", "stop" ); - } - } - - # shuffle - elsif ( lc( @$a[1] ) eq "shuffle" || lc( @$a[1] ) eq "shufflet" ) { - Log3 $name, 3, "ONKYO_AVR set $name " . @$a[1]; - - if ( $state ne "on" ) { - $return = -"Device power is turned off, this function is unavailable at that stage."; - } - else { - $return = ONKYO_AVR_SendCommand( $hash, "net-usb", "random" ); - } - } - - # repeat - elsif ( lc( @$a[1] ) eq "repeat" || lc( @$a[1] ) eq "repeatt" ) { - Log3 $name, 3, "ONKYO_AVR set $name " . @$a[1]; - - if ( $state ne "on" ) { - $return = -"Device power is turned off, this function is unavailable at that stage."; - } - else { - $return = ONKYO_AVR_SendCommand( $hash, "net-usb", "repeat" ); - } - } - - # previous - elsif ( lc( @$a[1] ) eq "previous" ) { - Log3 $name, 3, "ONKYO_AVR set $name " . @$a[1]; - - if ( $state ne "on" ) { - $return = -"Device power is turned off, this function is unavailable at that stage."; - } - else { - $return = ONKYO_AVR_SendCommand( $hash, "net-usb", "trdown" ); - } - } - - # next - elsif ( lc( @$a[1] ) eq "next" ) { - Log3 $name, 3, "ONKYO_AVR set $name " . @$a[1]; - - if ( $state ne "on" ) { - $return = -"Device power is turned off, this function is unavailable at that stage."; - } - else { - $return = ONKYO_AVR_SendCommand( $hash, "net-usb", "trup" ); - } - } - - # sleep - elsif ( lc( @$a[1] ) eq "sleep" ) { - if ( !defined( @$a[2] ) ) { - $return = "No argument given, choose one of minutes off"; - } - else { - Log3 $name, 3, "ONKYO_AVR set $name " . @$a[1] . " " . @$a[2]; - - if ( @$a[2] eq "off" ) { - $return = ONKYO_AVR_SendCommand( $hash, "sleep", "off" ); - } - elsif ( @$a[2] =~ m/^\d+$/ && @$a[2] > 0 && @$a[2] <= 90 ) { - $return = - ONKYO_AVR_SendCommand( $hash, "sleep", - ONKYO_AVR_dec2hex( @$a[2] ) ); - } - else { - $return = -"Argument does not seem to be a valid integer between 0 and 90"; - } - } - } - - # mute - elsif ( lc( @$a[1] ) eq "mute" || lc( @$a[1] ) eq "mutet" ) { - if ( defined( @$a[2] ) ) { - Log3 $name, 3, "ONKYO_AVR set $name " . @$a[1] . " " . @$a[2]; - } - else { - Log3 $name, 3, "ONKYO_AVR set $name " . @$a[1]; - } - - if ( $state eq "on" ) { - if ( !defined( @$a[2] ) || @$a[2] eq "toggle" ) { - $return = ONKYO_AVR_SendCommand( $hash, "mute", "toggle" ); - } - elsif ( lc( @$a[2] ) eq "off" ) { - $return = ONKYO_AVR_SendCommand( $hash, "mute", "off" ); - } - elsif ( lc( @$a[2] ) eq "on" ) { - $return = ONKYO_AVR_SendCommand( $hash, "mute", "on" ); - } - else { - $return = "Argument does not seem to be one of on off toogle"; - } - } - else { - $return = "Device needs to be ON to mute/unmute audio."; - } - } - - # volume - elsif ( lc( @$a[1] ) eq "volume" ) { - if ( !defined( @$a[2] ) ) { - $return = "No argument given"; - } - else { - Log3 $name, 3, "ONKYO_AVR set $name " . @$a[1] . " " . @$a[2]; - - if ( $state eq "on" ) { - if ( @$a[2] =~ m/^\d+$/ && @$a[2] >= 0 && @$a[2] <= 100 ) { - $return = - ONKYO_AVR_SendCommand( $hash, "volume", - ONKYO_AVR_dec2hex( @$a[2] ) ); - } - else { - $return = -"Argument does not seem to be a valid integer between 0 and 100"; - } - } - else { - $return = "Device needs to be ON to adjust volume."; - } - } - } - - # volumeUp/volumeDown - elsif ( lc( @$a[1] ) =~ /^(volumeup|volumedown)$/ ) { - Log3 $name, 3, "ONKYO_AVR set $name " . @$a[1]; - my $volumeSteps = AttrVal( $name, "volumeSteps", "1" ); - my $volume = ReadingsVal( $name, "volume", "0" ); - - if ( $state eq "on" ) { - if ( lc( @$a[1] ) eq "volumeup" ) { - if ( $volumeSteps > 1 ) { - $return = - ONKYO_AVR_SendCommand( $hash, "volume", - ONKYO_AVR_dec2hex( $volume + $volumeSteps ) ); - } - else { - $return = - ONKYO_AVR_SendCommand( $hash, "volume", "level-up" ); - } - } - else { - if ( $volumeSteps > 1 ) { - $return = - ONKYO_AVR_SendCommand( $hash, "volume", - ONKYO_AVR_dec2hex( $volume - $volumeSteps ) ); - } - else { - $return = - ONKYO_AVR_SendCommand( $hash, "volume", "level-down" ); - } - } - } - else { - $return = "Device needs to be ON to adjust volume."; - } - } - - # input - elsif ( lc( @$a[1] ) eq "input" ) { - if ( !defined( @$a[2] ) ) { - $return = "No input given"; - } - else { - if ( $state eq "off" ) { - $return = ONKYO_AVR_SendCommand( $hash, "power", "on" ); - $return .= fhem "sleep 2;set $name input " . @$a[2]; - } - else { - Log3 $name, 3, "ONKYO_AVR set $name " . @$a[1] . " " . @$a[2]; - $return = ONKYO_AVR_SendCommand( $hash, "input", @$a[2] ); - } - } - } - - # inputUp - elsif ( lc( @$a[1] ) eq "inputup" ) { - if ( $state eq "off" ) { - $return = ONKYO_AVR_SendCommand( $hash, "power", "on" ); - $return .= fhem "sleep 2;set $name inputUp"; - } - else { - Log3 $name, 3, "ONKYO_AVR set $name " . @$a[1]; - $return = ONKYO_AVR_SendCommand( $hash, "input", "up" ); - } - } - - # inputDown - elsif ( lc( @$a[1] ) eq "inputdown" ) { - if ( $state eq "off" ) { - $return = ONKYO_AVR_SendCommand( $hash, "power", "on" ); - $return .= fhem "sleep 2;set $name inputDown"; - } - else { - Log3 $name, 3, "ONKYO_AVR set $name " . @$a[1]; - $return = ONKYO_AVR_SendCommand( $hash, "input", "down" ); - } - } - - # implicit commands through available readings - elsif ( grep $_ eq lc( @$a[1] ), @implicit_cmds ) { - Log3 $name, 3, "ONKYO_AVR set $name " . @$a[1] . " " . @$a[2]; - - if ( !defined( @$a[2] ) ) { - $return = "No argument given"; - } - else { - $return = ONKYO_AVR_SendCommand( $hash, @$a[1], @$a[2] ); - } - } - - # return usage hint - else { - $return = $usage; - } - readingsEndUpdate( $hash, 1 ); - - # return result - return $return; } -############################################################################################################ -# -# Begin of helper functions -# -############################################################################################################ +sub ONKYO_AVR_Shutdown($) { + my ($hash) = @_; + my $name = $hash->{NAME}; + Log3 $name, 5, "ONKYO_AVR $name: called function ONKYO_AVR_Shutdown()"; + + DevIo_CloseDev($hash); + return undef; +} + +# module Fn #################################################################### +sub ONKYO_AVR_DevInit($) { + my ($hash) = @_; + my $name = $hash->{NAME}; + + if ( lc( ReadingsVal( $name, "state", "?" ) ) eq "opened" ) { + DoTrigger( $name, "CONNECTED" ); + } + else { + DoTrigger( $name, "DISCONNECTED" ); + } +} + +sub ONKYO_AVR_addExtension($$$) { + my ( $name, $func, $link ) = @_; + + my $url = "?/$link"; + + return 0 + if ( defined( $data{FWEXT}{$url} ) + && $data{FWEXT}{$url}{deviceName} ne $name ); + + Log3 $name, 2, + "ONKYO_AVR $name: Registering ONKYO_AVR for webhook URI $url ..."; + $data{FWEXT}{$url}{deviceName} = $name; + $data{FWEXT}{$url}{FUNC} = $func; + $data{FWEXT}{$url}{LINK} = $link; + + return 1; +} + +sub ONKYO_AVR_removeExtension($) { + my ($link) = @_; + + my $url = "?/$link"; + my $name = $data{FWEXT}{$url}{deviceName}; + Log3 $name, 2, + "ONKYO_AVR $name: Unregistering ONKYO_AVR for webhook URI $url..."; + delete $data{FWEXT}{$url}; +} + +sub ONKYO_AVR_CGI() { + my ($request) = @_; + + # data received + if ( $request =~ m,^\?\/ONKYO_AVR\/cover\/(.+)\.(.+)$, ) { + + Log3 undef, 5, "ONKYO_AVR: sending cover $1.$2"; + + if ( $1 eq "empty" && $2 eq "jpg" ) { + FW_serveSpecial( 'sonos_empty', 'jpg', + AttrVal( "global", "modpath", "." ) . '/FHEM/lib/UPnP', 1 ); + } + else { + FW_serveSpecial( + $1, + $2, + AttrVal( "global", "modpath", "." ) + . '/www/images/default/ONKYO_AVR', + 1 + ); + } + + return ( undef, undef ); + } + + # no data received + else { + Log3 undef, 5, "ONKYO_AVR: received malformed request\n$request"; + } + + return ( "text/plain; charset=utf-8", "Call failure: " . $request ); +} -################################### sub ONKYO_AVR_SendCommand($$$) { my ( $hash, $cmd, $value ) = @_; my $name = $hash->{NAME}; @@ -3029,35 +2999,6 @@ sub ONKYO_AVR_SendCommand($$$) { return; } -################################### -sub ONKYO_AVR_Write($$) { - my ( $hash, $cmd ) = @_; - my $name = $hash->{NAME}; - my $str = ONKYO_AVR_Pack( $cmd, $hash->{PROTOCOLVERSION} ); - - Log3 $name, 1, -"ONKYO_AVR $name: $hash->{DeviceName} snd ERROR - could not transcode $cmd to HEX command" - and return - if ( !$str ); - - # Log3 $name, 5, - # "ONKYO_AVR $name: $hash->{DeviceName} snd " . ONKYO_AVR_hexdump($str); - Log3 $name, 5, "ONKYO_AVR $name: $hash->{DeviceName} snd $str"; - - DevIo_SimpleWrite( $hash, "$str", 0 ); - - # do connection check latest after TIMEOUT - my $next = gettimeofday() + $hash->{TIMEOUT}; - if ( !defined( $hash->{helper}{nextConnectionCheck} ) - || $hash->{helper}{nextConnectionCheck} > $next ) - { - $hash->{helper}{nextConnectionCheck} = $next; - RemoveInternalTimer($hash); - InternalTimer( $next, "ONKYO_AVR_connectionCheck", $hash, 0 ); - } -} - -################################### sub ONKYO_AVR_connectionCheck ($) { my ($hash) = @_; my $name = $hash->{NAME}; @@ -3089,7 +3030,6 @@ sub ONKYO_AVR_connectionCheck ($) { delete $attr{$name}{verbose} if ( $verbose eq "" ); } -################################### sub ONKYO_AVR_WriteFile($$) { my ( $fileName, $data ) = @_; @@ -3099,7 +3039,6 @@ sub ONKYO_AVR_WriteFile($$) { close IMGFILE; } -################################### sub ONKYO_AVR_Pack($;$) { my ( $d, $protocol ) = @_; @@ -3149,7 +3088,6 @@ sub ONKYO_AVR_Pack($;$) { pack( "a* N N N a*", 'ISCP', 0x10, ( length $d ), 0x01000000, $d ); } -################################### sub ONKYO_AVR_hexdump { my $s = shift; my $r = unpack 'H*', $s; @@ -3157,19 +3095,16 @@ sub ONKYO_AVR_hexdump { $r . ' ' . $s; } -################################### sub ONKYO_AVR_hex2dec($) { my ($hex) = @_; return unpack( 's', pack 's', hex($hex) ); } -################################### sub ONKYO_AVR_hex2image($) { my ($hex) = @_; return pack( "H*", $hex ); } -################################### sub ONKYO_AVR_dec2hex($) { my ($dec) = @_; my $hex = uc( sprintf( "%x", $dec ) ); @@ -3178,7 +3113,6 @@ sub ONKYO_AVR_dec2hex($) { return $hex; } -################################### sub ONKYO_AVR_GetStateAV($) { my ($hash) = @_; my $name = $hash->{NAME}; @@ -3202,8 +3136,6 @@ sub ONKYO_AVR_GetStateAV($) { } } -##################################### -# Callback from 95_remotecontrol for command makenotify. sub ONKYO_AVR_RCmakenotify($$) { my ( $name, $ndev ) = @_; my $nname = "notify_$name"; @@ -3213,9 +3145,6 @@ sub ONKYO_AVR_RCmakenotify($$) { return "Notify created by ENIGMA2: $nname"; } -##################################### -# RC layouts - sub ONKYO_AVR_RClayout_SVG() { my @row; diff --git a/fhem/FHEM/70_PHTV.pm b/fhem/FHEM/70_PHTV.pm index aa64522b0..b5fd4ccb7 100644 --- a/fhem/FHEM/70_PHTV.pm +++ b/fhem/FHEM/70_PHTV.pm @@ -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 . -# -############################################################################## - 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 PHTV []"; + 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 PHTV []"; - 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]+[}\]])|(\s*\s*\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; diff --git a/fhem/FHEM/70_Pushover.pm b/fhem/FHEM/70_Pushover.pm index 3cf9a36d0..c96e9b857 100644 --- a/fhem/FHEM/70_Pushover.pm +++ b/fhem/FHEM/70_Pushover.pm @@ -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; ############################################################################### diff --git a/fhem/FHEM/71_ONKYO_AVR_ZONE.pm b/fhem/FHEM/71_ONKYO_AVR_ZONE.pm index afa3012f1..588d2c6e7 100644 --- a/fhem/FHEM/71_ONKYO_AVR_ZONE.pm +++ b/fhem/FHEM/71_ONKYO_AVR_ZONE.pm @@ -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 '<command> 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 '<command> 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}; diff --git a/fhem/FHEM/74_THINKINGCLEANER.pm b/fhem/FHEM/74_THINKINGCLEANER.pm index a67c16cf4..0fe8707f5 100644 --- a/fhem/FHEM/74_THINKINGCLEANER.pm +++ b/fhem/FHEM/74_THINKINGCLEANER.pm @@ -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 diff --git a/fhem/FHEM/75_msgConfig.pm b/fhem/FHEM/75_msgConfig.pm index 2ed6a2681..668159aff 100755 --- a/fhem/FHEM/75_msgConfig.pm +++ b/fhem/FHEM/75_msgConfig.pm @@ -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 ) = @_; diff --git a/fhem/FHEM/98_powerMap.pm b/fhem/FHEM/98_powerMap.pm index 19fedd273..0872d28ae 100644 --- a/fhem/FHEM/98_powerMap.pm +++ b/fhem/FHEM/98_powerMap.pm @@ -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 diff --git a/fhem/FHEM/ONKYOdb.pm b/fhem/FHEM/ONKYOdb.pm index 18d246004..b5edf1172 100644 --- a/fhem/FHEM/ONKYOdb.pm +++ b/fhem/FHEM/ONKYOdb.pm @@ -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; diff --git a/fhem/FHEM/RESIDENTStk.pm b/fhem/FHEM/RESIDENTStk.pm index aea2b8e94..7dcff942d 100644 --- a/fhem/FHEM/RESIDENTStk.pm +++ b/fhem/FHEM/RESIDENTStk.pm @@ -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} diff --git a/fhem/FHEM/msgSchema.pm b/fhem/FHEM/msgSchema.pm index fd7bb196e..49df7fff8 100755 --- a/fhem/FHEM/msgSchema.pm +++ b/fhem/FHEM/msgSchema.pm @@ -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;