mirror of
https://github.com/fhem/fhem-mirror.git
synced 2025-04-06 12:18:46 +00:00
74_Unifi: Added support for Controller v3 (v4 already supported).
git-svn-id: https://svn.fhem.de/fhem/trunk@9130 2b470e98-0d58-463d-a4d8-8e2adae1ed80
This commit is contained in:
parent
e167ccfec5
commit
aab2d16cc2
@ -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.
|
||||
- feature: 74_Unifi: - Added support for Controller v3 (v4 already supported)
|
||||
- change: 74_Unifi: - changed get-Name from 'clientDetails' to 'clientData'
|
||||
- changed start from defFn to NotifyFn
|
||||
- added 'get clientData' selection by client-ID
|
||||
|
@ -1,5 +1,5 @@
|
||||
###############################################################################
|
||||
# $Id: 74_Unifi.pm 2015-08-24 07:10 - rapster - rapster at x0e dot de $
|
||||
# $Id: 74_Unifi.pm 2015-08-24 23:00 - rapster - rapster at x0e dot de $
|
||||
|
||||
package main;
|
||||
use strict;
|
||||
@ -42,28 +42,32 @@ sub Unifi_Define($$) {
|
||||
);
|
||||
$hash->{httpParams} = {
|
||||
hash => $hash,
|
||||
timeout => 5,
|
||||
timeout => 8,
|
||||
method => "POST",
|
||||
noshutdown => 0,
|
||||
ignoreredirects => 1,
|
||||
loglevel => 5,
|
||||
sslargs => { SSL_verify_mode => 'SSL_VERIFY_NONE' },
|
||||
header => "Content-Type: application/json;charset=UTF-8"
|
||||
header => ($hash->{version} == 3) ? undef : "Content-Type: application/json;charset=UTF-8"
|
||||
};
|
||||
$hash->{loginParams} = {
|
||||
%{$hash->{httpParams}},
|
||||
url => $hash->{url}."api/login",
|
||||
data => "{'username':'".$a[4]."', 'password':'".$a[5]."'}",
|
||||
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]);
|
||||
}else {
|
||||
$hash->{loginParams}->{url} = $hash->{url}."api/login";
|
||||
$hash->{loginParams}->{data} = "{'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} = '';
|
||||
readingsSingleUpdate($hash,"state","disconnected",1);
|
||||
}
|
||||
|
||||
|
||||
Log3 $name, 5, "$name: Defined with url:$hash->{url}, interval:$hash->{interval}, siteID:$hash->{siteID}, version:$hash->{version}";
|
||||
return undef;
|
||||
}
|
||||
@ -216,11 +220,8 @@ sub Unifi_DoUpdate($@) {
|
||||
}
|
||||
|
||||
if ($hash->{STATE} ne 'connected') {
|
||||
if($manual) {
|
||||
Log3 $name, 3, "$name: DoUpdate - Manual Updates only allowed while connected, End now...";
|
||||
} else {
|
||||
Unifi_Login_Send($hash)
|
||||
}
|
||||
readingsSingleUpdate($hash,"state","disconnected",1) if($hash->{STATE} ne 'disconnected');
|
||||
Unifi_Login_Send($hash)
|
||||
} else {
|
||||
Unifi_GetClients_Send($hash);
|
||||
# Do more...
|
||||
@ -255,37 +256,63 @@ sub Unifi_Login_Receive($) {
|
||||
if ($err ne "") {
|
||||
Log3 $name, 5, "$name: Login_Receive - Error while requesting ".$param->{url}." - $err";
|
||||
}
|
||||
elsif ($data ne "") {
|
||||
if ($param->{code} == 200 || $param->{code} == 400) {
|
||||
eval {
|
||||
$data = decode_json($data);
|
||||
1;
|
||||
} or do {
|
||||
my $e = $@;
|
||||
Log3 $name, 5, "$name: Login_Receive - Failed to decode returned json object! Will try again after interval... - error:$e";
|
||||
|
||||
InternalTimer(time()+$hash->{interval}, 'Unifi_Login_Send', $hash, 0);
|
||||
return undef;
|
||||
};
|
||||
if ($data->{meta}->{rc} eq "ok") {
|
||||
Log3 $name, 5, "$name: Login_Receive - Login successfully! - state:'$data->{meta}->{rc}'";
|
||||
elsif ($data ne "" && $hash->{version} == 3) {
|
||||
if ($data =~ /Invalid username or password/si) {
|
||||
Log3 $name, 1, "$name: Login_Receive - Login Failed! Invalid username or password! Will try again after interval... ";
|
||||
} else {
|
||||
Log3 $name, 5, "$name: Login_Receive - Login Failed! Version 3 should not deliver data on successfull login. Will try again after interval... ";
|
||||
}
|
||||
readingsSingleUpdate($hash,"state","disconnected",1) if($hash->{STATE} ne "disconnected");
|
||||
InternalTimer(time()+$hash->{interval}, 'Unifi_Login_Send', $hash, 0);
|
||||
return undef;
|
||||
}
|
||||
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($hash->{version} != 3) {
|
||||
eval {
|
||||
$data = decode_json($data);
|
||||
1;
|
||||
} or do {
|
||||
my $e = $@;
|
||||
Log3 $name, 5, "$name: Login_Receive - Failed to decode returned json object! Will try again after interval... - error:$e";
|
||||
readingsSingleUpdate($hash,"state","disconnected",1) if($hash->{STATE} ne "disconnected");
|
||||
InternalTimer(time()+$hash->{interval}, 'Unifi_Login_Send', $hash, 0);
|
||||
return undef;
|
||||
};
|
||||
}
|
||||
if ($hash->{version} == 3 || $data->{meta}->{rc} eq "ok") { # v3 has no rc-state
|
||||
Log3 $name, 5, "$name: Login_Receive - state=ok || version=3";
|
||||
$param->{cookies} = '';
|
||||
for (split("\r\n",$param->{httpheader})) {
|
||||
if(/^Set-Cookie/) {
|
||||
s/Set-Cookie:\s(.*?);.*/Cookie: $1/;
|
||||
$param->{cookies} .= $_.'\r\n';
|
||||
$param->{cookies} .= $_.(($hash->{version} == 3) ? '' : '\r\n'); #v3 has only one cookie and no header at all
|
||||
}
|
||||
}
|
||||
Log3 $name, 5, "$name: Login_Receive - Received-cookies:$param->{cookies}";
|
||||
|
||||
readingsSingleUpdate($hash,"state","connected",1);
|
||||
Unifi_DoUpdate($hash);
|
||||
if($param->{cookies} ne '') {
|
||||
Log3 $name, 5, "$name: Login_Receive - Login successfully! $param->{cookies}";
|
||||
readingsSingleUpdate($hash,"state","connected",1);
|
||||
RemoveInternalTimer($hash);
|
||||
Unifi_DoUpdate($hash);
|
||||
}else {
|
||||
Log3 $name, 5, "$name: Login_Receive - Something went wrong, login seems ok but no cookies received. Will try again after interval...";
|
||||
readingsSingleUpdate($hash,"state","disconnected",1) if($hash->{STATE} ne "disconnected");
|
||||
InternalTimer(time()+$hash->{interval}, 'Unifi_Login_Send', $hash, 0);
|
||||
}
|
||||
return undef;
|
||||
}
|
||||
else {
|
||||
if (defined($data->{meta}->{msg})) {
|
||||
my $loglevel = ($data->{meta}->{msg} eq 'api.err.Invalid') ? 1 : 5;
|
||||
Log3 $name, $loglevel, "$name: Login_Receive - Login Failed! - state:'$data->{meta}->{rc}' - msg:'$data->{meta}->{msg}'";
|
||||
if ($data->{meta}->{msg} eq 'api.err.Invalid') {
|
||||
Log3 $name, 1, "$name: Login_Receive - Login Failed! Invalid username or password!"
|
||||
." - state:'$data->{meta}->{rc}' - msg:'$data->{meta}->{msg}' - Will try again after interval...";
|
||||
} elsif ($data->{meta}->{msg} eq 'api.err.LoginRequired') {
|
||||
Log3 $name, 1, "$name: Login_Receive - Login Failed! This error while login indicates that you use wrong <version> or"
|
||||
." have to define <version> in your fhem definition. - Will try again after interval...";
|
||||
}else {
|
||||
Log3 $name, 5, "$name: Login_Receive - Login Failed! - state:'$data->{meta}->{rc}' - msg:'$data->{meta}->{msg}'";
|
||||
}
|
||||
} else {
|
||||
Log3 $name, 5, "$name: Login_Receive - Login Failed (without message)! - state:'$data->{meta}->{rc}'";
|
||||
}
|
||||
@ -298,7 +325,7 @@ sub Unifi_Login_Receive($) {
|
||||
}
|
||||
}
|
||||
Log3 $name, 5, "$name: Login_Receive - Connect/Login to Unifi-Controller failed! Will try again after interval...";
|
||||
readingsSingleUpdate($hash,"state","disconnected",1) if($hash->{READINGS}->{state}->{VAL} ne "disconnected");
|
||||
readingsSingleUpdate($hash,"state","disconnected",1) if($hash->{STATE} ne "disconnected");
|
||||
InternalTimer(time()+$hash->{interval}, 'Unifi_Login_Send', $hash, 0);
|
||||
return undef;
|
||||
}
|
||||
@ -311,7 +338,7 @@ sub Unifi_GetClients_Send($) {
|
||||
my $param = {
|
||||
%{$hash->{httpParams}},
|
||||
url => $hash->{url}."api/s/$hash->{siteID}/stat/sta",
|
||||
header => $hash->{loginParams}->{cookies}.$hash->{httpParams}->{header},
|
||||
header => ($hash->{version} == 3) ? $hash->{loginParams}->{cookies} : $hash->{loginParams}->{cookies}.$hash->{httpParams}->{header},
|
||||
callback => \&Unifi_GetClients_Receive
|
||||
};
|
||||
HttpUtils_NonblockingGet($param);
|
||||
@ -327,7 +354,7 @@ sub Unifi_GetClients_Receive($) {
|
||||
Log3 $name, 5, "$name: GetClients_Receive - Error while requesting ".$param->{url}." - $err";
|
||||
}
|
||||
elsif ($data ne "") {
|
||||
if ($param->{code} == 200 || $param->{code} == 401 || $param->{code} == 400) {
|
||||
if ($param->{code} == 200 || $param->{code} == 400 || $param->{code} == 401) {
|
||||
eval {
|
||||
$data = decode_json($data);
|
||||
1;
|
||||
@ -360,8 +387,7 @@ sub Unifi_GetClients_Receive($) {
|
||||
}
|
||||
else {
|
||||
if (defined($data->{meta}->{msg})) {
|
||||
Log3 $name, 5, "$name: GetClients_Receive - Failed! - state:'$data->{meta}->{rc}' - msg:'$data->{meta}->{msg}'";
|
||||
if($data->{meta}->{msg} eq 'api.err.LoginRequired') {
|
||||
if ($data->{meta}->{msg} eq 'api.err.LoginRequired') {
|
||||
Log3 $name, 5, "$name: GetClients_Receive - LoginRequired detected...";
|
||||
if($hash->{STATE} ne 'disconnected') {
|
||||
Log3 $name, 5, "$name: GetClients_Receive - I am the first who detected LoginRequired. Do re-login...";
|
||||
@ -370,6 +396,14 @@ sub Unifi_GetClients_Receive($) {
|
||||
Unifi_DoUpdate($hash);
|
||||
}
|
||||
}
|
||||
elsif ($data->{meta}->{msg} eq "api.err.NoSiteContext" || ($hash->{version} == 3 && $data->{meta}->{msg} eq "api.err.InvalidObject")) {
|
||||
Log3 $name, 1, "$name: GetClients_Receive - 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. Will try again after interval...";
|
||||
}
|
||||
else {
|
||||
Log3 $name, 5, "$name: GetClients_Receive - Failed! - state:'$data->{meta}->{rc}' - msg:'$data->{meta}->{msg}'";
|
||||
}
|
||||
} else {
|
||||
Log3 $name, 5, "$name: GetClients_Receive - Failed (without message)! - state:'$data->{meta}->{rc}'";
|
||||
}
|
||||
@ -383,11 +417,26 @@ sub Unifi_GetClients_Receive($) {
|
||||
}
|
||||
###############################################################################
|
||||
|
||||
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;
|
||||
}
|
||||
###############################################################################
|
||||
|
||||
### KNOWN RESPONSES ###
|
||||
# { "data" : [ ] , "meta" : { "msg" : "api.err.Invalid" , "rc" : "error"}}
|
||||
# { "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"}}
|
||||
# { "data" : [ ] , "meta" : { "msg" : "api.err.NoSiteContext" , "rc" : "error"}}
|
||||
# { "data" : [ ] , "meta" : { "msg" : "api.err.LoginRequired" , "rc" : "error"}}
|
||||
# { "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 / Unifi v4 is used wiith controller v3
|
||||
###############################################################################
|
||||
|
||||
|
||||
@ -441,19 +490,22 @@ The device will be still connected, even it is in PowerSave-Mode. (In this mode
|
||||
</ul>
|
||||
[<interval>]:
|
||||
<ul>
|
||||
<code>optional: interval to fetch the information from the unifi-api. <br>
|
||||
<code>(optional without <siteID> and <version>)<br>
|
||||
Interval to fetch the information from the unifi-api. <br>
|
||||
default: 30 seconds</code><br>
|
||||
</ul>
|
||||
[<siteID>]:
|
||||
<ul>
|
||||
<code>optional: You can find the site-ID by selecting the site in the UniFi web interface.<br>
|
||||
e.g. (https://localhost:8443/manage/s/foobar) siteId = 'foobar'.<br>
|
||||
default: 'default'</code><br>
|
||||
<code>(optional without <version>)<br>
|
||||
You can find the site-ID by selecting the site in the UniFi web interface.<br>
|
||||
e.g. https://192.168.12.13:8443/manage/s/foobar the siteId you must use is: foobar.<br>
|
||||
default: default</code><br>
|
||||
</ul>
|
||||
[<version>]:
|
||||
<ul>
|
||||
<code>optional: Your unifi-controller version.<br>
|
||||
This is not used at the moment, both v3.x and v4.x controller are supported.<br>
|
||||
<code>(optional if you use unifi v4)<br>
|
||||
Unifi-controller version. <br>
|
||||
Version must be specified if version is not 4. At the moment version 3 and 4 are supported.<br>
|
||||
default: 4</code><br>
|
||||
</ul> <br>
|
||||
<br>
|
||||
@ -470,8 +522,7 @@ The device will be still connected, even it is in PowerSave-Mode. (In this mode
|
||||
<h4>Set</h4>
|
||||
<ul>
|
||||
<li><code>set <name> update</code><br>
|
||||
Makes immediately a manual update. </li><br>
|
||||
Note: Manual updates are only possible while unifi-controller is connected and device is not disabled.
|
||||
Makes immediately a manual update. </li>
|
||||
</ul><br>
|
||||
<ul>
|
||||
<li><code>set <name> clear <readings|clientData|all></code><br>
|
||||
|
Loading…
x
Reference in New Issue
Block a user