Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[asuswrt] Initial contribution - new binding to get informations from Asus-Routers #13815

Merged
merged 13 commits into from
Jul 15, 2023
Merged
5 changes: 5 additions & 0 deletions bom/openhab-addons/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,11 @@
<artifactId>org.openhab.binding.astro</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.openhab.addons.bundles</groupId>
<artifactId>org.openhab.binding.asuswrt</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.openhab.addons.bundles</groupId>
<artifactId>org.openhab.binding.atlona</artifactId>
Expand Down
13 changes: 13 additions & 0 deletions bundles/org.openhab.binding.asuswrt/NOTICE
Original file line number Diff line number Diff line change
@@ -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
90 changes: 90 additions & 0 deletions bundles/org.openhab.binding.asuswrt/README.md
wildcs marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
# Asuswrt Binding

This binding adds support to read information from ASUS-Routers (Copyright © ASUS).

## Supported Things

This binding supports ASUS routers with Asuswrt or [Asuswrt-Merlin](https://www.asuswrt-merlin.net/) firmware.
Firmware 5.x.x (some DSL models) is NOT supported (not Asuswrt).

| ThingType | Name | Descripion |
|---------------|------------|-----------------------------------------|
| bridge | router | router binding is connecting |
| - | interface | network interface of router |
| - | client | client is connected to the bridge |

### `router` Thing Configuration

| Name | Type | Description | Default | Required | Advanced |
|-----------------|---------|---------------------------------------|---------------------|----------|----------|
| hostname | text | Hostname or IP address of the device | router.asus.com | yes | no |
| username | text | Username to access the device | N/A | yes | no |
| password | text | Password to access the device | N/A | yes | no |
| useSSL | boolean | Connect over SSL or use http:// | false | no | no |
| refreshInterval | integer | Interval the device is polled in sec. | 20 | no | yes |
| httpPort | integer | HTTP-Port | 80 | no | yes |
| httpsPort | integer | HTTPS-Port | 443 | no | yes |

### `interface` Thing Configuration

| Name | Type | Description | Default | Required | Advanced |
|-----------------|---------|---------------------------------------|---------------------|----------|----------|
| interfaceName | text | options name of interface (wan/lan) | N/A | yes | no |

### `client` Thing Configuration

| Name | Type | Description | Default | Required | Advanced |
|-----------------|---------|---------------------------------------|---------------------|----------|----------|
| macAddress | text | Unique MAC-Address of the device | N/A | yes | no |
| clientNick | text | Nickname used by OH | N/A | no | no |


## Properties

All devices support some of the following properties:

| property | description | things supporting this channel |
|------------------|------------------------------|---------------------------------------|
| vendor | Vendor of device | router, client |
| dnsName | DNS-Name of device | router, client |


## Channels

All devices support some of the following channels:

| group | channel |type | description | things supporting this channel |
|------------------|--------------------|------------------------|--------------------------------------------|-----------------------------------|
| networkInfo | macAddress | text (RO) | HW-Address | interface, client |
| | ipAddress | text (RO) | IP-Address | interface |
| | ipMethod | text (RO) | Ip-Method (static/dhcp) | interface, client |
| | subnet | text (RO) | Subnetmask | interface |
| | gateway | text (RO) | Default-Gateway | interface |
| | dnsServers | text (RO) | DNS-Servers | interface |
| | networkState | Switch (RO) | Client is online | interface, client |
| | internetState | Switch (RO) | Client connected to Internet | client |
| sysInfoGroup | memTotal | Number:DataAmountype | Total memory in MB | router |
| | memUsed | Number:DataAmountype | Used memory in MB | router |
| | memFree | Number:DataAmountype | Free memory in MB | router |
| | memUsedPercent | Number:Dimensionles | Used memory in % | router |
| | cpuUsedPercent | Number:Dimensionles | Total CPU usage in percent over all cores | router |
| clientListGroup | knownClients | text (RO) | Known clients with name and MAC-Addresses | router |
| | onlineClients | text (RO) | Online clients with name and MAC-Addresses | router |
| | onlineMACs | text (RO) | List with mac-addresses of online clients | router |
| | onlineClientsCount | Number:Dimensionless | Count of online clients | router |
| traffic | curRX | Number:DataTransferRate| Current DataTransferRate MBits/s (receive) | interface, client |
| | curTX | Number:DataTransferRate| Current DataTransferRate MBits/s (send) | interface, client |
| | todayRX | Number:DataAmount | Data received since 0:00 a clock in MB | interface, client |
| | todayTX | Number:DataAmount | Data sent since 0:00 a clock in MB | interface, client |
| | totalRX | Number:DataAmount | Data received since reboot in MB | interface, client |
| | totalTX | Number:DataAmount | Data sent since reboot in MB | interface, client |


## Events

All devices support some of the following Events:
| group | event |kind | description | things supporting this event |
|------------------|-------------------|------------|------------------------------------------------------------------------|---------------------------------|
| networkInfo | connectionEvent | Trigger | Fired if connection is established ('connected') or ('disconnected') | interface |
| | clientOnlineEvent | Trigger | Fired if Client leaves ('gone') or enters ('connected') the network | client |
| clientListGroup | clientOnlineEvent | Trigger | Fired if Client leaves ('gone') or enters ('connected') the network | router |
17 changes: 17 additions & 0 deletions bundles/org.openhab.binding.asuswrt/pom.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://maven.apache.org/POM/4.0.0"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">

<modelVersion>4.0.0</modelVersion>

<parent>
<groupId>org.openhab.addons.bundles</groupId>
<artifactId>org.openhab.addons.reactor.bundles</artifactId>
<version>4.0.0-SNAPSHOT</version>
</parent>

<artifactId>org.openhab.binding.asuswrt</artifactId>

<name>openHAB Add-ons :: Bundles :: Asuswrt Binding</name>

</project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<?xml version="1.0" encoding="UTF-8"?>
<features name="org.openhab.binding.asuswrt-${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-asuswrt" description="Asuswrt Binding" version="${project.version}">
<feature>openhab-runtime-base</feature>
<bundle start-level="80">mvn:org.openhab.addons.bundles/org.openhab.binding.asuswrt/${project.version}</bundle>
</feature>
</features>
Original file line number Diff line number Diff line change
@@ -0,0 +1,223 @@
/**
* Copyright (c) 2010-2023 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.asuswrt.internal;

import static org.openhab.binding.asuswrt.internal.constants.AsuswrtBindingConstants.*;
import static org.openhab.binding.asuswrt.internal.constants.AsuswrtBindingSettings.*;
import static org.openhab.binding.asuswrt.internal.helpers.AsuswrtUtils.*;

import java.util.Date;
import java.util.HashMap;
import java.util.Map;

import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.openhab.binding.asuswrt.internal.structures.AsuswrtClientInfo;
import org.openhab.binding.asuswrt.internal.structures.AsuswrtClientList;
import org.openhab.binding.asuswrt.internal.structures.AsuswrtInterfaceList;
import org.openhab.binding.asuswrt.internal.structures.AsuswrtIpInfo;
import org.openhab.binding.asuswrt.internal.things.AsuswrtRouter;
import org.openhab.core.config.discovery.AbstractDiscoveryService;
import org.openhab.core.config.discovery.DiscoveryResult;
import org.openhab.core.config.discovery.DiscoveryResultBuilder;
import org.openhab.core.thing.Thing;
import org.openhab.core.thing.ThingUID;
import org.openhab.core.thing.binding.ThingHandler;
import org.openhab.core.thing.binding.ThingHandlerService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
* The {@link AsuswrtDiscoveryService} is responsible for discover clients
*
* @author Christian Wild - Initial contribution
*/
@NonNullByDefault
public class AsuswrtDiscoveryService extends AbstractDiscoveryService implements ThingHandlerService {
private final Logger logger = LoggerFactory.getLogger(AsuswrtDiscoveryService.class);
private String uid = "";
protected @NonNullByDefault({}) AsuswrtRouter router;

/***********************************
*
* INITIALIZATION
*
************************************/
public AsuswrtDiscoveryService() {
super(SUPPORTED_THING_TYPES_UIDS, DISCOVERY_TIMEOUT_S, false);
}

@Override
public void activate() {
}

@Override
public void deactivate() {
super.deactivate();
removeAllResults();
}

@Override
public void setThingHandler(@Nullable ThingHandler handler) {
if (handler instanceof AsuswrtRouter) {
AsuswrtRouter router = (AsuswrtRouter) handler;
wildcs marked this conversation as resolved.
Show resolved Hide resolved
router.setDiscoveryService(this);
this.router = router;
this.uid = router.getUID().getAsString();
}
}

@Override
public @Nullable ThingHandler getThingHandler() {
return this.router;
}

/***********************************
*
* SCAN HANDLING
*
************************************/

/**
* Start scan manually
*/
@Override
public void startScan() {
logger.trace("{} starting scan", uid);
if (router != null) {
/* query Data */
router.queryDeviceData(false);
/* discover interfaces */
AsuswrtInterfaceList ifList = router.getInterfaces();
handleInterfaceScan(ifList);
/* discover clients */
AsuswrtClientList clientList = router.getClients();
handleClientScan(clientList);
}
}

@Override
public void stopScan() {
super.stopScan();
removeOlderResults(getTimestampOfLastScan());
}

/**
* Remove all scan results
*/
public void removeAllResults() {
wildcs marked this conversation as resolved.
Show resolved Hide resolved
removeOlderResults(new Date().getTime());
}

/**
* Work with result from get interfaces from router
* Create DiscoveryResult from interfaceList
*/
public void handleInterfaceScan(AsuswrtInterfaceList ifList) {
wildcs marked this conversation as resolved.
Show resolved Hide resolved
try {
for (AsuswrtIpInfo ifInfo : ifList) {
DiscoveryResult discoveryResult = createInterfaceResult(ifInfo);
thingDiscovered(discoveryResult);
}
} catch (Exception e) {
logger.debug("error handling scan reults", e);
wildcs marked this conversation as resolved.
Show resolved Hide resolved
}
}

/**
* Work with result from get clients from router
* Create DiscoveryResult from clientList
*/
public void handleClientScan(AsuswrtClientList clientList) {
try {
for (AsuswrtClientInfo client : clientList) {
DiscoveryResult discoveryResult = createClientResult(client);
thingDiscovered(discoveryResult);
}
} catch (Exception e) {
logger.debug("error handling scan results", e);
wildcs marked this conversation as resolved.
Show resolved Hide resolved
}
}

/***********************************
*
* CREATE DISCOVERY RESULTS
*
************************************/
/**
wildcs marked this conversation as resolved.
Show resolved Hide resolved
* Create DiscoveryResult from single interfaceInfo
*/
public DiscoveryResult createInterfaceResult(AsuswrtIpInfo interfaceInfo) {
wildcs marked this conversation as resolved.
Show resolved Hide resolved
String ifName = interfaceInfo.getName();
String macAddress = interfaceInfo.getMAC();
String label = "AwrtInterface_" + ifName;

Map<String, Object> properties = new HashMap<>();
properties.put(NETWORK_REPRESENTATION_PROPERTY, ifName);
properties.put(Thing.PROPERTY_MAC_ADDRESS, macAddress);

logger.debug("{} thing discovered: '{}", uid, label);
if (this.router != null) {
ThingUID bridgeUID = router.getUID();
ThingUID thingUID = new ThingUID(THING_TYPE_INTERFACE, bridgeUID, ifName);
return DiscoveryResultBuilder.create(thingUID).withProperties(properties)
.withRepresentationProperty(NETWORK_REPRESENTATION_PROPERTY).withBridge(bridgeUID).withLabel(label)
.build();
} else {
ThingUID thingUID = new ThingUID(BINDING_ID, ifName);
return DiscoveryResultBuilder.create(thingUID).withProperties(properties)
.withRepresentationProperty(NETWORK_REPRESENTATION_PROPERTY).withLabel(label).build();
}
}

/**
* Create DiscoveryResult from single clientInfo
*/
public DiscoveryResult createClientResult(AsuswrtClientInfo clientInfo) {
wildcs marked this conversation as resolved.
Show resolved Hide resolved
String macAddress = clientInfo.getMac();
String unformatedMac = unformatMac(macAddress);
String clientName;
String nickName;
String label = "AwrtClient_";

/* create label and thing names */
clientName = stringOrDefault(clientInfo.getName(), "client_" + unformatedMac);
nickName = stringOrDefault(clientInfo.getNickName(), clientName);
if (nickName.equals(clientName)) {
label += nickName;
} else {
label += nickName + " (" + clientName + ")";
}

/* create properties */
Map<String, Object> properties = new HashMap<>();
properties.put(Thing.PROPERTY_MAC_ADDRESS, macAddress);
properties.put(Thing.PROPERTY_VENDOR, clientInfo.getVendor());
properties.put(PROPERTY_CLIENT_NAME, clientName);
properties.put(CHANNEL_CLIENT_NICKNAME, nickName);

logger.debug("{} thing discovered: '{}", uid, label);
if (this.router != null) {
ThingUID bridgeUID = router.getUID();
ThingUID thingUID = new ThingUID(THING_TYPE_CLIENT, bridgeUID, unformatedMac);
return DiscoveryResultBuilder.create(thingUID).withProperties(properties)
.withRepresentationProperty(CLIENT_REPRESENTATION_PROPERTY).withTTL(DISCOVERY_AUTOREMOVE_S)
.withBridge(bridgeUID).withLabel(label).build();
} else {
ThingUID thingUID = new ThingUID(BINDING_ID, unformatedMac);
return DiscoveryResultBuilder.create(thingUID).withProperties(properties)
.withRepresentationProperty(CLIENT_REPRESENTATION_PROPERTY).withTTL(DISCOVERY_AUTOREMOVE_S)
.withLabel(label).build();
}
}
}
Loading