mirror of
https://github.com/openhab/openhab-addons.git
synced 2025-01-10 07:02:02 +01:00
[speedtest] Binding for Ookla's Speedtest - Initial contribution (#9913)
Also-by: Brian Homeyer <bhomeyer@gmail.com> Signed-off-by: Michael Weger <weger.michael@gmx.net>
This commit is contained in:
parent
46a73befae
commit
86f829fa21
@ -324,6 +324,7 @@
|
||||
/bundles/org.openhab.binding.sonyaudio/ @freke
|
||||
/bundles/org.openhab.binding.sonyprojector/ @lolodomo
|
||||
/bundles/org.openhab.binding.souliss/ @lucacalcaterra @fazioa
|
||||
/bundles/org.openhab.binding.speedtest/ @MikeTheTux
|
||||
/bundles/org.openhab.binding.spotify/ @Hilbrand
|
||||
/bundles/org.openhab.binding.squeezebox/ @digitaldan @mhilbush
|
||||
/bundles/org.openhab.binding.surepetcare/ @renescherer @HerzScheisse
|
||||
|
@ -1611,6 +1611,11 @@
|
||||
<artifactId>org.openhab.binding.souliss</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.openhab.addons.bundles</groupId>
|
||||
<artifactId>org.openhab.binding.speedtest</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.openhab.addons.bundles</groupId>
|
||||
<artifactId>org.openhab.binding.spotify</artifactId>
|
||||
|
13
bundles/org.openhab.binding.speedtest/NOTICE
Normal file
13
bundles/org.openhab.binding.speedtest/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
|
118
bundles/org.openhab.binding.speedtest/README.md
Normal file
118
bundles/org.openhab.binding.speedtest/README.md
Normal file
@ -0,0 +1,118 @@
|
||||
# Speedtest Binding
|
||||
|
||||
The Speedtest Binding can be used to perform a network speed test for your openHAB instance.
|
||||
It is based on the command line interface (CLI) version of Ookla's Speedtest (https://www.speedtest.net/apps/cli).
|
||||
|
||||
The Ookla CLI Speedtest application MUST be installed on your openHAB instance when using the Speedtest Binding.
|
||||
|
||||
## Why Ookla's Speedtest?
|
||||
|
||||
Fully supported and maintained by Ookla with the latest Speedtest technology for best results:
|
||||
|
||||
- Consistent measurement, even on high bandwidth
|
||||
- Consistent measurement, very much independent from the performance of the host system
|
||||
|
||||
## What functionality does Ookla's Speedtest offer?
|
||||
|
||||
The Speedtest Binding is using the following functionality, provided by Ookla's Speedtest:
|
||||
|
||||
- Output of an accurate timestamp per measurement
|
||||
- Output of Ping time and Jitter
|
||||
- Output of Bandwidth, transferred Bytes and elapsed time for Down-/Upload
|
||||
- Output of the used interface with Internal/External IP, MAC Address and the Internet Service Provider (ISP)
|
||||
- Output of a result ID and a result URL
|
||||
- Output of the used Server and location the Speedtest was run against
|
||||
- Possibility to pre-select a server used for testing
|
||||
|
||||
## What interfaces does the Speedtest Binding offer?
|
||||
|
||||
The Speedtest Binding provides the Ookla's Speedtest functionality via the following openHAB interface:
|
||||
|
||||
- Execute Speedtest time based or triggered
|
||||
- Provide results via openHAB Channels
|
||||
- List available Ookla Speedtest servers that can be used for testing (optional)
|
||||
|
||||
## Ookla License and Privacy Terms
|
||||
|
||||
When using this binding, you automatically accept the license and privacy terms of Ookla's Speedtest.
|
||||
You can find the latest version of those terms at the following webpages:
|
||||
|
||||
- https://www.speedtest.net/about/eula
|
||||
- https://www.speedtest.net/about/terms
|
||||
- https://www.speedtest.net/about/privacy
|
||||
|
||||
## Supported Things
|
||||
|
||||
Speedtest thing.
|
||||
|
||||
## Binding Configuration
|
||||
|
||||
For this binding to work, you MUST install Ookla's Speedtest command line tool (`speedtest` or `speedtest.exe`).
|
||||
It will not work with other versions like `speedtest-cli` or other `speedtest` variants.
|
||||
|
||||
To install Ookla's version of Speedtest, head to https://www.speedtest.net/apps/cli and follow the instructions for your Operating System.
|
||||
|
||||
## Thing Configuration
|
||||
|
||||
| Parameter | Description | Default |
|
||||
|-------------------|------------------------------------------------------------------------------------------------------------------------------|---------|
|
||||
| `refreshInterval` | How often to test network speed, in minutes | `60` |
|
||||
| `execPath` | The path of the Ookla Speedtest executable.<br/>Linux machines may leave this blank and it defaults to `/usr/bin/speedtest`. | |
|
||||
| `serverID` | Optional: A specific server that shall be used for testing. You can pick the server ID from the "Thing Properties".<br/>If this is left blank the best option will be selected by Ookla. | |
|
||||
|
||||
The `refreshInterval` parameter can also be set to `0` which means "Do not test automatically".
|
||||
This can be used if you want to use the "Trigger Test" channel in order to test via rules, or an item instead.
|
||||
|
||||
Ensure that the user that openHAB is running with, has the permissions to access and execute the executable.
|
||||
|
||||
## Properties
|
||||
|
||||
| Property | Description |
|
||||
|---------------------|------------------------------------------------------------------------------------------------------------|
|
||||
| Server List 1...10 | A List of Ookla Speedtest servers that can be used in order to specify a specific server for the Speedtest.<br/>Configure the Server ID via the `serverID` Thing Configuration Parameter. |
|
||||
|
||||
## Channels
|
||||
|
||||
| Channel | Type | Description |
|
||||
|-----------------------|---------------------------|-------------------------------------------------------------------|
|
||||
| `server` | `String` | The remote server that the Speedtest was run against |
|
||||
| `pingJitter` | `Number:Time` | Ping Jitter - the variation in the response time |
|
||||
| `pingLatency` | `Number:Time` | Ping Latency - the reaction time of your internet connection |
|
||||
| `downloadBandwidth` | `Number:DataTransferRate` | Download bandwidth, e.g. in Mbit/s |
|
||||
| `downloadBytes` | `Number:DataAmount` | Amount of data that was used for the last download bandwidth test |
|
||||
| `downloadElapsed` | `Number:Time` | Time spent for the last download bandwidth test |
|
||||
| `uploadBandwidth` | `Number:DataTransferRate` | Upload bandwidth, e.g. in Mbit/s |
|
||||
| `uploadBytes` | `Number:DataAmount` | Amount of data that was used for the last upload bandwidth test |
|
||||
| `uploadElapsed` | `Number:Time` | Time spent for the last upload bandwidth test |
|
||||
| `isp` | `String` | Your Internet Service Provider (ISP) as calculated by Ookla |
|
||||
| `interfaceInternalIp` | `String` | IP address of the internal interface that was used for the test |
|
||||
| `interfaceExternalIp` | `String` | IP address of the external interface that was used for the test |
|
||||
| `resultUrl` | `String` | The URL to the Speedtest results in HTML on the Ookla webserver |
|
||||
| `triggerTest` | `Switch` | Trigger in order to run Speedtest manually |
|
||||
|
||||
## Full Example
|
||||
|
||||
### Thing File
|
||||
|
||||
```java
|
||||
Thing speedtest:speedtest:myspeedtest "Ookla Speedtest" [ execPath="/usr/bin/speedtest", refreshInterval=60 ]
|
||||
```
|
||||
|
||||
### Item File
|
||||
|
||||
```java
|
||||
String Speedtest_Server "Server" { channel="speedtest:speedtest:myspeedtest:server" }
|
||||
Number:Time Speedtest_Ping_Jitter "Ping Jitter" { channel="speedtest:speedtest:myspeedtest:pingJitter" }
|
||||
Number:Time Speedtest_Ping_Latency "Ping Latency" { channel="speedtest:speedtest:myspeedtest:pingLatency" }
|
||||
Number:DataTransferRate Speedtest_Download_Bandwith "Download Bandwith" { channel="speedtest:speedtest:myspeedtest:downloadBandwidth" }
|
||||
Number:DataAmount Speedtest_Download_Bytes "Download Bytes" { channel="speedtest:speedtest:myspeedtest:downloadBytes" }
|
||||
Number:Time Speedtest_Download_Elapsed "Download Elapsed" { channel="speedtest:speedtest:myspeedtest:downloadElapsed" }
|
||||
Number:DataTransferRate Speedtest_Upload_Bandwith "Upload Bandwith" { channel="speedtest:speedtest:myspeedtest:uploadBandwidth" }
|
||||
Number:DataAmount Speedtest_Upload_Bytes "Upload Bytes" { channel="speedtest:speedtest:myspeedtest:uploadBytes" }
|
||||
Number:Time Speedtest_Upload_Elapsed "Upload Elapsed" { channel="speedtest:speedtest:myspeedtest:uploadElapsed" }
|
||||
String Speedtest_ISP "ISP" { channel="speedtest:speedtest:myspeedtest:isp" }
|
||||
String Speedtest_Interface_InternalIP "Internal IP Address" { channel="speedtest:speedtest:myspeedtest:interfaceInternalIp" }
|
||||
String Speedtest_Interface_ExternalIP "External IP Address" { channel="speedtest:speedtest:myspeedtest:interfaceExternalIp" }
|
||||
String Speedtest_ResultURL "Result URL" { channel="speedtest:speedtest:myspeedtest:resultUrl" }
|
||||
Switch Speedtest_TriggerTest "Trigger Test" { channel="speedtest:speedtest:myspeedtest:triggerTest" }
|
||||
```
|
17
bundles/org.openhab.binding.speedtest/pom.xml
Normal file
17
bundles/org.openhab.binding.speedtest/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 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.speedtest</artifactId>
|
||||
|
||||
<name>openHAB Add-ons :: Bundles :: Speedtest Binding</name>
|
||||
|
||||
</project>
|
@ -0,0 +1,9 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<features name="org.openhab.binding.speedtest-${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-speedtest" description="Speedtest Binding" version="${project.version}">
|
||||
<feature>openhab-runtime-base</feature>
|
||||
<bundle start-level="80">mvn:org.openhab.addons.bundles/org.openhab.binding.speedtest/${project.version}</bundle>
|
||||
</feature>
|
||||
</features>
|
@ -0,0 +1,71 @@
|
||||
/**
|
||||
* 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.speedtest.internal;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.openhab.core.thing.ThingTypeUID;
|
||||
|
||||
/**
|
||||
* The {@link SpeedtestBindingConstants} class defines common constants, which are
|
||||
* used across the whole binding.
|
||||
*
|
||||
* @author Brian Homeyer - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class SpeedtestBindingConstants {
|
||||
|
||||
private static final String BINDING_ID = "speedtest";
|
||||
|
||||
// List of all Thing Type UIDs
|
||||
public static final ThingTypeUID THING_TYPE_SPEEDTEST = new ThingTypeUID(BINDING_ID, "speedtest");
|
||||
|
||||
// Config
|
||||
public static final String REFRESH_INTERVAL = "refreshInterval";
|
||||
public static final String EXEC_PATH = "execPath";
|
||||
public static final String SPEEDTEST_VERSION = "speedTestVersion";
|
||||
public static final String SERVER_ID = "serverID";
|
||||
|
||||
// Channels
|
||||
public static final String SERVER = "server";
|
||||
public static final String PING_JITTER = "pingJitter";
|
||||
public static final String PING_LATENCY = "pingLatency";
|
||||
public static final String DOWNLOAD_BANDWIDTH = "downloadBandwidth";
|
||||
public static final String DOWNLOAD_BYTES = "downloadBytes";
|
||||
public static final String DOWNLOAD_ELAPSED = "downloadElapsed";
|
||||
public static final String UPLOAD_BANDWIDTH = "uploadBandwidth";
|
||||
public static final String UPLOAD_BYTES = "uploadBytes";
|
||||
public static final String UPLOAD_ELAPSED = "uploadElapsed";
|
||||
public static final String ISP = "isp";
|
||||
public static final String INTERFACE_INTERNALIP = "interfaceInternalIp";
|
||||
public static final String INTERFACE_EXTERNALIP = "interfaceExternalIp";
|
||||
public static final String RESULT_URL = "resultUrl";
|
||||
public static final String TRIGGER_TEST = "triggerTest";
|
||||
|
||||
public static final String PROPERTY_SERVER_LIST1 = "Server List 1";
|
||||
public static final String PROPERTY_SERVER_LIST2 = "Server List 2";
|
||||
public static final String PROPERTY_SERVER_LIST3 = "Server List 3";
|
||||
public static final String PROPERTY_SERVER_LIST4 = "Server List 4";
|
||||
public static final String PROPERTY_SERVER_LIST5 = "Server List 5";
|
||||
public static final String PROPERTY_SERVER_LIST6 = "Server List 6";
|
||||
public static final String PROPERTY_SERVER_LIST7 = "Server List 7";
|
||||
public static final String PROPERTY_SERVER_LIST8 = "Server List 8";
|
||||
public static final String PROPERTY_SERVER_LIST9 = "Server List 9";
|
||||
public static final String PROPERTY_SERVER_LIST10 = "Server List 10";
|
||||
|
||||
public static final Set<String> SUPPORTED_CHANNEL_IDS = Set.of(SERVER, PING_JITTER, PING_LATENCY,
|
||||
DOWNLOAD_BANDWIDTH, DOWNLOAD_BYTES, DOWNLOAD_ELAPSED, UPLOAD_BANDWIDTH, UPLOAD_BYTES, UPLOAD_ELAPSED, ISP,
|
||||
INTERFACE_INTERNALIP, INTERFACE_EXTERNALIP, RESULT_URL);
|
||||
}
|
@ -0,0 +1,29 @@
|
||||
/**
|
||||
* 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.speedtest.internal;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
|
||||
/**
|
||||
* The {@link SpeedtestConfiguration} class contains fields mapping thing configuration parameters.
|
||||
*
|
||||
* @author Brian Homeyer - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class SpeedtestConfiguration {
|
||||
public int refreshInterval = 60;
|
||||
public String execPath = "";
|
||||
public String serverID = "";
|
||||
public String speedTestVersion = "";
|
||||
}
|
@ -0,0 +1,539 @@
|
||||
/**
|
||||
* 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.speedtest.internal;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStreamReader;
|
||||
import java.util.Arrays;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ScheduledFuture;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.regex.PatternSyntaxException;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
import org.openhab.binding.speedtest.internal.dto.ResultContainer;
|
||||
import org.openhab.binding.speedtest.internal.dto.ResultsContainerServerList;
|
||||
import org.openhab.core.library.types.OnOffType;
|
||||
import org.openhab.core.library.types.QuantityType;
|
||||
import org.openhab.core.library.types.StringType;
|
||||
import org.openhab.core.library.unit.Units;
|
||||
import org.openhab.core.thing.ChannelUID;
|
||||
import org.openhab.core.thing.Thing;
|
||||
import org.openhab.core.thing.ThingStatus;
|
||||
import org.openhab.core.thing.ThingStatusDetail;
|
||||
import org.openhab.core.thing.binding.BaseThingHandler;
|
||||
import org.openhab.core.types.Command;
|
||||
import org.openhab.core.types.RefreshType;
|
||||
import org.openhab.core.types.State;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
|
||||
/**
|
||||
* The {@link SpeedtestHandler} is responsible for handling commands, which are
|
||||
* sent to one of the channels.
|
||||
*
|
||||
* @author Brian Homeyer - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class SpeedtestHandler extends BaseThingHandler {
|
||||
private final Logger logger = LoggerFactory.getLogger(SpeedtestHandler.class);
|
||||
private SpeedtestConfiguration config = new SpeedtestConfiguration();
|
||||
private Gson gson = new Gson();
|
||||
private static Runtime rt = Runtime.getRuntime();
|
||||
private long pollingInterval = 60;
|
||||
private String serverID = "";
|
||||
|
||||
private @Nullable ScheduledFuture<?> pollingJob;
|
||||
public volatile boolean isRunning = false;
|
||||
|
||||
public static final String[] SHELL_WINDOWS = new String[] { "cmd" };
|
||||
public static final String[] SHELL_NIX = new String[] { "sh", "bash", "zsh", "csh" };
|
||||
|
||||
private String speedTestCommand = "";
|
||||
private static volatile OS os = OS.NOT_SET;
|
||||
private static final Object LOCK = new Object();
|
||||
|
||||
private String pingJitter = "";
|
||||
private String pingLatency = "";
|
||||
private String downloadBandwidth = "";
|
||||
private String downloadBytes = "";
|
||||
private String downloadElapsed = "";
|
||||
private String uploadBandwidth = "";
|
||||
private String uploadBytes = "";
|
||||
private String uploadElapsed = "";
|
||||
private String isp = "";
|
||||
private String interfaceInternalIp = "";
|
||||
private String interfaceExternalIp = "";
|
||||
private String resultUrl = "";
|
||||
private String server = "";
|
||||
|
||||
/**
|
||||
* Contains information about which operating system openHAB is running on.
|
||||
*/
|
||||
public enum OS {
|
||||
WINDOWS,
|
||||
LINUX,
|
||||
MAC,
|
||||
SOLARIS,
|
||||
UNKNOWN,
|
||||
NOT_SET
|
||||
}
|
||||
|
||||
public SpeedtestHandler(Thing thing) {
|
||||
super(thing);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleCommand(ChannelUID channelUID, Command command) {
|
||||
logger.debug("handleCommand channel: {} command: {}", channelUID, command);
|
||||
String ch = channelUID.getId();
|
||||
if (command instanceof RefreshType) {
|
||||
if (!server.isBlank()) {
|
||||
updateChannels();
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (ch.equals(SpeedtestBindingConstants.TRIGGER_TEST)) {
|
||||
if (command instanceof OnOffType) {
|
||||
if (command == OnOffType.ON) {
|
||||
getSpeed();
|
||||
updateState(channelUID, OnOffType.OFF);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initialize() {
|
||||
config = getConfigAs(SpeedtestConfiguration.class);
|
||||
pollingInterval = config.refreshInterval;
|
||||
serverID = config.serverID;
|
||||
if (!config.execPath.isEmpty()) {
|
||||
speedTestCommand = config.execPath;
|
||||
} else {
|
||||
switch (getOperatingSystemType()) {
|
||||
case WINDOWS:
|
||||
speedTestCommand = "";
|
||||
break;
|
||||
case LINUX:
|
||||
case MAC:
|
||||
case SOLARIS:
|
||||
speedTestCommand = "/usr/bin/speedtest";
|
||||
break;
|
||||
default:
|
||||
speedTestCommand = "";
|
||||
}
|
||||
}
|
||||
|
||||
updateStatus(ThingStatus.UNKNOWN);
|
||||
|
||||
if (!checkConfig(speedTestCommand)) { // check the config
|
||||
return;
|
||||
}
|
||||
if (!getSpeedTestVersion()) {
|
||||
return;
|
||||
}
|
||||
getServerList();
|
||||
updateStatus(ThingStatus.ONLINE);
|
||||
isRunning = true;
|
||||
onUpdate(); // Setup the scheduler
|
||||
}
|
||||
|
||||
/**
|
||||
* This is called to start the refresh job and also to reset that refresh job when a config change is done.
|
||||
*/
|
||||
private void onUpdate() {
|
||||
logger.debug("Polling Interval Set: {}", pollingInterval);
|
||||
if (pollingInterval > 0) {
|
||||
ScheduledFuture<?> pollingJob = this.pollingJob;
|
||||
if (pollingJob == null || pollingJob.isCancelled()) {
|
||||
this.pollingJob = scheduler.scheduleWithFixedDelay(pollingRunnable, 0, pollingInterval,
|
||||
TimeUnit.MINUTES);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dispose() {
|
||||
logger.debug("Disposing Speedtest Handler Thing");
|
||||
isRunning = false;
|
||||
ScheduledFuture<?> pollingJob = this.pollingJob;
|
||||
if (pollingJob != null) {
|
||||
pollingJob.cancel(true);
|
||||
this.pollingJob = null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when this thing gets it's configuration changed.
|
||||
*/
|
||||
@Override
|
||||
public void thingUpdated(Thing thing) {
|
||||
dispose();
|
||||
this.thing = thing;
|
||||
initialize();
|
||||
}
|
||||
|
||||
/**
|
||||
* Polling event used to get speed data from speedtest
|
||||
*/
|
||||
private Runnable pollingRunnable = () -> {
|
||||
try {
|
||||
getSpeed();
|
||||
} catch (Exception e) {
|
||||
logger.warn("An exception occurred while running Speedtest: '{}'", e.getMessage());
|
||||
updateStatus(ThingStatus.OFFLINE);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Gets the version information from speedtest, this is really for debug in the event they change things
|
||||
*/
|
||||
private boolean getSpeedTestVersion() {
|
||||
String versionString = doExecuteRequest(" -V", String.class);
|
||||
if ((versionString != null) && !versionString.isEmpty()) {
|
||||
int newLI = versionString.indexOf(System.lineSeparator());
|
||||
String versionLine = versionString.substring(0, newLI);
|
||||
if (versionString.indexOf("Speedtest by Ookla") > -1) {
|
||||
logger.debug("Speedtest Version: {}", versionLine);
|
||||
return true;
|
||||
} else {
|
||||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR,
|
||||
"@text/offline.configuration-error.type");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the server list from the speedtest command. Update the properties of the thing so the user
|
||||
* can see the list of servers closest to them.
|
||||
*/
|
||||
private boolean getServerList() {
|
||||
String serverListTxt = "";
|
||||
ResultsContainerServerList tmpCont = doExecuteRequest(" -f json -L", ResultsContainerServerList.class);
|
||||
if (tmpCont != null) {
|
||||
int id = 1;
|
||||
Map<String, String> properties = editProperties();
|
||||
for (ResultsContainerServerList.Server server : tmpCont.servers) {
|
||||
serverListTxt = "ID: " + server.id.toString() + ", " + server.host + " (" + server.location + ")";
|
||||
switch (id) {
|
||||
case 1:
|
||||
properties.replace(SpeedtestBindingConstants.PROPERTY_SERVER_LIST1, serverListTxt);
|
||||
break;
|
||||
case 2:
|
||||
properties.replace(SpeedtestBindingConstants.PROPERTY_SERVER_LIST2, serverListTxt);
|
||||
break;
|
||||
case 3:
|
||||
properties.replace(SpeedtestBindingConstants.PROPERTY_SERVER_LIST3, serverListTxt);
|
||||
break;
|
||||
case 4:
|
||||
properties.replace(SpeedtestBindingConstants.PROPERTY_SERVER_LIST4, serverListTxt);
|
||||
break;
|
||||
case 5:
|
||||
properties.replace(SpeedtestBindingConstants.PROPERTY_SERVER_LIST5, serverListTxt);
|
||||
break;
|
||||
case 6:
|
||||
properties.replace(SpeedtestBindingConstants.PROPERTY_SERVER_LIST6, serverListTxt);
|
||||
break;
|
||||
case 7:
|
||||
properties.replace(SpeedtestBindingConstants.PROPERTY_SERVER_LIST7, serverListTxt);
|
||||
break;
|
||||
case 8:
|
||||
properties.replace(SpeedtestBindingConstants.PROPERTY_SERVER_LIST8, serverListTxt);
|
||||
break;
|
||||
case 9:
|
||||
properties.replace(SpeedtestBindingConstants.PROPERTY_SERVER_LIST9, serverListTxt);
|
||||
break;
|
||||
case 10:
|
||||
properties.replace(SpeedtestBindingConstants.PROPERTY_SERVER_LIST10, serverListTxt);
|
||||
break;
|
||||
}
|
||||
id++;
|
||||
}
|
||||
updateProperties(properties);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the speedtest data and convert it from JSON and send it to update the channels.
|
||||
*/
|
||||
private void getSpeed() {
|
||||
logger.debug("Getting Speed Measurement");
|
||||
String postCommand = "";
|
||||
if (!serverID.isBlank()) {
|
||||
postCommand = " -s " + serverID;
|
||||
}
|
||||
ResultContainer tmpCont = doExecuteRequest(" -f json --accept-license --accept-gdpr" + postCommand,
|
||||
ResultContainer.class);
|
||||
if (tmpCont != null) {
|
||||
if (tmpCont.getType().equals("result")) {
|
||||
pingJitter = tmpCont.getPing().getJitter();
|
||||
pingLatency = tmpCont.getPing().getLatency();
|
||||
downloadBandwidth = tmpCont.getDownload().getBandwidth();
|
||||
downloadBytes = tmpCont.getDownload().getBytes();
|
||||
downloadElapsed = tmpCont.getDownload().getElapsed();
|
||||
uploadBandwidth = tmpCont.getUpload().getBandwidth();
|
||||
uploadBytes = tmpCont.getUpload().getBytes();
|
||||
uploadElapsed = tmpCont.getUpload().getElapsed();
|
||||
isp = tmpCont.getIsp();
|
||||
interfaceInternalIp = tmpCont.getInterface().getInternalIp();
|
||||
interfaceExternalIp = tmpCont.getInterface().getExternalIp();
|
||||
resultUrl = tmpCont.getResult().getUrl();
|
||||
server = tmpCont.getServer().getName() + " (" + tmpCont.getServer().getId().toString() + ") "
|
||||
+ tmpCont.getServer().getLocation();
|
||||
updateChannels();
|
||||
}
|
||||
} else {
|
||||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR,
|
||||
"@text/offline.configuration-error.results");
|
||||
}
|
||||
}
|
||||
|
||||
private @Nullable <T> T doExecuteRequest(String arguments, Class<T> type) {
|
||||
try {
|
||||
String dataOut = executeCmd(speedTestCommand + arguments);
|
||||
if (type != String.class) {
|
||||
@Nullable
|
||||
T obj = gson.fromJson(dataOut, type);
|
||||
return obj;
|
||||
} else {
|
||||
@SuppressWarnings("unchecked")
|
||||
T obj = (T) dataOut;
|
||||
return obj;
|
||||
}
|
||||
} catch (Exception e) {
|
||||
logger.debug("Exception: {}", e.getMessage());
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the channels
|
||||
*/
|
||||
private void updateChannels() {
|
||||
logger.debug("Updating channels");
|
||||
|
||||
State newState = new QuantityType<>(Double.parseDouble(pingJitter) / 1000.0, Units.SECOND);
|
||||
logger.debug("pingJitter: {}", newState);
|
||||
updateState(new ChannelUID(getThing().getUID(), SpeedtestBindingConstants.PING_JITTER), newState);
|
||||
|
||||
newState = new QuantityType<>(Double.parseDouble(pingLatency) / 1000.0, Units.SECOND);
|
||||
logger.debug("pingLatency: {}", newState);
|
||||
updateState(new ChannelUID(getThing().getUID(), SpeedtestBindingConstants.PING_LATENCY), newState);
|
||||
|
||||
newState = new QuantityType<>(Double.parseDouble(downloadBandwidth) / 125000.0, Units.MEGABIT_PER_SECOND);
|
||||
logger.debug("downloadBandwidth: {}", newState);
|
||||
updateState(new ChannelUID(getThing().getUID(), SpeedtestBindingConstants.DOWNLOAD_BANDWIDTH), newState);
|
||||
|
||||
newState = new QuantityType<>(Double.parseDouble(downloadBytes), Units.BYTE);
|
||||
logger.debug("downloadBytes: {}", newState);
|
||||
updateState(new ChannelUID(getThing().getUID(), SpeedtestBindingConstants.DOWNLOAD_BYTES), newState);
|
||||
|
||||
newState = new QuantityType<>(Double.parseDouble(downloadElapsed) / 1000.0, Units.SECOND);
|
||||
logger.debug("downloadElapsed: {}", newState);
|
||||
updateState(new ChannelUID(getThing().getUID(), SpeedtestBindingConstants.DOWNLOAD_ELAPSED), newState);
|
||||
|
||||
newState = new QuantityType<>(Double.parseDouble(uploadBandwidth) / 125000.0, Units.MEGABIT_PER_SECOND);
|
||||
logger.debug("uploadBandwidth: {}", newState);
|
||||
updateState(new ChannelUID(getThing().getUID(), SpeedtestBindingConstants.UPLOAD_BANDWIDTH), newState);
|
||||
|
||||
newState = new QuantityType<>(Double.parseDouble(uploadBytes), Units.BYTE);
|
||||
logger.debug("uploadBytes: {}", newState);
|
||||
updateState(new ChannelUID(getThing().getUID(), SpeedtestBindingConstants.UPLOAD_BYTES), newState);
|
||||
|
||||
newState = new QuantityType<>(Double.parseDouble(uploadElapsed) / 1000.0, Units.SECOND);
|
||||
logger.debug("uploadElapsed: {}", newState);
|
||||
updateState(new ChannelUID(getThing().getUID(), SpeedtestBindingConstants.UPLOAD_ELAPSED), newState);
|
||||
|
||||
updateState(new ChannelUID(getThing().getUID(), SpeedtestBindingConstants.INTERFACE_EXTERNALIP),
|
||||
new StringType(String.valueOf(interfaceExternalIp)));
|
||||
updateState(new ChannelUID(getThing().getUID(), SpeedtestBindingConstants.INTERFACE_INTERNALIP),
|
||||
new StringType(String.valueOf(interfaceInternalIp)));
|
||||
updateState(new ChannelUID(getThing().getUID(), SpeedtestBindingConstants.ISP),
|
||||
new StringType(String.valueOf(isp)));
|
||||
updateState(new ChannelUID(getThing().getUID(), SpeedtestBindingConstants.RESULT_URL),
|
||||
new StringType(String.valueOf(resultUrl)));
|
||||
updateState(new ChannelUID(getThing().getUID(), SpeedtestBindingConstants.SERVER),
|
||||
new StringType(String.valueOf(server)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks to make sure the executable for speedtest is valid
|
||||
*/
|
||||
public boolean checkConfig(String execPath) {
|
||||
File file = new File(execPath);
|
||||
if (!checkFileExists(file)) { // Check if entered path exists
|
||||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR,
|
||||
"@text/offline.configuration-error.file");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!checkFileExecutable(file)) { // Check if speedtest is executable
|
||||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR,
|
||||
"@text/offline.configuration-error.exec");
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes a given command and returns back the String data of stdout.
|
||||
*/
|
||||
private String executeCmd(String commandLine) {
|
||||
int timeOut = 60000;
|
||||
String[] cmdArray;
|
||||
String[] shell;
|
||||
logger.debug("Passing to shell for parsing command.");
|
||||
switch (getOperatingSystemType()) {
|
||||
case WINDOWS:
|
||||
shell = SHELL_WINDOWS;
|
||||
logger.debug("OS: WINDOWS ({})", getOperatingSystemName());
|
||||
cmdArray = createCmdArray(shell, "/c", commandLine);
|
||||
break;
|
||||
case LINUX:
|
||||
case MAC:
|
||||
case SOLARIS:
|
||||
// assume sh is present, should all be POSIX-compliant
|
||||
shell = SHELL_NIX;
|
||||
logger.debug("OS: *NIX ({})", getOperatingSystemName());
|
||||
cmdArray = createCmdArray(shell, "-c", commandLine);
|
||||
break;
|
||||
default:
|
||||
logger.debug("OS: Unknown ({})", getOperatingSystemName());
|
||||
return "";
|
||||
}
|
||||
|
||||
if (cmdArray.length == 0) {
|
||||
logger.debug("Empty command received, not executing");
|
||||
return "";
|
||||
}
|
||||
|
||||
logger.debug("The command to be executed will be '{}'", Arrays.asList(cmdArray));
|
||||
|
||||
Process proc;
|
||||
try {
|
||||
proc = rt.exec(cmdArray);
|
||||
} catch (Exception e) {
|
||||
logger.debug("An exception occurred while executing '{}': '{}'", Arrays.asList(cmdArray), e.getMessage());
|
||||
return "";
|
||||
}
|
||||
|
||||
StringBuilder outputBuilder = new StringBuilder();
|
||||
try (InputStreamReader isr = new InputStreamReader(proc.getInputStream());
|
||||
BufferedReader br = new BufferedReader(isr)) {
|
||||
String line;
|
||||
while ((line = br.readLine()) != null) {
|
||||
outputBuilder.append(line).append(System.lineSeparator());
|
||||
logger.debug("Exec [{}]: '{}'", "OUTPUT", line);
|
||||
}
|
||||
} catch (IOException e) {
|
||||
logger.warn("An exception occurred while reading the stdout when executing '{}': '{}'", commandLine,
|
||||
e.getMessage());
|
||||
}
|
||||
|
||||
boolean exitVal = false;
|
||||
try {
|
||||
exitVal = proc.waitFor(timeOut, TimeUnit.MILLISECONDS);
|
||||
} catch (InterruptedException e) {
|
||||
logger.debug("An exception occurred while waiting for the process ('{}') to finish: '{}'", commandLine,
|
||||
e.getMessage());
|
||||
}
|
||||
|
||||
if (!exitVal) {
|
||||
logger.debug("Forcibly termininating the process ('{}') after a timeout of {} ms", commandLine, timeOut);
|
||||
proc.destroyForcibly();
|
||||
}
|
||||
return outputBuilder.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Transforms the command string into an array.
|
||||
* Either invokes the shell and passes using the "c" option
|
||||
* or (if command already starts with one of the shells) splits by space.
|
||||
*
|
||||
* @param shell (path), picks to first one to execute the command
|
||||
* @param cOption "c"-option string
|
||||
* @param commandLine to execute
|
||||
* @return command array
|
||||
*/
|
||||
protected String[] createCmdArray(String[] shell, String cOption, String commandLine) {
|
||||
boolean startsWithShell = false;
|
||||
for (String sh : shell) {
|
||||
if (commandLine.startsWith(sh + " ")) {
|
||||
startsWithShell = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!startsWithShell) {
|
||||
return new String[] { shell[0], cOption, commandLine };
|
||||
} else {
|
||||
logger.debug("Splitting by spaces");
|
||||
try {
|
||||
String[] splitCmd = commandLine.split(" ");
|
||||
return splitCmd;
|
||||
} catch (PatternSyntaxException e) {
|
||||
logger.warn("An exception occurred while splitting '{}': '{}'", commandLine, e.getMessage());
|
||||
return new String[] {};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static OS getOperatingSystemType() {
|
||||
synchronized (LOCK) {
|
||||
if (os == OS.NOT_SET) {
|
||||
String operSys = System.getProperty("os.name");
|
||||
if (operSys == null) {
|
||||
os = OS.UNKNOWN;
|
||||
} else {
|
||||
operSys = operSys.toLowerCase();
|
||||
|
||||
if (operSys.contains("win")) {
|
||||
os = OS.WINDOWS;
|
||||
} else if (operSys.contains("nix") || operSys.contains("nux") || operSys.contains("aix")) {
|
||||
os = OS.LINUX;
|
||||
} else if (operSys.contains("mac")) {
|
||||
os = OS.MAC;
|
||||
} else if (operSys.contains("sunos")) {
|
||||
os = OS.SOLARIS;
|
||||
} else {
|
||||
os = OS.UNKNOWN;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return os;
|
||||
}
|
||||
|
||||
public static String getOperatingSystemName() {
|
||||
String osname = System.getProperty("os.name");
|
||||
return osname != null ? osname : "unknown";
|
||||
}
|
||||
|
||||
public boolean checkFileExists(File file) {
|
||||
return file.exists() && !file.isDirectory();
|
||||
}
|
||||
|
||||
public boolean checkFileExecutable(File file) {
|
||||
return file.canExecute();
|
||||
}
|
||||
}
|
@ -0,0 +1,56 @@
|
||||
/**
|
||||
* 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.speedtest.internal;
|
||||
|
||||
import static org.openhab.binding.speedtest.internal.SpeedtestBindingConstants.*;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
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.Component;
|
||||
|
||||
/**
|
||||
* The {@link SpeedtestHandlerFactory} is responsible for creating things and thing
|
||||
* handlers.
|
||||
*
|
||||
* @author Brian Homeyer - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
@Component(configurationPid = "binding.speedtest", service = ThingHandlerFactory.class)
|
||||
public class SpeedtestHandlerFactory extends BaseThingHandlerFactory {
|
||||
|
||||
private static final Set<ThingTypeUID> SUPPORTED_THING_TYPES_UIDS = Set.of(THING_TYPE_SPEEDTEST);
|
||||
|
||||
@Override
|
||||
public boolean supportsThingType(ThingTypeUID thingTypeUID) {
|
||||
return SUPPORTED_THING_TYPES_UIDS.contains(thingTypeUID);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected @Nullable ThingHandler createHandler(Thing thing) {
|
||||
ThingTypeUID thingTypeUID = thing.getThingTypeUID();
|
||||
|
||||
if (THING_TYPE_SPEEDTEST.equals(thingTypeUID)) {
|
||||
return new SpeedtestHandler(thing);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
@ -0,0 +1,402 @@
|
||||
/**
|
||||
* 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.speedtest.internal.dto;
|
||||
|
||||
import com.google.gson.annotations.Expose;
|
||||
import com.google.gson.annotations.SerializedName;
|
||||
|
||||
/**
|
||||
* The {@link ResultContainer} class defines a container for Speedtest results.
|
||||
*
|
||||
* @author Brian Homeyer - Initial contribution
|
||||
*/
|
||||
public class ResultContainer {
|
||||
|
||||
@SerializedName("type")
|
||||
@Expose
|
||||
private String type;
|
||||
@SerializedName("timestamp")
|
||||
@Expose
|
||||
private String timestamp;
|
||||
@SerializedName("ping")
|
||||
@Expose
|
||||
private Ping ping;
|
||||
@SerializedName("download")
|
||||
@Expose
|
||||
private Download download;
|
||||
@SerializedName("upload")
|
||||
@Expose
|
||||
private Upload upload;
|
||||
@SerializedName("packetLoss")
|
||||
@Expose
|
||||
private Double packetLoss;
|
||||
@SerializedName("isp")
|
||||
@Expose
|
||||
private String isp;
|
||||
@SerializedName("interface")
|
||||
@Expose
|
||||
private Interface networkInterface;
|
||||
@SerializedName("server")
|
||||
@Expose
|
||||
private Server server;
|
||||
@SerializedName("result")
|
||||
@Expose
|
||||
private Result result;
|
||||
|
||||
public String getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
public void setType(String type) {
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
public String getTimestamp() {
|
||||
return timestamp;
|
||||
}
|
||||
|
||||
public void setTimestamp(String timestamp) {
|
||||
this.timestamp = timestamp;
|
||||
}
|
||||
|
||||
public Ping getPing() {
|
||||
return ping;
|
||||
}
|
||||
|
||||
public void setPing(Ping ping) {
|
||||
this.ping = ping;
|
||||
}
|
||||
|
||||
public Download getDownload() {
|
||||
return download;
|
||||
}
|
||||
|
||||
public void setDownload(Download download) {
|
||||
this.download = download;
|
||||
}
|
||||
|
||||
public Upload getUpload() {
|
||||
return upload;
|
||||
}
|
||||
|
||||
public void setUpload(Upload upload) {
|
||||
this.upload = upload;
|
||||
}
|
||||
|
||||
public Double getPacketLoss() {
|
||||
return packetLoss;
|
||||
}
|
||||
|
||||
public void setPacketLoss(Double packetLoss) {
|
||||
this.packetLoss = packetLoss;
|
||||
}
|
||||
|
||||
public String getIsp() {
|
||||
return isp;
|
||||
}
|
||||
|
||||
public void setIsp(String isp) {
|
||||
this.isp = isp;
|
||||
}
|
||||
|
||||
public Interface getInterface() {
|
||||
return networkInterface;
|
||||
}
|
||||
|
||||
public void setInterface(Interface networkInterface) {
|
||||
this.networkInterface = networkInterface;
|
||||
}
|
||||
|
||||
public Server getServer() {
|
||||
return server;
|
||||
}
|
||||
|
||||
public void setServer(Server server) {
|
||||
this.server = server;
|
||||
}
|
||||
|
||||
public Result getResult() {
|
||||
return result;
|
||||
}
|
||||
|
||||
public void setResult(Result result) {
|
||||
this.result = result;
|
||||
}
|
||||
|
||||
public class Download {
|
||||
|
||||
@SerializedName("bandwidth")
|
||||
@Expose
|
||||
private String bandwidth;
|
||||
@SerializedName("bytes")
|
||||
@Expose
|
||||
private String bytes;
|
||||
@SerializedName("elapsed")
|
||||
@Expose
|
||||
private String elapsed;
|
||||
|
||||
public String getBandwidth() {
|
||||
return bandwidth;
|
||||
}
|
||||
|
||||
public void setBandwidth(String bandwidth) {
|
||||
this.bandwidth = bandwidth;
|
||||
}
|
||||
|
||||
public String getBytes() {
|
||||
return bytes;
|
||||
}
|
||||
|
||||
public void setBytes(String bytes) {
|
||||
this.bytes = bytes;
|
||||
}
|
||||
|
||||
public String getElapsed() {
|
||||
return elapsed;
|
||||
}
|
||||
|
||||
public void setElapsed(String elapsed) {
|
||||
this.elapsed = elapsed;
|
||||
}
|
||||
}
|
||||
|
||||
public class Interface {
|
||||
|
||||
@SerializedName("internalIp")
|
||||
@Expose
|
||||
private String internalIp;
|
||||
@SerializedName("name")
|
||||
@Expose
|
||||
private String name;
|
||||
@SerializedName("macAddr")
|
||||
@Expose
|
||||
private String macAddr;
|
||||
@SerializedName("isVpn")
|
||||
@Expose
|
||||
private Boolean isVpn;
|
||||
@SerializedName("externalIp")
|
||||
@Expose
|
||||
private String externalIp;
|
||||
|
||||
public String getInternalIp() {
|
||||
return internalIp;
|
||||
}
|
||||
|
||||
public void setInternalIp(String internalIp) {
|
||||
this.internalIp = internalIp;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public String getMacAddr() {
|
||||
return macAddr;
|
||||
}
|
||||
|
||||
public void setMacAddr(String macAddr) {
|
||||
this.macAddr = macAddr;
|
||||
}
|
||||
|
||||
public Boolean getIsVpn() {
|
||||
return isVpn;
|
||||
}
|
||||
|
||||
public void setIsVpn(Boolean isVpn) {
|
||||
this.isVpn = isVpn;
|
||||
}
|
||||
|
||||
public String getExternalIp() {
|
||||
return externalIp;
|
||||
}
|
||||
|
||||
public void setExternalIp(String externalIp) {
|
||||
this.externalIp = externalIp;
|
||||
}
|
||||
}
|
||||
|
||||
public class Ping {
|
||||
|
||||
@SerializedName("jitter")
|
||||
@Expose
|
||||
private String jitter;
|
||||
@SerializedName("latency")
|
||||
@Expose
|
||||
private String latency;
|
||||
|
||||
public String getJitter() {
|
||||
return jitter;
|
||||
}
|
||||
|
||||
public void setJitter(String jitter) {
|
||||
this.jitter = jitter;
|
||||
}
|
||||
|
||||
public String getLatency() {
|
||||
return latency;
|
||||
}
|
||||
|
||||
public void setLatency(String latency) {
|
||||
this.latency = latency;
|
||||
}
|
||||
}
|
||||
|
||||
public class Result {
|
||||
|
||||
@SerializedName("id")
|
||||
@Expose
|
||||
private String id;
|
||||
@SerializedName("url")
|
||||
@Expose
|
||||
private String url;
|
||||
|
||||
public String getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(String id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public String getUrl() {
|
||||
return url;
|
||||
}
|
||||
|
||||
public void setUrl(String url) {
|
||||
this.url = url;
|
||||
}
|
||||
}
|
||||
|
||||
public class Server {
|
||||
|
||||
@SerializedName("id")
|
||||
@Expose
|
||||
private Integer id;
|
||||
@SerializedName("name")
|
||||
@Expose
|
||||
private String name;
|
||||
@SerializedName("location")
|
||||
@Expose
|
||||
private String location;
|
||||
@SerializedName("country")
|
||||
@Expose
|
||||
private String country;
|
||||
@SerializedName("host")
|
||||
@Expose
|
||||
private String host;
|
||||
@SerializedName("port")
|
||||
@Expose
|
||||
private Integer port;
|
||||
@SerializedName("ip")
|
||||
@Expose
|
||||
private String ip;
|
||||
|
||||
public Integer getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(Integer id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public String getLocation() {
|
||||
return location;
|
||||
}
|
||||
|
||||
public void setLocation(String location) {
|
||||
this.location = location;
|
||||
}
|
||||
|
||||
public String getCountry() {
|
||||
return country;
|
||||
}
|
||||
|
||||
public void setCountry(String country) {
|
||||
this.country = country;
|
||||
}
|
||||
|
||||
public String getHost() {
|
||||
return host;
|
||||
}
|
||||
|
||||
public void setHost(String host) {
|
||||
this.host = host;
|
||||
}
|
||||
|
||||
public Integer getPort() {
|
||||
return port;
|
||||
}
|
||||
|
||||
public void setPort(Integer port) {
|
||||
this.port = port;
|
||||
}
|
||||
|
||||
public String getIp() {
|
||||
return ip;
|
||||
}
|
||||
|
||||
public void setIp(String ip) {
|
||||
this.ip = ip;
|
||||
}
|
||||
}
|
||||
|
||||
public class Upload {
|
||||
|
||||
@SerializedName("bandwidth")
|
||||
@Expose
|
||||
private String bandwidth;
|
||||
@SerializedName("bytes")
|
||||
@Expose
|
||||
private String bytes;
|
||||
@SerializedName("elapsed")
|
||||
@Expose
|
||||
private String elapsed;
|
||||
|
||||
public String getBandwidth() {
|
||||
return bandwidth;
|
||||
}
|
||||
|
||||
public void setBandwidth(String bandwidth) {
|
||||
this.bandwidth = bandwidth;
|
||||
}
|
||||
|
||||
public String getBytes() {
|
||||
return bytes;
|
||||
}
|
||||
|
||||
public void setBytes(String bytes) {
|
||||
this.bytes = bytes;
|
||||
}
|
||||
|
||||
public String getElapsed() {
|
||||
return elapsed;
|
||||
}
|
||||
|
||||
public void setElapsed(String elapsed) {
|
||||
this.elapsed = elapsed;
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,59 @@
|
||||
/**
|
||||
* 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.speedtest.internal.dto;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import com.google.gson.annotations.Expose;
|
||||
import com.google.gson.annotations.SerializedName;
|
||||
|
||||
/**
|
||||
* The {@link ResultsContainerServerList} class defines a container for the Speedtest server list.
|
||||
*
|
||||
* @author Brian Homeyer - Initial contribution
|
||||
*/
|
||||
|
||||
public class ResultsContainerServerList {
|
||||
@SerializedName("type")
|
||||
@Expose
|
||||
public String type;
|
||||
@SerializedName("timestamp")
|
||||
@Expose
|
||||
public String timestamp;
|
||||
@SerializedName("servers")
|
||||
@Expose
|
||||
public List<Server> servers = null;
|
||||
|
||||
public class Server {
|
||||
|
||||
@SerializedName("id")
|
||||
@Expose
|
||||
public Integer id;
|
||||
@SerializedName("name")
|
||||
@Expose
|
||||
public String name;
|
||||
@SerializedName("location")
|
||||
@Expose
|
||||
public String location;
|
||||
@SerializedName("country")
|
||||
@Expose
|
||||
public String country;
|
||||
@SerializedName("host")
|
||||
@Expose
|
||||
public String host;
|
||||
@SerializedName("port")
|
||||
@Expose
|
||||
public Integer port;
|
||||
}
|
||||
}
|
@ -0,0 +1,10 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<addon:addon id="speedtest" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xmlns:addon="https://openhab.org/schemas/addon/v1.0.0"
|
||||
xsi:schemaLocation="https://openhab.org/schemas/addon/v1.0.0 https://openhab.org/schemas/addon-1.0.0.xsd">
|
||||
|
||||
<type>binding</type>
|
||||
<name>Speedtest Binding</name>
|
||||
<description>Binding for Ookla Speedtest (https://www.speedtest.net/)</description>
|
||||
|
||||
</addon:addon>
|
@ -0,0 +1,64 @@
|
||||
# add-on
|
||||
|
||||
addon.speedtest.name = Speedtest Binding
|
||||
addon.speedtest.description = Binding for Ookla Speedtest (https://www.speedtest.net/)
|
||||
|
||||
# thing types
|
||||
|
||||
thing-type.speedtest.speedtest.label = Speedtest Binding
|
||||
thing-type.speedtest.speedtest.description = Binding for Ookla Speedtest (https://www.speedtest.net/)
|
||||
|
||||
# thing types config
|
||||
|
||||
thing-type.config.speedtest.speedtest.execPath.label = Executable Path
|
||||
thing-type.config.speedtest.speedtest.execPath.description = The path of the Ookla Speedtest executable. Linux machines may leave this blank and it defaults to `/usr/bin/speedtest`.
|
||||
thing-type.config.speedtest.speedtest.group.config-info.label = Speedtest Configuration
|
||||
thing-type.config.speedtest.speedtest.refreshInterval.label = Refresh Interval
|
||||
thing-type.config.speedtest.speedtest.refreshInterval.description = How often to test network speed
|
||||
thing-type.config.speedtest.speedtest.refreshInterval.option.15 = 15 minutes
|
||||
thing-type.config.speedtest.speedtest.refreshInterval.option.30 = 30 minutes
|
||||
thing-type.config.speedtest.speedtest.refreshInterval.option.60 = Every Hour
|
||||
thing-type.config.speedtest.speedtest.refreshInterval.option.360 = Every 6 Hours
|
||||
thing-type.config.speedtest.speedtest.refreshInterval.option.720 = Every 12 Hours
|
||||
thing-type.config.speedtest.speedtest.refreshInterval.option.1440 = Once a Day
|
||||
thing-type.config.speedtest.speedtest.refreshInterval.option.0 = Don't test automatically
|
||||
thing-type.config.speedtest.speedtest.serverID.label = Server ID
|
||||
thing-type.config.speedtest.speedtest.serverID.description = Optional: A specific server that shall be used for testing. You can pick the server ID from the "Thing Properties". If this is left blank the best option will be selected by Ookla.
|
||||
|
||||
# channel types
|
||||
|
||||
channel-type.speedtest.downloadBandwidth.label = Download Bandwidth
|
||||
channel-type.speedtest.downloadBandwidth.description = Download bandwidth, e.g. in Mbit/s
|
||||
channel-type.speedtest.downloadBytes.label = Downloaded Bytes
|
||||
channel-type.speedtest.downloadBytes.description = Amount of data that was used for the last upload bandwidth test
|
||||
channel-type.speedtest.downloadElapsed.label = Download Elapsed Time
|
||||
channel-type.speedtest.downloadElapsed.description = Time spent for the last download bandwidth test
|
||||
channel-type.speedtest.interfaceExternalIp.label = Interface External IP
|
||||
channel-type.speedtest.interfaceExternalIp.description = IP address of the external interface that was used for the test
|
||||
channel-type.speedtest.interfaceInternalIp.label = Interface Internal IP
|
||||
channel-type.speedtest.interfaceInternalIp.description = IP address of the internal interface that was used for the test
|
||||
channel-type.speedtest.isp.label = ISP
|
||||
channel-type.speedtest.isp.description = Your Internet Service Provider (ISP) as calculated by Ookla
|
||||
channel-type.speedtest.pingJitter.label = Ping Jitter
|
||||
channel-type.speedtest.pingJitter.description = Ping Jitter - the variation in the response time
|
||||
channel-type.speedtest.pingLatency.label = Ping Latency
|
||||
channel-type.speedtest.pingLatency.description = Ping Latency - the reaction time of your internet connection
|
||||
channel-type.speedtest.resultUrl.label = Result URL
|
||||
channel-type.speedtest.resultUrl.description = The URL to the Speedtest results in HTML on the Ookla webserver
|
||||
channel-type.speedtest.server.label = Server
|
||||
channel-type.speedtest.server.description = The remote server that the Speedtest was run against
|
||||
channel-type.speedtest.triggerTest.label = Trigger Test
|
||||
channel-type.speedtest.triggerTest.description = Trigger in order to run Speedtest manually
|
||||
channel-type.speedtest.uploadBandwidth.label = Upload Bandwidth
|
||||
channel-type.speedtest.uploadBandwidth.description = Upload bandwidth, e.g. in Mbit/s
|
||||
channel-type.speedtest.uploadBytes.label = Uploaded Bytes
|
||||
channel-type.speedtest.uploadBytes.description = Amount of data that was used for the last upload bandwidth test
|
||||
channel-type.speedtest.uploadElapsed.label = Upload Elapsed Time
|
||||
channel-type.speedtest.uploadElapsed.description = Time spent for the last upload bandwidth test
|
||||
|
||||
# thing status
|
||||
|
||||
offline.configuration-error.type = Unsupported type of Speedtest recognized, Speedtest CLI tool from Ookla is REQUIRED (https://www.speedtest.net/). Please check installation/configuration.
|
||||
offline.configuration-error.results = Speedtest is not returning valid results.
|
||||
offline.configuration-error.file = Speedtest executable not found. Please check configuration.
|
||||
offline.configuration-error.exec = Speedtest CLI tool not executable. Please check installation/configuration.
|
@ -0,0 +1,158 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<thing:thing-descriptions bindingId="speedtest"
|
||||
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">
|
||||
|
||||
<thing-type id="speedtest">
|
||||
<label>Speedtest Binding</label>
|
||||
<description>Binding for Ookla Speedtest (https://www.speedtest.net/)</description>
|
||||
<channels>
|
||||
<channel id="server" typeId="server"/>
|
||||
|
||||
<channel id="pingJitter" typeId="pingJitter"/>
|
||||
<channel id="pingLatency" typeId="pingLatency"/>
|
||||
|
||||
<channel id="downloadBandwidth" typeId="downloadBandwidth"/>
|
||||
<channel id="downloadBytes" typeId="downloadBytes"/>
|
||||
<channel id="downloadElapsed" typeId="downloadElapsed"/>
|
||||
|
||||
<channel id="uploadBandwidth" typeId="uploadBandwidth"/>
|
||||
<channel id="uploadBytes" typeId="uploadBytes"/>
|
||||
<channel id="uploadElapsed" typeId="uploadElapsed"/>
|
||||
|
||||
<channel id="isp" typeId="isp"/>
|
||||
|
||||
<channel id="interfaceInternalIp" typeId="interfaceInternalIp"/>
|
||||
<channel id="interfaceExternalIp" typeId="interfaceExternalIp"/>
|
||||
|
||||
<channel id="resultUrl" typeId="resultUrl"/>
|
||||
|
||||
<channel id="triggerTest" typeId="triggerTest"/>
|
||||
</channels>
|
||||
<properties>
|
||||
<property name="Server List 1"></property>
|
||||
<property name="Server List 2"></property>
|
||||
<property name="Server List 3"></property>
|
||||
<property name="Server List 4"></property>
|
||||
<property name="Server List 5"></property>
|
||||
<property name="Server List 6"></property>
|
||||
<property name="Server List 7"></property>
|
||||
<property name="Server List 8"></property>
|
||||
<property name="Server List 9"></property>
|
||||
<property name="Server List 10"></property>
|
||||
</properties>
|
||||
<config-description>
|
||||
<parameter-group name="config-info">
|
||||
<label>Speedtest Configuration</label>
|
||||
</parameter-group>
|
||||
<parameter name="refreshInterval" type="integer" required="true" groupName="config-info">
|
||||
<label>Refresh Interval</label>
|
||||
<description>How often to test network speed</description>
|
||||
<options>
|
||||
<option value="15">15 minutes</option>
|
||||
<option value="30">30 minutes</option>
|
||||
<option value="60">Every Hour</option>
|
||||
<option value="360">Every 6 Hours</option>
|
||||
<option value="720">Every 12 Hours</option>
|
||||
<option value="1440">Once a Day</option>
|
||||
<option value="0">Don't test automatically</option>
|
||||
</options>
|
||||
<default>60</default>
|
||||
</parameter>
|
||||
<parameter name="execPath" type="text" required="false" groupName="config-info">
|
||||
<label>Executable Path</label>
|
||||
<description>The path of the Ookla Speedtest executable. Linux machines may leave this blank and it defaults to
|
||||
`/usr/bin/speedtest`.</description>
|
||||
</parameter>
|
||||
<parameter name="serverID" type="text" required="false" groupName="config-info">
|
||||
<label>Server ID</label>
|
||||
<description>Optional: A specific server that shall be used for testing. You can pick the server ID from the "Thing
|
||||
Properties". If this is left blank the best option will be selected by Ookla.</description>
|
||||
</parameter>
|
||||
</config-description>
|
||||
</thing-type>
|
||||
<channel-type id="server">
|
||||
<item-type>String</item-type>
|
||||
<label>Server</label>
|
||||
<description>The remote server that the Speedtest was run against</description>
|
||||
<state readOnly="true"></state>
|
||||
</channel-type>
|
||||
<channel-type id="pingJitter" advanced="true">
|
||||
<item-type>Number:Time</item-type>
|
||||
<label>Ping Jitter</label>
|
||||
<description>Ping Jitter - the variation in the response time</description>
|
||||
<state readOnly="true" pattern="%.2f ms"></state>
|
||||
</channel-type>
|
||||
<channel-type id="pingLatency">
|
||||
<item-type>Number:Time</item-type>
|
||||
<label>Ping Latency</label>
|
||||
<description>Ping Latency - the reaction time of your internet connection</description>
|
||||
<state readOnly="true" pattern="%.2f ms"></state>
|
||||
</channel-type>
|
||||
<channel-type id="downloadBandwidth">
|
||||
<item-type>Number:DataTransferRate</item-type>
|
||||
<label>Download Bandwidth</label>
|
||||
<description>Download bandwidth, e.g. in Mbit/s</description>
|
||||
<state readOnly="true" pattern="%.2f Mbit/s"></state>
|
||||
</channel-type>
|
||||
<channel-type id="downloadBytes" advanced="true">
|
||||
<item-type>Number:DataAmount</item-type>
|
||||
<label>Downloaded Bytes</label>
|
||||
<description>Amount of data that was used for the last upload bandwidth test</description>
|
||||
<state readOnly="true" pattern="%.0f MB"></state>
|
||||
</channel-type>
|
||||
<channel-type id="downloadElapsed" advanced="true">
|
||||
<item-type>Number:Time</item-type>
|
||||
<label>Download Elapsed Time</label>
|
||||
<description>Time spent for the last download bandwidth test</description>
|
||||
<state readOnly="true" pattern="%.2f s"></state>
|
||||
</channel-type>
|
||||
<channel-type id="uploadBandwidth">
|
||||
<item-type>Number:DataTransferRate</item-type>
|
||||
<label>Upload Bandwidth</label>
|
||||
<description>Upload bandwidth, e.g. in Mbit/s</description>
|
||||
<state readOnly="true" pattern="%.2f Mbit/s"></state>
|
||||
</channel-type>
|
||||
<channel-type id="uploadBytes" advanced="true">
|
||||
<item-type>Number:DataAmount</item-type>
|
||||
<label>Uploaded Bytes</label>
|
||||
<description>Amount of data that was used for the last upload bandwidth test</description>
|
||||
<state readOnly="true" pattern="%.0f MB"></state>
|
||||
</channel-type>
|
||||
<channel-type id="uploadElapsed" advanced="true">
|
||||
<item-type>Number:Time</item-type>
|
||||
<label>Upload Elapsed Time</label>
|
||||
<description>Time spent for the last upload bandwidth test</description>
|
||||
<state readOnly="true" pattern="%.2f s"></state>
|
||||
</channel-type>
|
||||
<channel-type id="isp">
|
||||
<item-type>String</item-type>
|
||||
<label>ISP</label>
|
||||
<description>Your Internet Service Provider (ISP) as calculated by Ookla</description>
|
||||
<state readOnly="true"></state>
|
||||
</channel-type>
|
||||
<channel-type id="interfaceInternalIp" advanced="true">
|
||||
<item-type>String</item-type>
|
||||
<label>Interface Internal IP</label>
|
||||
<description>IP address of the internal interface that was used for the test</description>
|
||||
<state readOnly="true"></state>
|
||||
</channel-type>
|
||||
<channel-type id="interfaceExternalIp" advanced="true">
|
||||
<item-type>String</item-type>
|
||||
<label>Interface External IP</label>
|
||||
<description>IP address of the external interface that was used for the test</description>
|
||||
<state readOnly="true"></state>
|
||||
</channel-type>
|
||||
<channel-type id="resultUrl">
|
||||
<item-type>String</item-type>
|
||||
<label>Result URL</label>
|
||||
<description>The URL to the Speedtest results in HTML on the Ookla webserver</description>
|
||||
<state readOnly="true"></state>
|
||||
</channel-type>
|
||||
<channel-type id="triggerTest" advanced="true">
|
||||
<item-type>Switch</item-type>
|
||||
<label>Trigger Test</label>
|
||||
<description>Trigger in order to run Speedtest manually</description>
|
||||
</channel-type>
|
||||
</thing:thing-descriptions>
|
@ -358,6 +358,7 @@
|
||||
<module>org.openhab.binding.sonyaudio</module>
|
||||
<module>org.openhab.binding.sonyprojector</module>
|
||||
<module>org.openhab.binding.souliss</module>
|
||||
<module>org.openhab.binding.speedtest</module>
|
||||
<module>org.openhab.binding.spotify</module>
|
||||
<module>org.openhab.binding.squeezebox</module>
|
||||
<module>org.openhab.binding.surepetcare</module>
|
||||
|
Loading…
Reference in New Issue
Block a user