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.
|
||||
# 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
|
||||
- new: 72_UBUS_CALL: module to send call requests via uBus client
|
||||
- new: 72_UBUS_CLIENT: module to communicate with uBus devices
|
||||
|
@ -4,7 +4,7 @@
|
||||
#
|
||||
# 89_AndroidDB
|
||||
#
|
||||
# Version 0.4
|
||||
# Version 0.6
|
||||
#
|
||||
# FHEM Integration for Android Devices
|
||||
#
|
||||
@ -36,16 +36,19 @@ sub AndroidDB_Initialize ($)
|
||||
$hash->{ShutdownFn} = "AndroidDB::Shutdown";
|
||||
|
||||
$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;
|
||||
|
||||
use strict;
|
||||
use warnings;
|
||||
|
||||
use SetExtensions;
|
||||
|
||||
use Storable qw(dclone);
|
||||
use GPUtils qw(:all);
|
||||
|
||||
BEGIN {
|
||||
@ -55,42 +58,113 @@ BEGIN {
|
||||
readingsBulkUpdateIfChanged
|
||||
readingsBeginUpdate
|
||||
readingsEndUpdate
|
||||
setDevAttrList
|
||||
CommandDefine
|
||||
CommandSet
|
||||
CommandAttr
|
||||
Log3
|
||||
AttrVal
|
||||
ReadingsVal
|
||||
AssignIoPort
|
||||
InternalTimer
|
||||
asyncOutput
|
||||
gettimeofday
|
||||
parseParams
|
||||
defs
|
||||
attr
|
||||
modules
|
||||
data
|
||||
init_done
|
||||
))
|
||||
};
|
||||
|
||||
# Remote control presets
|
||||
my %PRESET = (
|
||||
my $PRESET = {
|
||||
'MagentaTVStick' => {
|
||||
'APPS' => 'KEYCODE_ALL_APPS',
|
||||
'BACK' => 'KEYCODE_BACK',
|
||||
'EPG' => 'KEYCODE_TV_INPUT_HDMI_2',
|
||||
'HOME' => 'KEYCODE_HOME',
|
||||
'INFO' => 'KEYCODE_INFO',
|
||||
'MEGATHEK' => 'KEYCODE_TV_INPUT_HDMI_3',
|
||||
'MUTE' => 'KEYCODE_MUTE',
|
||||
'OK' => 'KEYCODE_DPAD_CENTER',
|
||||
'POWER' => 'KEYCODE_POWER',
|
||||
'PROG+' => 'KEYCODE_CHANNEL_UP',
|
||||
'PROG-' => 'KEYCODE_CHANNEL_DOWN',
|
||||
'RECORD' => 'KEYCODE_MEDIA_RECORD',
|
||||
'SEARCH' => 'KEYCODE_TV_INPUT_HDMI_1',
|
||||
'TV' => 'KEYCODE_TV_INPUT_HDMI_4'
|
||||
'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'
|
||||
},
|
||||
'SonyTV' => {
|
||||
'POWER' => 'KEYCODE_POWER'
|
||||
'MagentaOne' => {
|
||||
'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 ($$$)
|
||||
{
|
||||
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);
|
||||
|
||||
@ -100,9 +174,31 @@ sub Define ($$$)
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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 ($$)
|
||||
{
|
||||
my ($hash, $name) = @_;
|
||||
@ -126,39 +222,119 @@ sub Set ($@)
|
||||
my $name = shift @$a;
|
||||
my $opt = shift @$a // return 'No set command specified';
|
||||
|
||||
#
|
||||
# Preprare list of available commands
|
||||
my $options = 'reboot sendKey shell';
|
||||
my @macroList = ();
|
||||
my $preset = AttrVal ($hash->{NAME}, 'preset', '');
|
||||
my $macros = AttrVal ($hash->{NAME}, 'macros', '');
|
||||
push @macroList, sort keys %{$PRESET{$preset}} if ($preset ne '' && exists($PRESET{$preset}));
|
||||
push @macroList, sort keys %{$PRESET{_custom_}} if ($macros ne '' && exists($PRESET{_custom_}));
|
||||
#
|
||||
|
||||
# Standard commands
|
||||
my $options = 'exportPresets reboot rollHorz rollVert sendKey sendNumKeys sendText shell tap';
|
||||
|
||||
# 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;
|
||||
$options .= ' remoteControl:'.join(',', sort grep { !$e{$_}++ } @macroList) if (scalar(@macroList) > 0);
|
||||
$opt = lc($opt);
|
||||
$options .= ' remoteControl:'.join(',', sort grep { !$e{$_}++ } @presetList) if (scalar(@presetList) > 0);
|
||||
|
||||
# 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 $key = shift @$a // return "Usage: set $name $opt KeyCode";
|
||||
my ($rc, $result, $error) = AndroidDBHost::Run ($hash, 'shell', '.*', 'input', 'keyevent', $key);
|
||||
my $lcopt = lc($opt);
|
||||
|
||||
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);
|
||||
}
|
||||
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);
|
||||
return $error if ($rc == 0);
|
||||
}
|
||||
elsif ($opt eq 'shell') {
|
||||
elsif ($lcopt eq 'shell') {
|
||||
return "Usage: set $name $opt ShellCommand" if (scalar(@$a) == 0);
|
||||
my ($rc, $result, $error) = AndroidDBHost::Run ($hash, $opt, '.*', @$a);
|
||||
return $result.$error,
|
||||
}
|
||||
elsif ($opt eq 'remotecontrol') {
|
||||
elsif ($lcopt eq 'remotecontrol') {
|
||||
my $macroName = shift @$a // return "Usage: set $name $opt MacroName";
|
||||
$preset = '_custom_' if (exists($PRESET{_custom_}) && exists($PRESET{_custom_}{$macroName}));
|
||||
return "Preset and/or macro $macroName not defined" if ($preset eq '' || !exists($PRESET{$preset}{$macroName}));
|
||||
$preset = '_custom_' if (exists($hash->{adb}{preset}{_custom_}) && exists($hash->{adb}{preset}{_custom_}{$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',
|
||||
split (',', $PRESET{$preset}{$macroName}));
|
||||
split (',', $hash->{adb}{preset}{$preset}{$macroName}));
|
||||
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 {
|
||||
return "Unknown argument $opt, choose one of $options";
|
||||
}
|
||||
@ -171,12 +347,35 @@ sub Get ($@)
|
||||
my $name = shift @$a;
|
||||
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') {
|
||||
return "Command not implemented";
|
||||
my $lcopt = lc($opt);
|
||||
|
||||
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 {
|
||||
return "Unknown argument $opt, choose one of $options";
|
||||
@ -190,20 +389,31 @@ sub Attr ($@)
|
||||
|
||||
if ($cmd eq 'set') {
|
||||
if ($attrName eq 'macros') {
|
||||
foreach my $macroDef (split /\s+/, $attrVal) {
|
||||
my ($macroName, $macroKeycodes) = split (':', $macroDef);
|
||||
$PRESET{_custom_}{$macroName} = $macroKeycodes;
|
||||
foreach my $macroDef (split /;/, $attrVal) {
|
||||
my ($macroName, $macroPar) = split (':', $macroDef);
|
||||
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') {
|
||||
if (!LoadPreset ($hash, $attrVal)) {
|
||||
Log3 $hash, 2, "Can't load preset from file $attrVal";
|
||||
return "Cannot load preset from file $attrVal";
|
||||
if (!LoadPresets ($hash, $attrVal)) {
|
||||
return "Cannot load presets from file $attrVal";
|
||||
}
|
||||
}
|
||||
elsif ($attrName eq 'connect') {
|
||||
AndroidDBHost::Connect ($hash) if (!$init_done && $attrVal eq '1');
|
||||
}
|
||||
}
|
||||
elsif ($cmd eq 'del') {
|
||||
delete $PRESET{_custom_} if (exists($PRESET{_custom_}));
|
||||
delete $hash->{adb}{preset}{_custom_} if (exists($hash->{adb}{preset}{_custom_}));
|
||||
}
|
||||
|
||||
return undef;
|
||||
@ -213,50 +423,194 @@ sub Attr ($@)
|
||||
# Load macro definitions from file
|
||||
# File format:
|
||||
# - Lines starting with a # are treated as comments
|
||||
# - Empty lines are ignored
|
||||
# - Lines containing a single word are setting the preset name for the
|
||||
# following lines
|
||||
# - Lines in format Name:KeyList are defining a macro. KeyList is a comma
|
||||
# separated list of keycodes.
|
||||
# - Lines in format Name:Command:Parameters are defining a command macro.
|
||||
##############################################################################
|
||||
|
||||
sub LoadPreset ($$)
|
||||
sub LoadPresets ($$)
|
||||
{
|
||||
my ($hash, $fileName) = @_;
|
||||
|
||||
my @lines;
|
||||
# Read file
|
||||
my @lines;
|
||||
if (open (PRESETFILE, "<$fileName")) {
|
||||
@lines = <PRESETFILE>;
|
||||
close (PRESETFILE);
|
||||
}
|
||||
else {
|
||||
ShowMessage ($hash, 2, "Can't open file $fileName");
|
||||
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;
|
||||
my $presetName = '';
|
||||
|
||||
foreach my $l (@lines) {
|
||||
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;
|
||||
next;
|
||||
}
|
||||
elsif (defined($keyList) && $presetName eq '') {
|
||||
Log3 $hash, 2, "Preset name must be specified before first macro definition in file $fileName";
|
||||
return 0;
|
||||
else {
|
||||
if ($presetName eq '') {
|
||||
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;
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
=pod
|
||||
=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
|
||||
|
||||
<a name="AndroidDB"></a>
|
||||
@ -270,8 +624,10 @@ sub LoadPreset ($$)
|
||||
<a name="AndroidDBdefine"></a>
|
||||
<b>Define</b><br/><br/>
|
||||
<ul>
|
||||
<code>define <name> AndroidDB {<NameOrIP>}</code>
|
||||
The parameter 'NameOrIP' is the hostname or the IP address of the Android device.
|
||||
<code>define <name> AndroidDB <NameOrIP>[<Port>]</code>
|
||||
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>
|
||||
<br/>
|
||||
</ul>
|
||||
@ -279,6 +635,14 @@ sub LoadPreset ($$)
|
||||
<a name="AndroidDBset"></a>
|
||||
<b>Set</b><br/><br/>
|
||||
<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/>
|
||||
Reboot the device.
|
||||
</li><br/>
|
||||
@ -288,23 +652,61 @@ sub LoadPreset ($$)
|
||||
in attribute 'macros' are overwriting macros with the same name in a preset selected
|
||||
by attribute 'preset'.
|
||||
</li><br/>
|
||||
<li><b>set <name> sendKey <KeyCode></b><br/>
|
||||
Send a key code to the Android device.
|
||||
<li><b>set <name> rollHorz <DeltaX></b><br/>
|
||||
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><b>set <name> shell <Command> [<Arguments>]</b><br/>
|
||||
Execute shell command on Android device.
|
||||
</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>
|
||||
|
||||
<a name="AndroidDBattr"></a>
|
||||
<b>Attributes</b><br/><br/>
|
||||
<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>
|
||||
<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'
|
||||
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/>
|
||||
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/>
|
||||
<a name="preset"></a>
|
||||
<li><b>preset <PresetName></b><br/>
|
||||
@ -320,12 +722,17 @@ sub LoadPreset ($$)
|
||||
<pre>
|
||||
# Comment
|
||||
PresetName1
|
||||
MacroName1:KeyCode[,...]
|
||||
MacroName2:KeyCode[,...]
|
||||
MacroDef1
|
||||
MacroDef2
|
||||
...
|
||||
PresetName2
|
||||
...
|
||||
</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/>
|
||||
</ul>
|
||||
|
||||
|
@ -4,7 +4,7 @@
|
||||
#
|
||||
# 89_AndroidDBHost
|
||||
#
|
||||
# Version 0.4
|
||||
# Version 0.6
|
||||
#
|
||||
# FHEM Integration for Android Debug Bridge
|
||||
#
|
||||
@ -19,8 +19,6 @@
|
||||
# Windows/MacOSX/Linux x86: https://developer.android.com/studio/releases/platform-tools
|
||||
#
|
||||
|
||||
|
||||
|
||||
package main;
|
||||
|
||||
use strict;
|
||||
@ -86,7 +84,7 @@ sub Define ($$$)
|
||||
$hash->{adb}{port} = $port // 5037;
|
||||
$hash->{adb}{cmd} = $h->{adb} // '/usr/bin/adb';
|
||||
$hash->{Clients} = ':AndroidDB:';
|
||||
$hash->{NOTIFYDEV} = 'global,TYPE=(AndroidDBHost|AndroidDB)';
|
||||
$hash->{NOTIFYDEV} = 'global,TYPE=AndroidDBHost';
|
||||
|
||||
# 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}");
|
||||
@ -132,7 +130,15 @@ sub Notify ($$)
|
||||
return if (!$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) {
|
||||
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';
|
||||
$clState =~ s/device/connected/;
|
||||
readingsSingleUpdate ($clHash, 'state', $clState, 1);
|
||||
@ -253,10 +260,9 @@ sub Set ($@)
|
||||
my ($rc, $result, $error) = Execute ($hash, $command, '.*', @$a);
|
||||
return $result.$error;
|
||||
}
|
||||
elsif ($opt eq 'disconnect') {
|
||||
my ($rc, $result, $error) = Execute ($hash, 'disconnect', 'disconnected');
|
||||
UpdateClientStates ($hash);
|
||||
return "Disconnecting all devices failed $result $error" if ($rc == 0);
|
||||
elsif ($opt eq 'disconnectall') {
|
||||
my ($rc, $error) = DisconnectAll ($hash);
|
||||
return "Disconnecting all devices failed: $error" if ($rc == 0);
|
||||
}
|
||||
else {
|
||||
return "Unknown argument $opt, choose one of $options";
|
||||
@ -311,8 +317,8 @@ sub Get ($@)
|
||||
# Return value:
|
||||
# (returncode, stdout, stderr)
|
||||
# Return codes:
|
||||
# 0 - error
|
||||
# 1 - success
|
||||
# 0 = error
|
||||
# 1 = success
|
||||
##############################################################################
|
||||
|
||||
sub Execute ($@)
|
||||
@ -324,6 +330,8 @@ sub Execute ($@)
|
||||
Log3 $ioHash->{NAME}, 2, 'Execute: ADB server not running';
|
||||
return (0, '', 'ADB server not running');
|
||||
}
|
||||
|
||||
Log3 $ioHash->{NAME}, 5, "Executing $ioHash->{adb}{cmd} $command ".join(' ',@args);
|
||||
|
||||
# Execute ADB command
|
||||
local (*CHILDIN, *CHILDOUT, *CHILDERR);
|
||||
@ -370,13 +378,13 @@ sub Execute ($@)
|
||||
|
||||
sub IsConnected ($)
|
||||
{
|
||||
my $clHash = shift // return 0;
|
||||
my $clHash = shift // return -1;
|
||||
|
||||
my $ioHash = $clHash->{IODev} // return -1;
|
||||
|
||||
# Get active connections
|
||||
my $device = GetDeviceList ($ioHash) // return -1;
|
||||
my $devCount = keys %$device;
|
||||
my $devCount = scalar(keys %$device);
|
||||
|
||||
if ($devCount == 1) {
|
||||
return exists($device->{$clHash->{ADBDevice}}) ? 1 : 2;
|
||||
@ -403,13 +411,13 @@ sub Connect ($)
|
||||
my $ioHash = $clHash->{IODev} // return -1;
|
||||
|
||||
my $connect = IsConnected ($clHash);
|
||||
# Log3 $clHash->{NAME}, 2, "Connection state is $connect";
|
||||
if ($connect == 1) {
|
||||
return 1;
|
||||
}
|
||||
elsif ($connect == 2) {
|
||||
# Disconnect all devices
|
||||
my ($rc, $result, $error) = Execute ($ioHash, 'disconnect', 'disconnected');
|
||||
return -1 if ($rc == 0);
|
||||
DisconnectAll ($ioHash);
|
||||
}
|
||||
elsif ($connect == -1) {
|
||||
Log3 $clHash->{NAME}, 2, 'Cannot detect connection state';
|
||||
@ -424,11 +432,11 @@ sub Connect ($)
|
||||
}
|
||||
|
||||
##############################################################################
|
||||
# Connect to Android device
|
||||
# Disconnect Android device
|
||||
#
|
||||
# Return value:
|
||||
# 0 = error
|
||||
# 1 = connected
|
||||
# -1, 0 = error
|
||||
# 1 = success
|
||||
##############################################################################
|
||||
|
||||
sub Disconnect ($)
|
||||
@ -443,6 +451,21 @@ sub Disconnect ($)
|
||||
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
|
||||
##############################################################################
|
||||
@ -482,12 +505,9 @@ sub Run ($@)
|
||||
my $ioHash = $clHash->{IODev} // return (0, '', 'Cannot detect IO device');
|
||||
|
||||
if (!Connect ($clHash)) {
|
||||
readingsSingleUpdate ($clHash, 'state', 'connected', 1);
|
||||
return (0, '', 'Cannot connect to device');
|
||||
}
|
||||
|
||||
readingsSingleUpdate ($clHash, 'state', 'connected', 1);
|
||||
|
||||
return Execute ($ioHash, $command, $succExp, @args);
|
||||
}
|
||||
|
||||
@ -546,6 +566,9 @@ sub TCPConnect ($$$)
|
||||
<li><b>set <name> command <Command> [<Args>]</b><br/>
|
||||
Execute ADB command.
|
||||
</li><br/>
|
||||
<li><b>set <name> disconnectAll</b><br/>
|
||||
Disconnect all devices.
|
||||
</li><br/>
|
||||
<li><b>set <name> start</b><br/>
|
||||
Start ADB server.
|
||||
</li><br/>
|
||||
|
Loading…
x
Reference in New Issue
Block a user