mirror of
https://github.com/fhem/fhem-mirror.git
synced 2025-03-03 10:46:53 +00:00
AndroidDB: added new features
git-svn-id: https://svn.fhem.de/fhem/trunk@25737 2b470e98-0d58-463d-a4d8-8e2adae1ed80
This commit is contained in:
parent
3711b9deb9
commit
e4fd8a1c80
@ -1,5 +1,7 @@
|
|||||||
# Add changes at the top of the list. Keep it in ASCII, and 80-char wide.
|
# Add changes at the top of the list. Keep it in ASCII, and 80-char wide.
|
||||||
# Do not insert empty lines here, update check depends on it.
|
# Do not insert empty lines here, update check depends on it.
|
||||||
|
- feature: 89_AndroidDB: Added new features
|
||||||
|
- bugfix: 89_AndroidDBHost: Bug fixes
|
||||||
- bugfix: 70_DENON_AVR: handled some uninitialized values
|
- bugfix: 70_DENON_AVR: handled some uninitialized values
|
||||||
- new: 72_UBUS_CALL: module to send call requests via uBus client
|
- new: 72_UBUS_CALL: module to send call requests via uBus client
|
||||||
- new: 72_UBUS_CLIENT: module to communicate with uBus devices
|
- new: 72_UBUS_CLIENT: module to communicate with uBus devices
|
||||||
|
@ -4,7 +4,7 @@
|
|||||||
#
|
#
|
||||||
# 89_AndroidDB
|
# 89_AndroidDB
|
||||||
#
|
#
|
||||||
# Version 0.4
|
# Version 0.6
|
||||||
#
|
#
|
||||||
# FHEM Integration for Android Devices
|
# FHEM Integration for Android Devices
|
||||||
#
|
#
|
||||||
@ -36,16 +36,19 @@ sub AndroidDB_Initialize ($)
|
|||||||
$hash->{ShutdownFn} = "AndroidDB::Shutdown";
|
$hash->{ShutdownFn} = "AndroidDB::Shutdown";
|
||||||
|
|
||||||
$hash->{parseParams} = 1;
|
$hash->{parseParams} = 1;
|
||||||
$hash->{AttrList} = 'macros:textField-long preset:MagentaTVStick,SonyTV presetFile';
|
$hash->{AttrList} = 'connect:0,1 macros:textField-long preset presetFile';
|
||||||
|
|
||||||
|
$data{RC_layout}{MagentaTVStick} = "AndroidDB::RCLayoutMagentaTVStick";
|
||||||
|
$data{RC_layout}{MagentaOne} = "AndroidDB::RCLayoutMagentaOne";
|
||||||
|
$data{RC_layout}{MagentaTVExtended} = "AndroidDB::RCLayoutMagentaTVExt";
|
||||||
}
|
}
|
||||||
|
|
||||||
package AndroidDB;
|
package AndroidDB;
|
||||||
|
|
||||||
use strict;
|
use strict;
|
||||||
use warnings;
|
use warnings;
|
||||||
|
|
||||||
use SetExtensions;
|
use SetExtensions;
|
||||||
|
use Storable qw(dclone);
|
||||||
use GPUtils qw(:all);
|
use GPUtils qw(:all);
|
||||||
|
|
||||||
BEGIN {
|
BEGIN {
|
||||||
@ -55,42 +58,113 @@ BEGIN {
|
|||||||
readingsBulkUpdateIfChanged
|
readingsBulkUpdateIfChanged
|
||||||
readingsBeginUpdate
|
readingsBeginUpdate
|
||||||
readingsEndUpdate
|
readingsEndUpdate
|
||||||
|
setDevAttrList
|
||||||
|
CommandDefine
|
||||||
|
CommandSet
|
||||||
|
CommandAttr
|
||||||
Log3
|
Log3
|
||||||
AttrVal
|
AttrVal
|
||||||
ReadingsVal
|
ReadingsVal
|
||||||
AssignIoPort
|
AssignIoPort
|
||||||
|
InternalTimer
|
||||||
|
asyncOutput
|
||||||
|
gettimeofday
|
||||||
|
parseParams
|
||||||
defs
|
defs
|
||||||
|
attr
|
||||||
|
modules
|
||||||
|
data
|
||||||
|
init_done
|
||||||
))
|
))
|
||||||
};
|
};
|
||||||
|
|
||||||
# Remote control presets
|
# Remote control presets
|
||||||
my %PRESET = (
|
my $PRESET = {
|
||||||
'MagentaTVStick' => {
|
'MagentaTVStick' => {
|
||||||
'APPS' => 'KEYCODE_ALL_APPS',
|
'ASSISTANT' => 'KEYCODE_ASSIST',
|
||||||
'BACK' => 'KEYCODE_BACK',
|
'APPS' => 'KEYCODE_ALL_APPS',
|
||||||
'EPG' => 'KEYCODE_TV_INPUT_HDMI_2',
|
'BACK' => 'KEYCODE_BACK',
|
||||||
'HOME' => 'KEYCODE_HOME',
|
'EPG' => 'KEYCODE_TV_INPUT_HDMI_2',
|
||||||
'INFO' => 'KEYCODE_INFO',
|
'HOME' => 'KEYCODE_HOME',
|
||||||
'MEGATHEK' => 'KEYCODE_TV_INPUT_HDMI_3',
|
'LEFT' => 'KEYCODE_DPAD_LEFT',
|
||||||
'MUTE' => 'KEYCODE_MUTE',
|
'RIGHT' => 'KEYCODE_DPAD_RIGHT',
|
||||||
'OK' => 'KEYCODE_DPAD_CENTER',
|
'UP' => 'KEYCODE_DPAD_UP',
|
||||||
'POWER' => 'KEYCODE_POWER',
|
'DOWN' => 'KEYCODE_DPAD_DOWN',
|
||||||
'PROG+' => 'KEYCODE_CHANNEL_UP',
|
'INFO' => 'KEYCODE_INFO',
|
||||||
'PROG-' => 'KEYCODE_CHANNEL_DOWN',
|
'MEGATHEK' => 'KEYCODE_TV_INPUT_HDMI_3',
|
||||||
'RECORD' => 'KEYCODE_MEDIA_RECORD',
|
'MUTE' => 'KEYCODE_VOLUME_MUTE',
|
||||||
'SEARCH' => 'KEYCODE_TV_INPUT_HDMI_1',
|
'OK' => 'KEYCODE_DPAD_CENTER',
|
||||||
'TV' => 'KEYCODE_TV_INPUT_HDMI_4'
|
'PLAYPAUSE' => 'KEYCODE_MEDIA_PLAY_PAUSE',
|
||||||
|
'POWER' => 'KEYCODE_POWER',
|
||||||
|
'PROG+' => 'KEYCODE_CHANNEL_UP',
|
||||||
|
'PROG-' => 'KEYCODE_CHANNEL_DOWN',
|
||||||
|
'RECORD' => 'KEYCODE_MEDIA_RECORD',
|
||||||
|
'SEARCH' => 'KEYCODE_TV_INPUT_HDMI_1',
|
||||||
|
'STREAMINFO' => '--longpress,KEYCODE_INFO',
|
||||||
|
'TV' => 'KEYCODE_TV_INPUT_HDMI_4',
|
||||||
|
'VOL+' => 'KEYCODE_VOLUME_UP',
|
||||||
|
'VOL-' => 'KEYCODE_VOLUME_DOWN'
|
||||||
},
|
},
|
||||||
'SonyTV' => {
|
'MagentaOne' => {
|
||||||
'POWER' => 'KEYCODE_POWER'
|
'ASSISTANT' => 'KEYCODE_ASSIST',
|
||||||
|
'APPS' => 'KEYCODE_ALL_APPS',
|
||||||
|
'BACK' => 'KEYCODE_BACK',
|
||||||
|
'EPG' => 'KEYCODE_TV_INPUT_HDMI_2',
|
||||||
|
'HOME' => 'KEYCODE_HOME',
|
||||||
|
'LEFT' => 'KEYCODE_DPAD_LEFT',
|
||||||
|
'RIGHT' => 'KEYCODE_DPAD_RIGHT',
|
||||||
|
'UP' => 'KEYCODE_DPAD_UP',
|
||||||
|
'DOWN' => 'KEYCODE_DPAD_DOWN',
|
||||||
|
'INFO' => 'KEYCODE_INFO',
|
||||||
|
'MEGATHEK' => 'KEYCODE_TV_INPUT_HDMI_3',
|
||||||
|
'MUTE' => 'KEYCODE_VOLUME_MUTE',
|
||||||
|
'OK' => 'KEYCODE_DPAD_CENTER',
|
||||||
|
'PLAYPAUSE' => 'KEYCODE_MEDIA_PLAY_PAUSE',
|
||||||
|
'POWER' => 'KEYCODE_POWER',
|
||||||
|
'PROG+' => 'KEYCODE_CHANNEL_UP',
|
||||||
|
'PROG-' => 'KEYCODE_CHANNEL_DOWN',
|
||||||
|
'RECORD' => 'KEYCODE_MEDIA_RECORD',
|
||||||
|
'SEARCH' => 'KEYCODE_TV_INPUT_HDMI_1',
|
||||||
|
'STREAMINFO' => '--longpress,KEYCODE_INFO',
|
||||||
|
'TV' => 'KEYCODE_TV_INPUT_HDMI_4',
|
||||||
|
'VOL+' => 'KEYCODE_VOLUME_UP',
|
||||||
|
'VOL-' => 'KEYCODE_VOLUME_DOWN'
|
||||||
|
},
|
||||||
|
'AndroidTV' => {
|
||||||
|
'APPS' => 'KEYCODE_ALL_APPS',
|
||||||
|
'BACK' => 'KEYCODE_BACK',
|
||||||
|
'EPG' => 'KEYCODE_GUIDE',
|
||||||
|
'HOME' => 'KEYCODE_HOME',
|
||||||
|
'LEFT' => 'KEYCODE_DPAD_LEFT',
|
||||||
|
'RIGHT' => 'KEYCODE_DPAD_RIGHT',
|
||||||
|
'UP' => 'KEYCODE_DPAD_UP',
|
||||||
|
'DOWN' => 'KEYCODE_DPAD_DOWN',
|
||||||
|
'INFO' => 'KEYCODE_INFO',
|
||||||
|
'MUTE' => 'KEYCODE_VOLUME_MUTE',
|
||||||
|
'OK' => 'KEYCODE_DPAD_CENTER',
|
||||||
|
'PLAYPAUSE' => 'KEYCODE_MEDIA_PLAY_PAUSE',
|
||||||
|
'POWER' => 'KEYCODE_POWER',
|
||||||
|
'PROG+' => 'KEYCODE_CHANNEL_UP',
|
||||||
|
'PROG-' => 'KEYCODE_CHANNEL_DOWN',
|
||||||
|
'RECORD' => 'KEYCODE_MEDIA_RECORD',
|
||||||
|
'SEARCH' => 'KEYCODE_SEARCH',
|
||||||
|
'VOL+' => 'KEYCODE_VOLUME_UP',
|
||||||
|
'VOL-' => 'KEYCODE_VOLUME_DOWN',
|
||||||
|
'RED' => 'KEYCODE_PROG_RED',
|
||||||
|
'GREEN' => 'KEYCODE_PROG_GREEN',
|
||||||
|
'BLUE' => 'KEYCODE_PROG_BLUE',
|
||||||
|
'YELLOW' => 'KEYCODE_PROG_YELLOW'
|
||||||
}
|
}
|
||||||
);
|
};
|
||||||
|
|
||||||
|
# Command presets
|
||||||
|
my $MACRO = { };
|
||||||
|
|
||||||
sub Define ($$$)
|
sub Define ($$$)
|
||||||
{
|
{
|
||||||
my ($hash, $a, $h) = @_;
|
my ($hash, $a, $h) = @_;
|
||||||
|
|
||||||
my $usage = "define $hash->{NAME} AndroidDB {NameOrIP}";
|
my $usage = "define $hash->{NAME} AndroidDB {NameOrIP[:Port]}";
|
||||||
|
|
||||||
return $usage if (scalar(@$a) < 3);
|
return $usage if (scalar(@$a) < 3);
|
||||||
|
|
||||||
@ -100,9 +174,31 @@ sub Define ($$$)
|
|||||||
|
|
||||||
AssignIoPort ($hash);
|
AssignIoPort ($hash);
|
||||||
|
|
||||||
|
$attr{$hash->{NAME}}{webCmd} = 'remoteControl';
|
||||||
|
|
||||||
|
# Clone predefined presets and macros
|
||||||
|
$hash->{adb}{preset} = dclone $PRESET;
|
||||||
|
$hash->{adb}{macro} = dclone $MACRO;
|
||||||
|
|
||||||
|
InitAfterStart ($hash) if ($init_done);
|
||||||
|
|
||||||
return undef;
|
return undef;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
sub InitAfterStart ($)
|
||||||
|
{
|
||||||
|
my ($hash) = @_;
|
||||||
|
|
||||||
|
my @presets = map { $_ eq '_custom_' ? () : $_ } keys %{$hash->{adb}{preset}};
|
||||||
|
|
||||||
|
if (scalar(@presets) > 0) {
|
||||||
|
my $attrPreset = 'preset:select,'.join(',',@presets);
|
||||||
|
my $attributes = $modules{AndroidDB}{AttrList};
|
||||||
|
$attributes =~ s/preset/$attrPreset/;
|
||||||
|
setDevAttrList ($hash->{NAME}, $attributes);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
sub Undef ($$)
|
sub Undef ($$)
|
||||||
{
|
{
|
||||||
my ($hash, $name) = @_;
|
my ($hash, $name) = @_;
|
||||||
@ -126,39 +222,119 @@ sub Set ($@)
|
|||||||
my $name = shift @$a;
|
my $name = shift @$a;
|
||||||
my $opt = shift @$a // return 'No set command specified';
|
my $opt = shift @$a // return 'No set command specified';
|
||||||
|
|
||||||
|
#
|
||||||
# Preprare list of available commands
|
# Preprare list of available commands
|
||||||
my $options = 'reboot sendKey shell';
|
#
|
||||||
my @macroList = ();
|
|
||||||
my $preset = AttrVal ($hash->{NAME}, 'preset', '');
|
# Standard commands
|
||||||
my $macros = AttrVal ($hash->{NAME}, 'macros', '');
|
my $options = 'exportPresets reboot rollHorz rollVert sendKey sendNumKeys sendText shell tap';
|
||||||
push @macroList, sort keys %{$PRESET{$preset}} if ($preset ne '' && exists($PRESET{$preset}));
|
|
||||||
push @macroList, sort keys %{$PRESET{_custom_}} if ($macros ne '' && exists($PRESET{_custom_}));
|
# Add remote control key presets to command remoteControl
|
||||||
|
my @presetList = ();
|
||||||
|
my $preset = AttrVal ($name, 'preset', '');
|
||||||
|
push @presetList, sort keys %{$hash->{adb}{preset}{$preset}} if ($preset ne '' && exists($hash->{adb}{preset}{$preset}));
|
||||||
|
push @presetList, sort keys %{$hash->{adb}{preset}{_custom_}} if (exists($hash->{adb}{preset}{_custom_}));
|
||||||
my %e;
|
my %e;
|
||||||
$options .= ' remoteControl:'.join(',', sort grep { !$e{$_}++ } @macroList) if (scalar(@macroList) > 0);
|
$options .= ' remoteControl:'.join(',', sort grep { !$e{$_}++ } @presetList) if (scalar(@presetList) > 0);
|
||||||
$opt = lc($opt);
|
|
||||||
|
# Add remote control layouts to command createRemote
|
||||||
|
my @layouts = keys %{$data{RC_layout}};
|
||||||
|
$options .= ' createRemote:'.join(',', sort @layouts) if (scalar(@layouts) > 0);
|
||||||
|
|
||||||
|
# Add command macros to command list
|
||||||
|
my @macroList = ();
|
||||||
|
push @macroList, sort keys %{$hash->{adb}{macro}{$preset}} if ($preset ne '' && exists($hash->{adb}{macro}{$preset}));
|
||||||
|
push @macroList, sort keys %{$hash->{adb}{macro}{_custom_}} if (exists($hash->{adb}{macro}{_custom_}));
|
||||||
|
my %e;
|
||||||
|
$options .= ' '.join(' ', sort grep { !$e{$_}++ } @macroList) if (scalar(@macroList) > 0);
|
||||||
|
|
||||||
if ($opt eq 'sendkey') {
|
my $lcopt = lc($opt);
|
||||||
my $key = shift @$a // return "Usage: set $name $opt KeyCode";
|
|
||||||
my ($rc, $result, $error) = AndroidDBHost::Run ($hash, 'shell', '.*', 'input', 'keyevent', $key);
|
if ($lcopt eq 'sendkey') {
|
||||||
|
return "Usage: set $name $opt [--longpress] KeyCode [...]" if (scalar(@$a) == 0);
|
||||||
|
return "Only one KeyCode allowed when option '--longpress' is specified"
|
||||||
|
if ($$a[0] eq '--longpress' && scalar(@$a) > 2);
|
||||||
|
my ($rc, $result, $error) = AndroidDBHost::Run ($hash, 'shell', '.*', 'input', 'keyevent', @$a);
|
||||||
return $error if ($rc == 0);
|
return $error if ($rc == 0);
|
||||||
}
|
}
|
||||||
elsif ($opt eq 'reboot') {
|
elsif ($lcopt eq 'sendnumkeys') {
|
||||||
|
my $number = shift @$a // return "Usage: set $name $opt Number";
|
||||||
|
return 'Parameter Number must be in range 1-9999' if ($number !~ /^[0-9]+$/ || $number < 1 || $number > 9999);
|
||||||
|
my ($rc, $result, $error) = AndroidDBHost::Run ($hash, 'shell', '.*', 'input', 'text', $number);
|
||||||
|
return $error if ($rc == 0);
|
||||||
|
}
|
||||||
|
elsif ($lcopt eq 'sendtext') {
|
||||||
|
return "Usage: set $name $opt Text" if (scalar(@$a) < 1);
|
||||||
|
my ($rc, $result, $error) = AndroidDBHost::Run ($hash, 'shell', '.*', 'input', 'text', join(' ',@$a));
|
||||||
|
return $error if ($rc == 0);
|
||||||
|
}
|
||||||
|
elsif ($lcopt eq 'reboot') {
|
||||||
my ($rc, $result, $error) = AndroidDBHost::Run ($hash, $opt);
|
my ($rc, $result, $error) = AndroidDBHost::Run ($hash, $opt);
|
||||||
return $error if ($rc == 0);
|
return $error if ($rc == 0);
|
||||||
}
|
}
|
||||||
elsif ($opt eq 'shell') {
|
elsif ($lcopt eq 'shell') {
|
||||||
return "Usage: set $name $opt ShellCommand" if (scalar(@$a) == 0);
|
return "Usage: set $name $opt ShellCommand" if (scalar(@$a) == 0);
|
||||||
my ($rc, $result, $error) = AndroidDBHost::Run ($hash, $opt, '.*', @$a);
|
my ($rc, $result, $error) = AndroidDBHost::Run ($hash, $opt, '.*', @$a);
|
||||||
return $result.$error,
|
return $result.$error,
|
||||||
}
|
}
|
||||||
elsif ($opt eq 'remotecontrol') {
|
elsif ($lcopt eq 'remotecontrol') {
|
||||||
my $macroName = shift @$a // return "Usage: set $name $opt MacroName";
|
my $macroName = shift @$a // return "Usage: set $name $opt MacroName";
|
||||||
$preset = '_custom_' if (exists($PRESET{_custom_}) && exists($PRESET{_custom_}{$macroName}));
|
$preset = '_custom_' if (exists($hash->{adb}{preset}{_custom_}) && exists($hash->{adb}{preset}{_custom_}{$macroName}));
|
||||||
return "Preset and/or macro $macroName not defined" if ($preset eq '' || !exists($PRESET{$preset}{$macroName}));
|
return "Preset and/or macro $macroName not defined in preset $preset"
|
||||||
|
if ($preset eq '' || !exists($hash->{adb}{preset}{$preset}{$macroName}));
|
||||||
my ($rc, $result, $error) = AndroidDBHost::Run ($hash, 'shell', '.*', 'input', 'keyevent',
|
my ($rc, $result, $error) = AndroidDBHost::Run ($hash, 'shell', '.*', 'input', 'keyevent',
|
||||||
split (',', $PRESET{$preset}{$macroName}));
|
split (',', $hash->{adb}{preset}{$preset}{$macroName}));
|
||||||
return $error if ($rc == 0);
|
return $error if ($rc == 0);
|
||||||
}
|
}
|
||||||
|
elsif ($lcopt eq 'tap') {
|
||||||
|
my ($x, $y) = @$a;
|
||||||
|
return "Usage: set $name $opt tap X Y" if (!defined($y));
|
||||||
|
my ($rc, $result, $error) = AndroidDBHost::Run ($hash, 'shell', '.*', 'input', 'tap', $x, $y);
|
||||||
|
return $error if ($rc == 0);
|
||||||
|
}
|
||||||
|
elsif ($lcopt eq 'rollhorz' || $lcopt eq 'rollvert') {
|
||||||
|
my $delta = shift @$a // return "Usage: set $name $opt Delta";
|
||||||
|
my ($dx, $dy) = $opt eq 'rollhorz' ? ($delta, 0) : (0, $delta);
|
||||||
|
my ($rc, $result, $error) = AndroidDBHost::Run ($hash, 'shell', '.*', 'input', 'roll', $dx, $dy);
|
||||||
|
return $error if ($rc == 0);
|
||||||
|
}
|
||||||
|
elsif ($lcopt eq 'createremote') {
|
||||||
|
my $layout = shift @$a // return "Usage: set $name $opt LayoutName";
|
||||||
|
my $rcName = $name.'_RC';
|
||||||
|
return "$name: Can't create remotecontrol device $rcName"
|
||||||
|
if (CommandDefine (undef, "$rcName remotecontrol"));
|
||||||
|
Log3 $name, 2, "$name: Created remotecontrol device $rcName";
|
||||||
|
return "$name: Can't select layout $layout for remotecontrol device $rcName"
|
||||||
|
if (CommandSet (undef, "$rcName layout $layout"));
|
||||||
|
Log3 $name, 2, "Selected layout $layout for $rcName";
|
||||||
|
my $room = AttrVal ($name, 'room', '');
|
||||||
|
if ($room ne '') {
|
||||||
|
Log3 $name, 2, "$name: Assigning $rcName to room $room";
|
||||||
|
CommandAttr (undef, "$rcName room $room");
|
||||||
|
}
|
||||||
|
CommandSet (undef, "$rcName makenotify $name");
|
||||||
|
CommandAttr (undef, "$name group $name");
|
||||||
|
CommandAttr (undef, "$rcName group $name");
|
||||||
|
Log3 $name, 2, "Created notify device notify_$rcName";
|
||||||
|
}
|
||||||
|
elsif ($lcopt eq 'exportpresets') {
|
||||||
|
my $filename = shift @$a // return "Usage: set $name $opt Filename";
|
||||||
|
my $rc = ExportPresets ($hash, $filename);
|
||||||
|
return "Error while saving presets to file $filename" if ($rc == 0);
|
||||||
|
return "Presets saved to file $filename";
|
||||||
|
}
|
||||||
|
elsif (exists($hash->{adb}{macro}{_custom_}) && exists($hash->{adb}{macro}{_custom_}{$opt})) {
|
||||||
|
my ($args, $pars) = parseParams ($hash->{adb}{macro}{_custom_}{$opt});
|
||||||
|
my $cmd = shift @$args;
|
||||||
|
my ($rc, $result, $error) = AndroidDBHost::Run ($hash, $cmd, '.*', @$args);
|
||||||
|
return $rc == 0 ? $error : $result;
|
||||||
|
}
|
||||||
|
elsif ($preset ne '' && exists($hash->{adb}{macro}{$preset}) && exists($hash->{adb}{macro}{$preset}{$opt})) {
|
||||||
|
my ($args, $pars) = parseParams ($hash->{adb}{macro}{$preset}{$opt});
|
||||||
|
my $cmd = shift @$args;
|
||||||
|
my ($rc, $result, $error) = AndroidDBHost::Run ($hash, $cmd, '.*', @$args);
|
||||||
|
return $rc == 0 ? $error : $result;
|
||||||
|
}
|
||||||
else {
|
else {
|
||||||
return "Unknown argument $opt, choose one of $options";
|
return "Unknown argument $opt, choose one of $options";
|
||||||
}
|
}
|
||||||
@ -171,12 +347,35 @@ sub Get ($@)
|
|||||||
my $name = shift @$a;
|
my $name = shift @$a;
|
||||||
my $opt = shift @$a // return 'No get command specified';
|
my $opt = shift @$a // return 'No get command specified';
|
||||||
|
|
||||||
my $options = 'presets';
|
my $options = 'keyPreset';
|
||||||
|
my @presetList = sort keys %{$hash->{adb}{preset}};
|
||||||
|
$options .= ':'.join(',', @presetList) if (scalar(@presetList) > 0);
|
||||||
|
my @macroList = sort keys %{$hash->{adb}{macro}};
|
||||||
|
$options .= ' cmdPreset:'.join(',', @macroList) if (scalar(@macroList) > 0);
|
||||||
|
|
||||||
$opt = lc($opt);
|
my $attrPreset = AttrVal ($name, 'preset', '');
|
||||||
|
|
||||||
if ($opt eq 'presets') {
|
my $lcopt = lc($opt);
|
||||||
return "Command not implemented";
|
|
||||||
|
if ($lcopt eq 'keypreset') {
|
||||||
|
my $preset = shift @$a // $attrPreset;
|
||||||
|
return "Usage: get $name $opt PresetName" if ($preset eq '');
|
||||||
|
return "Key preset $preset not found" if (!exists($hash->{adb}{preset}{$preset}));
|
||||||
|
my $presetDef = "Definition of key preset $preset:<br/><br/>";
|
||||||
|
foreach my $macro (sort keys %{$hash->{adb}{preset}{$preset}}) {
|
||||||
|
$presetDef .= "$macro = $hash->{adb}{preset}{$preset}{$macro}<br/>";
|
||||||
|
}
|
||||||
|
return $presetDef;
|
||||||
|
}
|
||||||
|
elsif ($lcopt eq 'cmdpreset') {
|
||||||
|
my $preset = shift @$a // $attrPreset;
|
||||||
|
return "Usage: get $name $opt PresetName" if ($preset eq '');
|
||||||
|
return "Command preset $preset not found" if (!exists($hash->{adb}{macro}{$preset}));
|
||||||
|
my $macroDef = "Definition of command preset $preset:<br/><br/>";
|
||||||
|
foreach my $macro (sort keys %{$hash->{adb}{macro}{$preset}}) {
|
||||||
|
$macroDef .= "$macro = $hash->{adb}{macro}{$preset}{$macro}<br/>";
|
||||||
|
}
|
||||||
|
return $macroDef;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
return "Unknown argument $opt, choose one of $options";
|
return "Unknown argument $opt, choose one of $options";
|
||||||
@ -190,20 +389,31 @@ sub Attr ($@)
|
|||||||
|
|
||||||
if ($cmd eq 'set') {
|
if ($cmd eq 'set') {
|
||||||
if ($attrName eq 'macros') {
|
if ($attrName eq 'macros') {
|
||||||
foreach my $macroDef (split /\s+/, $attrVal) {
|
foreach my $macroDef (split /;/, $attrVal) {
|
||||||
my ($macroName, $macroKeycodes) = split (':', $macroDef);
|
my ($macroName, $macroPar) = split (':', $macroDef);
|
||||||
$PRESET{_custom_}{$macroName} = $macroKeycodes;
|
if (!defined($macroDef)) {
|
||||||
|
Log3 $name, 2, "Missing defintion for macro $macroName";
|
||||||
|
return "Missing definition for macro $macroName";
|
||||||
|
}
|
||||||
|
if ($macroPar =~ /^[0-9]+/ || $macroPar =~ /^KEYCODE_/) {
|
||||||
|
$hash->{adb}{preset}{_custom_}{$macroName} = $macroPar;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
$hash->{adb}{macro}{_custom_}{$macroName} = $macroPar;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
elsif ($attrName eq 'presetFile') {
|
elsif ($attrName eq 'presetFile') {
|
||||||
if (!LoadPreset ($hash, $attrVal)) {
|
if (!LoadPresets ($hash, $attrVal)) {
|
||||||
Log3 $hash, 2, "Can't load preset from file $attrVal";
|
return "Cannot load presets from file $attrVal";
|
||||||
return "Cannot load preset from file $attrVal";
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
elsif ($attrName eq 'connect') {
|
||||||
|
AndroidDBHost::Connect ($hash) if (!$init_done && $attrVal eq '1');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
elsif ($cmd eq 'del') {
|
elsif ($cmd eq 'del') {
|
||||||
delete $PRESET{_custom_} if (exists($PRESET{_custom_}));
|
delete $hash->{adb}{preset}{_custom_} if (exists($hash->{adb}{preset}{_custom_}));
|
||||||
}
|
}
|
||||||
|
|
||||||
return undef;
|
return undef;
|
||||||
@ -213,50 +423,194 @@ sub Attr ($@)
|
|||||||
# Load macro definitions from file
|
# Load macro definitions from file
|
||||||
# File format:
|
# File format:
|
||||||
# - Lines starting with a # are treated as comments
|
# - Lines starting with a # are treated as comments
|
||||||
|
# - Empty lines are ignored
|
||||||
# - Lines containing a single word are setting the preset name for the
|
# - Lines containing a single word are setting the preset name for the
|
||||||
# following lines
|
# following lines
|
||||||
# - Lines in format Name:KeyList are defining a macro. KeyList is a comma
|
# - Lines in format Name:KeyList are defining a macro. KeyList is a comma
|
||||||
# separated list of keycodes.
|
# separated list of keycodes.
|
||||||
|
# - Lines in format Name:Command:Parameters are defining a command macro.
|
||||||
##############################################################################
|
##############################################################################
|
||||||
|
|
||||||
sub LoadPreset ($$)
|
sub LoadPresets ($$)
|
||||||
{
|
{
|
||||||
my ($hash, $fileName) = @_;
|
my ($hash, $fileName) = @_;
|
||||||
|
|
||||||
my @lines;
|
# Read file
|
||||||
|
my @lines;
|
||||||
if (open (PRESETFILE, "<$fileName")) {
|
if (open (PRESETFILE, "<$fileName")) {
|
||||||
@lines = <PRESETFILE>;
|
@lines = <PRESETFILE>;
|
||||||
close (PRESETFILE);
|
close (PRESETFILE);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
ShowMessage ($hash, 2, "Can't open file $fileName");
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# Delete old presets
|
||||||
|
my @presets = keys %{$hash->{adb}{preset}};
|
||||||
|
my @macros = keys %{$hash->{adb}{macro}};
|
||||||
|
foreach my $e (@presets) { delete $hash->{adb}{preset}{$e} if ($e ne '_custom_'); }
|
||||||
|
foreach my $e (@macros) { delete $hash->{adb}{macro}{$e} if ($e ne '_custom_'); }
|
||||||
|
|
||||||
chomp @lines;
|
chomp @lines;
|
||||||
my $presetName = '';
|
my $presetName = '';
|
||||||
|
|
||||||
foreach my $l (@lines) {
|
foreach my $l (@lines) {
|
||||||
next if ($l =~ /^#/); # Comments are allowed
|
next if ($l =~ /^#/); # Comments are allowed
|
||||||
my ($macroName, $keyList) = split (':', $l);
|
|
||||||
if (!defined($keyList)) {
|
my ($macroName, $macroPar) = split (':', $l);
|
||||||
|
if (!defined($macroPar)) {
|
||||||
|
next if (!defined($macroName) || $macroName eq '');
|
||||||
|
if ($macroName !~ /^[a-zA-Z0-9-_]+$/) {
|
||||||
|
ShowMessage ($hash, 2, "Invalid character in macro name $macroName in file $fileName");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
$presetName = $macroName;
|
$presetName = $macroName;
|
||||||
next;
|
|
||||||
}
|
}
|
||||||
elsif (defined($keyList) && $presetName eq '') {
|
else {
|
||||||
Log3 $hash, 2, "Preset name must be specified before first macro definition in file $fileName";
|
if ($presetName eq '') {
|
||||||
return 0;
|
ShowMessage ($hash, 2, "No preset name set for macro name $macroName in file $fileName");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
if ($macroPar =~ /^[0-9]+/ || $macroPar =~ /^(KEYCODE_|--longpress)/) {
|
||||||
|
$hash->{adb}{preset}{$presetName}{$macroName} = $macroPar;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
$hash->{adb}{macro}{$presetName}{$macroName} = $macroPar;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
$PRESET{$presetName}{$macroName} = $keyList;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# Init options of attribute 'preset'
|
||||||
|
InitAfterStart ($hash);
|
||||||
|
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
sub ExportPresets ($$)
|
||||||
|
{
|
||||||
|
my ($hash, $filename) = @_;
|
||||||
|
|
||||||
|
if (open (PRESETFILE, ">$filename")) {
|
||||||
|
foreach my $preset (sort keys %{$hash->{adb}{preset}}) {
|
||||||
|
next if ($preset eq '_custom_');
|
||||||
|
print PRESETFILE "#\n# Preset $preset\n#\n$preset\n#\n";
|
||||||
|
foreach my $macro (sort keys %{$hash->{adb}{preset}{$preset}}) {
|
||||||
|
print PRESETFILE "$macro:$hash->{adb}{preset}{$preset}{$macro}\n";
|
||||||
|
}
|
||||||
|
if (exists($hash->{adb}{macro}{$preset})) {
|
||||||
|
foreach my $macro (sort keys %{$hash->{adb}{macro}{$preset}}) {
|
||||||
|
print PRESETFILE "$macro:$hash->{adb}{macro}{$preset}{$macro}\n";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
foreach my $preset (sort keys %{$hash->{adb}{macro}}) {
|
||||||
|
next if ($preset eq '_custom_' || exists($hash->{adb}{preset}{$preset}));
|
||||||
|
print PRESETFILE "#\n# Preset $preset\n#\n$preset\n#\n";
|
||||||
|
foreach my $macro (sort keys %{$hash->{adb}{macro}{$preset}}) {
|
||||||
|
print PRESETFILE "$macro:$hash->{adb}{macro}{$preset}{$macro}\n";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
close (PRESETFILE);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
sub ShowMessage ($$$)
|
||||||
|
{
|
||||||
|
my ($hash, $level, $msg) = @_;
|
||||||
|
|
||||||
|
Log3 $hash->{NAME}, $level, $msg;
|
||||||
|
|
||||||
|
if ($init_done && exists($hash->{CL})) {
|
||||||
|
my $cl = $hash->{CL};
|
||||||
|
InternalTimer (gettimeofday()+1, sub { asyncOutput ($cl, $msg) }, undef, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
##############################################################################
|
||||||
|
# Remote control layout for Magenta TV Stick
|
||||||
|
##############################################################################
|
||||||
|
|
||||||
|
sub RCLayoutMagentaTVStick () {
|
||||||
|
my @row = (
|
||||||
|
'sendKey KEYCODE_POWER:POWEROFF,:blank,sendKey KEYCODE_VOLUME_MUTE:MUTE',
|
||||||
|
':blank,sendKey KEYCODE_TV_INPUT_HDMI_3:PS3Rectangle,:blank',
|
||||||
|
':blank,sendKey KEYCODE_ALL_APPS:TOOLS,:blank',
|
||||||
|
'sendKey KEYCODE_TV_INPUT_HDMI_4:TV,:blank,sendKey KEYCODE_TV_INPUT_HDMI_2:GUIDE',
|
||||||
|
':blank,sendKey KEYCODE_DPAD_UP:UP,:blank',
|
||||||
|
'sendKey KEYCODE_DPAD_LEFT:LEFT,sendKey KEYCODE_DPAD_CENTER:OK,sendKey KEYCODE_DPAD_RIGHT:RIGHT',
|
||||||
|
':blank,sendKey KEYCODE_DPAD_DOWN:DOWN,:blank',
|
||||||
|
'sendKey KEYCODE_BACK:BACKDroid,sendKey KEYCODE_HOME:HOMEDroid,sendKey KEYCODE_INFO:INFO',
|
||||||
|
'sendKey KEYCODE_MEDIA_RECORD:REC,sendKey KEYCODE_TV_INPUT_HDMI_1:SEARCH,sendKey KEYCODE_VOLUME_UP:VOLUP',
|
||||||
|
'sendKey KEYCODE_MEDIA_PLAY_PAUSE:PLAYPAUSE,sendKey KEYCODE_ASSIST:SOURCE,sendKey KEYCODE_VOLUME_DOWN:VOLDOWN',
|
||||||
|
'attr rc_iconpath icons/remotecontrol',
|
||||||
|
'attr rc_iconprefix black_btn_'
|
||||||
|
);
|
||||||
|
|
||||||
|
return @row;
|
||||||
|
}
|
||||||
|
|
||||||
|
##############################################################################
|
||||||
|
# Remote control layout for Magenta One
|
||||||
|
##############################################################################
|
||||||
|
|
||||||
|
sub RCLayoutMagentaOne () {
|
||||||
|
my @row = (
|
||||||
|
'sendKey KEYCODE_POWER:POWEROFF,:blank,sendKey KEYCODE_VOLUME_MUTE:MUTE',
|
||||||
|
':blank,:blank,:blank',
|
||||||
|
'sendKey KEYCODE_TV_INPUT_HDMI_1:SEARCH,sendKey KEYCODE_ASSIST:SOURCE,sendKey KEYCODE_GUIDE:GUIDE',
|
||||||
|
'sendKey KEYCODE_BACK:BACKDroid,sendKey KEYCODE_HOME:HOMEDroid,sendKey KEYCODE_ALL_APPS:MENUDroid',
|
||||||
|
':blank,sendKey KEYCODE_DPAD_UP:UP,:blank',
|
||||||
|
'sendKey KEYCODE_DPAD_LEFT:LEFT,sendKey KEYCODE_DPAD_CENTER:OK,sendKey KEYCODE_DPAD_RIGHT:RIGHT',
|
||||||
|
':blank,sendKey KEYCODE_DPAD_DOWN:DOWN,:blank',
|
||||||
|
'sendKey KEYCODE_VOLUME_UP:VOLUP,sendKey KEYCODE_MEDIA_PLAY_PAUSE:PLAYPAUSE,sendKey KEYCODE_CHANNEL_UP:CHUP',
|
||||||
|
'sendKey KEYCODE_VOLUME_DOWN:VOLDOWN,:blank,sendKey KEYCODE_CHANNEL_DOWN:CHDOWN',
|
||||||
|
'sendKey KEYCODE_1:1,sendKey KEYCODE_2:2,sendKey KEYCODE_3:3',
|
||||||
|
'sendKey KEYCODE_4:4,sendKey KEYCODE_5:5,sendKey KEYCODE_6:6',
|
||||||
|
'sendKey KEYCODE_7:7,sendKey KEYCODE_8:8,sendKey KEYCODE_9:9',
|
||||||
|
'sendKey KEYCODE_INFO:INFO,sendKey KEYCODE_0:0,sendKey KEYCODE_MEDIA_RECORD:REC',
|
||||||
|
'attr rc_iconpath icons/remotecontrol',
|
||||||
|
'attr rc_iconprefix black_btn_'
|
||||||
|
);
|
||||||
|
|
||||||
|
return @row;
|
||||||
|
}
|
||||||
|
|
||||||
|
##############################################################################
|
||||||
|
# Extended remote control layout for Magenta TV
|
||||||
|
##############################################################################
|
||||||
|
|
||||||
|
sub RCLayoutMagentaTVExt () {
|
||||||
|
my @row = (
|
||||||
|
'sendKey KEYCODE_POWER:POWEROFF,:blank,sendKey KEYCODE_VOLUME_MUTE:MUTE,:blank',
|
||||||
|
':blank,:blank,:blank,:blank',
|
||||||
|
'sendKey KEYCODE_TV_INPUT_HDMI_1:SEARCH,sendKey KEYCODE_ASSIST:SOURCE,sendKey KEYCODE_GUIDE:GUIDE,sendKey KEYCODE_TV_INPUT_HDMI_4:TV',
|
||||||
|
'sendKey KEYCODE_BACK:BACKDroid,sendKey KEYCODE_HOME:HOMEDroid,sendKey KEYCODE_ALL_APPS:MENUDroid,:blank',
|
||||||
|
':blank,sendKey KEYCODE_DPAD_UP:UP,:blank,:blank',
|
||||||
|
'sendKey KEYCODE_DPAD_LEFT:LEFT,sendKey KEYCODE_DPAD_CENTER:OK,sendKey KEYCODE_DPAD_RIGHT:RIGHT,:blank',
|
||||||
|
':blank,sendKey KEYCODE_DPAD_DOWN:DOWN,:blank,:blank',
|
||||||
|
'sendKey KEYCODE_VOLUME_UP:VOLUP,sendKey KEYCODE_MEDIA_PLAY_PAUSE:PLAYPAUSE,sendKey KEYCODE_CHANNEL_UP:CHUP,:blank',
|
||||||
|
'sendKey KEYCODE_VOLUME_DOWN:VOLDOWN,:blank,sendKey KEYCODE_CHANNEL_DOWN:CHDOWN,:blank',
|
||||||
|
'sendKey KEYCODE_1:1,sendKey KEYCODE_2:2,sendKey KEYCODE_3:3,:blank',
|
||||||
|
'sendKey KEYCODE_4:4,sendKey KEYCODE_5:5,sendKey KEYCODE_6:6,:blank',
|
||||||
|
'sendKey KEYCODE_7:7,sendKey KEYCODE_8:8,sendKey KEYCODE_9:9,:blank',
|
||||||
|
'sendKey KEYCODE_INFO:INFO,sendKey KEYCODE_0:0,sendKey KEYCODE_MEDIA_RECORD:REC,:blank',
|
||||||
|
'sendKey KEYCODE_PROG_RED:RED,sendKey KEYCODE_PROG_GREEN:GREEN,sendKey KEYCODE_PROG_YELLOW:YELLOW,sendKey KEYCODE_PROG_BLUE:BLUE',
|
||||||
|
'attr rc_iconpath icons/remotecontrol',
|
||||||
|
'attr rc_iconprefix black_btn_'
|
||||||
|
);
|
||||||
|
|
||||||
|
return @row;
|
||||||
|
}
|
||||||
|
|
||||||
1;
|
1;
|
||||||
|
|
||||||
=pod
|
=pod
|
||||||
=item device
|
=item device
|
||||||
=item summary Allows to control an Android device via ADB
|
=item summary Allows to control an Android device via ADB (Android Debug Bridge)
|
||||||
=begin html
|
=begin html
|
||||||
|
|
||||||
<a name="AndroidDB"></a>
|
<a name="AndroidDB"></a>
|
||||||
@ -270,8 +624,10 @@ sub LoadPreset ($$)
|
|||||||
<a name="AndroidDBdefine"></a>
|
<a name="AndroidDBdefine"></a>
|
||||||
<b>Define</b><br/><br/>
|
<b>Define</b><br/><br/>
|
||||||
<ul>
|
<ul>
|
||||||
<code>define <name> AndroidDB {<NameOrIP>}</code>
|
<code>define <name> AndroidDB <NameOrIP>[<Port>]</code>
|
||||||
The parameter 'NameOrIP' is the hostname or the IP address of the Android device.
|
The parameter <i>NameOrIP</i> is the hostname or the IP address of the Android device.
|
||||||
|
The parameter <i>Port</i> specifies the TCP port to be used for the device connection.
|
||||||
|
The default port is 5555.
|
||||||
</ul>
|
</ul>
|
||||||
<br/>
|
<br/>
|
||||||
</ul>
|
</ul>
|
||||||
@ -279,6 +635,14 @@ sub LoadPreset ($$)
|
|||||||
<a name="AndroidDBset"></a>
|
<a name="AndroidDBset"></a>
|
||||||
<b>Set</b><br/><br/>
|
<b>Set</b><br/><br/>
|
||||||
<ul>
|
<ul>
|
||||||
|
<li><b>set <Name> createRemote <Layout></b><br/>
|
||||||
|
Create a remote control device for the Android device. Create a notify device for
|
||||||
|
the remote control to send button events to the AndroidDB device.
|
||||||
|
</li>br/>
|
||||||
|
<li><b>set <Name> exportPresets <Filename></b><br/>
|
||||||
|
Export the currently loaded presets to file. This file can be modified and assigned
|
||||||
|
again by using attribute 'preset'.
|
||||||
|
</li><br/>
|
||||||
<li><b>set <name> reboot</b><br/>
|
<li><b>set <name> reboot</b><br/>
|
||||||
Reboot the device.
|
Reboot the device.
|
||||||
</li><br/>
|
</li><br/>
|
||||||
@ -288,23 +652,61 @@ sub LoadPreset ($$)
|
|||||||
in attribute 'macros' are overwriting macros with the same name in a preset selected
|
in attribute 'macros' are overwriting macros with the same name in a preset selected
|
||||||
by attribute 'preset'.
|
by attribute 'preset'.
|
||||||
</li><br/>
|
</li><br/>
|
||||||
<li><b>set <name> sendKey <KeyCode></b><br/>
|
<li><b>set <name> rollHorz <DeltaX></b><br/>
|
||||||
Send a key code to the Android device.
|
Scroll display horizontally. Not supported on all types of Android devices.
|
||||||
|
</li><br/>
|
||||||
|
<li><b>set <name> rollVert <DeltaY></b><br/>
|
||||||
|
Scroll display vertically. Not supported on all types of Android devices.
|
||||||
|
</li><br/>
|
||||||
|
<li><b>set <name> sendKey [longpress] <KeyCode></b> [...]<br/>
|
||||||
|
Send a key code to the Android device. If option 'longpress' is specified, only one
|
||||||
|
<i>KeyCode</i> is allowed.
|
||||||
|
</li><br/>
|
||||||
|
<li><b>set <name> sendNumKeys <Number></b><br/>
|
||||||
|
Send digits of <i>Number</i> to the Android device. <i>Number</i> must be in range 0-9999.
|
||||||
|
</li><br/>
|
||||||
|
<li><b>set <name> sendText <Text></b><br/>
|
||||||
|
Send <i>Text</i> to the Android device.
|
||||||
</li><br/>
|
</li><br/>
|
||||||
<li><b>set <name> shell <Command> [<Arguments>]</b><br/>
|
<li><b>set <name> shell <Command> [<Arguments>]</b><br/>
|
||||||
Execute shell command on Android device.
|
Execute shell command on Android device.
|
||||||
</li><br/>
|
</li><br/>
|
||||||
|
<li><b>set <name> tap <X> <Y></b><br/>
|
||||||
|
Simulate a tap on a touchscreen (only available an devices having a touchscreen).
|
||||||
|
</li><br/>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<a name="AndroidDBget"></a>
|
||||||
|
<b>Get</b><br/><br/>
|
||||||
|
<ul>
|
||||||
|
<li><b>set <Name> keyPreset <PresetName></b><br/>
|
||||||
|
List key preset definition.
|
||||||
|
</li>br/>
|
||||||
|
<li><b>set <Name> cmdPreset <PresetName></b><br/>
|
||||||
|
List command preset definition.
|
||||||
|
</li>br/>
|
||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
<a name="AndroidDBattr"></a>
|
<a name="AndroidDBattr"></a>
|
||||||
<b>Attributes</b><br/><br/>
|
<b>Attributes</b><br/><br/>
|
||||||
<ul>
|
<ul>
|
||||||
|
<a name="connect"></a>
|
||||||
|
<li><b>connect 0|1</b><br/>
|
||||||
|
If set to 1, a connection to the Android device will be established during
|
||||||
|
FHEM start. Note: Set this attribute for one Android device only!
|
||||||
|
</li><br/>
|
||||||
<a name="macros"></a>
|
<a name="macros"></a>
|
||||||
<li><b>macros <MacroDef> [...]</b><br/>
|
<li><b>macros <MacroDef>[;...]</b><br/>
|
||||||
Define a list of keycode macros to be sent to an Android device with 'remoteControl'
|
Define a list of keycode macros to be sent to an Android device with 'remoteControl'
|
||||||
command. A 'MacroDef' is using the following syntax:<br/>
|
command or define shortcuts for remote commands.<br/>
|
||||||
|
A 'MacroDef' is using the following syntax:<br/>
|
||||||
MacroName:KeyCode[,...]<br/>
|
MacroName:KeyCode[,...]<br/>
|
||||||
Several macro definitions can be specified by seperating them using a blank character.
|
or<br/>
|
||||||
|
MacroName:Command<br/><br/>
|
||||||
|
Parameter <i>Command</i> is a adb command.<br/>
|
||||||
|
Example, define a command 'set listpackages':<br/>
|
||||||
|
<pre>attr myDev macros listpackages:shell pm list packages -f</pre><br/>
|
||||||
|
Several macro definitions can be specified by seperating them using a semicolon.
|
||||||
</li><br/>
|
</li><br/>
|
||||||
<a name="preset"></a>
|
<a name="preset"></a>
|
||||||
<li><b>preset <PresetName></b><br/>
|
<li><b>preset <PresetName></b><br/>
|
||||||
@ -320,12 +722,17 @@ sub LoadPreset ($$)
|
|||||||
<pre>
|
<pre>
|
||||||
# Comment
|
# Comment
|
||||||
PresetName1
|
PresetName1
|
||||||
MacroName1:KeyCode[,...]
|
MacroDef1
|
||||||
MacroName2:KeyCode[,...]
|
MacroDef2
|
||||||
...
|
...
|
||||||
PresetName2
|
PresetName2
|
||||||
...
|
...
|
||||||
</pre>
|
</pre>
|
||||||
|
A 'MacroDef' is using the following syntax:<br/>
|
||||||
|
MacroName:KeyCode[,...]<br/>
|
||||||
|
or<br/>
|
||||||
|
MacroName:Command:Parameters<br/><br/>
|
||||||
|
Usually <i>Command</i> is 'shell'.
|
||||||
</li><br/>
|
</li><br/>
|
||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
|
@ -4,7 +4,7 @@
|
|||||||
#
|
#
|
||||||
# 89_AndroidDBHost
|
# 89_AndroidDBHost
|
||||||
#
|
#
|
||||||
# Version 0.4
|
# Version 0.6
|
||||||
#
|
#
|
||||||
# FHEM Integration for Android Debug Bridge
|
# FHEM Integration for Android Debug Bridge
|
||||||
#
|
#
|
||||||
@ -19,8 +19,6 @@
|
|||||||
# Windows/MacOSX/Linux x86: https://developer.android.com/studio/releases/platform-tools
|
# Windows/MacOSX/Linux x86: https://developer.android.com/studio/releases/platform-tools
|
||||||
#
|
#
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
package main;
|
package main;
|
||||||
|
|
||||||
use strict;
|
use strict;
|
||||||
@ -86,7 +84,7 @@ sub Define ($$$)
|
|||||||
$hash->{adb}{port} = $port // 5037;
|
$hash->{adb}{port} = $port // 5037;
|
||||||
$hash->{adb}{cmd} = $h->{adb} // '/usr/bin/adb';
|
$hash->{adb}{cmd} = $h->{adb} // '/usr/bin/adb';
|
||||||
$hash->{Clients} = ':AndroidDB:';
|
$hash->{Clients} = ':AndroidDB:';
|
||||||
$hash->{NOTIFYDEV} = 'global,TYPE=(AndroidDBHost|AndroidDB)';
|
$hash->{NOTIFYDEV} = 'global,TYPE=AndroidDBHost';
|
||||||
|
|
||||||
# Check path and rights of platform tools
|
# Check path and rights of platform tools
|
||||||
return "ADB command not found or is not executable in $hash->{adb}{pt}" if (! -x "$hash->{adb}{cmd}");
|
return "ADB command not found or is not executable in $hash->{adb}{pt}" if (! -x "$hash->{adb}{cmd}");
|
||||||
@ -132,7 +130,15 @@ sub Notify ($$)
|
|||||||
return if (!$events);
|
return if (!$events);
|
||||||
|
|
||||||
if ($devhash->{NAME} eq 'global' && grep (/INITIALIZED/, @$events)) {
|
if ($devhash->{NAME} eq 'global' && grep (/INITIALIZED/, @$events)) {
|
||||||
InternalTimer (gettimeofday()+60, 'AndroidDBHost::CheckADBServerTimer', $hash, 0);
|
foreach my $clName (keys %defs) {
|
||||||
|
my $clHash = $defs{$clName};
|
||||||
|
if ($clHash->{TYPE} eq 'AndroidDB') {
|
||||||
|
AndroidDB::InitAfterStart ($clHash);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# First refresh of client connection state after 10 seconds
|
||||||
|
InternalTimer (gettimeofday()+10, 'AndroidDBHost::CheckADBServerTimer', $hash, 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -194,7 +200,8 @@ sub UpdateClientStates ($)
|
|||||||
|
|
||||||
foreach my $d (keys %defs) {
|
foreach my $d (keys %defs) {
|
||||||
my $clHash = $defs{$d};
|
my $clHash = $defs{$d};
|
||||||
if ($clHash->{TYPE} eq 'AndroidDB') {
|
next if (!defined($clHash->{TYPE}) || !defined($clHash->{IODev}));
|
||||||
|
if ($clHash->{TYPE} eq 'AndroidDB' && $clHash->{IODev} == $hash) {
|
||||||
my $clState = $device->{$clHash->{ADBDevice}} // 'disconnected';
|
my $clState = $device->{$clHash->{ADBDevice}} // 'disconnected';
|
||||||
$clState =~ s/device/connected/;
|
$clState =~ s/device/connected/;
|
||||||
readingsSingleUpdate ($clHash, 'state', $clState, 1);
|
readingsSingleUpdate ($clHash, 'state', $clState, 1);
|
||||||
@ -253,10 +260,9 @@ sub Set ($@)
|
|||||||
my ($rc, $result, $error) = Execute ($hash, $command, '.*', @$a);
|
my ($rc, $result, $error) = Execute ($hash, $command, '.*', @$a);
|
||||||
return $result.$error;
|
return $result.$error;
|
||||||
}
|
}
|
||||||
elsif ($opt eq 'disconnect') {
|
elsif ($opt eq 'disconnectall') {
|
||||||
my ($rc, $result, $error) = Execute ($hash, 'disconnect', 'disconnected');
|
my ($rc, $error) = DisconnectAll ($hash);
|
||||||
UpdateClientStates ($hash);
|
return "Disconnecting all devices failed: $error" if ($rc == 0);
|
||||||
return "Disconnecting all devices failed $result $error" if ($rc == 0);
|
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
return "Unknown argument $opt, choose one of $options";
|
return "Unknown argument $opt, choose one of $options";
|
||||||
@ -311,8 +317,8 @@ sub Get ($@)
|
|||||||
# Return value:
|
# Return value:
|
||||||
# (returncode, stdout, stderr)
|
# (returncode, stdout, stderr)
|
||||||
# Return codes:
|
# Return codes:
|
||||||
# 0 - error
|
# 0 = error
|
||||||
# 1 - success
|
# 1 = success
|
||||||
##############################################################################
|
##############################################################################
|
||||||
|
|
||||||
sub Execute ($@)
|
sub Execute ($@)
|
||||||
@ -324,6 +330,8 @@ sub Execute ($@)
|
|||||||
Log3 $ioHash->{NAME}, 2, 'Execute: ADB server not running';
|
Log3 $ioHash->{NAME}, 2, 'Execute: ADB server not running';
|
||||||
return (0, '', 'ADB server not running');
|
return (0, '', 'ADB server not running');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Log3 $ioHash->{NAME}, 5, "Executing $ioHash->{adb}{cmd} $command ".join(' ',@args);
|
||||||
|
|
||||||
# Execute ADB command
|
# Execute ADB command
|
||||||
local (*CHILDIN, *CHILDOUT, *CHILDERR);
|
local (*CHILDIN, *CHILDOUT, *CHILDERR);
|
||||||
@ -370,13 +378,13 @@ sub Execute ($@)
|
|||||||
|
|
||||||
sub IsConnected ($)
|
sub IsConnected ($)
|
||||||
{
|
{
|
||||||
my $clHash = shift // return 0;
|
my $clHash = shift // return -1;
|
||||||
|
|
||||||
my $ioHash = $clHash->{IODev} // return -1;
|
my $ioHash = $clHash->{IODev} // return -1;
|
||||||
|
|
||||||
# Get active connections
|
# Get active connections
|
||||||
my $device = GetDeviceList ($ioHash) // return -1;
|
my $device = GetDeviceList ($ioHash) // return -1;
|
||||||
my $devCount = keys %$device;
|
my $devCount = scalar(keys %$device);
|
||||||
|
|
||||||
if ($devCount == 1) {
|
if ($devCount == 1) {
|
||||||
return exists($device->{$clHash->{ADBDevice}}) ? 1 : 2;
|
return exists($device->{$clHash->{ADBDevice}}) ? 1 : 2;
|
||||||
@ -403,13 +411,13 @@ sub Connect ($)
|
|||||||
my $ioHash = $clHash->{IODev} // return -1;
|
my $ioHash = $clHash->{IODev} // return -1;
|
||||||
|
|
||||||
my $connect = IsConnected ($clHash);
|
my $connect = IsConnected ($clHash);
|
||||||
|
# Log3 $clHash->{NAME}, 2, "Connection state is $connect";
|
||||||
if ($connect == 1) {
|
if ($connect == 1) {
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
elsif ($connect == 2) {
|
elsif ($connect == 2) {
|
||||||
# Disconnect all devices
|
# Disconnect all devices
|
||||||
my ($rc, $result, $error) = Execute ($ioHash, 'disconnect', 'disconnected');
|
DisconnectAll ($ioHash);
|
||||||
return -1 if ($rc == 0);
|
|
||||||
}
|
}
|
||||||
elsif ($connect == -1) {
|
elsif ($connect == -1) {
|
||||||
Log3 $clHash->{NAME}, 2, 'Cannot detect connection state';
|
Log3 $clHash->{NAME}, 2, 'Cannot detect connection state';
|
||||||
@ -424,11 +432,11 @@ sub Connect ($)
|
|||||||
}
|
}
|
||||||
|
|
||||||
##############################################################################
|
##############################################################################
|
||||||
# Connect to Android device
|
# Disconnect Android device
|
||||||
#
|
#
|
||||||
# Return value:
|
# Return value:
|
||||||
# 0 = error
|
# -1, 0 = error
|
||||||
# 1 = connected
|
# 1 = success
|
||||||
##############################################################################
|
##############################################################################
|
||||||
|
|
||||||
sub Disconnect ($)
|
sub Disconnect ($)
|
||||||
@ -443,6 +451,21 @@ sub Disconnect ($)
|
|||||||
return $rc;
|
return $rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
##############################################################################
|
||||||
|
# Disconnect all Android devices
|
||||||
|
##############################################################################
|
||||||
|
|
||||||
|
sub DisconnectAll ($)
|
||||||
|
{
|
||||||
|
my ($hash) = @_;
|
||||||
|
|
||||||
|
my ($rc, $result, $error) = Execute ($hash, 'disconnect', 'disconnected');
|
||||||
|
UpdateClientStates ($hash);
|
||||||
|
return (0, $error) if ($rc == 0);
|
||||||
|
|
||||||
|
return (1, 'ok');
|
||||||
|
}
|
||||||
|
|
||||||
##############################################################################
|
##############################################################################
|
||||||
# Get list of devices
|
# Get list of devices
|
||||||
##############################################################################
|
##############################################################################
|
||||||
@ -482,12 +505,9 @@ sub Run ($@)
|
|||||||
my $ioHash = $clHash->{IODev} // return (0, '', 'Cannot detect IO device');
|
my $ioHash = $clHash->{IODev} // return (0, '', 'Cannot detect IO device');
|
||||||
|
|
||||||
if (!Connect ($clHash)) {
|
if (!Connect ($clHash)) {
|
||||||
readingsSingleUpdate ($clHash, 'state', 'connected', 1);
|
|
||||||
return (0, '', 'Cannot connect to device');
|
return (0, '', 'Cannot connect to device');
|
||||||
}
|
}
|
||||||
|
|
||||||
readingsSingleUpdate ($clHash, 'state', 'connected', 1);
|
|
||||||
|
|
||||||
return Execute ($ioHash, $command, $succExp, @args);
|
return Execute ($ioHash, $command, $succExp, @args);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -546,6 +566,9 @@ sub TCPConnect ($$$)
|
|||||||
<li><b>set <name> command <Command> [<Args>]</b><br/>
|
<li><b>set <name> command <Command> [<Args>]</b><br/>
|
||||||
Execute ADB command.
|
Execute ADB command.
|
||||||
</li><br/>
|
</li><br/>
|
||||||
|
<li><b>set <name> disconnectAll</b><br/>
|
||||||
|
Disconnect all devices.
|
||||||
|
</li><br/>
|
||||||
<li><b>set <name> start</b><br/>
|
<li><b>set <name> start</b><br/>
|
||||||
Start ADB server.
|
Start ADB server.
|
||||||
</li><br/>
|
</li><br/>
|
||||||
|
Loading…
x
Reference in New Issue
Block a user