added migrated 2.x add-ons
Signed-off-by: Kai Kreuzer <kai@openhab.org>
This commit is contained in:
32
bundles/org.openhab.binding.unifi/.classpath
Normal file
32
bundles/org.openhab.binding.unifi/.classpath
Normal file
@@ -0,0 +1,32 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<classpath>
|
||||
<classpathentry kind="src" output="target/classes" path="src/main/java">
|
||||
<attributes>
|
||||
<attribute name="optional" value="true"/>
|
||||
<attribute name="maven.pomderived" value="true"/>
|
||||
</attributes>
|
||||
</classpathentry>
|
||||
<classpathentry excluding="**" kind="src" output="target/classes" path="src/main/resources">
|
||||
<attributes>
|
||||
<attribute name="maven.pomderived" value="true"/>
|
||||
</attributes>
|
||||
</classpathentry>
|
||||
<classpathentry kind="src" output="target/test-classes" path="src/test/java">
|
||||
<attributes>
|
||||
<attribute name="optional" value="true"/>
|
||||
<attribute name="maven.pomderived" value="true"/>
|
||||
<attribute name="test" value="true"/>
|
||||
</attributes>
|
||||
</classpathentry>
|
||||
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-11">
|
||||
<attributes>
|
||||
<attribute name="maven.pomderived" value="true"/>
|
||||
</attributes>
|
||||
</classpathentry>
|
||||
<classpathentry kind="con" path="org.eclipse.m2e.MAVEN2_CLASSPATH_CONTAINER">
|
||||
<attributes>
|
||||
<attribute name="maven.pomderived" value="true"/>
|
||||
</attributes>
|
||||
</classpathentry>
|
||||
<classpathentry kind="output" path="target/classes"/>
|
||||
</classpath>
|
||||
23
bundles/org.openhab.binding.unifi/.project
Normal file
23
bundles/org.openhab.binding.unifi/.project
Normal file
@@ -0,0 +1,23 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<projectDescription>
|
||||
<name>org.openhab.binding.unifi</name>
|
||||
<comment></comment>
|
||||
<projects>
|
||||
</projects>
|
||||
<buildSpec>
|
||||
<buildCommand>
|
||||
<name>org.eclipse.jdt.core.javabuilder</name>
|
||||
<arguments>
|
||||
</arguments>
|
||||
</buildCommand>
|
||||
<buildCommand>
|
||||
<name>org.eclipse.m2e.core.maven2Builder</name>
|
||||
<arguments>
|
||||
</arguments>
|
||||
</buildCommand>
|
||||
</buildSpec>
|
||||
<natures>
|
||||
<nature>org.eclipse.jdt.core.javanature</nature>
|
||||
<nature>org.eclipse.m2e.core.maven2Nature</nature>
|
||||
</natures>
|
||||
</projectDescription>
|
||||
13
bundles/org.openhab.binding.unifi/NOTICE
Normal file
13
bundles/org.openhab.binding.unifi/NOTICE
Normal file
@@ -0,0 +1,13 @@
|
||||
This content is produced and maintained by the openHAB project.
|
||||
|
||||
* Project home: https://www.openhab.org
|
||||
|
||||
== Declared Project Licenses
|
||||
|
||||
This program and the accompanying materials are made available under the terms
|
||||
of the Eclipse Public License 2.0 which is available at
|
||||
https://www.eclipse.org/legal/epl-2.0/.
|
||||
|
||||
== Source Code
|
||||
|
||||
https://github.com/openhab/openhab-addons
|
||||
158
bundles/org.openhab.binding.unifi/README.md
Normal file
158
bundles/org.openhab.binding.unifi/README.md
Normal file
@@ -0,0 +1,158 @@
|
||||
# UniFi Binding
|
||||
|
||||
This binding integrates with [Ubiquiti UniFi Networks](https://www.ubnt.com/products/#unifi) allowing for presence detection of network clients.
|
||||
|
||||
|
||||
## Supported Things
|
||||
|
||||
* `controller` - An instance of the UniFi controller software
|
||||
* `wirelessClient` - Any wireless client connected to a UniFi wireless network
|
||||
|
||||
|
||||
## Discovery
|
||||
|
||||
Discovery is currently not supported.
|
||||
|
||||
|
||||
## Binding Configuration
|
||||
|
||||
The binding has no configuration options, all configuration is done at the Bridge and Thing levels.
|
||||
|
||||
|
||||
## Bridge Configuration
|
||||
|
||||
You need at least one UniFi Controller (Bridge) for this binding to work. It requires a network accessible instance of the [Ubiquiti Networks Controller Software](https://www.ubnt.com/download/unifi).
|
||||
|
||||
The following table describes the Bridge configuration parameters:
|
||||
|
||||
| Parameter | Description | Config | Default |
|
||||
| ------------------------ | ---------------------------------------------- |--------- | ------- |
|
||||
| host | Hostname of IP address of the UniFi Controller | Required | - |
|
||||
| port | Port of the UniFi Controller | Required | - |
|
||||
| username | The username to access the UniFi Controller | Required | - |
|
||||
| password | The password to access the UniFi Controller | Required | - |
|
||||
| refresh | Refresh interval in seconds | Optional | 10 |
|
||||
|
||||
|
||||
## Thing Configuration
|
||||
|
||||
You must define a UniFi Controller (Bridge) before defining UniFi Clients (Things) for this binding to work.
|
||||
|
||||
The following table describes the Thing configuration parameters:
|
||||
|
||||
| Parameter | Description | Config | Default |
|
||||
| ------------ | -------------------------------------------------------------|--------- | ------- |
|
||||
| cid | The MAC address, IP address, hostname or alias of the client | Required | - |
|
||||
| site | The site where the client should be found | Optional | - |
|
||||
| considerHome | The interval in seconds to consider the client as home | Optional | 180 |
|
||||
|
||||
Here's some additional notes regarding the thing configuration parameters:
|
||||
|
||||
##### `cid`
|
||||
|
||||
The `cid` parameter is a universal "client identifier". It accepts the following values:
|
||||
|
||||
1. MAC address [highest priority]
|
||||
1. IP address
|
||||
1. Hostname (as show by the controller)
|
||||
1. Alias (as defined by you in the controller UI) [lowest priority]
|
||||
|
||||
The priority essentially means the binding attempts to lookup by MAC address, then by IP address, then by hostname and finally by alias. Once it finds a matching client, it short circuits and stops searching. Most of the time, you will simply use the MAC address.
|
||||
|
||||
##### `site`
|
||||
|
||||
The `site` parameter is optional. If you leave it blank, the client will appear `ONLINE` if found in *any* site defined on the controller.
|
||||
|
||||
You may use the `site` parameter as a filter if you only want the client to appear home if it is found in the site defined in the `site` parameter.
|
||||
|
||||
Additionally, you may use friendly site names as they appear in the controller UI.
|
||||
|
||||
##### `considerHome`
|
||||
|
||||
The `considerHome` parameter allows you to control how quickly the binding marks a client as away. For example, using the default of `180` (seconds), the binding will report a client away as soon as `lastSeen` + `180` (seconds) < `now`
|
||||
|
||||
|
||||
## Channels
|
||||
|
||||
The Wireless Client information that is retrieved is available as these channels:
|
||||
|
||||
| Channel ID | Item Type | Description | Permissions |
|
||||
|------------|-----------|--------------------------------------------------------------------- | ----------- |
|
||||
| online | Switch | Online status of the client | Read |
|
||||
| site | String | Site name (from the controller web UI) the client is associated with | Read |
|
||||
| macAddress | String | MAC address of the client | Read |
|
||||
| ipAddress | String | IP address of the client | Read |
|
||||
| ap | String | Access point (AP) the client is connected to | Read |
|
||||
| essid | String | Network name (ESSID) the client is connected to | Read |
|
||||
| rssi | Number | Received signal strength indicator (RSSI) of the client | Read |
|
||||
| uptime | Number | Uptime of the wireless client (in seconds) | Read |
|
||||
| lastSeen | DateTime | Date and Time the wireless client was last seen | Read |
|
||||
| blocked | Switch | Blocked status of the client | Read, Write |
|
||||
| reconnect | Switch | Force the client to be reconnect | Write |
|
||||
|
||||
_Note: All channels with the Write permission require administrator credentials as defined in the controller._
|
||||
|
||||
##### `blocked`
|
||||
|
||||
The `blocked` channel allows you to block / unblock a client via the controller.
|
||||
|
||||
##### `reconnect`
|
||||
|
||||
The `reconnect` channel allows you to force a client to reconnect. Sending `ON` to this channel will trigger a reconnect via the controller.
|
||||
|
||||
|
||||
## Full Example
|
||||
|
||||
things/unifi.things
|
||||
|
||||
```
|
||||
Bridge unifi:controller:home "UniFi Controller" [ host="unifi", port=8443, username="$username", password="$password", refresh=10 ] {
|
||||
Thing wirelessClient matthewsPhone "Matthew's iPhone" [ cid="$cid", site="default", considerHome=180 ]
|
||||
}
|
||||
```
|
||||
|
||||
Replace `$user`, `$password` and `$cid` accordingly.
|
||||
|
||||
items/unifi.items
|
||||
|
||||
```
|
||||
Switch MatthewsPhone "Matthew's iPhone [MAP(unifi.map):%s]" { channel="unifi:wirelessClient:home:matthewsPhone:online" }
|
||||
String MatthewsPhoneSite "Matthew's iPhone: Site [%s]" { channel="unifi:wirelessClient:home:matthewsPhone:site" }
|
||||
String MatthewsPhoneMAC "Matthew's iPhone: MAC [%s]" { channel="unifi:wirelessClient:home:matthewsPhone:macAddress" }
|
||||
String MatthewsPhoneIP "Matthew's iPhone: IP [%s]" { channel="unifi:wirelessClient:home:matthewsPhone:ipAddress" }
|
||||
String MatthewsPhoneAP "Matthew's iPhone: AP [%s]" { channel="unifi:wirelessClient:home:matthewsPhone:ap" }
|
||||
String MatthewsPhoneESSID "Matthew's iPhone: ESSID [%s]" { channel="unifi:wirelessClient:home:matthewsPhone:essid" }
|
||||
Number MatthewsPhoneRSSI "Matthew's iPhone: RSSI [%d]" { channel="unifi:wirelessClient:home:matthewsPhone:rssi" }
|
||||
Number MatthewsPhoneUptime "Matthew's iPhone: Uptime [%d]" { channel="unifi:wirelessClient:home:matthewsPhone:uptime" }
|
||||
DateTime MatthewsPhoneLastSeen "Matthew's iPhone: Last Seen [%1$tH:%1$tM:%1$tS]" { channel="unifi:wirelessClient:home:matthewsPhone:lastSeen" }
|
||||
Switch MatthewsPhoneBlocked "Matthew's iPhone: Blocked" { channel="unifi:wirelessClient:home:matthewsPhone:blocked" }
|
||||
Switch MatthewsPhoneReconnect "Matthew's iPhone: Reconnect" { channel="unifi:wirelessClient:home:matthewsPhone:reconnect" }
|
||||
```
|
||||
|
||||
transform/unifi.map
|
||||
|
||||
```
|
||||
ON=Home
|
||||
OFF=Away
|
||||
```
|
||||
|
||||
sitemaps/unifi.sitemap
|
||||
|
||||
```
|
||||
sitemap unifi label="UniFi Binding"
|
||||
{
|
||||
Frame {
|
||||
Text item=MatthewsPhone
|
||||
Text item=MatthewsPhoneSite
|
||||
Text item=MatthewsPhoneMAC
|
||||
Text item=MatthewsPhoneIP
|
||||
Text item=MatthewsPhoneAP
|
||||
Text item=MatthewsPhoneESSID
|
||||
Text item=MatthewsPhoneRSSI
|
||||
Text item=MatthewsPhoneUptime
|
||||
Text item=MatthewsPhoneLastSeen
|
||||
Switch item=MatthewsPhoneBlocked
|
||||
Switch item=MatthewsPhoneReconnect
|
||||
}
|
||||
}
|
||||
```
|
||||
17
bundles/org.openhab.binding.unifi/pom.xml
Normal file
17
bundles/org.openhab.binding.unifi/pom.xml
Normal file
@@ -0,0 +1,17 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-v4_0_0.xsd">
|
||||
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<parent>
|
||||
<groupId>org.openhab.addons.bundles</groupId>
|
||||
<artifactId>org.openhab.addons.reactor.bundles</artifactId>
|
||||
<version>3.0.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>org.openhab.binding.unifi</artifactId>
|
||||
|
||||
<name>openHAB Add-ons :: Bundles :: UniFi Binding</name>
|
||||
|
||||
</project>
|
||||
@@ -0,0 +1,9 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<features name="org.openhab.binding.unifi-${project.version}" xmlns="http://karaf.apache.org/xmlns/features/v1.4.0">
|
||||
<repository>mvn:org.openhab.core.features.karaf/org.openhab.core.features.karaf.openhab-core/${ohc.version}/xml/features</repository>
|
||||
|
||||
<feature name="openhab-binding-unifi" description="UniFi Binding" version="${project.version}">
|
||||
<feature>openhab-runtime-base</feature>
|
||||
<bundle start-level="80">mvn:org.openhab.addons.bundles/org.openhab.binding.unifi/${project.version}</bundle>
|
||||
</feature>
|
||||
</features>
|
||||
@@ -0,0 +1,58 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2020 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.unifi.internal;
|
||||
|
||||
import org.openhab.core.thing.ThingTypeUID;
|
||||
|
||||
/**
|
||||
* The {@link UniFiBindingConstants} class defines common constants, which are
|
||||
* used across the UniFi binding.
|
||||
*
|
||||
* @author Matthew Bowman - Initial contribution
|
||||
* @author Patrik Wimnell - Blocking / Unblocking client support
|
||||
*/
|
||||
public class UniFiBindingConstants {
|
||||
|
||||
public static final String BINDING_ID = "unifi";
|
||||
|
||||
// List of all Thing Types
|
||||
public static final ThingTypeUID THING_TYPE_CONTROLLER = new ThingTypeUID(BINDING_ID, "controller");
|
||||
public static final ThingTypeUID THING_TYPE_WIRED_CLIENT = new ThingTypeUID(BINDING_ID, "wiredClient");
|
||||
public static final ThingTypeUID THING_TYPE_WIRELESS_CLIENT = new ThingTypeUID(BINDING_ID, "wirelessClient");
|
||||
|
||||
// List of common wired + wireless client channels
|
||||
public static final String CHANNEL_ONLINE = "online";
|
||||
public static final String CHANNEL_SITE = "site";
|
||||
public static final String CHANNEL_MAC_ADDRESS = "macAddress";
|
||||
public static final String CHANNEL_IP_ADDRESS = "ipAddress";
|
||||
public static final String CHANNEL_UPTIME = "uptime";
|
||||
public static final String CHANNEL_LAST_SEEN = "lastSeen";
|
||||
public static final String CHANNEL_BLOCKED = "blocked";
|
||||
public static final String CHANNEL_RECONNECT = "reconnect";
|
||||
|
||||
// List of additional wired client channels
|
||||
// ..coming soon..
|
||||
|
||||
// List of additional wireless client channels
|
||||
public static final String CHANNEL_AP = "ap";
|
||||
public static final String CHANNEL_ESSID = "essid";
|
||||
public static final String CHANNEL_RSSI = "rssi";
|
||||
|
||||
// List of all Parameters
|
||||
public static final String PARAMETER_HOST = "host";
|
||||
public static final String PARAMETER_PORT = "port";
|
||||
public static final String PARAMETER_USERNAME = "username";
|
||||
public static final String PARAMETER_PASSWORD = "password";
|
||||
public static final String PARAMETER_SITE = "site";
|
||||
public static final String PARAMETER_CID = "cid";
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2020 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.unifi.internal;
|
||||
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
import org.openhab.binding.unifi.internal.handler.UniFiClientThingHandler;
|
||||
|
||||
/**
|
||||
* The {@link UniFiClientThingConfig} encapsulates all the configuration options for an instance of the
|
||||
* {@link UniFiClientThingHandler}.
|
||||
*
|
||||
* @author Matthew Bowman - Initial contribution
|
||||
*/
|
||||
public class UniFiClientThingConfig {
|
||||
|
||||
private String cid = "";
|
||||
|
||||
private String site = "";
|
||||
|
||||
private int considerHome = 180;
|
||||
|
||||
public String getClientID() {
|
||||
return cid;
|
||||
}
|
||||
|
||||
public String getSite() {
|
||||
return site;
|
||||
}
|
||||
|
||||
public int getConsiderHome() {
|
||||
return considerHome;
|
||||
}
|
||||
|
||||
public UniFiClientThingConfig tidy() {
|
||||
cid = StringUtils.lowerCase(StringUtils.strip(cid));
|
||||
site = StringUtils.lowerCase(StringUtils.strip(site));
|
||||
return this;
|
||||
}
|
||||
|
||||
public boolean isValid() {
|
||||
return StringUtils.isNotBlank(cid);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return String.format("UniFiClientConfig{cid: '%s', site: '%s', considerHome: %d}", cid, site, considerHome);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2020 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.unifi.internal;
|
||||
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
import org.openhab.binding.unifi.internal.handler.UniFiControllerThingHandler;
|
||||
|
||||
/**
|
||||
* The {@link UniFiControllerThingConfig} encapsulates all the configuration options for an instance of the
|
||||
* {@link UniFiControllerThingHandler}.
|
||||
*
|
||||
* @author Matthew Bowman - Initial contribution
|
||||
*/
|
||||
public class UniFiControllerThingConfig {
|
||||
|
||||
private String host = "unifi";
|
||||
|
||||
private int port = 8443;
|
||||
|
||||
private String username = "";
|
||||
|
||||
private String password = "";
|
||||
|
||||
private int refresh = 10;
|
||||
|
||||
public String getHost() {
|
||||
return host;
|
||||
}
|
||||
|
||||
public int getPort() {
|
||||
return port;
|
||||
}
|
||||
|
||||
public String getUsername() {
|
||||
return username;
|
||||
}
|
||||
|
||||
public String getPassword() {
|
||||
return password;
|
||||
}
|
||||
|
||||
public int getRefresh() {
|
||||
return refresh;
|
||||
}
|
||||
|
||||
public boolean isValid() {
|
||||
return StringUtils.isNotBlank(host) && StringUtils.isNotBlank(username) && StringUtils.isNotBlank(password);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "UniFiControllerConfig{host = " + host + ", port = " + port + ", username = " + username
|
||||
+ ", password = *****, refresh = " + refresh + "}";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2020 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.unifi.internal;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
import org.eclipse.jetty.client.HttpClient;
|
||||
import org.eclipse.jetty.util.ssl.SslContextFactory;
|
||||
import org.openhab.binding.unifi.internal.handler.UniFiClientThingHandler;
|
||||
import org.openhab.binding.unifi.internal.handler.UniFiControllerThingHandler;
|
||||
import org.openhab.core.io.net.http.HttpClientFactory;
|
||||
import org.openhab.core.io.net.http.HttpClientInitializationException;
|
||||
import org.openhab.core.thing.Bridge;
|
||||
import org.openhab.core.thing.Thing;
|
||||
import org.openhab.core.thing.ThingTypeUID;
|
||||
import org.openhab.core.thing.binding.BaseThingHandlerFactory;
|
||||
import org.openhab.core.thing.binding.ThingHandler;
|
||||
import org.openhab.core.thing.binding.ThingHandlerFactory;
|
||||
import org.osgi.service.component.annotations.Activate;
|
||||
import org.osgi.service.component.annotations.Component;
|
||||
import org.osgi.service.component.annotations.Reference;
|
||||
|
||||
/**
|
||||
* The {@link UniFiThingHandlerFactory} is responsible for creating things and thing
|
||||
* handlers.
|
||||
*
|
||||
* @author Matthew Bowman - Initial contribution
|
||||
*/
|
||||
@Component(service = ThingHandlerFactory.class, configurationPid = "binding.unifi")
|
||||
@NonNullByDefault
|
||||
public class UniFiThingHandlerFactory extends BaseThingHandlerFactory {
|
||||
|
||||
private final HttpClient httpClient;
|
||||
|
||||
@Activate
|
||||
public UniFiThingHandlerFactory(@Reference final HttpClientFactory httpClientFactory) {
|
||||
// [wip] mgb: disabled due to missing common name attributes with certs
|
||||
// this.httpClient = httpClientFactory.getCommonHttpClient();
|
||||
httpClient = new HttpClient(new SslContextFactory(true));
|
||||
try {
|
||||
httpClient.start();
|
||||
} catch (Exception e) {
|
||||
throw new HttpClientInitializationException("Could not start HttpClient", e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsThingType(ThingTypeUID thingTypeUID) {
|
||||
return UniFiControllerThingHandler.supportsThingType(thingTypeUID)
|
||||
|| UniFiClientThingHandler.supportsThingType(thingTypeUID);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected @Nullable ThingHandler createHandler(Thing thing) {
|
||||
ThingTypeUID thingTypeUID = thing.getThingTypeUID();
|
||||
if (UniFiControllerThingHandler.supportsThingType(thingTypeUID)) {
|
||||
return new UniFiControllerThingHandler((Bridge) thing, httpClient);
|
||||
} else if (UniFiClientThingHandler.supportsThingType(thingTypeUID)) {
|
||||
return new UniFiClientThingHandler(thing);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2020 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.unifi.internal.api;
|
||||
|
||||
/**
|
||||
* The {@link UniFiCommunicationException} signals there was a problem communicating with the controller.
|
||||
*
|
||||
* @author Matthew Bowman - Initial contribution
|
||||
*/
|
||||
public class UniFiCommunicationException extends UniFiException {
|
||||
|
||||
private static final long serialVersionUID = -7261308872245069364L;
|
||||
|
||||
public UniFiCommunicationException(Throwable cause) {
|
||||
super(cause);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2020 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.unifi.internal.api;
|
||||
|
||||
/**
|
||||
* The {@link UniFiException} represents a binding specific {@link Exception}.
|
||||
*
|
||||
* @author Matthew Bowman - Initial contribution
|
||||
*/
|
||||
public class UniFiException extends Exception {
|
||||
|
||||
private static final long serialVersionUID = -7422254981644510570L;
|
||||
|
||||
public UniFiException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
public UniFiException(String message, Throwable cause) {
|
||||
super(message, cause);
|
||||
}
|
||||
|
||||
public UniFiException(Throwable cause) {
|
||||
super(cause);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2020 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.unifi.internal.api;
|
||||
|
||||
/**
|
||||
* The {@link UniFiExpiredSessionException} signals the session with the controller has expired.
|
||||
*
|
||||
* @author Matthew Bowman - Initial contribution
|
||||
*/
|
||||
public class UniFiExpiredSessionException extends UniFiException {
|
||||
|
||||
private static final long serialVersionUID = -2002650048964514035L;
|
||||
|
||||
public UniFiExpiredSessionException(String message) {
|
||||
super(message);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2020 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.unifi.internal.api;
|
||||
|
||||
/**
|
||||
* The {@link UniFiInvalidCredentialsException} signals the credentials used to authenticate with the controller are
|
||||
* invalid.
|
||||
*
|
||||
* @author Matthew Bowman - Initial contribution
|
||||
*/
|
||||
public class UniFiInvalidCredentialsException extends UniFiException {
|
||||
|
||||
private static final long serialVersionUID = -7159360851783088458L;
|
||||
|
||||
public UniFiInvalidCredentialsException(String message) {
|
||||
super(message);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2020 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.unifi.internal.api;
|
||||
|
||||
/**
|
||||
* The {@link UniFiInvalidHostException} signals there was a problem with the hostname of the controller.
|
||||
*
|
||||
* @author Matthew Bowman - Initial contribution
|
||||
*/
|
||||
public class UniFiInvalidHostException extends UniFiException {
|
||||
|
||||
private static final long serialVersionUID = -7261308872245069364L;
|
||||
|
||||
public UniFiInvalidHostException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
public UniFiInvalidHostException(String message, Throwable cause) {
|
||||
super(message, cause);
|
||||
}
|
||||
|
||||
public UniFiInvalidHostException(Throwable cause) {
|
||||
super(cause);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2020 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.unifi.internal.api;
|
||||
|
||||
/**
|
||||
* The {@link UniFiNotAuthorizedException} signals the controller denied a request due to non-admin credentials.
|
||||
*
|
||||
* @author Matthew Bowman - Initial contribution
|
||||
*/
|
||||
public class UniFiNotAuthorizedException extends UniFiException {
|
||||
|
||||
private static final long serialVersionUID = 1379973398415636322L;
|
||||
|
||||
public UniFiNotAuthorizedException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
public UniFiNotAuthorizedException(String message, Throwable cause) {
|
||||
super(message, cause);
|
||||
}
|
||||
|
||||
public UniFiNotAuthorizedException(Throwable cause) {
|
||||
super(cause);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2020 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.unifi.internal.api;
|
||||
|
||||
/**
|
||||
* The {@link UniFiSSLException} signals a failure establishing an SSL connection with the controller.
|
||||
*
|
||||
* @author Matthew Bowman - Initial contribution
|
||||
*/
|
||||
public class UniFiSSLException extends UniFiException {
|
||||
|
||||
private static final long serialVersionUID = 4688857482270932413L;
|
||||
|
||||
public UniFiSSLException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
public UniFiSSLException(String message, Throwable cause) {
|
||||
super(message, cause);
|
||||
}
|
||||
|
||||
public UniFiSSLException(Throwable cause) {
|
||||
super(cause);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,96 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2020 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.unifi.internal.api.cache;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* The {@link UniFiCache} is a specialised lookup table that stores objects using multiple keys in the form
|
||||
* <code>prefix:suffix</code>. Each implementation is responsible for providing a list of supported prefixes and must
|
||||
* implement {@link #getSuffix(Object, String)} to provide a value specific suffix derived from the prefix.
|
||||
*
|
||||
* Objects are then retrieved simply by using the <code>suffix</code> key component and all combinations of
|
||||
* <code>prefix:suffix</code> are searched in the order of their priority.
|
||||
*
|
||||
* @author Matthew Bowman - Initial contribution
|
||||
*/
|
||||
public abstract class UniFiCache<T> {
|
||||
|
||||
private static final String SEPARATOR = ":";
|
||||
|
||||
public static final String PREFIX_ALIAS = "alias";
|
||||
|
||||
public static final String PREFIX_DESC = "desc";
|
||||
|
||||
public static final String PREFIX_HOSTNAME = "hostname";
|
||||
|
||||
public static final String PREFIX_ID = "id";
|
||||
|
||||
public static final String PREFIX_IP = "ip";
|
||||
|
||||
public static final String PREFIX_MAC = "mac";
|
||||
|
||||
public static final String PREFIX_NAME = "name";
|
||||
|
||||
private final Logger logger = LoggerFactory.getLogger(getClass());
|
||||
|
||||
private Map<String, T> map = new HashMap<>();
|
||||
|
||||
private String[] prefixes;
|
||||
|
||||
protected UniFiCache(String... prefixes) {
|
||||
this.prefixes = prefixes;
|
||||
}
|
||||
|
||||
public final T get(Object id) {
|
||||
T value = null;
|
||||
for (String prefix : prefixes) {
|
||||
String key = prefix + SEPARATOR + id;
|
||||
if (map.containsKey(key)) {
|
||||
value = map.get(key);
|
||||
logger.trace("Cache HIT : '{}' -> {}", key, value);
|
||||
break;
|
||||
} else {
|
||||
logger.trace("Cache MISS : '{}'", key);
|
||||
}
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
public final void put(T value) {
|
||||
for (String prefix : prefixes) {
|
||||
String suffix = getSuffix(value, prefix);
|
||||
if (StringUtils.isNotBlank(suffix)) {
|
||||
String key = prefix + SEPARATOR + suffix;
|
||||
map.put(key, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public final void putAll(UniFiCache<T> cache) {
|
||||
map.putAll(cache.map);
|
||||
}
|
||||
|
||||
public final Collection<T> values() {
|
||||
return map.values().stream().distinct().collect(Collectors.toList());
|
||||
}
|
||||
|
||||
protected abstract String getSuffix(T value, String prefix);
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2020 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.unifi.internal.api.cache;
|
||||
|
||||
import org.openhab.binding.unifi.internal.api.model.UniFiClient;
|
||||
|
||||
/**
|
||||
* The {@link UniFiClientCache} is a specific implementation of {@link UniFiCache} for the purpose of caching
|
||||
* {@link UniFiClient} instances.
|
||||
*
|
||||
* The cache uses the following prefixes: <code>mac</code>, <code>ip</code>, <code>hostname</code>, and
|
||||
* <code>alias</code>
|
||||
*
|
||||
* @author Matthew Bowman - Initial contribution
|
||||
*/
|
||||
public class UniFiClientCache extends UniFiCache<UniFiClient> {
|
||||
|
||||
public UniFiClientCache() {
|
||||
super(PREFIX_MAC, PREFIX_IP, PREFIX_HOSTNAME, PREFIX_ALIAS);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getSuffix(UniFiClient client, String prefix) {
|
||||
switch (prefix) {
|
||||
case PREFIX_MAC:
|
||||
return client.getMac();
|
||||
case PREFIX_IP:
|
||||
return client.getIp();
|
||||
case PREFIX_HOSTNAME:
|
||||
return client.getHostname();
|
||||
case PREFIX_ALIAS:
|
||||
return client.getAlias();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2020 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.unifi.internal.api.cache;
|
||||
|
||||
import org.openhab.binding.unifi.internal.api.model.UniFiDevice;
|
||||
|
||||
/**
|
||||
* The {@link UniFiDeviceCache} is a specific implementation of {@link UniFiCache} for the purpose of caching
|
||||
* {@link UniFiDevice} instances.
|
||||
*
|
||||
* The cache uses the following prefixes: <code>mac</code>
|
||||
*
|
||||
* @author Matthew Bowman - Initial contribution
|
||||
*/
|
||||
public class UniFiDeviceCache extends UniFiCache<UniFiDevice> {
|
||||
|
||||
public UniFiDeviceCache() {
|
||||
super(PREFIX_MAC);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getSuffix(UniFiDevice device, String prefix) {
|
||||
switch (prefix) {
|
||||
case PREFIX_MAC:
|
||||
return device.getMac();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2020 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.unifi.internal.api.cache;
|
||||
|
||||
import org.openhab.binding.unifi.internal.api.model.UniFiSite;
|
||||
|
||||
/**
|
||||
* The {@link UniFiSiteCache} is a specific implementation of {@link UniFiCache} for the purpose of caching
|
||||
* {@link UniFiSite} instances.
|
||||
*
|
||||
* The cache uses the following prefixes: <code>id</code>, <code>name</code>, <code>desc</code>
|
||||
*
|
||||
* @author Matthew Bowman - Initial contribution
|
||||
*/
|
||||
public class UniFiSiteCache extends UniFiCache<UniFiSite> {
|
||||
|
||||
public UniFiSiteCache() {
|
||||
super(PREFIX_ID, PREFIX_NAME, PREFIX_DESC);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getSuffix(UniFiSite site, String prefix) {
|
||||
switch (prefix) {
|
||||
case PREFIX_ID:
|
||||
return site.getId();
|
||||
case PREFIX_NAME:
|
||||
return site.getName();
|
||||
case PREFIX_DESC:
|
||||
return site.getDescription();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,127 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2020 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.unifi.internal.api.model;
|
||||
|
||||
import java.util.Calendar;
|
||||
|
||||
import org.apache.commons.lang.BooleanUtils;
|
||||
import org.openhab.binding.unifi.internal.api.UniFiException;
|
||||
import org.openhab.binding.unifi.internal.api.util.UniFiTidyLowerCaseStringDeserializer;
|
||||
import org.openhab.binding.unifi.internal.api.util.UniFiTimestampDeserializer;
|
||||
|
||||
import com.google.gson.annotations.JsonAdapter;
|
||||
import com.google.gson.annotations.SerializedName;
|
||||
|
||||
/**
|
||||
* The {@link UniFiClient} is the base data model for any (wired or wireless) connected to a UniFi network.
|
||||
*
|
||||
* @author Matthew Bowman - Initial contribution
|
||||
* @author Patrik Wimnell - Blocking / Unblocking client support
|
||||
*/
|
||||
public abstract class UniFiClient {
|
||||
|
||||
protected final transient UniFiController controller;
|
||||
|
||||
@SerializedName("_id")
|
||||
protected String id;
|
||||
|
||||
protected String siteId;
|
||||
|
||||
@JsonAdapter(UniFiTidyLowerCaseStringDeserializer.class)
|
||||
protected String mac;
|
||||
|
||||
protected String ip;
|
||||
|
||||
@JsonAdapter(UniFiTidyLowerCaseStringDeserializer.class)
|
||||
protected String hostname;
|
||||
|
||||
@SerializedName("name")
|
||||
@JsonAdapter(UniFiTidyLowerCaseStringDeserializer.class)
|
||||
protected String alias;
|
||||
|
||||
protected Integer uptime;
|
||||
|
||||
@JsonAdapter(UniFiTimestampDeserializer.class)
|
||||
protected Calendar lastSeen;
|
||||
|
||||
protected boolean blocked;
|
||||
|
||||
protected UniFiClient(UniFiController controller) {
|
||||
this.controller = controller;
|
||||
}
|
||||
|
||||
public String getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public String getMac() {
|
||||
return mac;
|
||||
}
|
||||
|
||||
public String getIp() {
|
||||
return this.ip;
|
||||
}
|
||||
|
||||
public String getHostname() {
|
||||
return hostname;
|
||||
}
|
||||
|
||||
public String getAlias() {
|
||||
return alias;
|
||||
}
|
||||
|
||||
public Integer getUptime() {
|
||||
return uptime;
|
||||
}
|
||||
|
||||
public Calendar getLastSeen() {
|
||||
return lastSeen;
|
||||
}
|
||||
|
||||
public boolean isBlocked() {
|
||||
return blocked;
|
||||
}
|
||||
|
||||
public abstract Boolean isWired();
|
||||
|
||||
public final Boolean isWireless() {
|
||||
return BooleanUtils.negate(isWired());
|
||||
}
|
||||
|
||||
protected abstract String getDeviceMac();
|
||||
|
||||
public UniFiSite getSite() {
|
||||
return controller.getSite(siteId);
|
||||
}
|
||||
|
||||
public UniFiDevice getDevice() {
|
||||
return controller.getDevice(getDeviceMac());
|
||||
}
|
||||
|
||||
// Functional API
|
||||
|
||||
public void block(boolean blocked) throws UniFiException {
|
||||
controller.block(this, blocked);
|
||||
}
|
||||
|
||||
public void reconnect() throws UniFiException {
|
||||
controller.reconnect(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return String.format(
|
||||
"UniFiClient{mac: '%s', ip: '%s', hostname: '%s', alias: '%s', wired: %b, blocked: %b, device: %s}",
|
||||
mac, ip, hostname, alias, isWired(), blocked, getDevice());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,306 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2020 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.unifi.internal.api.model;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
import org.eclipse.jetty.client.HttpClient;
|
||||
import org.openhab.binding.unifi.internal.api.UniFiException;
|
||||
import org.openhab.binding.unifi.internal.api.UniFiExpiredSessionException;
|
||||
import org.openhab.binding.unifi.internal.api.UniFiNotAuthorizedException;
|
||||
import org.openhab.binding.unifi.internal.api.cache.UniFiClientCache;
|
||||
import org.openhab.binding.unifi.internal.api.cache.UniFiDeviceCache;
|
||||
import org.openhab.binding.unifi.internal.api.cache.UniFiSiteCache;
|
||||
import org.openhab.binding.unifi.internal.api.util.UniFiClientDeserializer;
|
||||
import org.openhab.binding.unifi.internal.api.util.UniFiClientInstanceCreator;
|
||||
import org.openhab.binding.unifi.internal.api.util.UniFiDeviceInstanceCreator;
|
||||
import org.openhab.binding.unifi.internal.api.util.UniFiSiteInstanceCreator;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import com.google.gson.FieldNamingPolicy;
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.GsonBuilder;
|
||||
|
||||
/**
|
||||
* The {@link UniFiController} is the main communication point with an external instance of the Ubiquiti Networks
|
||||
* Controller Software.
|
||||
*
|
||||
* @author Matthew Bowman - Initial contribution
|
||||
* @author Patrik Wimnell - Blocking / Unblocking client support
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class UniFiController {
|
||||
|
||||
private final Logger logger = LoggerFactory.getLogger(UniFiController.class);
|
||||
|
||||
private UniFiSiteCache sitesCache = new UniFiSiteCache();
|
||||
|
||||
private UniFiDeviceCache devicesCache = new UniFiDeviceCache();
|
||||
|
||||
private UniFiClientCache clientsCache = new UniFiClientCache();
|
||||
|
||||
private UniFiClientCache insightsCache = new UniFiClientCache();
|
||||
|
||||
private final HttpClient httpClient;
|
||||
|
||||
private final String host;
|
||||
|
||||
private final int port;
|
||||
|
||||
private final String username;
|
||||
|
||||
private final String password;
|
||||
|
||||
private final Gson gson;
|
||||
|
||||
public UniFiController(HttpClient httpClient, String host, int port, String username, String password) {
|
||||
this.httpClient = httpClient;
|
||||
this.host = host;
|
||||
this.port = port;
|
||||
this.username = username;
|
||||
this.password = password;
|
||||
UniFiSiteInstanceCreator siteInstanceCreator = new UniFiSiteInstanceCreator(this);
|
||||
UniFiDeviceInstanceCreator deviceInstanceCreator = new UniFiDeviceInstanceCreator(this);
|
||||
UniFiClientInstanceCreator clientInstanceCreator = new UniFiClientInstanceCreator(this);
|
||||
this.gson = new GsonBuilder().setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES)
|
||||
.registerTypeAdapter(UniFiSite.class, siteInstanceCreator)
|
||||
.registerTypeAdapter(UniFiDevice.class, deviceInstanceCreator)
|
||||
.registerTypeAdapter(UniFiClient.class, new UniFiClientDeserializer())
|
||||
.registerTypeAdapter(UniFiUnknownClient.class, clientInstanceCreator)
|
||||
.registerTypeAdapter(UniFiWiredClient.class, clientInstanceCreator)
|
||||
.registerTypeAdapter(UniFiWirelessClient.class, clientInstanceCreator).create();
|
||||
}
|
||||
|
||||
// Public API
|
||||
|
||||
public void start() throws UniFiException {
|
||||
login();
|
||||
}
|
||||
|
||||
public void stop() throws UniFiException {
|
||||
logout();
|
||||
}
|
||||
|
||||
public void login() throws UniFiException {
|
||||
UniFiControllerRequest<Void> req = newRequest(Void.class);
|
||||
req.setPath("/api/login");
|
||||
req.setBodyParameter("username", username);
|
||||
req.setBodyParameter("password", password);
|
||||
// scurb: Changed strict = false to make blocking feature work
|
||||
req.setBodyParameter("strict", false);
|
||||
req.setBodyParameter("remember", false);
|
||||
executeRequest(req);
|
||||
}
|
||||
|
||||
public void logout() throws UniFiException {
|
||||
UniFiControllerRequest<Void> req = newRequest(Void.class);
|
||||
req.setPath("/logout");
|
||||
executeRequest(req);
|
||||
}
|
||||
|
||||
public void refresh() throws UniFiException {
|
||||
synchronized (this) {
|
||||
sitesCache = getSites();
|
||||
devicesCache = getDevices();
|
||||
clientsCache = getClients();
|
||||
insightsCache = getInsights();
|
||||
}
|
||||
}
|
||||
|
||||
// Site API
|
||||
|
||||
public @Nullable UniFiSite getSite(@Nullable String id) {
|
||||
UniFiSite site = null;
|
||||
if (StringUtils.isNotBlank(id)) {
|
||||
synchronized (this) {
|
||||
site = sitesCache.get(id);
|
||||
}
|
||||
if (site == null) {
|
||||
logger.debug("Could not find a matching site for id = '{}'", id);
|
||||
}
|
||||
}
|
||||
return site;
|
||||
}
|
||||
|
||||
// Device API
|
||||
|
||||
public @Nullable UniFiDevice getDevice(@Nullable String id) {
|
||||
UniFiDevice device = null;
|
||||
if (StringUtils.isNotBlank(id)) {
|
||||
synchronized (this) {
|
||||
device = devicesCache.get(id);
|
||||
}
|
||||
if (device == null) {
|
||||
logger.debug("Could not find a matching device for id = '{}'", id);
|
||||
}
|
||||
}
|
||||
return device;
|
||||
}
|
||||
|
||||
// Client API
|
||||
|
||||
public @Nullable UniFiClient getClient(@Nullable String id) {
|
||||
UniFiClient client = null;
|
||||
if (StringUtils.isNotBlank(id)) {
|
||||
synchronized (this) {
|
||||
// mgb: first check active clients and fallback to insights if not found
|
||||
client = clientsCache.get(id);
|
||||
if (client == null) {
|
||||
client = insightsCache.get(id);
|
||||
}
|
||||
}
|
||||
if (client == null) {
|
||||
logger.debug("Could not find a matching client for id = {}", id);
|
||||
}
|
||||
}
|
||||
return client;
|
||||
}
|
||||
|
||||
protected void block(UniFiClient client, boolean blocked) throws UniFiException {
|
||||
UniFiControllerRequest<Void> req = newRequest(Void.class);
|
||||
req.setPath("/api/s/" + client.getSite().getName() + "/cmd/stamgr");
|
||||
req.setBodyParameter("cmd", blocked ? "block-sta" : "unblock-sta");
|
||||
req.setBodyParameter("mac", client.getMac());
|
||||
executeRequest(req);
|
||||
}
|
||||
|
||||
protected void reconnect(UniFiClient client) throws UniFiException {
|
||||
UniFiControllerRequest<Void> req = newRequest(Void.class);
|
||||
req.setPath("/api/s/" + client.getSite().getName() + "/cmd/stamgr");
|
||||
req.setBodyParameter("cmd", "kick-sta");
|
||||
req.setBodyParameter("mac", client.getMac());
|
||||
executeRequest(req);
|
||||
}
|
||||
|
||||
// Internal API
|
||||
|
||||
private <T> UniFiControllerRequest<T> newRequest(Class<T> responseType) {
|
||||
return new UniFiControllerRequest<>(responseType, gson, httpClient, host, port);
|
||||
}
|
||||
|
||||
private <T> @Nullable T executeRequest(UniFiControllerRequest<T> request) throws UniFiException {
|
||||
T result;
|
||||
try {
|
||||
result = request.execute();
|
||||
} catch (UniFiExpiredSessionException e) {
|
||||
login();
|
||||
result = executeRequest(request);
|
||||
} catch (UniFiNotAuthorizedException e) {
|
||||
logger.warn("Not Authorized! Please make sure your controller credentials have administrator rights");
|
||||
result = null;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private UniFiSiteCache getSites() throws UniFiException {
|
||||
UniFiControllerRequest<UniFiSite[]> req = newRequest(UniFiSite[].class);
|
||||
req.setPath("/api/self/sites");
|
||||
UniFiSite[] sites = executeRequest(req);
|
||||
UniFiSiteCache cache = new UniFiSiteCache();
|
||||
if (sites != null) {
|
||||
logger.debug("Found {} UniFi Site(s): {}", sites.length, lazyFormatAsList(sites));
|
||||
for (UniFiSite site : sites) {
|
||||
cache.put(site);
|
||||
}
|
||||
}
|
||||
return cache;
|
||||
}
|
||||
|
||||
private UniFiDeviceCache getDevices() throws UniFiException {
|
||||
UniFiDeviceCache cache = new UniFiDeviceCache();
|
||||
Collection<UniFiSite> sites = sitesCache.values();
|
||||
for (UniFiSite site : sites) {
|
||||
cache.putAll(getDevices(site));
|
||||
}
|
||||
return cache;
|
||||
}
|
||||
|
||||
private UniFiDeviceCache getDevices(UniFiSite site) throws UniFiException {
|
||||
UniFiControllerRequest<UniFiDevice[]> req = newRequest(UniFiDevice[].class);
|
||||
req.setPath("/api/s/" + site.getName() + "/stat/device");
|
||||
UniFiDevice[] devices = executeRequest(req);
|
||||
UniFiDeviceCache cache = new UniFiDeviceCache();
|
||||
if (devices != null) {
|
||||
logger.debug("Found {} UniFi Device(s): {}", devices.length, lazyFormatAsList(devices));
|
||||
for (UniFiDevice device : devices) {
|
||||
cache.put(device);
|
||||
}
|
||||
}
|
||||
return cache;
|
||||
}
|
||||
|
||||
private UniFiClientCache getClients() throws UniFiException {
|
||||
UniFiClientCache cache = new UniFiClientCache();
|
||||
Collection<UniFiSite> sites = sitesCache.values();
|
||||
for (UniFiSite site : sites) {
|
||||
cache.putAll(getClients(site));
|
||||
}
|
||||
return cache;
|
||||
}
|
||||
|
||||
private UniFiClientCache getClients(UniFiSite site) throws UniFiException {
|
||||
UniFiControllerRequest<UniFiClient[]> req = newRequest(UniFiClient[].class);
|
||||
req.setPath("/api/s/" + site.getName() + "/stat/sta");
|
||||
UniFiClient[] clients = executeRequest(req);
|
||||
UniFiClientCache cache = new UniFiClientCache();
|
||||
if (clients != null) {
|
||||
logger.debug("Found {} UniFi Client(s): {}", clients.length, lazyFormatAsList(clients));
|
||||
for (UniFiClient client : clients) {
|
||||
cache.put(client);
|
||||
}
|
||||
}
|
||||
return cache;
|
||||
}
|
||||
|
||||
private UniFiClientCache getInsights() throws UniFiException {
|
||||
UniFiClientCache cache = new UniFiClientCache();
|
||||
Collection<UniFiSite> sites = sitesCache.values();
|
||||
for (UniFiSite site : sites) {
|
||||
cache.putAll(getInsights(site));
|
||||
}
|
||||
return cache;
|
||||
}
|
||||
|
||||
private UniFiClientCache getInsights(UniFiSite site) throws UniFiException {
|
||||
UniFiControllerRequest<UniFiClient[]> req = newRequest(UniFiClient[].class);
|
||||
req.setPath("/api/s/" + site.getName() + "/stat/alluser");
|
||||
req.setQueryParameter("within", 168); // scurb: Changed to 7 days.
|
||||
UniFiClient[] clients = executeRequest(req);
|
||||
UniFiClientCache cache = new UniFiClientCache();
|
||||
if (clients != null) {
|
||||
logger.debug("Found {} UniFi Insights(s): {}", clients.length, lazyFormatAsList(clients));
|
||||
for (UniFiClient client : clients) {
|
||||
cache.put(client);
|
||||
}
|
||||
}
|
||||
return cache;
|
||||
}
|
||||
|
||||
private static Object lazyFormatAsList(Object[] arr) {
|
||||
return new Object() {
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
String value = "";
|
||||
for (Object o : arr) {
|
||||
value += "\n - " + o.toString();
|
||||
}
|
||||
return value;
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,225 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2020 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.unifi.internal.api.model;
|
||||
|
||||
import java.net.ConnectException;
|
||||
import java.net.UnknownHostException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.TimeoutException;
|
||||
|
||||
import javax.net.ssl.SSLException;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
import org.eclipse.jetty.client.HttpClient;
|
||||
import org.eclipse.jetty.client.HttpResponseException;
|
||||
import org.eclipse.jetty.client.api.ContentProvider;
|
||||
import org.eclipse.jetty.client.api.ContentResponse;
|
||||
import org.eclipse.jetty.client.api.Request;
|
||||
import org.eclipse.jetty.client.util.StringContentProvider;
|
||||
import org.eclipse.jetty.http.HttpMethod;
|
||||
import org.eclipse.jetty.http.HttpScheme;
|
||||
import org.eclipse.jetty.http.HttpStatus;
|
||||
import org.eclipse.jetty.http.HttpURI;
|
||||
import org.eclipse.jetty.http.MimeTypes;
|
||||
import org.openhab.binding.unifi.internal.api.UniFiCommunicationException;
|
||||
import org.openhab.binding.unifi.internal.api.UniFiException;
|
||||
import org.openhab.binding.unifi.internal.api.UniFiExpiredSessionException;
|
||||
import org.openhab.binding.unifi.internal.api.UniFiInvalidCredentialsException;
|
||||
import org.openhab.binding.unifi.internal.api.UniFiInvalidHostException;
|
||||
import org.openhab.binding.unifi.internal.api.UniFiNotAuthorizedException;
|
||||
import org.openhab.binding.unifi.internal.api.UniFiSSLException;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.GsonBuilder;
|
||||
import com.google.gson.JsonElement;
|
||||
import com.google.gson.JsonObject;
|
||||
import com.google.gson.JsonParser;
|
||||
import com.google.gson.JsonPrimitive;
|
||||
import com.google.gson.JsonSyntaxException;
|
||||
|
||||
/**
|
||||
* The {@link UniFiControllerRequest} encapsulates a request sent by the {@link UniFiController}.
|
||||
*
|
||||
* @author Matthew Bowman - Initial contribution
|
||||
*
|
||||
* @param <T> The response type expected as a result of the request's execution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class UniFiControllerRequest<T> {
|
||||
|
||||
private static final String CONTENT_TYPE_APPLICATION_JSON = MimeTypes.Type.APPLICATION_JSON.asString();
|
||||
|
||||
private static final long TIMEOUT_SECONDS = 5;
|
||||
|
||||
private static final String PROPERTY_DATA = "data";
|
||||
|
||||
private final Logger logger = LoggerFactory.getLogger(UniFiControllerRequest.class);
|
||||
|
||||
private final Gson gson;
|
||||
|
||||
private final HttpClient httpClient;
|
||||
|
||||
private final String host;
|
||||
|
||||
private final int port;
|
||||
|
||||
private String path = "/";
|
||||
|
||||
private Map<String, String> queryParameters = new HashMap<>();
|
||||
|
||||
private Map<String, String> bodyParameters = new HashMap<>();
|
||||
|
||||
private final Class<T> resultType;
|
||||
|
||||
// Public API
|
||||
|
||||
public UniFiControllerRequest(Class<T> resultType, Gson gson, HttpClient httpClient, String host, int port) {
|
||||
this.resultType = resultType;
|
||||
this.gson = gson;
|
||||
this.httpClient = httpClient;
|
||||
this.host = host;
|
||||
this.port = port;
|
||||
}
|
||||
|
||||
public void setPath(String path) {
|
||||
this.path = path;
|
||||
}
|
||||
|
||||
public void setBodyParameter(String key, Object value) {
|
||||
this.bodyParameters.put(key, String.valueOf(value));
|
||||
}
|
||||
|
||||
public void setQueryParameter(String key, Object value) {
|
||||
this.queryParameters.put(key, String.valueOf(value));
|
||||
}
|
||||
|
||||
public @Nullable T execute() throws UniFiException {
|
||||
T result = null;
|
||||
String json = getContent();
|
||||
// mgb: only try and unmarshall non-void result types
|
||||
if (!Void.class.equals(resultType)) {
|
||||
JsonObject jsonObject = new JsonParser().parse(json).getAsJsonObject();
|
||||
if (jsonObject.has(PROPERTY_DATA) && jsonObject.get(PROPERTY_DATA).isJsonArray()) {
|
||||
result = gson.fromJson(jsonObject.getAsJsonArray(PROPERTY_DATA), resultType);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
// Private API
|
||||
|
||||
private String getContent() throws UniFiException {
|
||||
String content;
|
||||
ContentResponse response = getContentResponse();
|
||||
int status = response.getStatus();
|
||||
switch (status) {
|
||||
case HttpStatus.OK_200:
|
||||
content = response.getContentAsString();
|
||||
if (logger.isTraceEnabled()) {
|
||||
logger.trace("<< {} {} \n{}", status, HttpStatus.getMessage(status), prettyPrintJson(content));
|
||||
}
|
||||
break;
|
||||
case HttpStatus.BAD_REQUEST_400:
|
||||
throw new UniFiInvalidCredentialsException("Invalid Credentials");
|
||||
case HttpStatus.UNAUTHORIZED_401:
|
||||
throw new UniFiExpiredSessionException("Expired Credentials");
|
||||
case HttpStatus.FORBIDDEN_403:
|
||||
throw new UniFiNotAuthorizedException("Unauthorized Access");
|
||||
default:
|
||||
throw new UniFiException("Unknown HTTP status code " + status + " returned by the controller");
|
||||
}
|
||||
return content;
|
||||
}
|
||||
|
||||
private ContentResponse getContentResponse() throws UniFiException {
|
||||
Request request = newRequest();
|
||||
logger.trace(">> {} {}", request.getMethod(), request.getURI());
|
||||
ContentResponse response;
|
||||
try {
|
||||
response = request.send();
|
||||
} catch (TimeoutException | InterruptedException e) {
|
||||
throw new UniFiCommunicationException(e);
|
||||
} catch (ExecutionException e) {
|
||||
// mgb: unwrap the cause and try to cleanly handle it
|
||||
Throwable cause = e.getCause();
|
||||
if (cause instanceof UnknownHostException) {
|
||||
// invalid hostname
|
||||
throw new UniFiInvalidHostException(cause);
|
||||
} else if (cause instanceof ConnectException) {
|
||||
// cannot connect
|
||||
throw new UniFiCommunicationException(cause);
|
||||
} else if (cause instanceof SSLException) {
|
||||
// cannot establish ssl connection
|
||||
throw new UniFiSSLException(cause);
|
||||
} else if (cause instanceof HttpResponseException
|
||||
&& ((HttpResponseException) cause).getResponse() instanceof ContentResponse) {
|
||||
// the UniFi controller violates the HTTP protocol
|
||||
// - it returns 401 UNAUTHORIZED without the WWW-Authenticate response header
|
||||
// - this causes an ExceptionException to be thrown
|
||||
// - we unwrap the response from the exception for proper handling of the 401 status code
|
||||
response = (ContentResponse) ((HttpResponseException) cause).getResponse();
|
||||
} else {
|
||||
// catch all
|
||||
throw new UniFiException(cause);
|
||||
}
|
||||
}
|
||||
return response;
|
||||
}
|
||||
|
||||
private Request newRequest() {
|
||||
HttpMethod method = bodyParameters.isEmpty() ? HttpMethod.GET : HttpMethod.POST;
|
||||
HttpURI uri = new HttpURI(HttpScheme.HTTPS.asString(), host, port, path);
|
||||
Request request = httpClient.newRequest(uri.toString()).timeout(TIMEOUT_SECONDS, TimeUnit.SECONDS)
|
||||
.method(method);
|
||||
for (Entry<String, String> entry : queryParameters.entrySet()) {
|
||||
request.param(entry.getKey(), entry.getValue());
|
||||
}
|
||||
if (!bodyParameters.isEmpty()) {
|
||||
String jsonBody = getRequestBodyAsJson();
|
||||
ContentProvider content = new StringContentProvider(CONTENT_TYPE_APPLICATION_JSON, jsonBody,
|
||||
StandardCharsets.UTF_8);
|
||||
request = request.content(content);
|
||||
}
|
||||
return request;
|
||||
}
|
||||
|
||||
private String getRequestBodyAsJson() {
|
||||
JsonParser jsonParser = new JsonParser();
|
||||
JsonObject jsonObject = new JsonObject();
|
||||
JsonElement jsonElement = null;
|
||||
for (Entry<String, String> entry : bodyParameters.entrySet()) {
|
||||
try {
|
||||
jsonElement = jsonParser.parse(entry.getValue());
|
||||
} catch (JsonSyntaxException e) {
|
||||
jsonElement = new JsonPrimitive(entry.getValue());
|
||||
}
|
||||
jsonObject.add(entry.getKey(), jsonElement);
|
||||
}
|
||||
return jsonObject.toString();
|
||||
}
|
||||
|
||||
private static String prettyPrintJson(String content) {
|
||||
JsonParser parser = new JsonParser();
|
||||
JsonObject json = parser.parse(content).getAsJsonObject();
|
||||
Gson prettyGson = new GsonBuilder().setPrettyPrinting().create();
|
||||
return prettyGson.toJson(json);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2020 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.unifi.internal.api.model;
|
||||
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
import org.openhab.binding.unifi.internal.api.util.UniFiTidyLowerCaseStringDeserializer;
|
||||
|
||||
import com.google.gson.annotations.JsonAdapter;
|
||||
import com.google.gson.annotations.SerializedName;
|
||||
|
||||
/**
|
||||
* The {@link UniFiDevice} represents the data model of a UniFi Wireless Device
|
||||
* (better known as an Access Point).
|
||||
*
|
||||
* @author Matthew Bowman - Initial contribution
|
||||
*/
|
||||
public class UniFiDevice {
|
||||
|
||||
protected final transient UniFiController controller;
|
||||
|
||||
@SerializedName("_id")
|
||||
private String id;
|
||||
|
||||
@JsonAdapter(UniFiTidyLowerCaseStringDeserializer.class)
|
||||
private String mac;
|
||||
|
||||
private String model;
|
||||
|
||||
private String name;
|
||||
|
||||
private String siteId;
|
||||
|
||||
public UniFiDevice(UniFiController controller) {
|
||||
this.controller = controller;
|
||||
}
|
||||
|
||||
public String getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public String getModel() {
|
||||
return model;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return StringUtils.defaultIfBlank(name, mac);
|
||||
}
|
||||
|
||||
public String getMac() {
|
||||
return mac;
|
||||
}
|
||||
|
||||
public UniFiSite getSite() {
|
||||
return controller.getSite(siteId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return String.format("UniFiDevice{mac: '%s', name: '%s', model: '%s', site: %s}", mac, name, model, getSite());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2020 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.unifi.internal.api.model;
|
||||
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
|
||||
import com.google.gson.annotations.SerializedName;
|
||||
|
||||
/**
|
||||
* The {@link UniFiSite} represents the data model of a UniFi site.
|
||||
*
|
||||
* @author Matthew Bowman - Initial contribution
|
||||
*/
|
||||
public class UniFiSite {
|
||||
|
||||
private final transient UniFiController controller;
|
||||
|
||||
public UniFiSite(UniFiController controller) {
|
||||
this.controller = controller;
|
||||
}
|
||||
|
||||
@SerializedName("_id")
|
||||
private String id;
|
||||
|
||||
private String name;
|
||||
|
||||
private String desc;
|
||||
|
||||
public String getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public String getDescription() {
|
||||
return desc;
|
||||
}
|
||||
|
||||
public boolean matchesName(String siteName) {
|
||||
return StringUtils.equalsIgnoreCase(desc, siteName) || StringUtils.equalsIgnoreCase(name, siteName)
|
||||
|| StringUtils.equalsIgnoreCase(id, siteName);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return String.format("UniFiSite{name: '%s', desc: '%s'}", name, desc);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2020 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.unifi.internal.api.model;
|
||||
|
||||
/**
|
||||
* A {@link UniFiUnknownClient} represents an unknown {@link UniFiClient}.
|
||||
*
|
||||
* An unknown client is neither a {@link UniFiWiredClient} nor a {@link UniFiWirelessClient}
|
||||
* because the <code>is_wired</code> property was missing from the JSON response of the controller.
|
||||
*
|
||||
* @author Matthew Bowman - Initial contribution
|
||||
*/
|
||||
public class UniFiUnknownClient extends UniFiClient {
|
||||
|
||||
public UniFiUnknownClient(UniFiController controller) {
|
||||
super(controller);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Boolean isWired() {
|
||||
return null; // mgb: no is_wired property in the json
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDeviceMac() {
|
||||
return null; // mgb: no device mac in the json
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2020 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.unifi.internal.api.model;
|
||||
|
||||
/**
|
||||
* A {@link UniFiWiredClient} represents a wired {@link UniFiClient}.
|
||||
*
|
||||
* A wired client is physically connected to the network - typically it is connected via an Ethernet cable.
|
||||
*
|
||||
* @author Matthew Bowman - Initial contribution
|
||||
*/
|
||||
public class UniFiWiredClient extends UniFiClient {
|
||||
|
||||
private String swMac;
|
||||
|
||||
public UniFiWiredClient(UniFiController controller) {
|
||||
super(controller);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Boolean isWired() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDeviceMac() {
|
||||
return swMac;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2020 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.unifi.internal.api.model;
|
||||
|
||||
import org.openhab.binding.unifi.internal.api.util.UniFiTidyLowerCaseStringDeserializer;
|
||||
|
||||
import com.google.gson.annotations.JsonAdapter;
|
||||
|
||||
/**
|
||||
* A {@link UniFiWirelessClient} represents a wireless {@link UniFiClient}.
|
||||
*
|
||||
* A wireless client is not physically connected to the network - typically it is connected via a Wi-Fi adapter.
|
||||
*
|
||||
* @author Matthew Bowman - Initial contribution
|
||||
*/
|
||||
public class UniFiWirelessClient extends UniFiClient {
|
||||
|
||||
@JsonAdapter(UniFiTidyLowerCaseStringDeserializer.class)
|
||||
private String apMac;
|
||||
|
||||
private String essid;
|
||||
|
||||
private Integer rssi;
|
||||
|
||||
public UniFiWirelessClient(UniFiController controller) {
|
||||
super(controller);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Boolean isWired() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDeviceMac() {
|
||||
return apMac;
|
||||
}
|
||||
|
||||
public String getEssid() {
|
||||
return essid;
|
||||
}
|
||||
|
||||
public Integer getRssi() {
|
||||
return rssi;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2020 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.unifi.internal.api.util;
|
||||
|
||||
import java.lang.reflect.Type;
|
||||
|
||||
import org.openhab.binding.unifi.internal.api.model.UniFiClient;
|
||||
import org.openhab.binding.unifi.internal.api.model.UniFiUnknownClient;
|
||||
import org.openhab.binding.unifi.internal.api.model.UniFiWiredClient;
|
||||
import org.openhab.binding.unifi.internal.api.model.UniFiWirelessClient;
|
||||
|
||||
import com.google.gson.JsonDeserializationContext;
|
||||
import com.google.gson.JsonDeserializer;
|
||||
import com.google.gson.JsonElement;
|
||||
import com.google.gson.JsonObject;
|
||||
import com.google.gson.JsonParseException;
|
||||
|
||||
/**
|
||||
*
|
||||
* The {@link UniFiClientDeserializer} is an implementation of {@link JsonDeserializer} that deserializes
|
||||
* {@link UniFiClient} instances based on each client's <code>is_wired</code> property contained in the JSON output of
|
||||
* the controller.
|
||||
*
|
||||
* @author Matthew Bowman - Initial contribution
|
||||
*/
|
||||
public class UniFiClientDeserializer implements JsonDeserializer<UniFiClient> {
|
||||
|
||||
private static final String PROPERTY_IS_WIRED = "is_wired";
|
||||
|
||||
@Override
|
||||
public UniFiClient deserialize(JsonElement json, Type type, JsonDeserializationContext context)
|
||||
throws JsonParseException {
|
||||
JsonObject jsonObject = json.getAsJsonObject();
|
||||
JsonElement isWiredElement = jsonObject.get(PROPERTY_IS_WIRED);
|
||||
// mgb: if the "is_wired "property is missing, the client is unknown
|
||||
if (isWiredElement == null) {
|
||||
return context.deserialize(json, UniFiUnknownClient.class);
|
||||
}
|
||||
boolean isWired = isWiredElement.getAsBoolean();
|
||||
if (isWired) {
|
||||
return context.deserialize(json, UniFiWiredClient.class);
|
||||
}
|
||||
return context.deserialize(json, UniFiWirelessClient.class);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2020 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.unifi.internal.api.util;
|
||||
|
||||
import java.lang.reflect.Type;
|
||||
|
||||
import org.openhab.binding.unifi.internal.api.model.UniFiClient;
|
||||
import org.openhab.binding.unifi.internal.api.model.UniFiController;
|
||||
import org.openhab.binding.unifi.internal.api.model.UniFiUnknownClient;
|
||||
import org.openhab.binding.unifi.internal.api.model.UniFiWiredClient;
|
||||
import org.openhab.binding.unifi.internal.api.model.UniFiWirelessClient;
|
||||
|
||||
import com.google.gson.InstanceCreator;
|
||||
|
||||
/**
|
||||
* The {@link UniFiClientInstanceCreator} creates instances of {@link UniFiClient}s during the JSON unmarshalling of
|
||||
* controller responses.
|
||||
*
|
||||
* @author Matthew Bowman - Initial contribution
|
||||
*/
|
||||
public class UniFiClientInstanceCreator implements InstanceCreator<UniFiClient> {
|
||||
|
||||
private final UniFiController controller;
|
||||
|
||||
public UniFiClientInstanceCreator(UniFiController controller) {
|
||||
this.controller = controller;
|
||||
}
|
||||
|
||||
@Override
|
||||
public UniFiClient createInstance(Type type) {
|
||||
if (UniFiUnknownClient.class.equals(type)) {
|
||||
return new UniFiUnknownClient(controller);
|
||||
}
|
||||
if (UniFiWirelessClient.class.equals(type)) {
|
||||
return new UniFiWirelessClient(controller);
|
||||
}
|
||||
if (UniFiWiredClient.class.equals(type)) {
|
||||
return new UniFiWiredClient(controller);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2020 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.unifi.internal.api.util;
|
||||
|
||||
import java.lang.reflect.Type;
|
||||
|
||||
import org.openhab.binding.unifi.internal.api.model.UniFiController;
|
||||
import org.openhab.binding.unifi.internal.api.model.UniFiDevice;
|
||||
|
||||
import com.google.gson.InstanceCreator;
|
||||
|
||||
/**
|
||||
*
|
||||
* The {@link UniFiDeviceInstanceCreator} creates instances of {@link UniFiDevice}s during the JSON unmarshalling of
|
||||
* controller responses.
|
||||
*
|
||||
* @author Matthew Bowman - Initial contribution
|
||||
*/
|
||||
public class UniFiDeviceInstanceCreator implements InstanceCreator<UniFiDevice> {
|
||||
|
||||
private final UniFiController controller;
|
||||
|
||||
public UniFiDeviceInstanceCreator(UniFiController controller) {
|
||||
this.controller = controller;
|
||||
}
|
||||
|
||||
@Override
|
||||
public UniFiDevice createInstance(Type type) {
|
||||
return new UniFiDevice(controller);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2020 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.unifi.internal.api.util;
|
||||
|
||||
import java.lang.reflect.Type;
|
||||
|
||||
import org.openhab.binding.unifi.internal.api.model.UniFiController;
|
||||
import org.openhab.binding.unifi.internal.api.model.UniFiSite;
|
||||
|
||||
import com.google.gson.InstanceCreator;
|
||||
|
||||
/**
|
||||
*
|
||||
* The {@link UniFiSiteInstanceCreator} creates instances of {@link UniFiSite}s during the JSON unmarshalling of
|
||||
* controller responses.
|
||||
*
|
||||
* @author Matthew Bowman - Initial contribution
|
||||
*/
|
||||
public class UniFiSiteInstanceCreator implements InstanceCreator<UniFiSite> {
|
||||
|
||||
private final UniFiController controller;
|
||||
|
||||
public UniFiSiteInstanceCreator(UniFiController controller) {
|
||||
this.controller = controller;
|
||||
}
|
||||
|
||||
@Override
|
||||
public UniFiSite createInstance(Type type) {
|
||||
return new UniFiSite(controller);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2020 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.unifi.internal.api.util;
|
||||
|
||||
import java.lang.reflect.Type;
|
||||
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
|
||||
import com.google.gson.JsonDeserializationContext;
|
||||
import com.google.gson.JsonDeserializer;
|
||||
import com.google.gson.JsonElement;
|
||||
import com.google.gson.JsonParseException;
|
||||
|
||||
/**
|
||||
*
|
||||
* The {@link UniFiTidyLowerCaseStringDeserializer} is an implementation of {@link JsonDeserializer} that deserializes
|
||||
* strings in a tidy lower case format.
|
||||
*
|
||||
* @author Matthew Bowman - Initial contribution
|
||||
*/
|
||||
public class UniFiTidyLowerCaseStringDeserializer implements JsonDeserializer<String> {
|
||||
|
||||
@Override
|
||||
public String deserialize(JsonElement json, Type type, JsonDeserializationContext context)
|
||||
throws JsonParseException {
|
||||
String s = json.getAsJsonPrimitive().getAsString();
|
||||
return StringUtils.lowerCase(StringUtils.strip(s));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2020 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.unifi.internal.api.util;
|
||||
|
||||
import java.lang.reflect.Type;
|
||||
import java.util.Calendar;
|
||||
|
||||
import com.google.gson.JsonDeserializationContext;
|
||||
import com.google.gson.JsonDeserializer;
|
||||
import com.google.gson.JsonElement;
|
||||
|
||||
/**
|
||||
* The {@link UniFiTimestampDeserializer} is an implementation of {@link JsonDeserializer} that deserializes timestamps
|
||||
* returned in the JSON responses of the UniFi controller.
|
||||
*
|
||||
* @author Matthew Bowman - Initial contribution
|
||||
*/
|
||||
public class UniFiTimestampDeserializer implements JsonDeserializer<Calendar> {
|
||||
|
||||
@Override
|
||||
public Calendar deserialize(JsonElement json, Type type, JsonDeserializationContext context) {
|
||||
String text = json.getAsJsonPrimitive().getAsString();
|
||||
long millis = Long.valueOf(text) * 1000;
|
||||
Calendar cal = Calendar.getInstance();
|
||||
cal.setTimeInMillis(millis);
|
||||
return cal;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,132 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2020 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.unifi.internal.handler;
|
||||
|
||||
import static org.openhab.core.thing.ThingStatus.*;
|
||||
import static org.openhab.core.types.RefreshType.REFRESH;
|
||||
|
||||
import java.lang.reflect.ParameterizedType;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNull;
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
import org.openhab.binding.unifi.internal.api.UniFiException;
|
||||
import org.openhab.binding.unifi.internal.api.model.UniFiController;
|
||||
import org.openhab.core.thing.Bridge;
|
||||
import org.openhab.core.thing.Channel;
|
||||
import org.openhab.core.thing.ChannelUID;
|
||||
import org.openhab.core.thing.Thing;
|
||||
import org.openhab.core.thing.ThingStatusDetail;
|
||||
import org.openhab.core.thing.binding.BaseThingHandler;
|
||||
import org.openhab.core.types.Command;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* @author Matthew Bowman - Initial contribution
|
||||
*
|
||||
* @param <E> entity - the UniFi entity class used by this thing handler
|
||||
* @param <C> config - the UniFi config class used by this thing handler
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public abstract class UniFiBaseThingHandler<E, C> extends BaseThingHandler {
|
||||
|
||||
private final Logger logger = LoggerFactory.getLogger(UniFiBaseThingHandler.class);
|
||||
|
||||
public UniFiBaseThingHandler(Thing thing) {
|
||||
super(thing);
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public final void initialize() {
|
||||
Bridge bridge = getBridge();
|
||||
if (bridge == null || bridge.getHandler() == null
|
||||
|| !(bridge.getHandler() instanceof UniFiControllerThingHandler)) {
|
||||
updateStatus(OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR,
|
||||
"You must choose a UniFi Controller for this thing.");
|
||||
return;
|
||||
}
|
||||
if (bridge.getStatus() == OFFLINE) {
|
||||
updateStatus(OFFLINE, ThingStatusDetail.BRIDGE_OFFLINE, "The UniFi Controller is currently offline.");
|
||||
}
|
||||
// mgb: derive the config class from the generic type
|
||||
Class<?> clazz = (Class<?>) (((ParameterizedType) getClass().getGenericSuperclass())
|
||||
.getActualTypeArguments()[1]);
|
||||
C config = (C) getConfigAs(clazz);
|
||||
initialize(config);
|
||||
}
|
||||
|
||||
/**
|
||||
* Utility method to access the {@link UniFiController} instance associated with this thing.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
@SuppressWarnings("null")
|
||||
private final @Nullable UniFiController getController() {
|
||||
Bridge bridge = getBridge();
|
||||
if (bridge != null && bridge.getHandler() != null
|
||||
&& (bridge.getHandler() instanceof UniFiControllerThingHandler)) {
|
||||
return ((UniFiControllerThingHandler) bridge.getHandler()).getController();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final void handleCommand(ChannelUID channelUID, Command command) {
|
||||
logger.debug("Handling command = {} for channel = {}", command, channelUID);
|
||||
// mgb: only handle commands if we're ONLINE
|
||||
if (getThing().getStatus() == ONLINE) {
|
||||
UniFiController controller = getController();
|
||||
if (controller != null) {
|
||||
E entity = getEntity(controller);
|
||||
if (entity != null) {
|
||||
if (command == REFRESH) {
|
||||
refreshChannel(entity, channelUID);
|
||||
} else {
|
||||
try {
|
||||
handleCommand(entity, channelUID, command);
|
||||
} catch (UniFiException e) {
|
||||
logger.warn("Unexpected error handling command = {} for channel = {} : {}", command,
|
||||
channelUID, e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected final void refresh() {
|
||||
// mgb: only refresh if we're ONLINE
|
||||
if (getThing().getStatus() == ONLINE) {
|
||||
UniFiController controller = getController();
|
||||
if (controller != null) {
|
||||
E entity = getEntity(controller);
|
||||
if (entity != null) {
|
||||
for (Channel channel : getThing().getChannels()) {
|
||||
ChannelUID channelUID = channel.getUID();
|
||||
refreshChannel(entity, channelUID);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected abstract void initialize(@NonNull C config);
|
||||
|
||||
protected abstract @Nullable E getEntity(UniFiController controller);
|
||||
|
||||
protected abstract void refreshChannel(E entity, ChannelUID channelUID);
|
||||
|
||||
protected abstract void handleCommand(E entity, ChannelUID channelUID, Command command) throws UniFiException;
|
||||
}
|
||||
@@ -0,0 +1,302 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2020 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.unifi.internal.handler;
|
||||
|
||||
import static org.openhab.binding.unifi.internal.UniFiBindingConstants.*;
|
||||
import static org.openhab.core.thing.ThingStatus.*;
|
||||
import static org.openhab.core.thing.ThingStatusDetail.CONFIGURATION_ERROR;
|
||||
|
||||
import java.time.ZoneId;
|
||||
import java.time.ZonedDateTime;
|
||||
import java.util.Calendar;
|
||||
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
import org.openhab.binding.unifi.internal.UniFiBindingConstants;
|
||||
import org.openhab.binding.unifi.internal.UniFiClientThingConfig;
|
||||
import org.openhab.binding.unifi.internal.api.UniFiException;
|
||||
import org.openhab.binding.unifi.internal.api.model.UniFiClient;
|
||||
import org.openhab.binding.unifi.internal.api.model.UniFiController;
|
||||
import org.openhab.binding.unifi.internal.api.model.UniFiDevice;
|
||||
import org.openhab.binding.unifi.internal.api.model.UniFiSite;
|
||||
import org.openhab.binding.unifi.internal.api.model.UniFiWiredClient;
|
||||
import org.openhab.binding.unifi.internal.api.model.UniFiWirelessClient;
|
||||
import org.openhab.core.library.types.DateTimeType;
|
||||
import org.openhab.core.library.types.DecimalType;
|
||||
import org.openhab.core.library.types.OnOffType;
|
||||
import org.openhab.core.library.types.StringType;
|
||||
import org.openhab.core.thing.ChannelUID;
|
||||
import org.openhab.core.thing.Thing;
|
||||
import org.openhab.core.thing.ThingTypeUID;
|
||||
import org.openhab.core.types.Command;
|
||||
import org.openhab.core.types.State;
|
||||
import org.openhab.core.types.UnDefType;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* The {@link UniFiClientThingHandler} is responsible for handling commands and status
|
||||
* updates for UniFi Wireless Devices.
|
||||
*
|
||||
* @author Matthew Bowman - Initial contribution
|
||||
* @author Patrik Wimnell - Blocking / Unblocking client support
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class UniFiClientThingHandler extends UniFiBaseThingHandler<UniFiClient, UniFiClientThingConfig> {
|
||||
|
||||
public static boolean supportsThingType(ThingTypeUID thingTypeUID) {
|
||||
return UniFiBindingConstants.THING_TYPE_WIRELESS_CLIENT.equals(thingTypeUID);
|
||||
}
|
||||
|
||||
private final Logger logger = LoggerFactory.getLogger(UniFiClientThingHandler.class);
|
||||
|
||||
private UniFiClientThingConfig config = new UniFiClientThingConfig();
|
||||
|
||||
public UniFiClientThingHandler(Thing thing) {
|
||||
super(thing);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected synchronized void initialize(UniFiClientThingConfig config) {
|
||||
// mgb: called when the config changes
|
||||
if (thing.getStatus() == INITIALIZING) {
|
||||
logger.debug("Initializing the UniFi Client Handler with config = {}", config);
|
||||
if (!config.isValid()) {
|
||||
updateStatus(OFFLINE, CONFIGURATION_ERROR,
|
||||
"You must define a MAC address, IP address, hostname or alias for this thing.");
|
||||
return;
|
||||
}
|
||||
this.config = config;
|
||||
updateStatus(ONLINE);
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean belongsToSite(UniFiClient client, String siteName) {
|
||||
boolean result = true; // mgb: assume true = proof by contradiction
|
||||
if (StringUtils.isNotEmpty(siteName)) {
|
||||
UniFiSite site = client.getSite();
|
||||
// mgb: if the 'site' can't be found or the name doesn't match...
|
||||
if (site == null || !site.matchesName(siteName)) {
|
||||
// mgb: ... then the client doesn't belong to this thing's configured 'site' and we 'filter' it
|
||||
result = false;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected synchronized @Nullable UniFiClient getEntity(UniFiController controller) {
|
||||
UniFiClient client = controller.getClient(config.getClientID());
|
||||
// mgb: short circuit
|
||||
if (client == null || !belongsToSite(client, config.getSite())) {
|
||||
return null;
|
||||
}
|
||||
return client;
|
||||
}
|
||||
|
||||
private State getDefaultState(String channelID, boolean clientHome) {
|
||||
State state = UnDefType.NULL;
|
||||
switch (channelID) {
|
||||
case CHANNEL_ONLINE:
|
||||
case CHANNEL_SITE:
|
||||
case CHANNEL_AP:
|
||||
case CHANNEL_ESSID:
|
||||
case CHANNEL_RSSI:
|
||||
case CHANNEL_MAC_ADDRESS:
|
||||
case CHANNEL_IP_ADDRESS:
|
||||
case CHANNEL_BLOCKED:
|
||||
state = (clientHome ? UnDefType.NULL : UnDefType.UNDEF); // skip the update if the client is home
|
||||
break;
|
||||
case CHANNEL_UPTIME:
|
||||
// mgb: uptime should default to 0 seconds
|
||||
state = (clientHome ? UnDefType.NULL : new DecimalType(0)); // skip the update if the client is home
|
||||
break;
|
||||
case CHANNEL_LAST_SEEN:
|
||||
// mgb: lastSeen should keep the last state no matter what
|
||||
state = UnDefType.NULL;
|
||||
break;
|
||||
case CHANNEL_RECONNECT:
|
||||
state = OnOffType.OFF;
|
||||
break;
|
||||
}
|
||||
return state;
|
||||
}
|
||||
|
||||
private synchronized boolean isClientHome(UniFiClient client) {
|
||||
boolean online = false;
|
||||
if (client != null) {
|
||||
Calendar lastSeen = client.getLastSeen();
|
||||
if (lastSeen == null) {
|
||||
logger.warn("Could not determine if client is online: cid = {}, lastSeen = null", config.getClientID());
|
||||
} else {
|
||||
Calendar considerHome = (Calendar) lastSeen.clone();
|
||||
considerHome.add(Calendar.SECOND, config.getConsiderHome());
|
||||
Calendar now = Calendar.getInstance();
|
||||
online = (now.compareTo(considerHome) < 0);
|
||||
}
|
||||
}
|
||||
return online;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void refreshChannel(UniFiClient client, ChannelUID channelUID) {
|
||||
boolean clientHome = isClientHome(client);
|
||||
UniFiDevice device = client.getDevice();
|
||||
UniFiSite site = (device == null ? null : device.getSite());
|
||||
String channelID = channelUID.getIdWithoutGroup();
|
||||
State state = getDefaultState(channelID, clientHome);
|
||||
switch (channelID) {
|
||||
// mgb: common wired + wireless client channels
|
||||
|
||||
// :online
|
||||
case CHANNEL_ONLINE:
|
||||
state = OnOffType.from(clientHome);
|
||||
break;
|
||||
|
||||
// :site
|
||||
case CHANNEL_SITE:
|
||||
if (clientHome && site != null && StringUtils.isNotBlank(site.getDescription())) {
|
||||
state = StringType.valueOf(site.getDescription());
|
||||
}
|
||||
break;
|
||||
|
||||
// :macAddress
|
||||
case CHANNEL_MAC_ADDRESS:
|
||||
if (clientHome && StringUtils.isNotBlank(client.getMac())) {
|
||||
state = StringType.valueOf(client.getMac());
|
||||
}
|
||||
break;
|
||||
|
||||
// :ipAddress
|
||||
case CHANNEL_IP_ADDRESS:
|
||||
if (clientHome && StringUtils.isNotBlank(client.getIp())) {
|
||||
state = StringType.valueOf(client.getIp());
|
||||
}
|
||||
break;
|
||||
|
||||
// :uptime
|
||||
case CHANNEL_UPTIME:
|
||||
if (clientHome && client.getUptime() != null) {
|
||||
state = new DecimalType(client.getUptime());
|
||||
}
|
||||
break;
|
||||
|
||||
// :lastSeen
|
||||
case CHANNEL_LAST_SEEN:
|
||||
// mgb: we don't check clientOnline as lastSeen is also included in the Insights data
|
||||
if (client.getLastSeen() != null) {
|
||||
state = new DateTimeType(
|
||||
ZonedDateTime.ofInstant(client.getLastSeen().toInstant(), ZoneId.systemDefault()));
|
||||
}
|
||||
break;
|
||||
|
||||
// :blocked
|
||||
case CHANNEL_BLOCKED:
|
||||
state = OnOffType.from(client.isBlocked());
|
||||
break;
|
||||
|
||||
default:
|
||||
// mgb: additional wired client channels
|
||||
if (client.isWired() && (client instanceof UniFiWiredClient)) {
|
||||
state = getWiredChannelState((UniFiWiredClient) client, clientHome, channelID);
|
||||
}
|
||||
|
||||
// mgb: additional wireless client channels
|
||||
else if (client.isWireless() && (client instanceof UniFiWirelessClient)) {
|
||||
state = getWirelessChannelState((UniFiWirelessClient) client, clientHome, channelID);
|
||||
}
|
||||
break;
|
||||
}
|
||||
// mgb: only non null states get updates
|
||||
if (state != UnDefType.NULL) {
|
||||
updateState(channelID, state);
|
||||
}
|
||||
}
|
||||
|
||||
private State getWiredChannelState(UniFiWiredClient client, boolean clientHome, String channelID) {
|
||||
State state = UnDefType.NULL;
|
||||
return state;
|
||||
}
|
||||
|
||||
private State getWirelessChannelState(UniFiWirelessClient client, boolean clientHome, String channelID) {
|
||||
State state = UnDefType.NULL;
|
||||
switch (channelID) {
|
||||
// :ap
|
||||
case CHANNEL_AP:
|
||||
UniFiDevice device = client.getDevice();
|
||||
if (clientHome && device != null && StringUtils.isNotBlank(device.getName())) {
|
||||
state = StringType.valueOf(device.getName());
|
||||
}
|
||||
break;
|
||||
|
||||
// :essid
|
||||
case CHANNEL_ESSID:
|
||||
if (clientHome && StringUtils.isNotBlank(client.getEssid())) {
|
||||
state = StringType.valueOf(client.getEssid());
|
||||
}
|
||||
break;
|
||||
|
||||
// :rssi
|
||||
case CHANNEL_RSSI:
|
||||
if (clientHome && client.getRssi() != null) {
|
||||
state = new DecimalType(client.getRssi());
|
||||
}
|
||||
break;
|
||||
|
||||
// :reconnect
|
||||
case CHANNEL_RECONNECT:
|
||||
// nop - read-only channel
|
||||
break;
|
||||
}
|
||||
return state;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void handleCommand(UniFiClient client, ChannelUID channelUID, Command command) throws UniFiException {
|
||||
String channelID = channelUID.getIdWithoutGroup();
|
||||
switch (channelID) {
|
||||
case CHANNEL_BLOCKED:
|
||||
handleBlockedCommand(client, channelUID, command);
|
||||
break;
|
||||
case CHANNEL_RECONNECT:
|
||||
handleReconnectCommand(client, channelUID, command);
|
||||
break;
|
||||
default:
|
||||
logger.warn("Ignoring unsupported command = {} for channel = {}", command, channelUID);
|
||||
}
|
||||
}
|
||||
|
||||
private void handleBlockedCommand(UniFiClient client, ChannelUID channelUID, Command command)
|
||||
throws UniFiException {
|
||||
if (command instanceof OnOffType) {
|
||||
client.block(command == OnOffType.ON);
|
||||
} else {
|
||||
logger.warn("Ignoring unsupported command = {} for channel = {} - valid commands types are: OnOffType",
|
||||
command, channelUID);
|
||||
}
|
||||
}
|
||||
|
||||
private void handleReconnectCommand(UniFiClient client, ChannelUID channelUID, Command command)
|
||||
throws UniFiException {
|
||||
if (command instanceof OnOffType) {
|
||||
if (command == OnOffType.ON) {
|
||||
client.reconnect();
|
||||
updateState(channelUID, OnOffType.OFF);
|
||||
}
|
||||
} else {
|
||||
logger.warn("Ignoring unsupported command = {} for channel = {} - valid commands types are: OnOffType",
|
||||
command, channelUID);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,199 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2020 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.unifi.internal.handler;
|
||||
|
||||
import static org.openhab.core.thing.ThingStatus.OFFLINE;
|
||||
import static org.openhab.core.thing.ThingStatus.ONLINE;
|
||||
import static org.openhab.core.thing.ThingStatusDetail.*;
|
||||
|
||||
import java.util.concurrent.ScheduledFuture;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
import org.eclipse.jetty.client.HttpClient;
|
||||
import org.openhab.binding.unifi.internal.UniFiBindingConstants;
|
||||
import org.openhab.binding.unifi.internal.UniFiControllerThingConfig;
|
||||
import org.openhab.binding.unifi.internal.api.UniFiCommunicationException;
|
||||
import org.openhab.binding.unifi.internal.api.UniFiException;
|
||||
import org.openhab.binding.unifi.internal.api.UniFiInvalidCredentialsException;
|
||||
import org.openhab.binding.unifi.internal.api.UniFiInvalidHostException;
|
||||
import org.openhab.binding.unifi.internal.api.UniFiSSLException;
|
||||
import org.openhab.binding.unifi.internal.api.model.UniFiController;
|
||||
import org.openhab.core.thing.Bridge;
|
||||
import org.openhab.core.thing.ChannelUID;
|
||||
import org.openhab.core.thing.ThingStatus;
|
||||
import org.openhab.core.thing.ThingStatusDetail;
|
||||
import org.openhab.core.thing.ThingStatusInfo;
|
||||
import org.openhab.core.thing.ThingTypeUID;
|
||||
import org.openhab.core.thing.binding.BaseBridgeHandler;
|
||||
import org.openhab.core.thing.binding.builder.ThingStatusInfoBuilder;
|
||||
import org.openhab.core.types.Command;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* The {@link UniFiControllerThingHandler} is responsible for handling commands and status
|
||||
* updates for the UniFi Controller.
|
||||
*
|
||||
* @author Matthew Bowman - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class UniFiControllerThingHandler extends BaseBridgeHandler {
|
||||
|
||||
public static boolean supportsThingType(ThingTypeUID thingTypeUID) {
|
||||
return UniFiBindingConstants.THING_TYPE_CONTROLLER.equals(thingTypeUID);
|
||||
}
|
||||
|
||||
private static final String STATUS_DESCRIPTION_COMMUNICATION_ERROR = "Error communicating with the UniFi controller";
|
||||
|
||||
private static final String STATUS_DESCRIPTION_SSL_ERROR = "Error establishing an SSL connection with the UniFi controller";
|
||||
|
||||
private static final String STATUS_DESCRIPTION_INVALID_CREDENTIALS = "Invalid username and/or password - please double-check your configuration";
|
||||
|
||||
private static final String STATUS_DESCRIPTION_INVALID_HOSTNAME = "Invalid hostname - please double-check your configuration";
|
||||
|
||||
private final Logger logger = LoggerFactory.getLogger(UniFiControllerThingHandler.class);
|
||||
|
||||
private UniFiControllerThingConfig config = new UniFiControllerThingConfig();
|
||||
|
||||
private @Nullable volatile UniFiController controller; /* mgb: volatile because accessed from multiple threads */
|
||||
|
||||
private @Nullable ScheduledFuture<?> refreshJob;
|
||||
|
||||
private final HttpClient httpClient;
|
||||
|
||||
public UniFiControllerThingHandler(Bridge bridge, HttpClient httpClient) {
|
||||
super(bridge);
|
||||
this.httpClient = httpClient;
|
||||
}
|
||||
|
||||
// Public API
|
||||
|
||||
@Override
|
||||
public void initialize() {
|
||||
// mgb: called when the config changes
|
||||
cancelRefreshJob();
|
||||
config = getConfig().as(UniFiControllerThingConfig.class);
|
||||
logger.debug("Initializing the UniFi Controller Handler with config = {}", config);
|
||||
try {
|
||||
controller = new UniFiController(httpClient, config.getHost(), config.getPort(), config.getUsername(),
|
||||
config.getPassword());
|
||||
controller.start();
|
||||
updateStatus(ONLINE);
|
||||
} catch (UniFiInvalidHostException e) {
|
||||
updateStatus(OFFLINE, CONFIGURATION_ERROR, STATUS_DESCRIPTION_INVALID_HOSTNAME);
|
||||
} catch (UniFiCommunicationException e) {
|
||||
updateStatus(OFFLINE, COMMUNICATION_ERROR, STATUS_DESCRIPTION_COMMUNICATION_ERROR);
|
||||
} catch (UniFiSSLException e) {
|
||||
updateStatus(OFFLINE, CONFIGURATION_ERROR, STATUS_DESCRIPTION_SSL_ERROR);
|
||||
} catch (UniFiInvalidCredentialsException e) {
|
||||
updateStatus(OFFLINE, CONFIGURATION_ERROR, STATUS_DESCRIPTION_INVALID_CREDENTIALS);
|
||||
} catch (UniFiException e) {
|
||||
logger.error("Unknown error while configuring the UniFi Controller", e);
|
||||
updateStatus(OFFLINE, CONFIGURATION_ERROR, e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void updateStatus(ThingStatus status, ThingStatusDetail statusDetail, @Nullable String description) {
|
||||
if (status == ONLINE || (status == OFFLINE && statusDetail == COMMUNICATION_ERROR)) {
|
||||
scheduleRefreshJob();
|
||||
} else if (status == OFFLINE && statusDetail == CONFIGURATION_ERROR) {
|
||||
cancelRefreshJob();
|
||||
}
|
||||
// mgb: update the status only if it's changed
|
||||
ThingStatusInfo statusInfo = ThingStatusInfoBuilder.create(status, statusDetail).withDescription(description)
|
||||
.build();
|
||||
if (!statusInfo.equals(getThing().getStatusInfo())) {
|
||||
super.updateStatus(status, statusDetail, description);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dispose() {
|
||||
cancelRefreshJob();
|
||||
if (controller != null) {
|
||||
try {
|
||||
controller.stop();
|
||||
} catch (UniFiException e) {
|
||||
// mgb: nop as we're in dispose
|
||||
}
|
||||
controller = null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleCommand(ChannelUID channelUID, Command command) {
|
||||
// nop - read-only binding
|
||||
logger.warn("Ignoring command = {} for channel = {} - the UniFi binding is read-only!", command, channelUID);
|
||||
}
|
||||
|
||||
public @Nullable UniFiController getController() {
|
||||
return controller;
|
||||
}
|
||||
|
||||
public int getRefreshInterval() {
|
||||
return config.getRefresh();
|
||||
}
|
||||
|
||||
// Private API
|
||||
|
||||
private void scheduleRefreshJob() {
|
||||
synchronized (this) {
|
||||
if (refreshJob == null) {
|
||||
logger.debug("Scheduling refresh job every {}s", config.getRefresh());
|
||||
refreshJob = scheduler.scheduleWithFixedDelay(this::run, 0, config.getRefresh(), TimeUnit.SECONDS);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void cancelRefreshJob() {
|
||||
synchronized (this) {
|
||||
if (refreshJob != null) {
|
||||
logger.debug("Cancelling refresh job");
|
||||
refreshJob.cancel(true);
|
||||
refreshJob = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void run() {
|
||||
try {
|
||||
logger.trace("Executing refresh job");
|
||||
refresh();
|
||||
updateStatus(ONLINE);
|
||||
} catch (UniFiCommunicationException e) {
|
||||
updateStatus(OFFLINE, COMMUNICATION_ERROR, STATUS_DESCRIPTION_COMMUNICATION_ERROR);
|
||||
} catch (UniFiInvalidCredentialsException e) {
|
||||
updateStatus(OFFLINE, CONFIGURATION_ERROR, STATUS_DESCRIPTION_INVALID_CREDENTIALS);
|
||||
} catch (Exception e) {
|
||||
logger.warn("Unhandled exception while refreshing the UniFi Controller {} - {}", getThing().getUID(),
|
||||
e.getMessage());
|
||||
updateStatus(OFFLINE, COMMUNICATION_ERROR, e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
private void refresh() throws UniFiException {
|
||||
if (controller != null) {
|
||||
logger.debug("Refreshing the UniFi Controller {}", getThing().getUID());
|
||||
controller.refresh();
|
||||
// mgb: then refresh all the client things
|
||||
getThing().getThings().forEach((thing) -> {
|
||||
if (thing.getHandler() instanceof UniFiBaseThingHandler) {
|
||||
((UniFiBaseThingHandler) thing.getHandler()).refresh();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,76 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2020 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.unifi.internal.ssl;
|
||||
|
||||
import java.net.Socket;
|
||||
import java.security.cert.CertificateException;
|
||||
import java.security.cert.X509Certificate;
|
||||
|
||||
import javax.net.ssl.SSLEngine;
|
||||
import javax.net.ssl.X509ExtendedTrustManager;
|
||||
|
||||
/**
|
||||
*
|
||||
* The {@link UniFiTrustManager} is a "trust all" implementation of {@link X509ExtendedTrustManager}.
|
||||
*
|
||||
* @see {@link UniFiTrustManagerProvider}
|
||||
*
|
||||
* @author Matthew Bowman - Initial contribution
|
||||
*/
|
||||
public class UniFiTrustManager extends X509ExtendedTrustManager {
|
||||
|
||||
private static UniFiTrustManager instance = new UniFiTrustManager();
|
||||
|
||||
public static UniFiTrustManager getInstance() {
|
||||
return instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* private construction - singleton
|
||||
*/
|
||||
private UniFiTrustManager() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException {
|
||||
}
|
||||
|
||||
@Override
|
||||
public X509Certificate[] getAcceptedIssuers() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void checkClientTrusted(X509Certificate[] chain, String authType, Socket socket)
|
||||
throws CertificateException {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void checkClientTrusted(X509Certificate[] chain, String authType, SSLEngine engine)
|
||||
throws CertificateException {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void checkServerTrusted(X509Certificate[] chain, String authType, Socket socket)
|
||||
throws CertificateException {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void checkServerTrusted(X509Certificate[] chain, String authType, SSLEngine engine)
|
||||
throws CertificateException {
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2020 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.unifi.internal.ssl;
|
||||
|
||||
import javax.net.ssl.X509ExtendedTrustManager;
|
||||
|
||||
import org.openhab.core.io.net.http.TlsTrustManagerProvider;
|
||||
|
||||
/**
|
||||
*
|
||||
* The {@link UniFiTrustManagerProvider} is an implementation of {@link TlsTrustManagerProvider} which provides an
|
||||
* instance of {@link UniFiTrustManagerProvider} for all servers that use a certificate with the common name equal to
|
||||
* "UniFi" (<code>CN=UniFi</code>).
|
||||
*
|
||||
* @author Matthew Bowman - Initial contribution
|
||||
*/
|
||||
// @Component // [wip] mgb: disabled due to issues with service order loading
|
||||
public class UniFiTrustManagerProvider implements TlsTrustManagerProvider {
|
||||
|
||||
@Override
|
||||
public String getHostName() {
|
||||
return "UniFi";
|
||||
}
|
||||
|
||||
@Override
|
||||
public X509ExtendedTrustManager getTrustManager() {
|
||||
return UniFiTrustManager.getInstance();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<binding:binding id="unifi" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xmlns:binding="https://openhab.org/schemas/binding/v1.0.0"
|
||||
xsi:schemaLocation="https://openhab.org/schemas/binding/v1.0.0 https://openhab.org/schemas/binding-1.0.0.xsd">
|
||||
|
||||
<name>UniFi Binding</name>
|
||||
<description>The UniFi binding integrates the UniFi controller from Ubiquiti Networks to facilitate tracking of Wi-Fi
|
||||
clients.</description>
|
||||
<author>Matthew Bowman</author>
|
||||
|
||||
</binding:binding>
|
||||
@@ -0,0 +1,163 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<thing:thing-descriptions bindingId="unifi"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xmlns:thing="https://openhab.org/schemas/thing-description/v1.0.0"
|
||||
xsi:schemaLocation="https://openhab.org/schemas/thing-description/v1.0.0 https://openhab.org/schemas/thing-description-1.0.0.xsd">
|
||||
|
||||
<bridge-type id="controller">
|
||||
|
||||
<label>UniFi Controller</label>
|
||||
<description>A UniFi controller.</description>
|
||||
|
||||
<config-description>
|
||||
<parameter name="host" type="text" required="true">
|
||||
<label>Hostname</label>
|
||||
<description>Hostname of IP address of the UniFi Controller</description>
|
||||
<default>unifi</default>
|
||||
<context>network-address</context>
|
||||
</parameter>
|
||||
<parameter name="port" type="integer" max="65535" min="1" required="false">
|
||||
<label>Port</label>
|
||||
<description>Port of the UniFi Controller</description>
|
||||
<default>8443</default>
|
||||
</parameter>
|
||||
<parameter name="username" type="text" required="true">
|
||||
<label>Username</label>
|
||||
<description>The username to access the UniFi Controller.</description>
|
||||
</parameter>
|
||||
<parameter name="password" type="text" required="true">
|
||||
<label>Password</label>
|
||||
<description>The password to access the UniFi Controller.</description>
|
||||
<context>password</context>
|
||||
</parameter>
|
||||
<parameter name="refresh" type="integer" required="false">
|
||||
<label>Refresh Interval</label>
|
||||
<description>The refresh interval in seconds to poll the UniFi controller</description>
|
||||
<default>10</default>
|
||||
</parameter>
|
||||
</config-description>
|
||||
|
||||
</bridge-type>
|
||||
|
||||
<!-- <thing-type id="wiredClient"> .. coming soon .. </thing-type> -->
|
||||
|
||||
<thing-type id="wirelessClient">
|
||||
|
||||
<supported-bridge-type-refs>
|
||||
<bridge-type-ref id="controller"/>
|
||||
</supported-bridge-type-refs>
|
||||
<label>UniFi Wireless Client</label>
|
||||
<description>A wireless client connected to a UniFi wireless network</description>
|
||||
|
||||
<channels>
|
||||
<!-- common wired + wireless client channels -->
|
||||
<channel id="online" typeId="online"/>
|
||||
<channel id="site" typeId="site"/>
|
||||
<channel id="macAddress" typeId="macAddress"/>
|
||||
<channel id="ipAddress" typeId="ipAddress"/>
|
||||
<channel id="uptime" typeId="uptime"/>
|
||||
<channel id="lastSeen" typeId="lastSeen"/>
|
||||
<channel id="blocked" typeId="blocked"/>
|
||||
<!-- additional wireless client channels -->
|
||||
<channel id="ap" typeId="ap"/>
|
||||
<channel id="essid" typeId="essid"/>
|
||||
<channel id="rssi" typeId="rssi"/>
|
||||
<channel id="reconnect" typeId="reconnect"/>
|
||||
</channels>
|
||||
|
||||
<representation-property>cid</representation-property>
|
||||
|
||||
<config-description>
|
||||
<parameter name="cid" type="text" required="true">
|
||||
<label>Client ID</label>
|
||||
<description>The MAC address, IP address, hostname or alias of the client</description>
|
||||
</parameter>
|
||||
<parameter name="site" type="text" required="false">
|
||||
<label>Site</label>
|
||||
<description>The site where the client should be found (optional)</description>
|
||||
</parameter>
|
||||
<parameter name="considerHome" type="integer" required="false">
|
||||
<label>Consider Home Interval</label>
|
||||
<description>The interval in seconds to consider the client as home</description>
|
||||
<default>180</default>
|
||||
</parameter>
|
||||
</config-description>
|
||||
|
||||
</thing-type>
|
||||
|
||||
<channel-type id="online">
|
||||
<item-type>Switch</item-type>
|
||||
<label>Online</label>
|
||||
<description>Online status of the wireless client</description>
|
||||
<state readOnly="true"></state>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="site">
|
||||
<item-type>String</item-type>
|
||||
<label>Site Name</label>
|
||||
<description>UniFi Site the client is associated with</description>
|
||||
<state readOnly="true"></state>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="macAddress">
|
||||
<item-type>String</item-type>
|
||||
<label>MAC Address</label>
|
||||
<description>MAC address of the client</description>
|
||||
<state readOnly="true"></state>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="ipAddress">
|
||||
<item-type>String</item-type>
|
||||
<label>IP Address</label>
|
||||
<description>IP address of the client</description>
|
||||
<state readOnly="true"></state>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="uptime">
|
||||
<item-type>Number</item-type>
|
||||
<label>Uptime</label>
|
||||
<description>Uptime of the client (in seconds)</description>
|
||||
<state readOnly="true"></state>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="lastSeen">
|
||||
<item-type>DateTime</item-type>
|
||||
<label>Last Seen</label>
|
||||
<description>Timestamp of when the client was last seen</description>
|
||||
<state readOnly="true"></state>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="ap">
|
||||
<item-type>String</item-type>
|
||||
<label>Access Point</label>
|
||||
<description>Access Point the wireless client is connected to</description>
|
||||
<state readOnly="true"></state>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="essid">
|
||||
<item-type>String</item-type>
|
||||
<label>Wireless Network</label>
|
||||
<description>Wireless Network (ESSID) the wireless client is connected to</description>
|
||||
<state readOnly="true"></state>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="rssi">
|
||||
<item-type>Number</item-type>
|
||||
<label>Received Signal Strength Indicator</label>
|
||||
<description>Received Signal Strength Indicator (RSSI) of the wireless client</description>
|
||||
<state readOnly="true"></state>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="blocked">
|
||||
<item-type>Switch</item-type>
|
||||
<label>Blocked</label>
|
||||
<description>Is device blocked</description>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="reconnect">
|
||||
<item-type>Switch</item-type>
|
||||
<label>Reconnect</label>
|
||||
<description>Forces a client to reconnect</description>
|
||||
</channel-type>
|
||||
|
||||
</thing:thing-descriptions>
|
||||
Reference in New Issue
Block a user