diff --git a/bundles/org.openhab.binding.speedtest/README.md b/bundles/org.openhab.binding.speedtest/README.md index 733cff9e2..5f84faa23 100644 --- a/bundles/org.openhab.binding.speedtest/README.md +++ b/bundles/org.openhab.binding.speedtest/README.md @@ -76,6 +76,7 @@ Ensure that the user that openHAB is running with, has the permissions to access | Channel | Type | Description | |-----------------------|---------------------------|-------------------------------------------------------------------| | `server` | `String` | The remote server that the Speedtest was run against | +| `timestamp` | `DateTime` | Timestamp of the Speedtest run | | `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 | @@ -88,6 +89,7 @@ Ensure that the user that openHAB is running with, has the permissions to access | `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 | +| `resultImage` | `Image ` | The Speedtest results as image | | `triggerTest` | `Switch` | Trigger in order to run Speedtest manually | ## Full Example @@ -102,6 +104,7 @@ Thing speedtest:speedtest:myspeedtest "Ookla Speedtest" [ execPath="/usr/ ```java String Speedtest_Server "Server" { channel="speedtest:speedtest:myspeedtest:server" } +DateTime Speedtest_Timestamp "Timestamp" { channel="speedtest:speedtest:myspeedtest:timestamp" } 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" } @@ -114,5 +117,6 @@ String Speedtest_ISP "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" } +Image Speedtest_ResultImage "Result Image" { channel="speedtest:speedtest:myspeedtest:resultImage" } Switch Speedtest_TriggerTest "Trigger Test" { channel="speedtest:speedtest:myspeedtest:triggerTest" } ``` diff --git a/bundles/org.openhab.binding.speedtest/src/main/java/org/openhab/binding/speedtest/internal/SpeedtestBindingConstants.java b/bundles/org.openhab.binding.speedtest/src/main/java/org/openhab/binding/speedtest/internal/SpeedtestBindingConstants.java index cc9a9a364..b365943d9 100644 --- a/bundles/org.openhab.binding.speedtest/src/main/java/org/openhab/binding/speedtest/internal/SpeedtestBindingConstants.java +++ b/bundles/org.openhab.binding.speedtest/src/main/java/org/openhab/binding/speedtest/internal/SpeedtestBindingConstants.java @@ -40,6 +40,7 @@ public class SpeedtestBindingConstants { // Channels public static final String SERVER = "server"; + public static final String TIMESTAMP = "timestamp"; public static final String PING_JITTER = "pingJitter"; public static final String PING_LATENCY = "pingLatency"; public static final String DOWNLOAD_BANDWIDTH = "downloadBandwidth"; @@ -52,6 +53,7 @@ public class SpeedtestBindingConstants { 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 RESULT_IMAGE = "resultImage"; public static final String TRIGGER_TEST = "triggerTest"; public static final String PROPERTY_SERVER_LIST1 = "Server List 1"; @@ -65,7 +67,7 @@ public class SpeedtestBindingConstants { public static final String PROPERTY_SERVER_LIST9 = "Server List 9"; public static final String PROPERTY_SERVER_LIST10 = "Server List 10"; - public static final Set SUPPORTED_CHANNEL_IDS = Set.of(SERVER, PING_JITTER, PING_LATENCY, + public static final Set SUPPORTED_CHANNEL_IDS = Set.of(SERVER, TIMESTAMP, PING_JITTER, PING_LATENCY, DOWNLOAD_BANDWIDTH, DOWNLOAD_BYTES, DOWNLOAD_ELAPSED, UPLOAD_BANDWIDTH, UPLOAD_BYTES, UPLOAD_ELAPSED, ISP, - INTERFACE_INTERNALIP, INTERFACE_EXTERNALIP, RESULT_URL); + INTERFACE_INTERNALIP, INTERFACE_EXTERNALIP, RESULT_URL, RESULT_IMAGE, TRIGGER_TEST); } diff --git a/bundles/org.openhab.binding.speedtest/src/main/java/org/openhab/binding/speedtest/internal/SpeedtestHandler.java b/bundles/org.openhab.binding.speedtest/src/main/java/org/openhab/binding/speedtest/internal/SpeedtestHandler.java index 6c43ef3cf..d87d6fd6c 100644 --- a/bundles/org.openhab.binding.speedtest/src/main/java/org/openhab/binding/speedtest/internal/SpeedtestHandler.java +++ b/bundles/org.openhab.binding.speedtest/src/main/java/org/openhab/binding/speedtest/internal/SpeedtestHandler.java @@ -17,6 +17,8 @@ import java.io.BufferedReader; import java.io.File; import java.io.IOException; import java.io.InputStreamReader; +import java.time.ZonedDateTime; +import java.time.format.DateTimeParseException; import java.util.Arrays; import java.util.Map; import java.util.concurrent.ScheduledFuture; @@ -27,8 +29,12 @@ 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.i18n.TimeZoneProvider; +import org.openhab.core.io.net.http.HttpUtil; +import org.openhab.core.library.types.DateTimeType; import org.openhab.core.library.types.OnOffType; import org.openhab.core.library.types.QuantityType; +import org.openhab.core.library.types.RawType; import org.openhab.core.library.types.StringType; import org.openhab.core.library.unit.Units; import org.openhab.core.thing.ChannelUID; @@ -39,6 +45,7 @@ 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.openhab.core.types.UnDefType; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -58,6 +65,7 @@ public class SpeedtestHandler extends BaseThingHandler { private static Runtime rt = Runtime.getRuntime(); private long pollingInterval = 60; private String serverID = ""; + private final TimeZoneProvider timeZoneProvider; private @Nullable ScheduledFuture pollingJob; public volatile boolean isRunning = false; @@ -69,19 +77,21 @@ public class SpeedtestHandler extends BaseThingHandler { 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 State pingJitter = UnDefType.NULL; + private State pingLatency = UnDefType.NULL; + private State downloadBandwidth = UnDefType.NULL; + private State downloadBytes = UnDefType.NULL; + private State downloadElapsed = UnDefType.NULL; + private State uploadBandwidth = UnDefType.NULL; + private State uploadBytes = UnDefType.NULL; + private State uploadElapsed = UnDefType.NULL; private String isp = ""; private String interfaceInternalIp = ""; private String interfaceExternalIp = ""; private String resultUrl = ""; + private State resultImage = UnDefType.NULL; private String server = ""; + private State timestamp = UnDefType.NULL; /** * Contains information about which operating system openHAB is running on. @@ -95,8 +105,9 @@ public class SpeedtestHandler extends BaseThingHandler { NOT_SET } - public SpeedtestHandler(Thing thing) { + public SpeedtestHandler(Thing thing, TimeZoneProvider timeZoneProvider) { super(thing); + this.timeZoneProvider = timeZoneProvider; } @Override @@ -286,18 +297,85 @@ public class SpeedtestHandler extends BaseThingHandler { ResultContainer.class); if (tmpCont != null) { if ("result".equals(tmpCont.getType())) { - 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(); + try { + // timestamp format: "2023-07-20T19:34:54Z" + ZonedDateTime zonedDateTime = ZonedDateTime.parse(tmpCont.getTimestamp()) + .withZoneSameInstant(timeZoneProvider.getTimeZone()); + timestamp = new DateTimeType(zonedDateTime); + } catch (DateTimeParseException e) { + timestamp = UnDefType.NULL; + logger.debug("Exception: {}", e.getMessage()); + } + try { + pingJitter = new QuantityType<>(Double.parseDouble(tmpCont.getPing().getJitter()) / 1000.0, + Units.SECOND); + } catch (NumberFormatException e) { + pingJitter = UnDefType.NULL; + logger.debug("Exception: {}", e.getMessage()); + } + try { + pingLatency = new QuantityType<>(Double.parseDouble(tmpCont.getPing().getLatency()) / 1000.0, + Units.SECOND); + } catch (NumberFormatException e) { + pingLatency = UnDefType.NULL; + logger.debug("Exception: {}", e.getMessage()); + } + try { + downloadBandwidth = new QuantityType<>( + Double.parseDouble(tmpCont.getDownload().getBandwidth()) / 125000.0, + Units.MEGABIT_PER_SECOND); + } catch (NumberFormatException e) { + downloadBandwidth = UnDefType.NULL; + logger.debug("Exception: {}", e.getMessage()); + } + try { + downloadBytes = new QuantityType<>(Double.parseDouble(tmpCont.getDownload().getBytes()), + Units.BYTE); + } catch (NumberFormatException e) { + downloadBytes = UnDefType.NULL; + logger.debug("Exception: {}", e.getMessage()); + } + try { + downloadElapsed = new QuantityType<>( + Double.parseDouble(tmpCont.getDownload().getElapsed()) / 1000.0, Units.SECOND); + } catch (NumberFormatException e) { + downloadElapsed = UnDefType.NULL; + logger.debug("Exception: {}", e.getMessage()); + } + try { + uploadBandwidth = new QuantityType<>( + Double.parseDouble(tmpCont.getUpload().getBandwidth()) / 125000.0, + Units.MEGABIT_PER_SECOND); + } catch (NumberFormatException e) { + uploadBandwidth = UnDefType.NULL; + logger.debug("Exception: {}", e.getMessage()); + } + try { + uploadBytes = new QuantityType<>(Double.parseDouble(tmpCont.getUpload().getBytes()), Units.BYTE); + } catch (NumberFormatException e) { + uploadBytes = UnDefType.NULL; + logger.debug("Exception: {}", e.getMessage()); + } + try { + uploadElapsed = new QuantityType<>(Double.parseDouble(tmpCont.getUpload().getElapsed()) / 1000.0, + Units.SECOND); + } catch (NumberFormatException e) { + uploadElapsed = UnDefType.NULL; + logger.debug("Exception: {}", e.getMessage()); + } isp = tmpCont.getIsp(); interfaceInternalIp = tmpCont.getInterface().getInternalIp(); interfaceExternalIp = tmpCont.getInterface().getExternalIp(); resultUrl = tmpCont.getResult().getUrl(); + String url = String.valueOf(resultUrl) + ".png"; + logger.debug("Downloading result image from: {}", url); + RawType image = HttpUtil.downloadImage(url); + if (image != null) { + resultImage = image; + } else { + resultImage = UnDefType.NULL; + } + server = tmpCont.getServer().getName() + " (" + tmpCont.getServer().getId().toString() + ") " + tmpCont.getServer().getLocation(); updateChannels(); @@ -332,45 +410,53 @@ public class SpeedtestHandler extends BaseThingHandler { 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); + logger.debug("timestamp: {}", timestamp); + updateState(new ChannelUID(getThing().getUID(), SpeedtestBindingConstants.TIMESTAMP), timestamp); - newState = new QuantityType<>(Double.parseDouble(pingLatency) / 1000.0, Units.SECOND); - logger.debug("pingLatency: {}", newState); - updateState(new ChannelUID(getThing().getUID(), SpeedtestBindingConstants.PING_LATENCY), newState); + logger.debug("pingJitter: {}", pingJitter); + updateState(new ChannelUID(getThing().getUID(), SpeedtestBindingConstants.PING_JITTER), pingJitter); - 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); + logger.debug("pingLatency: {}", pingLatency); + updateState(new ChannelUID(getThing().getUID(), SpeedtestBindingConstants.PING_LATENCY), pingLatency); - newState = new QuantityType<>(Double.parseDouble(downloadBytes), Units.BYTE); - logger.debug("downloadBytes: {}", newState); - updateState(new ChannelUID(getThing().getUID(), SpeedtestBindingConstants.DOWNLOAD_BYTES), newState); + logger.debug("downloadBandwidth: {}", downloadBandwidth); + updateState(new ChannelUID(getThing().getUID(), SpeedtestBindingConstants.DOWNLOAD_BANDWIDTH), + downloadBandwidth); - newState = new QuantityType<>(Double.parseDouble(downloadElapsed) / 1000.0, Units.SECOND); - logger.debug("downloadElapsed: {}", newState); - updateState(new ChannelUID(getThing().getUID(), SpeedtestBindingConstants.DOWNLOAD_ELAPSED), newState); + logger.debug("downloadBytes: {}", downloadBytes); + updateState(new ChannelUID(getThing().getUID(), SpeedtestBindingConstants.DOWNLOAD_BYTES), downloadBytes); - 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); + logger.debug("downloadElapsed: {}", downloadElapsed); + updateState(new ChannelUID(getThing().getUID(), SpeedtestBindingConstants.DOWNLOAD_ELAPSED), downloadElapsed); - newState = new QuantityType<>(Double.parseDouble(uploadBytes), Units.BYTE); - logger.debug("uploadBytes: {}", newState); - updateState(new ChannelUID(getThing().getUID(), SpeedtestBindingConstants.UPLOAD_BYTES), newState); + logger.debug("uploadBandwidth: {}", uploadBandwidth); + updateState(new ChannelUID(getThing().getUID(), SpeedtestBindingConstants.UPLOAD_BANDWIDTH), uploadBandwidth); - newState = new QuantityType<>(Double.parseDouble(uploadElapsed) / 1000.0, Units.SECOND); - logger.debug("uploadElapsed: {}", newState); - updateState(new ChannelUID(getThing().getUID(), SpeedtestBindingConstants.UPLOAD_ELAPSED), newState); + logger.debug("uploadBytes: {}", uploadBytes); + updateState(new ChannelUID(getThing().getUID(), SpeedtestBindingConstants.UPLOAD_BYTES), uploadBytes); + logger.debug("uploadElapsed: {}", uploadElapsed); + updateState(new ChannelUID(getThing().getUID(), SpeedtestBindingConstants.UPLOAD_ELAPSED), uploadElapsed); + + logger.debug("interfaceExternalIp: {}", interfaceExternalIp); updateState(new ChannelUID(getThing().getUID(), SpeedtestBindingConstants.INTERFACE_EXTERNALIP), new StringType(interfaceExternalIp)); + + logger.debug("interfaceInternalIp: {}", interfaceInternalIp); updateState(new ChannelUID(getThing().getUID(), SpeedtestBindingConstants.INTERFACE_INTERNALIP), new StringType(interfaceInternalIp)); + + logger.debug("isp: {}", isp); updateState(new ChannelUID(getThing().getUID(), SpeedtestBindingConstants.ISP), new StringType(isp)); + + logger.debug("resultUrl: {}", resultUrl); updateState(new ChannelUID(getThing().getUID(), SpeedtestBindingConstants.RESULT_URL), new StringType(resultUrl)); + + logger.debug("resultImage: "); + updateState(new ChannelUID(getThing().getUID(), SpeedtestBindingConstants.RESULT_IMAGE), resultImage); + + logger.debug("server: {}", server); updateState(new ChannelUID(getThing().getUID(), SpeedtestBindingConstants.SERVER), new StringType(server)); } diff --git a/bundles/org.openhab.binding.speedtest/src/main/java/org/openhab/binding/speedtest/internal/SpeedtestHandlerFactory.java b/bundles/org.openhab.binding.speedtest/src/main/java/org/openhab/binding/speedtest/internal/SpeedtestHandlerFactory.java index 87024fe7f..388461c9b 100644 --- a/bundles/org.openhab.binding.speedtest/src/main/java/org/openhab/binding/speedtest/internal/SpeedtestHandlerFactory.java +++ b/bundles/org.openhab.binding.speedtest/src/main/java/org/openhab/binding/speedtest/internal/SpeedtestHandlerFactory.java @@ -19,12 +19,15 @@ import java.util.Set; import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.Nullable; +import org.openhab.core.i18n.TimeZoneProvider; 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 SpeedtestHandlerFactory} is responsible for creating things and thing @@ -37,6 +40,12 @@ import org.osgi.service.component.annotations.Component; public class SpeedtestHandlerFactory extends BaseThingHandlerFactory { private static final Set SUPPORTED_THING_TYPES_UIDS = Set.of(THING_TYPE_SPEEDTEST); + private final TimeZoneProvider timeZoneProvider; + + @Activate + public SpeedtestHandlerFactory(final @Reference TimeZoneProvider timeZoneProvider) { + this.timeZoneProvider = timeZoneProvider; + } @Override public boolean supportsThingType(ThingTypeUID thingTypeUID) { @@ -48,7 +57,7 @@ public class SpeedtestHandlerFactory extends BaseThingHandlerFactory { ThingTypeUID thingTypeUID = thing.getThingTypeUID(); if (THING_TYPE_SPEEDTEST.equals(thingTypeUID)) { - return new SpeedtestHandler(thing); + return new SpeedtestHandler(thing, timeZoneProvider); } return null; diff --git a/bundles/org.openhab.binding.speedtest/src/main/resources/OH-INF/i18n/speedtest.properties b/bundles/org.openhab.binding.speedtest/src/main/resources/OH-INF/i18n/speedtest.properties index 4ab1de818..03d72a3bf 100644 --- a/bundles/org.openhab.binding.speedtest/src/main/resources/OH-INF/i18n/speedtest.properties +++ b/bundles/org.openhab.binding.speedtest/src/main/resources/OH-INF/i18n/speedtest.properties @@ -5,8 +5,8 @@ addon.speedtest.description = Binding for Ookla Speedtest (https://www.speedtest # thing types -thing-type.speedtest.speedtest.label = Speedtest Binding -thing-type.speedtest.speedtest.description = Binding for Ookla Speedtest (https://www.speedtest.net/) +thing-type.speedtest.speedtest.label = Ookla Speedtest +thing-type.speedtest.speedtest.description = Ookla Speedtest (https://www.speedtest.net/) # thing types config @@ -43,10 +43,14 @@ 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.resultImage.label = Result Image +channel-type.speedtest.resultImage.description = The Speedtest results as image 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.timestamp.label = Timestamp +channel-type.speedtest.timestamp.description = Timestamp of the Speedtest run 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 diff --git a/bundles/org.openhab.binding.speedtest/src/main/resources/OH-INF/thing/thing-types.xml b/bundles/org.openhab.binding.speedtest/src/main/resources/OH-INF/thing/thing-types.xml index bf2162555..8c0922291 100644 --- a/bundles/org.openhab.binding.speedtest/src/main/resources/OH-INF/thing/thing-types.xml +++ b/bundles/org.openhab.binding.speedtest/src/main/resources/OH-INF/thing/thing-types.xml @@ -5,10 +5,11 @@ xsi:schemaLocation="https://openhab.org/schemas/thing-description/v1.0.0 https://openhab.org/schemas/thing-description-1.0.0.xsd"> - - Binding for Ookla Speedtest (https://www.speedtest.net/) + + Ookla Speedtest (https://www.speedtest.net/) + @@ -27,6 +28,7 @@ + @@ -41,6 +43,7 @@ + 1 @@ -78,6 +81,12 @@ The remote server that the Speedtest was run against + + DateTime + + Timestamp of the Speedtest run + + Number:Time @@ -150,6 +159,12 @@ The URL to the Speedtest results in HTML on the Ookla webserver + + Image + + The Speedtest results as image + + Switch diff --git a/bundles/org.openhab.binding.speedtest/src/main/resources/OH-INF/update/instructions.xml b/bundles/org.openhab.binding.speedtest/src/main/resources/OH-INF/update/instructions.xml new file mode 100644 index 000000000..0e105def4 --- /dev/null +++ b/bundles/org.openhab.binding.speedtest/src/main/resources/OH-INF/update/instructions.xml @@ -0,0 +1,17 @@ + + + + + + + speedtest:timestamp + + + speedtest:resultImage + + + + +