74_Unifi: - Code Cleanup

- Preparing for future functions
          - Some small bug fixes 


git-svn-id: https://svn.fhem.de/fhem/trunk@9160 2b470e98-0d58-463d-a4d8-8e2adae1ed80
This commit is contained in:
rapster 2015-08-29 19:25:28 +00:00
parent 14d9008d6d
commit b6f8484872

View File

@ -1,11 +1,11 @@
###############################################################################
# $Id: 74_Unifi.pm 2015-08-27 06:00 - rapster - rapster at x0e dot de $
# $Id: 74_Unifi.pm 2015-08-29 21:00 - rapster - rapster at x0e dot de $
package main;
use strict;
use warnings;
use HttpUtils;
use POSIX qw(strftime);
use POSIX;
use JSON qw(decode_json);
###############################################################################
@ -16,7 +16,6 @@ sub Unifi_Initialize($$) {
$hash->{SetFn} = "Unifi_Set";
$hash->{GetFn} = "Unifi_Get";
$hash->{AttrFn} = 'Unifi_Attr';
$hash->{NOTIFYDEV} = "global";
$hash->{NotifyFn} = "Unifi_Notify";
$hash->{AttrList} = "disable:1,0 "
."devAlias "
@ -34,13 +33,14 @@ sub Unifi_Define($$) {
return "Wrong syntax: <version> is not a valid number! Must be 3 or 4." if($a[8] && (!looks_like_number($a[8]) || $a[8] !~ /3|4/));
my $name = $a[0];
my $oldLoginData = ($hash->{loginParams}) ? $hash->{loginParams}->{data}.$hash->{url} : 0;
%$hash = ( %$hash,
CONNECTED => $hash->{CONNECTED} || 0,
url => "https://".$a[2].(($a[3] != 443) ? ':'.$a[3] : '').'/',
interval => $a[6] || 30,
siteID => $a[7] || 'default',
version => $a[8] || 4,
NOTIFYDEV => 'global',
unifi => {
CONNECTED => 0,
interval => $a[6] || 30,
version => $a[8] || 4,
url => "https://".$a[2].(($a[3] == 443) ? '' : ':'.$a[3]).'/api/s/'.(($a[7]) ? $a[7] : 'default').'/',
},
);
$hash->{httpParams} = {
hash => $hash,
@ -50,28 +50,16 @@ sub Unifi_Define($$) {
ignoreredirects => 1,
loglevel => 5,
sslargs => { SSL_verify_mode => 'SSL_VERIFY_NONE' },
header => ($hash->{version} == 3) ? undef : "Content-Type: application/json;charset=UTF-8"
};
$hash->{loginParams} = {
%{$hash->{httpParams}},
cookies => ($hash->{loginParams}->{cookies}) ? $hash->{loginParams}->{cookies} : '',
callback => \&Unifi_Login_Receive
};
if($hash->{version} == 3) {
$hash->{loginParams}->{url} = $hash->{url}."login";
$hash->{loginParams}->{data} = "login=login&username=".Unifi_Urlencode($a[4])."&password=".Unifi_Urlencode($a[5]);
if($hash->{unifi}->{version} == 3) {
( $hash->{httpParams}->{loginUrl} = $hash->{unifi}->{url} ) =~ s/api\/s.+/login/;
$hash->{httpParams}->{loginData} = "login=login&username=".$a[4]."&password=".$a[5];
}else {
$hash->{loginParams}->{url} = $hash->{url}."api/login";
$hash->{loginParams}->{data} = "{'username':'".$a[4]."', 'password':'".$a[5]."'}";
( $hash->{httpParams}->{loginUrl} = $hash->{unifi}->{url} ) =~ s/api\/s.+/api\/login/;
$hash->{httpParams}->{loginData} = '{"username":"'.$a[4].'", "password":"'.$a[5].'"}';
}
# Don't use old cookies when user, pw or url changed
if($oldLoginData && $oldLoginData ne $hash->{loginParams}->{data}.$hash->{url}) {
$hash->{loginParams}->{cookies} = '';
Unifi_CONNECTED($hash,'disconnected');
}
Log3 $name, 5, "$name: Defined with url:$hash->{url}, interval:$hash->{interval}, siteID:$hash->{siteID}, version:$hash->{version}";
Log3 $name, 5, "$name: Defined with url:$hash->{unifi}->{url}, interval:$hash->{unifi}->{interval}, version:$hash->{unifi}->{version}";
return undef;
}
###############################################################################
@ -149,7 +137,7 @@ sub Unifi_Get($@) {
my $clients = '';
my $devAliases = AttrVal($name,"devAlias",0);
for (keys %{$hash->{clients}}) { # Replace ID's with Aliases
if ( $devAliases && $devAliases =~ /$_:(.+?)(\s|$)/
if ( ($devAliases && $devAliases =~ /$_:(.+?)(\s|$)/)
|| ($devAliases && defined $hash->{clients}->{$_}->{name} && $devAliases =~ /$hash->{clients}->{$_}->{name}:(.+?)(\s|$)/)
|| ($devAliases && defined $hash->{clients}->{$_}->{hostname} && $devAliases =~ /$hash->{clients}->{$_}->{hostname}:(.+?)(\s|$)/)
|| (defined $hash->{clients}->{$_}->{name} && $hash->{clients}->{$_}->{name} =~ /^([\w\.\-]+)$/)
@ -166,7 +154,7 @@ sub Unifi_Get($@) {
elsif ($getName eq 'clientData' && $clients) {
if($getVal && $getVal ne 'all') { # Make ID from Alias
for (keys %{$hash->{clients}}) {
if ( $devAliases && $devAliases =~ /$_:$getVal/
if ( ($devAliases && $devAliases =~ /$_:$getVal/)
|| ($devAliases && defined $hash->{clients}->{$_}->{name} && $devAliases =~ /$hash->{clients}->{$_}->{name}:$getVal/)
|| ($devAliases && defined $hash->{clients}->{$_}->{hostname} && $devAliases =~ /$hash->{clients}->{$_}->{hostname}:$getVal/)
|| (defined $hash->{clients}->{$_}->{name} && $hash->{clients}->{$_}->{name} eq $getVal)
@ -251,13 +239,17 @@ sub Unifi_DoUpdate($@) {
}
if (Unifi_CONNECTED($hash)) {
$hash->{unifi}->{updateStartTime} = time();
$hash->{updateDispatch} = { # {updateDispatch}->{callFn}[callFnRef,'receiveFn',receiveFnRef]
Unifi_GetClients_Send => [\&Unifi_GetClients_Send,'Unifi_GetClients_Receive',\&Unifi_GetClients_Receive],
Unifi_GetAnother_Send => [\&Unifi_GetAnother_Send,'Unifi_GetAnother_Receive',\&Unifi_GetAnother_Receive],
Unifi_DoAfterUpdate => [\&Unifi_DoAfterUpdate,''],
Unifi_GetAccesspoints_Send => [\&Unifi_GetAccesspoints_Send,'Unifi_GetAccesspoints_Receive',\&Unifi_GetAccesspoints_Receive],
Unifi_GetWlans_Send => [\&Unifi_GetWlans_Send,'Unifi_GetWlans_Receive',\&Unifi_GetWlans_Receive],
Unifi_GetUnarchivedAlerts_Send => [\&Unifi_GetUnarchivedAlerts_Send,'Unifi_GetUnarchivedAlerts_Receive',\&Unifi_GetUnarchivedAlerts_Receive],
Unifi_GetEvents_Send => [\&Unifi_GetEvents_Send,'Unifi_GetEvents_Receive',\&Unifi_GetEvents_Receive],
Unifi_GetWlanGroups_Send => [\&Unifi_GetWlanGroups_Send,'Unifi_GetWlanGroups_Receive',\&Unifi_GetWlanGroups_Receive],
Unifi_ProcessUpdate => [\&Unifi_ProcessUpdate,''],
};
Unifi_NextUpdateFn($hash,$self);
InternalTimer(time()+$hash->{interval}, 'Unifi_DoUpdate', $hash, 0);
}
else {
Unifi_CONNECTED($hash,'disconnected');
@ -272,7 +264,12 @@ sub Unifi_Login_Send($) {
my ($name,$self) = ($hash->{NAME},Unifi_Whoami());
Log3 $name, 5, "$name ($self) - executed.";
HttpUtils_NonblockingGet($hash->{loginParams});
HttpUtils_NonblockingGet( {
%{$hash->{httpParams}},
url => $hash->{httpParams}->{loginUrl},
data => $hash->{httpParams}->{loginData},
callback => \&Unifi_Login_Receive
} );
return undef;
}
sub Unifi_Login_Receive($) {
@ -283,41 +280,34 @@ sub Unifi_Login_Receive($) {
if ($err ne "") {
Log3 $name, 5, "$name ($self) - Error while requesting ".$param->{url}." - $err";
}
elsif ($data ne "" && $hash->{version} == 3) {
elsif ($data ne "" && $hash->{unifi}->{version} == 3) {
if ($data =~ /Invalid username or password/si) {
Log3 $name, 1, "$name ($self) - Login Failed! Invalid username or password!";
} else {
Log3 $name, 5, "$name ($self) - Login Failed! Version 3 should not deliver data on successfull login.";
}
}
elsif ($data ne "" || $hash->{version} == 3) { # v3 Login is empty if login is successfully
if ($param->{code} == 200 || $param->{code} == 400 || $param->{code} == 401 || ($hash->{version} == 3 && ($param->{code} == 302 || $param->{code} == 200))) {
if($data ne "") {
eval {
$data = decode_json($data);
1;
} or do {
my $e = $@;
$data->{meta}->{rc} = 'error';
$data->{meta}->{msg} = 'Unifi.FailedToDecodeJSON - $e';
};
}
if ($hash->{version} == 3 || $data->{meta}->{rc} eq "ok") { # v3 has no rc-state
elsif ($data ne "" || $hash->{unifi}->{version} == 3) { # v3 Login is empty if login is successfully
if ($param->{code} == 200 || $param->{code} == 400 || $param->{code} == 401 || ($hash->{unifi}->{version} == 3 && ($param->{code} == 302 || $param->{code} == 200))) {
eval { $data = decode_json($data); 1; } or do { $data = { meta => {rc => 'error.decode_json', msg => $@} }; };
if ($hash->{unifi}->{version} == 3 || $data->{meta}->{rc} eq "ok") { # v3 has no rc-state
Log3 $name, 5, "$name ($self) - state=ok || version=3";
$param->{cookies} = '';
$hash->{httpParams}->{header} = '';
for (split("\r\n",$param->{httpheader})) {
if(/^Set-Cookie/) {
s/Set-Cookie:\s(.*?);.*/Cookie: $1/;
$param->{cookies} .= $_.(($hash->{version} == 3) ? '' : '\r\n'); #v3 has only one cookie and no header at all
$hash->{httpParams}->{header} .= $_.'\r\n';
}
}
if($param->{cookies} ne '') {
Log3 $name, 5, "$name ($self) - Login successfully! $param->{cookies}";
if($hash->{httpParams}->{header} ne '') {
$hash->{httpParams}->{header} =~ s/\\r\\n$//;
Log3 $name, 5, "$name ($self) - Login successfully! $hash->{httpParams}->{header}";
Unifi_CONNECTED($hash,'connected');
Unifi_DoUpdate($hash);
return undef;
} else {
$hash->{httpParams}->{header} = undef;
Log3 $name, 5, "$name ($self) - Something went wrong, login seems ok but no cookies received.";
}
}
@ -336,7 +326,7 @@ sub Unifi_Login_Receive($) {
} else {
Log3 $name, 5, "$name ($self) - Login Failed (without msg)! - state:'$data->{meta}->{rc}'";
}
$param->{cookies} = '';
$hash->{httpParams}->{header} = undef;
}
} else {
Log3 $name, 5, "$name ($self) - Failed with HTTP Code $param->{code}!";
@ -346,7 +336,7 @@ sub Unifi_Login_Receive($) {
}
Log3 $name, 5, "$name ($self) - Connect/Login to Unifi-Controller failed. Will try again after interval...";
Unifi_CONNECTED($hash,'disconnected');
InternalTimer(time()+$hash->{interval}, 'Unifi_Login_Send', $hash, 0);
InternalTimer(time()+$hash->{unifi}->{interval}, 'Unifi_Login_Send', $hash, 0);
return undef;
}
###############################################################################
@ -356,13 +346,11 @@ sub Unifi_GetClients_Send($) {
my ($name,$self) = ($hash->{NAME},Unifi_Whoami());
Log3 $name, 5, "$name ($self) - executed.";
my $param = {
%{$hash->{httpParams}},
url => $hash->{url}."api/s/$hash->{siteID}/stat/sta",
header => ($hash->{version} == 3) ? $hash->{loginParams}->{cookies} : $hash->{loginParams}->{cookies}.$hash->{httpParams}->{header},
HttpUtils_NonblockingGet( {
%{$hash->{httpParams}},
url => $hash->{unifi}->{url}."stat/sta",
callback => $hash->{updateDispatch}->{$self}[2]
};
HttpUtils_NonblockingGet($param);
} );
return undef;
}
sub Unifi_GetClients_Receive($) {
@ -371,118 +359,281 @@ sub Unifi_GetClients_Receive($) {
Log3 $name, 5, "$name ($self) - executed.";
if ($err ne "") {
Log3 $name, 5, "$name ($self) - Error while requesting ".$param->{url}." - $err";
Unifi_ReceiveFailure($hash,{rc => 'Error while requesting', msg => $param->{url}." - $err"});
}
elsif ($data ne "") {
if ($param->{code} == 200 || $param->{code} == 400 || $param->{code} == 401) {
eval {
$data = decode_json($data);
1;
} or do {
my $e = $@;
$data->{meta}->{rc} = 'error';
$data->{meta}->{msg} = 'Unifi.FailedToDecodeJSON - $e';
};
eval { $data = decode_json($data); 1; } or do { $data = { meta => {rc => 'error.decode_json', msg => $@} }; };
if ($data->{meta}->{rc} eq "ok") {
Log3 $name, 5, "$name ($self) - state:'$data->{meta}->{rc}'";
readingsBeginUpdate($hash);
my $devAliases = AttrVal($name,"devAlias",0);
my $connectedClientIDs = {};
my $i = 1;
my $clientName;
$hash->{unifi}->{connectedClients} = undef;
for my $h (@{$data->{data}}) {
$clientName = $h->{user_id};
if ( $devAliases && $devAliases =~ /$clientName:(.+?)(\s|$)/
|| ($devAliases && defined $h->{name} && $devAliases =~ /$h->{name}:(.+?)(\s|$)/)
|| ($devAliases && defined $h->{hostname} && $devAliases =~ /$h->{hostname}:(.+?)(\s|$)/)
|| (defined $h->{name} && $h->{name} =~ /^([\w\.\-]+)$/)
|| (defined $h->{hostname} && $h->{hostname} =~ /^([\w\.\-]+)$/)
) {
$clientName = $1;
}
$hash->{clients}->{$h->{user_id}} = $h;
$connectedClientIDs->{$h->{user_id}} = 1;
readingsBulkUpdate($hash,$clientName."_hostname",(defined $h->{hostname}) ? $h->{hostname} : (defined $h->{ip}) ? $h->{ip} : 'Unknown');
readingsBulkUpdate($hash,$clientName."_last_seen",strftime "%Y-%m-%d %H:%M:%S",localtime($h->{last_seen}));
readingsBulkUpdate($hash,$clientName."_uptime",$h->{uptime});
readingsBulkUpdate($hash,$clientName,'connected');
}
for my $clientID (keys %{$hash->{clients}}) {
if (!defined($connectedClientIDs->{$clientID}) && $hash->{READINGS}->{$clientID}->{VAL} ne 'disconnected') {
Log3 $name, 5, "$name ($self) - Client '$clientID' previously connected is now disconnected.";
if ( $devAliases && $devAliases =~ /$clientID:(.+?)(\s|$)/
|| ($devAliases && defined $hash->{clients}->{$clientID}->{name} && $devAliases =~ /$hash->{clients}->{$clientID}->{name}:(.+?)(\s|$)/)
|| ($devAliases && defined $hash->{clients}->{$clientID}->{hostname} && $devAliases =~ /$hash->{clients}->{$clientID}->{hostname}:(.+?)(\s|$)/)
|| (defined $hash->{clients}->{$clientID}->{name} && $hash->{clients}->{$clientID}->{name} =~ /^([\w\.\-]+)$/)
|| (defined $hash->{clients}->{$clientID}->{hostname} && $hash->{clients}->{$clientID}->{hostname} =~ /^([\w\.\-]+)$/)
) {
$clientID = $1;
}
readingsBulkUpdate($hash,$clientID,'disconnected') if($hash->{READINGS}->{$clientID}->{VAL} ne 'disconnected');
}
}
readingsEndUpdate($hash,1);
}
else {
if (defined($data->{meta}->{msg})) {
if ($data->{meta}->{msg} eq 'api.err.LoginRequired') {
Log3 $name, 5, "$name ($self) - LoginRequired detected...";
if(Unifi_CONNECTED($hash)) {
Log3 $name, 5, "$name ($self) - I am the first who detected LoginRequired. Do re-login...";
Unifi_CONNECTED($hash,'disconnected');
Unifi_DoUpdate($hash);
return undef;
}
}
elsif ($data->{meta}->{msg} eq "api.err.NoSiteContext" || ($hash->{version} == 3 && $data->{meta}->{msg} eq "api.err.InvalidObject")) {
Log3 $name, 1, "$name ($self) - Failed! - state:'$data->{meta}->{rc}' - msg:'$data->{meta}->{msg}'"
." - This error indicates that the <siteID> in your definition is wrong."
." Try to modify your definition with <sideID> = default.";
}
else {
Log3 $name, 5, "$name ($self) - Failed! - state:'$data->{meta}->{rc}' - msg:'$data->{meta}->{msg}'";
}
} else {
Log3 $name, 5, "$name ($self) - Failed (without message)! - state:'$data->{meta}->{rc}'";
$hash->{unifi}->{connectedClients}->{$h->{_id}} = 1;
$hash->{clients}->{$h->{_id}} = $h;
}
}
}
else {
Log3 $name, 5, "$name ($self) - Failed with HTTP Code $param->{code}.";
else { Unifi_ReceiveFailure($hash,$data->{meta}); }
} else {
Unifi_ReceiveFailure($hash,{rc => $param->{code}, msg => "Failed with HTTP Code $param->{code}."});
}
}
Unifi_NextUpdateFn($hash,$self);
return undef;
}
###############################################################################
sub Unifi_GetAnother_Send($) {
sub Unifi_GetWlans_Send($) {
my ($hash) = @_;
my ($name,$self) = ($hash->{NAME},Unifi_Whoami());
Log3 $name, 5, "$name ($self) - executed.";
$hash->{updateDispatch}->{$self}[2]->( {hash => $hash} ); # DUMMY
#HttpUtils_NonblockingGet($param);
HttpUtils_NonblockingGet( {
%{$hash->{httpParams}},
url => $hash->{unifi}->{url}."list/wlanconf",
callback => $hash->{updateDispatch}->{$self}[2],
} );
return undef;
}
sub Unifi_GetAnother_Receive($) {
sub Unifi_GetWlans_Receive($) {
my ($param, $err, $data) = @_;
my ($name,$self,$hash) = ($param->{hash}->{NAME},Unifi_Whoami(),$param->{hash});
Log3 $hash->{NAME}, 5, "$hash->{NAME} ($self) - executed.";
Log3 $name, 5, "$name ($self) - executed.";
# Do
if ($err ne "") {
Unifi_ReceiveFailure($hash,{rc => 'Error while requesting', msg => $param->{url}." - $err"});
}
elsif ($data ne "") {
if ($param->{code} == 200 || $param->{code} == 400 || $param->{code} == 401) {
eval { $data = decode_json($data); 1; } or do { $data = { meta => {rc => 'error.decode_json', msg => $@} }; };
if ($data->{meta}->{rc} eq "ok") {
Log3 $name, 5, "$name ($self) - state:'$data->{meta}->{rc}'";
for my $h (@{$data->{data}}) {
$hash->{wlans}->{$h->{_id}} = $h;
$hash->{wlans}->{$h->{_id}}->{x_passphrase} = '***'; # Don't show passphrase in list
}
}
else { Unifi_ReceiveFailure($hash,$data->{meta}); }
} else {
Unifi_ReceiveFailure($hash,{rc => $param->{code}, msg => "Failed with HTTP Code $param->{code}."});
}
}
Unifi_NextUpdateFn($hash,$self);
return undef;
}
###############################################################################
sub Unifi_GetWlanGroups_Send($) {
my ($hash) = @_;
my ($name,$self) = ($hash->{NAME},Unifi_Whoami());
Log3 $name, 5, "$name ($self) - executed.";
HttpUtils_NonblockingGet( {
%{$hash->{httpParams}},
url => $hash->{unifi}->{url}."list/wlangroup",
callback => $hash->{updateDispatch}->{$self}[2],
} );
return undef;
}
sub Unifi_GetWlanGroups_Receive($) {
my ($param, $err, $data) = @_;
my ($name,$self,$hash) = ($param->{hash}->{NAME},Unifi_Whoami(),$param->{hash});
Log3 $name, 5, "$name ($self) - executed.";
if ($err ne "") {
Unifi_ReceiveFailure($hash,{rc => 'Error while requesting', msg => $param->{url}." - $err"});
}
elsif ($data ne "") {
if ($param->{code} == 200 || $param->{code} == 400 || $param->{code} == 401) {
eval { $data = decode_json($data); 1; } or do { $data = { meta => {rc => 'error.decode_json', msg => $@} }; };
if ($data->{meta}->{rc} eq "ok") {
Log3 $name, 5, "$name ($self) - state:'$data->{meta}->{rc}'";
for my $h (@{$data->{data}}) {
$hash->{wlangroups}->{$h->{_id}} = $h;
}
}
else { Unifi_ReceiveFailure($hash,$data->{meta}); }
} else {
Unifi_ReceiveFailure($hash,{rc => $param->{code}, msg => "Failed with HTTP Code $param->{code}."});
}
}
Unifi_NextUpdateFn($hash,$self);
return undef;
}
###############################################################################
sub Unifi_GetUnarchivedAlerts_Send($) {
my ($hash) = @_;
my ($name,$self) = ($hash->{NAME},Unifi_Whoami());
Log3 $name, 5, "$name ($self) - executed.";
HttpUtils_NonblockingGet( {
%{$hash->{httpParams}},
url => $hash->{unifi}->{url}."list/alarm",
callback => $hash->{updateDispatch}->{$self}[2],
data => "{'_sort': '-time', 'archived': False}",
} );
return undef;
}
sub Unifi_GetUnarchivedAlerts_Receive($) {
my ($param, $err, $data) = @_;
my ($name,$self,$hash) = ($param->{hash}->{NAME},Unifi_Whoami(),$param->{hash});
Log3 $name, 5, "$name ($self) - executed.";
if ($err ne "") {
Unifi_ReceiveFailure($hash,{rc => 'Error while requesting', msg => $param->{url}." - $err"});
}
elsif ($data ne "") {
if ($param->{code} == 200 || $param->{code} == 400 || $param->{code} == 401) {
eval { $data = decode_json($data); 1; } or do { $data = { meta => {rc => 'error.decode_json', msg => $@} }; };
if ($data->{meta}->{rc} eq "ok") {
Log3 $name, 5, "$name ($self) - state:'$data->{meta}->{rc}'";
for my $h (@{$data->{data}}) {
$hash->{alerts_unarchived}->{$h->{_id}} = $h;
}
}
else { Unifi_ReceiveFailure($hash,$data->{meta}); }
} else {
Unifi_ReceiveFailure($hash,{rc => $param->{code}, msg => "Failed with HTTP Code $param->{code}."});
}
}
Unifi_NextUpdateFn($hash,$self);
return undef;
}
###############################################################################
sub Unifi_GetEvents_Send($) {
my ($hash) = @_;
my ($name,$self) = ($hash->{NAME},Unifi_Whoami());
Log3 $name, 5, "$name ($self) - executed.";
HttpUtils_NonblockingGet( {
%{$hash->{httpParams}},
url => $hash->{unifi}->{url}."stat/event",
callback => $hash->{updateDispatch}->{$self}[2],
data => "{'within': 24}", # last 24 hours
} );
return undef;
}
sub Unifi_GetEvents_Receive($) {
my ($param, $err, $data) = @_;
my ($name,$self,$hash) = ($param->{hash}->{NAME},Unifi_Whoami(),$param->{hash});
Log3 $name, 5, "$name ($self) - executed.";
if ($err ne "") {
Unifi_ReceiveFailure($hash,{rc => 'Error while requesting', msg => $param->{url}." - $err"});
}
elsif ($data ne "") {
if ($param->{code} == 200 || $param->{code} == 400 || $param->{code} == 401) {
eval { $data = decode_json($data); 1; } or do { $data = { meta => {rc => 'error.decode_json', msg => $@} }; };
if ($data->{meta}->{rc} eq "ok") {
Log3 $name, 5, "$name ($self) - state:'$data->{meta}->{rc}'";
for my $h (@{$data->{data}}) {
$hash->{events}->{$h->{_id}} = $h;
}
}
else { Unifi_ReceiveFailure($hash,$data->{meta}); }
} else {
Unifi_ReceiveFailure($hash,{rc => $param->{code}, msg => "Failed with HTTP Code $param->{code}."});
}
}
Unifi_NextUpdateFn($hash,$self);
return undef;
}
###############################################################################
sub Unifi_GetAccesspoints_Send($) {
my ($hash) = @_;
my ($name,$self) = ($hash->{NAME},Unifi_Whoami());
Log3 $name, 5, "$name ($self) - executed.";
HttpUtils_NonblockingGet( {
%{$hash->{httpParams}},
url => $hash->{unifi}->{url}."stat/device",
callback => $hash->{updateDispatch}->{$self}[2],
data => "{'_depth': 2, 'test': 0}",
} );
return undef;
}
sub Unifi_GetAccesspoints_Receive($) {
my ($param, $err, $data) = @_;
my ($name,$self,$hash) = ($param->{hash}->{NAME},Unifi_Whoami(),$param->{hash});
Log3 $name, 5, "$name ($self) - executed.";
if ($err ne "") {
Unifi_ReceiveFailure($hash,{rc => 'Error while requesting', msg => $param->{url}." - $err"});
}
elsif ($data ne "") {
if ($param->{code} == 200 || $param->{code} == 400 || $param->{code} == 401) {
eval { $data = decode_json($data); 1; } or do { $data = { meta => {rc => 'error.decode_json', msg => $@} }; };
if ($data->{meta}->{rc} eq "ok") {
Log3 $name, 5, "$name ($self) - state:'$data->{meta}->{rc}'";
for my $h (@{$data->{data}}) {
$hash->{accespoints}->{$h->{_id}} = $h;
}
}
else { Unifi_ReceiveFailure($hash,$data->{meta}); }
} else {
Unifi_ReceiveFailure($hash,{rc => $param->{code}, msg => "Failed with HTTP Code $param->{code}."});
}
}
Unifi_NextUpdateFn($hash,$self);
return undef;
}
###############################################################################
sub Unifi_DoAfterUpdate($) {
sub Unifi_ProcessUpdate($) {
my ($hash) = @_;
my ($name,$self) = ($hash->{NAME},Unifi_Whoami());
Log3 $name, 5, "$name ($self) - executed.";
Log3 $name, 5, "$name ($self) - executed after ".sprintf('%.4f',time() - $hash->{unifi}->{updateStartTime})." seconds.";
readingsBeginUpdate($hash);
#'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''#
### WLAN Client Readings
my ($clientName,$clientRef);
my $devAliases = AttrVal($name,"devAlias",0);
for my $clientID (keys %{$hash->{clients}}) {
$clientRef = $hash->{clients}->{$clientID};
if ( ($devAliases && $devAliases =~ /$clientID:(.+?)(\s|$)/)
|| ($devAliases && defined $clientRef->{name} && $devAliases =~ /$clientRef->{name}:(.+?)(\s|$)/)
|| ($devAliases && defined $clientRef->{hostname} && $devAliases =~ /$clientRef->{hostname}:(.+?)(\s|$)/)
|| (defined $clientRef->{name} && $clientRef->{name} =~ /^([\w\.\-]+)$/)
|| (defined $clientRef->{hostname} && $clientRef->{hostname} =~ /^([\w\.\-]+)$/)
) {
$clientName = $1;
} else { $clientName = $clientID; }
if (defined $hash->{unifi}->{connectedClients}->{$clientID}) {
readingsBulkUpdate($hash,$clientName."_hostname",(defined $clientRef->{hostname}) ? $clientRef->{hostname} : (defined $clientRef->{ip}) ? $clientRef->{ip} : 'Unknown');
readingsBulkUpdate($hash,$clientName."_last_seen",strftime "%Y-%m-%d %H:%M:%S",localtime($clientRef->{last_seen}));
readingsBulkUpdate($hash,$clientName."_uptime",$clientRef->{uptime});
readingsBulkUpdate($hash,$clientName,'connected');
}
elsif (defined($hash->{READINGS}->{$clientName}) && $hash->{READINGS}->{$clientName}->{VAL} ne 'disconnected') {
Log3 $name, 5, "$name ($self) - Client '$clientName' previously connected is now disconnected.";
readingsBulkUpdate($hash,$clientName,'disconnected');
}
}
### Other...
#'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''#
readingsEndUpdate($hash,1);
Log3 $name, 5, "$name ($self) - finished after ".sprintf('%.4f',time() - $hash->{unifi}->{updateStartTime})." seconds.";
InternalTimer(time()+$hash->{unifi}->{interval}, 'Unifi_DoUpdate', $hash, 0);
return undef;
}
@ -495,36 +646,64 @@ sub Unifi_NextUpdateFn($$) {
for (keys %{$hash->{updateDispatch}}) { # {updateDispatch}->{callFn}[callFnRef,'receiveFn',receiveFnRef]
if($hash->{updateDispatch}->{$_}[1] && $hash->{updateDispatch}->{$_}[1] eq $fn) {
delete $hash->{updateDispatch}->{$_};
} elsif(!$NextUpdateFn && $hash->{updateDispatch}->{$_}[0] && $_ ne 'Unifi_DoAfterUpdate') {
} elsif(!$NextUpdateFn && $hash->{updateDispatch}->{$_}[0] && $_ ne 'Unifi_ProcessUpdate') {
$NextUpdateFn = $hash->{updateDispatch}->{$_}[0];
}
}
if (!$NextUpdateFn && $hash->{updateDispatch}->{Unifi_DoAfterUpdate}[0]) {
$NextUpdateFn = $hash->{updateDispatch}->{Unifi_DoAfterUpdate}[0];
delete $hash->{updateDispatch}->{Unifi_DoAfterUpdate};
if (!$NextUpdateFn && $hash->{updateDispatch}->{Unifi_ProcessUpdate}[0]) {
$NextUpdateFn = $hash->{updateDispatch}->{Unifi_ProcessUpdate}[0];
delete $hash->{updateDispatch}->{Unifi_ProcessUpdate};
}
$NextUpdateFn->($hash) if($NextUpdateFn);
return undef;
}
###############################################################################
sub Unifi_ReceiveFailure($$$) {
my ($hash,$meta) = @_;
my ($name,$self) = ($hash->{NAME},Unifi_Whowasi());
if (defined $meta->{msg}) {
if ($meta->{msg} eq 'api.err.LoginRequired') {
Log3 $name, 5, "$name ($self) - LoginRequired detected...";
if(Unifi_CONNECTED($hash)) {
Log3 $name, 5, "$name ($self) - I am the first who detected LoginRequired. Do re-login...";
Unifi_CONNECTED($hash,'disconnected');
Unifi_DoUpdate($hash);
return undef;
}
}
elsif ($meta->{msg} eq "api.err.NoSiteContext" || ($hash->{unifi}->{version} == 3 && $meta->{msg} eq "api.err.InvalidObject")) {
Log3 $name, 1, "$name ($self) - Failed! - state:'$meta->{rc}' - msg:'$meta->{msg}'"
." - This error indicates that the <siteID> in your definition is wrong."
." Try to modify your definition with <sideID> = default.";
}
else {
Log3 $name, 5, "$name ($self) - Failed! - state:'$meta->{rc}' - msg:'$meta->{msg}'";
}
} else {
Log3 $name, 5, "$name ($self) - Failed (without message)! - state:'$meta->{rc}'";
}
}
###############################################################################
sub Unifi_CONNECTED($@) {
my ($hash,$set) = @_;
if ($set) {
$hash->{CONNECTED} = $set;
$hash->{unifi}->{CONNECTED} = $set;
RemoveInternalTimer($hash);
%{$hash->{updateDispatch}} = ();
if ($hash->{READINGS}->{state}->{VAL} ne $set) {
if (!defined($hash->{READINGS}->{state}->{VAL}) || $hash->{READINGS}->{state}->{VAL} ne $set) {
readingsSingleUpdate($hash,"state",$set,1);
}
return undef;
}
else {
if ($hash->{CONNECTED} eq 'disabled') {
if ($hash->{unifi}->{CONNECTED} eq 'disabled') {
return 'disabled';
}
elsif ($hash->{CONNECTED} eq 'connected') {
elsif ($hash->{unifi}->{CONNECTED} eq 'connected') {
return 1;
} else {
return 0;
@ -533,24 +712,15 @@ sub Unifi_CONNECTED($@) {
}
###############################################################################
sub Unifi_Urlencode($) {
my ($s) = @_;
$s =~ s/ /+/g;
$s =~ s/([^A-Za-z0-9\+-])/sprintf("%%%02X", ord($1))/seg;
return $s;
}
sub Unifi_Urldecode($) {
my ($s) = @_;
$s =~ s/\%([A-Fa-f0-9]{2})/pack('C', hex($1))/seg;
$s =~ s/\+/ /g;
return $s;
}
sub Unifi_Whoami() { return (split('::',(caller(1))[3]))[1] || ''; }
sub Unifi_Whowasi() { return (split('::',(caller(2))[3]))[1] || ''; }
###############################################################################
### KNOWN RESPONSES ###
# { "data" : [ ] , "meta" : { "msg" : "api.err.Invalid" , "rc" : "error"}} //Invalid Login credentials in v4, in v3 the login-html-page is returned
# { "data" : [ ] , "meta" : { "rc" : "ok"}}
# "api.err.NoPermission"
# { "data" : [ ] , "meta" : { "msg" : "api.err.InvalidArgs" , "rc" : "error"}} //posted data is not ok
# { "data" : [ ] , "meta" : { "msg" : "api.err.InvalidObject" , "rc" : "error"}} //Wrong siteID in v3
# { "data" : [ ] , "meta" : { "msg" : "api.err.NoSiteContext" , "rc" : "error"}} //Wrong siteID in v4
# { "data" : [ ] , "meta" : { "msg" : "api.err.LoginRequired" , "rc" : "error"}} //Login Required / cookie is invalid / While Login: Unifi v4 is used wiith controller v3
@ -648,7 +818,7 @@ The device will be still connected, even it is in PowerSave-Mode. (In this mode
<h4>Get</h4>
<ul>
<li><code>get &lt;name&gt; clientData &lt;all|user_id|controllerAlias|hostname|devAlias&gt;</code><br>
<li><code>get &lt;name&gt; clientData &lt;all|_id|controllerAlias|hostname|devAlias&gt;</code><br>
Show more details about clients.</li>
</ul>
@ -656,9 +826,9 @@ The device will be still connected, even it is in PowerSave-Mode. (In this mode
<h4>Attributes</h4>
<ul>
<li>attr devAlias<br>
Can be used to rename device names in the format <code>&lt;user_id|controllerAlias|hostname&gt;:Aliasname.</code><br>
Can be used to rename device names in the format <code>&lt;_id|controllerAlias|hostname&gt;:Aliasname.</code><br>
Separate using blank to rename multiple devices.<br>
Example (user_id):<code> attr unifi devAlias 5537d138e4b033c1832c5c84:iPhone-Claudiu</code><br>
Example (_id):<code> attr unifi devAlias 5537d138e4b033c1832c5c84:iPhone-Claudiu</code><br>
Example (controllerAlias):<code> attr unifi devAlias iPhoneControllerAlias:iPhone-Claudiu</code><br>
Example (hostname):<code> attr unifi devAlias iphone:iPhone-Claudiu</code><br></li>
<br>