AndroidDB: Improved connection handling

git-svn-id: https://svn.fhem.de/fhem/trunk/fhem@24481 2b470e98-0d58-463d-a4d8-8e2adae1ed80
This commit is contained in:
zap 2021-05-21 08:43:14 +00:00
parent 6e06e171b4
commit 98decb0193
3 changed files with 150 additions and 18 deletions

View File

@ -1,5 +1,6 @@
# 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.
- bugfix: 89_AndroidDB: Connection handling improved
- feature: 73_AutoShuttersControl: add abort rain unprotection waiting time
- bugfix: lib/FHEM/Core/Authentication/Passwords.pm
change commandref

View File

@ -4,7 +4,7 @@
#
# 89_AndroidDB
#
# Version 0.2
# Version 0.3
#
# FHEM Integration for Android Devices
#
@ -195,6 +195,11 @@ sub Attr ($@)
$PRESET{_custom_}{$macroName} = $macroKeycodes;
}
}
elsif ($attrName eq 'preset' && $attrVal =~ /^\@(.+)$/) {
if (!LoadPreset ($hash, $1)) {
Log3 $hash, 2, "Can't load preset from file $1";
}
}
}
elsif ($cmd eq 'del') {
delete $PRESET{_custom_} if (exists($PRESET{_custom_}));
@ -203,6 +208,49 @@ sub Attr ($@)
return undef;
}
##############################################################################
# Load macro definitions from file
# File format:
# - Lines starting with a # are treated as comments
# - 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.
##############################################################################
sub LoadPreset ($$)
{
my ($hash, $fileName) = @_;
my @lines;
if (open (PRESETFILE, "<$fileName")) {
@lines = <PRESETFILE>;
close (PRESETFILE);
}
else {
return 0;
}
chomp @lines;
my $presetName = '';
foreach my $l (@lines) {
next if ($l =~ /^#/); # Comments are allowed
my ($macroName, $keyList) = split (':', $l);
if (!defined($keyList)) {
$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;
}
$PRESET{$presetName}{$macroName} = $keyList;
}
return 1;
}
1;
=pod
@ -258,10 +306,21 @@ sub Attr ($@)
Several macro definitions can be specified by seperating them using a blank character.
</li><br/>
<a name="preset"></a>
<li><b>preset &lt;Preset&gt;</b><br/>
Select a preset of keycode macros. If the same macro name is defined in the selected
<li><b>preset {&lt;PresetName&gt;|@&lt;PresetFileName&gt;}</b><br/>
Select a preset of keycode macros or load a set of macros from a preset defintion
file. If the same macro name is defined in the selected
preset and in attribute 'macros', the definition in the 'macros' attribute overwrites
the definition in the preset.
the definition in the preset.<br/>
A preset defintion file is using the following format:<br/>
<pre>
# Comment
PresetName1
MacroName1:KeyCode[,...]
MacroName2:KeyCode[,...]
...
PresetName2
...
</pre>
</li><br/>
</ul>

View File

@ -4,7 +4,7 @@
#
# 89_AndroidDBHost
#
# Version 0.2
# Version 0.3
#
# FHEM Integration for Android Debug Bridge
#
@ -45,6 +45,7 @@ package AndroidDBHost;
use strict;
use warnings;
use Data::Dumper;
use IPC::Open3;
use SetExtensions;
@ -59,6 +60,7 @@ BEGIN {
readingsBulkUpdateIfChanged
readingsBeginUpdate
readingsEndUpdate
devspec2array
Log3
AttrVal
ReadingsVal
@ -67,6 +69,7 @@ BEGIN {
init_done
deviceEvents
gettimeofday
defs
))
};
@ -172,10 +175,35 @@ sub CheckADBServer ($)
}
readingsSingleUpdate ($hash, 'state', $newState, 1);
# Update status of client devices
UpdateClientStates ($hash) if ($newState eq 'running');
return $newState eq 'running' ? 1 : 0;
}
##############################################################################
# Update connection states of client devices
##############################################################################
sub UpdateClientStates ($)
{
my $hash = shift;
my $device = GetDeviceList ($hash) // return 0;
foreach my $d (keys %defs) {
my $clHash = $defs{$d};
if ($clHash->{TYPE} eq 'AndroidDB') {
my $clState = $device->{$clHash->{ADBDevice}} // 'disconnected';
$clState =~ s/device/connected/;
readingsSingleUpdate ($clHash, 'state', $clState, 1);
}
}
return 1;
}
##############################################################################
# Check if ADB server is running by connecting to port
##############################################################################
@ -199,7 +227,7 @@ sub Set ($@)
my $opt = shift @$a // return 'No set command specified';
# Preprare list of available commands
my $options = 'command start:noArg stop:noArg';
my $options = 'command disconnectAll:noArg start:noArg stop:noArg';
$opt = lc($opt);
@ -225,6 +253,11 @@ 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);
}
else {
return "Unknown argument $opt, choose one of $options";
}
@ -242,7 +275,7 @@ sub Get ($@)
my $opt = shift @$a // return 'No get command specified';
# Prepare list of available commands
my $options = 'status:noArg';
my $options = 'devices:noArg status:noArg';
$opt = lc($opt);
@ -251,6 +284,22 @@ sub Get ($@)
readingsSingleUpdate ($hash, 'state', $status, 1);
return "ADB server $status";
}
elsif ($opt eq 'devices') {
my $device = GetDeviceList ($hash) // return 'Cannot read device list';
my @clDevices = devspec2array ('TYPE=AndroidDB');
my $list = '<html>List of devices:<br/><br/>';
foreach my $d (keys %$device) {
my @f = ();
foreach my $cd (@clDevices) {
if (exists($defs{$cd}) && $defs{$cd}{ADBDevice} eq $d) {
push @f, $cd;
}
}
$list .= sprintf ('%22s %20s %s<br/>', $d, join(',', @f), $device->{$d});
}
$list .= '</html>';
return $list;
}
else {
return "Unknown argument $opt, choose one of $options";
}
@ -316,7 +365,7 @@ sub Execute ($@)
# -1 = Error
# 0 = No active connections
# 1 = Current device connected
# 2 = Multiple devices connected (need to disconnect)
# 2 = Other / multiple device(s) connected (need to disconnect)
##############################################################################
sub IsConnected ($)
@ -326,14 +375,13 @@ sub IsConnected ($)
my $ioHash = $clHash->{IODev} // return -1;
# Get active connections
my ($rc, $result, $error) = Execute ($ioHash, 'devices', 'list');
return -1 if ($rc == 0);
my $device = GetDeviceList ($ioHash) // return -1;
my $devCount = keys %$device;
my @devices = $result =~ /([0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}:[0-9]+)/g;
if (scalar(@devices) == 1 && $devices[0] eq $clHash->{ADBDevice}) {
return 1;
if ($devCount == 1) {
return exists($device->{$clHash->{ADBDevice}}) ? 1 : 2;
}
elsif (scalar(@devices) > 1) {
elsif ($devCount > 1) {
return 2;
}
@ -395,6 +443,27 @@ sub Disconnect ($)
return $rc;
}
##############################################################################
# Get list of devices
##############################################################################
sub GetDeviceList ($)
{
my $hash = shift;
my ($rc, $result, $error) = Execute ($hash, 'devices');
return undef if ($rc == 0);
my %devState = ();
my @devices = $result =~ /([0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}:[0-9]+\s+[a-zA-Z0-9]+)/g;
foreach my $d (@devices) {
my ($address, $state) = split /\s+/, $d;
$devState{$address} = $state // 'disconnected';
}
return \%devState;
}
##############################################################################
# Execute commmand and return status code and command output
#
@ -455,15 +524,18 @@ sub TCPConnect ($$$)
Dependencies: Perl module IPC::Open3, Android Platform Tools
<br/><br/>
Android DB Platform Tools installation:<br/>
Debian/Raspbian: apt-get install android-sdk-platform-tools<br/>
Windows/MacOSX/Linux x86: <a href="https://developer.android.com/studio/releases/platform-tools">Android Developer Portal</a>
<br/><br/>
<ul>
<li>Debian/Raspbian: apt-get install android-sdk-platform-tools</li>
<li>Windows/MacOSX/Linux x86: <a href="https://developer.android.com/studio/releases/platform-tools">Android Developer Portal</a></li>
</ul>
<br/>
<a name="AndroidDBHostdefine"></a>
<b>Define</b><br/><br/>
<ul>
<code>define &lt;name&gt; AndroidDBHost [server=&lt;host&gt;}[:&lt;port&gt;]] [adb=&lt;path&gt;]</code><br/><br/>
The parameter 'host' is the hostname of the system, where the ADB server is running. Default is 'localhost'.
Parameter 'adb' can be used to specify the path to the adb command (must include 'adb' or 'adb.exe').
Parameter 'adb' can be used to specify the path to the adb command (must include 'adb' or 'adb.exe').<br/>
<b>Note:</b> The adb command must be executable by the account under which FHEM is running.
</ul>
<br/>
</ul>