diff --git a/CODEOWNERS b/CODEOWNERS index e08a8885edf26..41e5ecba69f28 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -38,7 +38,7 @@ /bundles/org.openhab.binding.boschindego/ @jofleck /bundles/org.openhab.binding.boschshc/ @stefan-kaestle @coeing @GerdZanker /bundles/org.openhab.binding.bosesoundtouch/ @marvkis @tratho -/bundles/org.openhab.binding.broadlinkthermostat/ @flo_02_mu +/bundles/org.openhab.binding.broadlinkthermostat/ @flo-02-mu /bundles/org.openhab.binding.bsblan/ @hypetsch /bundles/org.openhab.binding.bticinosmarther/ @MrRonfo /bundles/org.openhab.binding.buienradar/ @gedejong @@ -52,12 +52,12 @@ /bundles/org.openhab.binding.daikin/ @caffineehacker /bundles/org.openhab.binding.danfossairunit/ @pravussum /bundles/org.openhab.binding.darksky/ @cweitkamp -/bundles/org.openhab.binding.deconz/ @J-N-K +/bundles/org.openhab.binding.deconz/ @openhab/add-ons-maintainers /bundles/org.openhab.binding.denonmarantz/ @jwveldhuis /bundles/org.openhab.binding.digiplex/ @rmichalak /bundles/org.openhab.binding.digitalstrom/ @MichaelOchel @msiegele /bundles/org.openhab.binding.dlinksmarthome/ @MikeJMajor -/bundles/org.openhab.binding.dmx/ @J-N-K +/bundles/org.openhab.binding.dmx/ @openhab/add-ons-maintainers /bundles/org.openhab.binding.doorbird/ @mhilbush /bundles/org.openhab.binding.draytonwiser/ @andrew-schofield /bundles/org.openhab.binding.dscalarm/ @RSStephens @@ -102,7 +102,7 @@ /bundles/org.openhab.binding.heos/ @Wire82 /bundles/org.openhab.binding.homematic/ @FStolte @gerrieg @mdicke2s /bundles/org.openhab.binding.hpprinter/ @cossey -/bundles/org.openhab.binding.http/ @J-N-K +/bundles/org.openhab.binding.http/ @openhab/add-ons-maintainers /bundles/org.openhab.binding.hue/ @cweitkamp /bundles/org.openhab.binding.hydrawise/ @digitaldan /bundles/org.openhab.binding.hyperion/ @tavalin @@ -144,7 +144,7 @@ /bundles/org.openhab.binding.luftdateninfo/ @weymann /bundles/org.openhab.binding.lutron/ @actong @bobadair /bundles/org.openhab.binding.magentatv/ @markus7017 -/bundles/org.openhab.binding.mail/ @J-N-K +/bundles/org.openhab.binding.mail/ @openhab/add-ons-maintainers /bundles/org.openhab.binding.max/ @marcelrv /bundles/org.openhab.binding.mcp23017/ @aogorek /bundles/org.openhab.binding.melcloud/ @lucacalcaterra @paulianttila @thewiep @@ -171,6 +171,7 @@ /bundles/org.openhab.binding.mqtt.generic/ @davidgraeff /bundles/org.openhab.binding.mqtt.homeassistant/ @davidgraeff /bundles/org.openhab.binding.mqtt.homie/ @davidgraeff +/bundles/org.openhab.binding.myq/ @digitaldan /bundles/org.openhab.binding.mystrom/ @pail23 /bundles/org.openhab.binding.nanoleaf/ @raepple @stefan-hoehn /bundles/org.openhab.binding.neato/ @jjlauterbach @@ -194,7 +195,7 @@ /bundles/org.openhab.binding.omnikinverter/ @hansbogert /bundles/org.openhab.binding.omnilink/ @ecdye /bundles/org.openhab.binding.onebusaway/ @sdwilsh -/bundles/org.openhab.binding.onewire/ @J-N-K +/bundles/org.openhab.binding.onewire/ @openhab/add-ons-maintainers /bundles/org.openhab.binding.onewiregpio/ @aogorek /bundles/org.openhab.binding.onkyo/ @pail23 @paulianttila /bundles/org.openhab.binding.opengarage/ @psmedley @@ -208,6 +209,7 @@ /bundles/org.openhab.binding.paradoxalarm/ @theater /bundles/org.openhab.binding.pentair/ @jsjames /bundles/org.openhab.binding.phc/ @gnlpfjh +/bundles/org.openhab.binding.pilight/ @stefanroellin @niklasdoerfler /bundles/org.openhab.binding.pioneeravr/ @Stratehm /bundles/org.openhab.binding.pixometer/ @Confectrician /bundles/org.openhab.binding.pjlinkdevice/ @nils @@ -246,7 +248,7 @@ /bundles/org.openhab.binding.smartmeter/ @msteigenberger /bundles/org.openhab.binding.smartthings/ @BobRak /bundles/org.openhab.binding.smhi/ @pacive -/bundles/org.openhab.binding.snmp/ @J-N-K +/bundles/org.openhab.binding.snmp/ @openhab/add-ons-maintainers /bundles/org.openhab.binding.solaredge/ @alexf2015 /bundles/org.openhab.binding.solarlog/ @johannrichard /bundles/org.openhab.binding.somfymylink/ @loungeflyz @@ -256,6 +258,7 @@ /bundles/org.openhab.binding.sonyprojector/ @lolodomo /bundles/org.openhab.binding.spotify/ @Hilbrand /bundles/org.openhab.binding.squeezebox/ @digitaldan @mhilbush +/bundles/org.openhab.binding.surepetcare/ @renescherer @HerzScheisse /bundles/org.openhab.binding.synopanalyzer/ @clinique /bundles/org.openhab.binding.systeminfo/ @svilenvul /bundles/org.openhab.binding.tacmi/ @twendt @Wolfgang1966 @marvkis @@ -269,7 +272,7 @@ /bundles/org.openhab.binding.tivo/ @mlobstein /bundles/org.openhab.binding.touchwand/ @roieg /bundles/org.openhab.binding.tplinksmarthome/ @Hilbrand -/bundles/org.openhab.binding.tr064/ @J-N-K +/bundles/org.openhab.binding.tr064/ @openhab/add-ons-maintainers /bundles/org.openhab.binding.tradfri/ @cweitkamp @kaikreuzer /bundles/org.openhab.binding.unifi/ @mgbowman /bundles/org.openhab.binding.unifiedremote/ @GiviMAD @@ -304,7 +307,6 @@ /bundles/org.openhab.io.imperihome/ @pdegeus /bundles/org.openhab.io.neeo/ @tmrobert8 /bundles/org.openhab.io.openhabcloud/ @kaikreuzer -/bundles/org.openhab.io.transport.modbus/ @ssalonen /bundles/org.openhab.persistence.dynamodb/ @ssalonen /bundles/org.openhab.persistence.influxdb/ @lujop /bundles/org.openhab.persistence.jdbc/ @openhab/add-ons-maintainers diff --git a/bom/openhab-addons/pom.xml b/bom/openhab-addons/pom.xml index 37bf387899738..1b0f7a7afa26e 100644 --- a/bom/openhab-addons/pom.xml +++ b/bom/openhab-addons/pom.xml @@ -1026,6 +1026,11 @@ org.openhab.binding.phc ${project.version} + + org.openhab.addons.bundles + org.openhab.binding.pilight + ${project.version} + org.openhab.addons.bundles org.openhab.binding.pioneeravr @@ -1266,6 +1271,11 @@ org.openhab.binding.squeezebox ${project.version} + + org.openhab.addons.bundles + org.openhab.binding.surepetcare + ${project.version} + org.openhab.addons.bundles org.openhab.binding.synopanalyzer diff --git a/bundles/org.openhab.binding.airvisualnode/noEmbedDependencies.profile b/bundles/org.openhab.binding.airvisualnode/noEmbedDependencies.profile deleted file mode 100644 index e69de29bb2d1d..0000000000000 diff --git a/bundles/org.openhab.binding.airvisualnode/pom.xml b/bundles/org.openhab.binding.airvisualnode/pom.xml index 6e89e6b57cf60..ac7f864a4e528 100644 --- a/bundles/org.openhab.binding.airvisualnode/pom.xml +++ b/bundles/org.openhab.binding.airvisualnode/pom.xml @@ -14,9 +14,15 @@ openHAB Add-ons :: Bundles :: AirVisual Node Air Quality Monitor Binding + + + !jcifs + + + - org.samba.jcifs + jcifs jcifs 1.3.17 compile diff --git a/bundles/org.openhab.binding.airvisualnode/src/main/feature/feature.xml b/bundles/org.openhab.binding.airvisualnode/src/main/feature/feature.xml index 66adc39bd0085..a01fcaa87ab3a 100644 --- a/bundles/org.openhab.binding.airvisualnode/src/main/feature/feature.xml +++ b/bundles/org.openhab.binding.airvisualnode/src/main/feature/feature.xml @@ -4,7 +4,6 @@ openhab-runtime-base - mvn:org.samba.jcifs/jcifs/1.3.17 mvn:org.openhab.addons.bundles/org.openhab.binding.airvisualnode/${project.version} diff --git a/bundles/org.openhab.binding.alarmdecoder/README.md b/bundles/org.openhab.binding.alarmdecoder/README.md index 86ef3fd2bdbd3..a7e72455c56b3 100644 --- a/bundles/org.openhab.binding.alarmdecoder/README.md +++ b/bundles/org.openhab.binding.alarmdecoder/README.md @@ -12,8 +12,6 @@ There are several versions of the adapter available: This binding allows openHAB to access the state of wired or wireless contacts and motion detectors connected to supported alarm panels, as well as the state of attached keypads and the messages send to attached LRR devices. Support is also available for sending keypad commands, including special/programmable keys supported by your panel. -For those upgrading from the OH1 version of the binding, the [original OH1 README](https://www.openhab.org/v2.5/addons/bindings/alarmdecoder1/) file is available for reference. - ## Supported Things The binding supports the following thing types: @@ -311,3 +309,7 @@ The alarmdecoder device cannot query the panel for the state of individual zones For this reason, the binding puts contacts into the "unknown" state (UNDEF), *until the panel goes into the READY state*. At that point, all contacts for which no update messages have arrived are presumed to be in the CLOSED state. In other words: to get to a clean slate after an openHAB restart, close all doors/windows such that the panel is READY. + +## Reference Information + +The protocol used to communicate with the Alarm Decoder is described [here](https://www.alarmdecoder.com/wiki/index.php/Protocol). diff --git a/bundles/org.openhab.binding.amazonechocontrol/README.md b/bundles/org.openhab.binding.amazonechocontrol/README.md index c67ab108348f0..12b595f9bf769 100644 --- a/bundles/org.openhab.binding.amazonechocontrol/README.md +++ b/bundles/org.openhab.binding.amazonechocontrol/README.md @@ -429,7 +429,7 @@ sitemap flashbriefings label="Flash Briefings" ## Smart Home Devices -Note: the cannels of smartHomeDevices and smartHomeDeviceGroup will be created dynamically based on the capabilities reported by the amazon server. This can take a little bit of time. +Note: the channels of smartHomeDevices and smartHomeDeviceGroup will be created dynamically based on the capabilities reported by the amazon server. This can take a little bit of time. The polling interval configured in the Account Thing to get the state is specified in minutes and has a minimum of 10. This means it takes up to 10 minutes to see the state of a channel. The reason for this low interval is, that the polling causes a big server load for the Smart Home Skills. #### Supported Things diff --git a/bundles/org.openhab.binding.ambientweather/src/main/java/org/openhab/binding/ambientweather/internal/handler/AmbientWeatherBridgeHandler.java b/bundles/org.openhab.binding.ambientweather/src/main/java/org/openhab/binding/ambientweather/internal/handler/AmbientWeatherBridgeHandler.java index 8461a3f95328a..1c86211334d22 100644 --- a/bundles/org.openhab.binding.ambientweather/src/main/java/org/openhab/binding/ambientweather/internal/handler/AmbientWeatherBridgeHandler.java +++ b/bundles/org.openhab.binding.ambientweather/src/main/java/org/openhab/binding/ambientweather/internal/handler/AmbientWeatherBridgeHandler.java @@ -18,7 +18,6 @@ import java.util.concurrent.ScheduledFuture; import java.util.concurrent.TimeUnit; -import org.apache.commons.lang.StringUtils; import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.Nullable; import org.openhab.binding.ambientweather.internal.config.BridgeConfig; @@ -128,7 +127,7 @@ public void initialize() { */ private boolean hasApplicationKey() { String configApplicationKey = getConfigAs(BridgeConfig.class).applicationKey; - if (StringUtils.isEmpty(configApplicationKey)) { + if (configApplicationKey == null || configApplicationKey.isEmpty()) { updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, "Missing application key"); return false; } @@ -141,7 +140,7 @@ private boolean hasApplicationKey() { */ private boolean hasApiKey() { String configApiKey = getConfigAs(BridgeConfig.class).apiKey; - if (StringUtils.isEmpty(configApiKey)) { + if (configApiKey == null || configApiKey.isEmpty()) { updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, "Missing API key"); return false; } diff --git a/bundles/org.openhab.binding.ambientweather/src/main/java/org/openhab/binding/ambientweather/internal/handler/AmbientWeatherEventListener.java b/bundles/org.openhab.binding.ambientweather/src/main/java/org/openhab/binding/ambientweather/internal/handler/AmbientWeatherEventListener.java index 27ceb2be23f61..39d54463ba909 100644 --- a/bundles/org.openhab.binding.ambientweather/src/main/java/org/openhab/binding/ambientweather/internal/handler/AmbientWeatherEventListener.java +++ b/bundles/org.openhab.binding.ambientweather/src/main/java/org/openhab/binding/ambientweather/internal/handler/AmbientWeatherEventListener.java @@ -18,7 +18,6 @@ import java.util.Map; import java.util.concurrent.ConcurrentHashMap; -import org.apache.commons.lang.StringUtils; import org.json.JSONObject; import org.openhab.binding.ambientweather.internal.model.DeviceJson; import org.openhab.binding.ambientweather.internal.model.EventDataGenericJson; @@ -318,8 +317,9 @@ private synchronized void handleData(String jsonData) { logger.debug("Listener: Data: {}", jsonData); try { EventDataGenericJson data = gson.fromJson(jsonData, EventDataGenericJson.class); - if (StringUtils.isNotEmpty(data.macAddress)) { - sendWeatherDataToHandler(data.macAddress, jsonData); + String macAddress = data == null ? null : data.macAddress; + if (macAddress != null && !macAddress.isEmpty()) { + sendWeatherDataToHandler(macAddress, jsonData); } } catch (JsonSyntaxException e) { logger.info("Listener: Exception parsing subscribed event: {}", e.getMessage()); diff --git a/bundles/org.openhab.binding.androiddebugbridge/README.md b/bundles/org.openhab.binding.androiddebugbridge/README.md index 9b1ce5c0dace0..c2c159549783d 100644 --- a/bundles/org.openhab.binding.androiddebugbridge/README.md +++ b/bundles/org.openhab.binding.androiddebugbridge/README.md @@ -6,7 +6,7 @@ If you are not familiar with adb I suggest you to search "How to enable adb over ## Supported Things -This binding was tested on the Fire TV Stick (android version 7.1.2, volume control not working) and Nexus5x (android version 8.1.0, everything works nice), please update this document if you tested it with other android versions to reflect the compatibility of the biding. +This binding was tested on the Fire TV Stick (android version 7.1.2, volume control not working) and Nexus5x (android version 8.1.0, everything works nice), please update this document if you tested it with other android versions to reflect the compatibility of the binding. ## Discovery diff --git a/bundles/org.openhab.binding.androiddebugbridge/src/main/java/org/openhab/binding/androiddebugbridge/internal/AndroidDebugBridgeDevice.java b/bundles/org.openhab.binding.androiddebugbridge/src/main/java/org/openhab/binding/androiddebugbridge/internal/AndroidDebugBridgeDevice.java index 8bcdd58e76cc8..1ddb6e642c197 100644 --- a/bundles/org.openhab.binding.androiddebugbridge/src/main/java/org/openhab/binding/androiddebugbridge/internal/AndroidDebugBridgeDevice.java +++ b/bundles/org.openhab.binding.androiddebugbridge/src/main/java/org/openhab/binding/androiddebugbridge/internal/AndroidDebugBridgeDevice.java @@ -117,7 +117,7 @@ public String getCurrentPackage() throws AndroidDebugBridgeDeviceException, Inte if (packageActivityName.contains("/")) return packageActivityName.split("/")[0]; } - throw new AndroidDebugBridgeDeviceReadException("can read package name"); + throw new AndroidDebugBridgeDeviceReadException("Unable to read package name"); } public boolean isAwake() @@ -137,7 +137,7 @@ public boolean isScreenOn() throws InterruptedException, AndroidDebugBridgeDevic logger.debug("Unable to parse device wake lock: {}", e.getMessage()); } } - throw new AndroidDebugBridgeDeviceReadException("can read screen state"); + throw new AndroidDebugBridgeDeviceReadException("Unable to read screen state"); } public boolean isPlayingMedia(String currentApp) @@ -175,12 +175,12 @@ public int getPowerWakeLock() throws InterruptedException, AndroidDebugBridgeDev String lockResp = runAdbShell("dumpsys", "power", "|", "grep", "Locks", "|", "grep", "'size='"); if (lockResp.contains("=")) { try { - return Integer.parseInt(lockResp.replace("\n", "").split("=")[1]); + return Integer.parseInt(lockResp.replace("\n", "").split("=")[1].trim()); } catch (NumberFormatException e) { logger.debug("Unable to parse device wake lock: {}", e.getMessage()); } } - throw new AndroidDebugBridgeDeviceReadException("can read wake lock"); + throw new AndroidDebugBridgeDeviceReadException("Unable to read wake lock"); } private void setVolume(int stream, int volume) diff --git a/bundles/org.openhab.binding.astro/src/main/java/org/openhab/binding/astro/internal/handler/AstroThingHandler.java b/bundles/org.openhab.binding.astro/src/main/java/org/openhab/binding/astro/internal/handler/AstroThingHandler.java index ad27bdb7f13c9..2e06df173cfc2 100644 --- a/bundles/org.openhab.binding.astro/src/main/java/org/openhab/binding/astro/internal/handler/AstroThingHandler.java +++ b/bundles/org.openhab.binding.astro/src/main/java/org/openhab/binding/astro/internal/handler/AstroThingHandler.java @@ -34,7 +34,7 @@ import javax.measure.quantity.Angle; -import org.apache.commons.lang.time.DateFormatUtils; +import org.apache.commons.lang3.time.DateFormatUtils; import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.Nullable; import org.openhab.binding.astro.internal.action.AstroActions; diff --git a/bundles/org.openhab.binding.astro/src/main/java/org/openhab/binding/astro/internal/job/Job.java b/bundles/org.openhab.binding.astro/src/main/java/org/openhab/binding/astro/internal/job/Job.java index 1ace958cb7da3..b4a5b8ee8ba7e 100644 --- a/bundles/org.openhab.binding.astro/src/main/java/org/openhab/binding/astro/internal/job/Job.java +++ b/bundles/org.openhab.binding.astro/src/main/java/org/openhab/binding/astro/internal/job/Job.java @@ -16,7 +16,7 @@ import static java.util.Calendar.SECOND; import static java.util.Collections.singletonList; import static java.util.stream.Collectors.toList; -import static org.apache.commons.lang.time.DateUtils.truncatedEquals; +import static org.apache.commons.lang3.time.DateUtils.truncatedEquals; import static org.openhab.binding.astro.internal.AstroBindingConstants.*; import static org.openhab.binding.astro.internal.util.DateTimeUtils.*; diff --git a/bundles/org.openhab.binding.astro/src/main/java/org/openhab/binding/astro/internal/util/DateTimeUtils.java b/bundles/org.openhab.binding.astro/src/main/java/org/openhab/binding/astro/internal/util/DateTimeUtils.java index 140e8c154971d..6e18febb2c5d4 100644 --- a/bundles/org.openhab.binding.astro/src/main/java/org/openhab/binding/astro/internal/util/DateTimeUtils.java +++ b/bundles/org.openhab.binding.astro/src/main/java/org/openhab/binding/astro/internal/util/DateTimeUtils.java @@ -16,7 +16,7 @@ import java.util.Date; import java.util.regex.Pattern; -import org.apache.commons.lang.time.DateUtils; +import org.apache.commons.lang3.time.DateUtils; import org.openhab.binding.astro.internal.config.AstroChannelConfig; import org.openhab.binding.astro.internal.model.Range; import org.slf4j.Logger; diff --git a/bundles/org.openhab.binding.atlona/src/main/java/org/openhab/binding/atlona/internal/pro3/AtlonaPro3PortocolHandler.java b/bundles/org.openhab.binding.atlona/src/main/java/org/openhab/binding/atlona/internal/pro3/AtlonaPro3PortocolHandler.java index 36745f55228a6..5bf66f38c64b4 100644 --- a/bundles/org.openhab.binding.atlona/src/main/java/org/openhab/binding/atlona/internal/pro3/AtlonaPro3PortocolHandler.java +++ b/bundles/org.openhab.binding.atlona/src/main/java/org/openhab/binding/atlona/internal/pro3/AtlonaPro3PortocolHandler.java @@ -1118,7 +1118,7 @@ public void responseReceived(String response) { } m = versionHdPattern.matcher(response); - if (m.matches()) { + if (!capabilities.isUHDModel() && m.matches()) { handleVersionResponse(m, response); return; } diff --git a/bundles/org.openhab.binding.autelis/src/main/java/org/openhab/binding/autelis/internal/discovery/AutelisDiscoveryParticipant.java b/bundles/org.openhab.binding.autelis/src/main/java/org/openhab/binding/autelis/internal/discovery/AutelisDiscoveryParticipant.java index 67a29b9a9fb5e..23c859c5ef95c 100644 --- a/bundles/org.openhab.binding.autelis/src/main/java/org/openhab/binding/autelis/internal/discovery/AutelisDiscoveryParticipant.java +++ b/bundles/org.openhab.binding.autelis/src/main/java/org/openhab/binding/autelis/internal/discovery/AutelisDiscoveryParticipant.java @@ -65,7 +65,7 @@ public Set getSupportedThingTypeUIDs() { properties.put("host", url.getHost()); properties.put("user", "admin"); properties.put("password", "admin"); - properties.put("port", new Integer(port)); + properties.put("port", Integer.valueOf(port)); DiscoveryResult result = DiscoveryResultBuilder.create(uid).withProperties(properties).withLabel(label) .build(); diff --git a/bundles/org.openhab.binding.autelis/src/main/java/org/openhab/binding/autelis/internal/handler/AutelisHandler.java b/bundles/org.openhab.binding.autelis/src/main/java/org/openhab/binding/autelis/internal/handler/AutelisHandler.java index 30e37717fa983..3669f87a56ed1 100644 --- a/bundles/org.openhab.binding.autelis/src/main/java/org/openhab/binding/autelis/internal/handler/AutelisHandler.java +++ b/bundles/org.openhab.binding.autelis/src/main/java/org/openhab/binding/autelis/internal/handler/AutelisHandler.java @@ -13,6 +13,8 @@ package org.openhab.binding.autelis.internal.handler; import java.io.StringReader; +import java.nio.charset.StandardCharsets; +import java.util.Base64; import java.util.Collections; import java.util.HashMap; import java.util.Map; @@ -25,14 +27,11 @@ import javax.xml.xpath.XPathExpressionException; import javax.xml.xpath.XPathFactory; -import org.apache.commons.lang.StringUtils; import org.eclipse.jetty.client.HttpClient; import org.eclipse.jetty.client.api.ContentResponse; import org.eclipse.jetty.client.api.Request; import org.eclipse.jetty.http.HttpHeader; import org.eclipse.jetty.http.HttpStatus; -import org.eclipse.jetty.util.B64Code; -import org.eclipse.jetty.util.StringUtil; import org.openhab.binding.autelis.internal.AutelisBindingConstants; import org.openhab.binding.autelis.internal.config.AutelisConfiguration; import org.openhab.core.library.types.DecimalType; @@ -288,17 +287,17 @@ private void configure() { String username = configuration.user; String password = configuration.password; - if (StringUtils.isBlank(username)) { + if (username == null || username.isBlank()) { updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, "username must not be empty"); return; } - if (StringUtils.isBlank(password)) { + if (password == null || password.isBlank()) { updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, "password must not be empty"); return; } - if (StringUtils.isBlank(host)) { + if (host == null || host.isBlank()) { updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, "hostname must not be empty"); return; } @@ -314,7 +313,8 @@ private void configure() { } baseURL = "http://" + host + ":" + port; - basicAuthentication = "Basic " + B64Code.encode(username + ":" + password, StringUtil.__ISO_8859_1); + basicAuthentication = "Basic " + + Base64.getEncoder().encodeToString((username + ":" + password).getBytes(StandardCharsets.ISO_8859_1)); logger.debug("Autelius binding configured with base url {} and refresh period of {}", baseURL, refresh); initPolling(0); @@ -443,7 +443,7 @@ private void pollAutelisController() { } } - if (StringUtils.isEmpty((value))) { + if (value == null || value.isEmpty()) { continue; } diff --git a/bundles/org.openhab.binding.bigassfan/src/main/java/org/openhab/binding/bigassfan/internal/BigAssFanConfig.java b/bundles/org.openhab.binding.bigassfan/src/main/java/org/openhab/binding/bigassfan/internal/BigAssFanConfig.java index 647572c288039..74b1dc7a70465 100644 --- a/bundles/org.openhab.binding.bigassfan/src/main/java/org/openhab/binding/bigassfan/internal/BigAssFanConfig.java +++ b/bundles/org.openhab.binding.bigassfan/src/main/java/org/openhab/binding/bigassfan/internal/BigAssFanConfig.java @@ -12,8 +12,6 @@ */ package org.openhab.binding.bigassfan.internal; -import org.apache.commons.lang.StringUtils; - /** * The {@link BigAssFanConfig} is responsible for storing the BigAssFan thing configuration. * @@ -60,13 +58,13 @@ public void setMacAddress(String macAddress) { } public boolean isValid() { - if (StringUtils.isBlank(label)) { + if (label == null || label.isBlank()) { return false; } - if (StringUtils.isBlank(ipAddress)) { + if (ipAddress == null || ipAddress.isBlank()) { return false; } - if (StringUtils.isBlank(macAddress)) { + if (macAddress == null || macAddress.isBlank()) { return false; } return true; diff --git a/bundles/org.openhab.binding.bigassfan/src/main/java/org/openhab/binding/bigassfan/internal/handler/BigAssFanHandler.java b/bundles/org.openhab.binding.bigassfan/src/main/java/org/openhab/binding/bigassfan/internal/handler/BigAssFanHandler.java index 4c2bf94b760a4..eacc428f217d3 100644 --- a/bundles/org.openhab.binding.bigassfan/src/main/java/org/openhab/binding/bigassfan/internal/handler/BigAssFanHandler.java +++ b/bundles/org.openhab.binding.bigassfan/src/main/java/org/openhab/binding/bigassfan/internal/handler/BigAssFanHandler.java @@ -40,7 +40,6 @@ import java.util.regex.Matcher; import java.util.regex.Pattern; -import org.apache.commons.lang.StringUtils; import org.openhab.binding.bigassfan.internal.BigAssFanConfig; import org.openhab.binding.bigassfan.internal.utils.BigAssFanConverter; import org.openhab.core.common.ThreadPoolManager; @@ -668,7 +667,7 @@ private String readMessage() { } private void processMessage(String incomingMessage) { - if (StringUtils.isEmpty(incomingMessage)) { + if (incomingMessage == null || incomingMessage.isEmpty()) { return; } @@ -742,11 +741,11 @@ private void processMessage(String incomingMessage) { private boolean isMe(String idFromDevice) { // Check match on MAC address - if (StringUtils.equalsIgnoreCase(idFromDevice, macAddress)) { + if (macAddress.equalsIgnoreCase(idFromDevice)) { return true; } // Didn't match MAC address, check match for label - if (StringUtils.equalsIgnoreCase(idFromDevice, label)) { + if (label.equalsIgnoreCase(idFromDevice)) { return true; } return false; diff --git a/bundles/org.openhab.binding.bluetooth.am43/src/main/java/org/openhab/binding/bluetooth/am43/internal/command/AM43Command.java b/bundles/org.openhab.binding.bluetooth.am43/src/main/java/org/openhab/binding/bluetooth/am43/internal/command/AM43Command.java index 92ce6cc0e2cf7..1204e11b6fd78 100644 --- a/bundles/org.openhab.binding.bluetooth.am43/src/main/java/org/openhab/binding/bluetooth/am43/internal/command/AM43Command.java +++ b/bundles/org.openhab.binding.bluetooth.am43/src/main/java/org/openhab/binding/bluetooth/am43/internal/command/AM43Command.java @@ -18,7 +18,7 @@ import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; -import org.apache.commons.lang.ArrayUtils; +import org.apache.commons.lang3.ArrayUtils; import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.Nullable; diff --git a/bundles/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/BlueGigaBluetoothCharacteristic.java b/bundles/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/BlueGigaBluetoothCharacteristic.java index afa8ba4843b83..43206c9b1c37f 100644 --- a/bundles/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/BlueGigaBluetoothCharacteristic.java +++ b/bundles/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/BlueGigaBluetoothCharacteristic.java @@ -27,7 +27,7 @@ */ public class BlueGigaBluetoothCharacteristic extends BluetoothCharacteristic { - private boolean notificationEnabled; + private boolean notifying; public BlueGigaBluetoothCharacteristic(int handle) { super(null, handle); @@ -45,11 +45,11 @@ public void setUUID(UUID uuid) { this.uuid = uuid; } - public boolean isNotificationEnabled() { - return notificationEnabled; + public boolean isNotifying() { + return notifying; } - public void setNotificationEnabled(boolean enable) { - this.notificationEnabled = enable; + public void setNotifying(boolean enable) { + this.notifying = enable; } } diff --git a/bundles/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/BlueGigaBluetoothDevice.java b/bundles/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/BlueGigaBluetoothDevice.java index c2cb969e8fe4e..ccff259a0b878 100644 --- a/bundles/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/BlueGigaBluetoothDevice.java +++ b/bundles/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/BlueGigaBluetoothDevice.java @@ -196,7 +196,7 @@ public boolean enableNotifications(BluetoothCharacteristic characteristic) { } BlueGigaBluetoothCharacteristic ch = (BlueGigaBluetoothCharacteristic) characteristic; - if (ch.isNotificationEnabled()) { + if (ch.isNotifying()) { return true; } @@ -241,12 +241,12 @@ public boolean enableNotifications(BluetoothCharacteristic characteristic) { @Override public boolean disableNotifications(BluetoothCharacteristic characteristic) { if (connection == -1) { - logger.debug("Cannot enable notifications, device not connected {}", this); + logger.debug("Cannot disable notifications, device not connected {}", this); return false; } BlueGigaBluetoothCharacteristic ch = (BlueGigaBluetoothCharacteristic) characteristic; - if (ch.isNotificationEnabled()) { + if (!ch.isNotifying()) { return true; } @@ -288,6 +288,12 @@ public boolean disableNotifications(BluetoothCharacteristic characteristic) { return true; } + @Override + public boolean isNotifying(BluetoothCharacteristic characteristic) { + BlueGigaBluetoothCharacteristic ch = (BlueGigaBluetoothCharacteristic) characteristic; + return ch.isNotifying(); + } + @Override public boolean enableNotifications(BluetoothDescriptor descriptor) { // TODO will be implemented in a followup PR @@ -613,7 +619,7 @@ private void handleProcedureCompletedEvent(BlueGigaProcedureCompletedEvent event if (!success) { logger.debug("write to descriptor failed"); } - ((BlueGigaBluetoothCharacteristic) procedureCharacteristic).setNotificationEnabled(success); + ((BlueGigaBluetoothCharacteristic) procedureCharacteristic).setNotifying(success); procedureProgress = BlueGigaProcedure.NONE; procedureCharacteristic = null; break; @@ -622,7 +628,7 @@ private void handleProcedureCompletedEvent(BlueGigaProcedureCompletedEvent event if (!success) { logger.debug("write to descriptor failed"); } - ((BlueGigaBluetoothCharacteristic) procedureCharacteristic).setNotificationEnabled(!success); + ((BlueGigaBluetoothCharacteristic) procedureCharacteristic).setNotifying(!success); procedureProgress = BlueGigaProcedure.NONE; procedureCharacteristic = null; break; @@ -656,7 +662,7 @@ private void handleDisconnectedEvent(BlueGigaDisconnectedEvent event) { } for (BlueGigaBluetoothCharacteristic ch : handleToCharacteristic.values()) { - ch.setNotificationEnabled(false); + ch.setNotifying(false); } cancelTimer(procedureTimer); diff --git a/bundles/org.openhab.binding.bluetooth.bluez/src/main/java/org/openhab/binding/bluetooth/bluez/internal/BlueZBluetoothDevice.java b/bundles/org.openhab.binding.bluetooth.bluez/src/main/java/org/openhab/binding/bluetooth/bluez/internal/BlueZBluetoothDevice.java index f4d78ab591e64..509f21fa429a1 100644 --- a/bundles/org.openhab.binding.bluetooth.bluez/src/main/java/org/openhab/binding/bluetooth/bluez/internal/BlueZBluetoothDevice.java +++ b/bundles/org.openhab.binding.bluetooth.bluez/src/main/java/org/openhab/binding/bluetooth/bluez/internal/BlueZBluetoothDevice.java @@ -57,6 +57,7 @@ * * @author Kai Kreuzer - Initial contribution and API * @author Benjamin Lafois - Replaced tinyB with bluezDbus + * @author Peter Rosenberg - Improve notifications and properties support * */ @NonNullByDefault @@ -399,6 +400,7 @@ public boolean discoverServices() { for (BluetoothGattCharacteristic dBusBlueZCharacteristic : dBusBlueZService.getGattCharacteristics()) { BluetoothCharacteristic characteristic = new BluetoothCharacteristic( UUID.fromString(dBusBlueZCharacteristic.getUuid()), 0); + convertCharacteristicProperties(dBusBlueZCharacteristic, characteristic); for (BluetoothGattDescriptor dBusBlueZDescriptor : dBusBlueZCharacteristic.getGattDescriptors()) { BluetoothDescriptor descriptor = new BluetoothDescriptor(characteristic, @@ -414,6 +416,42 @@ public boolean discoverServices() { return true; } + /** + * Convert the flags of BluetoothGattCharacteristic to the int bitset used by BluetoothCharacteristic. + * + * @param dBusBlueZCharacteristic source characteristic to read the flags from + * @param characteristic destination characteristic to write to properties to + */ + private void convertCharacteristicProperties(BluetoothGattCharacteristic dBusBlueZCharacteristic, + BluetoothCharacteristic characteristic) { + int properties = 0; + + for (String property : dBusBlueZCharacteristic.getFlags()) { + switch (property) { + case "broadcast": + properties |= BluetoothCharacteristic.PROPERTY_BROADCAST; + break; + case "read": + properties |= BluetoothCharacteristic.PROPERTY_READ; + break; + case "write-without-response": + properties |= BluetoothCharacteristic.PROPERTY_WRITE_NO_RESPONSE; + break; + case "write": + properties |= BluetoothCharacteristic.PROPERTY_WRITE; + break; + case "notify": + properties |= BluetoothCharacteristic.PROPERTY_NOTIFY; + break; + case "indicate": + properties |= BluetoothCharacteristic.PROPERTY_INDICATE; + break; + } + } + + characteristic.setProperties(properties); + } + @Override public boolean readCharacteristic(BluetoothCharacteristic characteristic) { BluetoothGattCharacteristic c = getDBusBlueZCharacteristicByUUID(characteristic.getUuid().toString()); @@ -428,7 +466,8 @@ public boolean readCharacteristic(BluetoothCharacteristic characteristic) { characteristic.setValue(value); notifyListeners(BluetoothEventType.CHARACTERISTIC_READ_COMPLETE, characteristic, BluetoothCompletionStatus.SUCCESS); - } catch (DBusException e) { + } catch (DBusException | DBusExecutionException e) { + // DBusExecutionException is thrown if the value cannot be read logger.debug("Exception occurred when trying to read characteristic '{}': {}", characteristic.getUuid(), e.getMessage()); notifyListeners(BluetoothEventType.CHARACTERISTIC_READ_COMPLETE, characteristic, @@ -438,6 +477,18 @@ public boolean readCharacteristic(BluetoothCharacteristic characteristic) { return true; } + @Override + public boolean isNotifying(BluetoothCharacteristic characteristic) { + BluetoothGattCharacteristic c = getDBusBlueZCharacteristicByUUID(characteristic.getUuid().toString()); + if (c != null) { + Boolean isNotifying = c.isNotifying(); + return Objects.requireNonNullElse(isNotifying, false); + } else { + logger.warn("Characteristic '{}' is missing on device '{}'.", characteristic.getUuid(), address); + return false; + } + } + @Override public boolean disableNotifications(BluetoothCharacteristic characteristic) { BluetoothGattCharacteristic c = getDBusBlueZCharacteristicByUUID(characteristic.getUuid().toString()); diff --git a/bundles/org.openhab.binding.bluetooth.generic/src/main/java/org/openhab/binding/bluetooth/generic/internal/GenericBluetoothHandler.java b/bundles/org.openhab.binding.bluetooth.generic/src/main/java/org/openhab/binding/bluetooth/generic/internal/GenericBluetoothHandler.java index 5717b08210729..633170f4284e4 100644 --- a/bundles/org.openhab.binding.bluetooth.generic/src/main/java/org/openhab/binding/bluetooth/generic/internal/GenericBluetoothHandler.java +++ b/bundles/org.openhab.binding.bluetooth.generic/src/main/java/org/openhab/binding/bluetooth/generic/internal/GenericBluetoothHandler.java @@ -58,6 +58,7 @@ * channels based off of a bluetooth device's GATT characteristics. * * @author Connor Petty - Initial contribution + * @author Peter Rosenberg - Use notifications * */ @NonNullByDefault @@ -68,6 +69,7 @@ public class GenericBluetoothHandler extends ConnectedBluetoothHandler { private final Map channelHandlers = new ConcurrentHashMap<>(); private final BluetoothGattParser gattParser = BluetoothGattParserFactory.getDefault(); private final CharacteristicChannelTypeProvider channelTypeProvider; + private final Map> handlerToChannels = new ConcurrentHashMap<>(); private @Nullable ScheduledFuture readCharacteristicJob = null; @@ -84,12 +86,14 @@ public void initialize() { readCharacteristicJob = scheduler.scheduleWithFixedDelay(() -> { if (device.getConnectionState() == ConnectionState.CONNECTED) { if (resolved) { - for (CharacteristicHandler charHandler : charHandlers.values()) { - if (charHandler.canRead()) { + handlerToChannels.forEach((charHandler, channelUids) -> { + // Only read the value manually if notification is not on. + // Also read it the first time before we activate notifications below. + if (!device.isNotifying(charHandler.characteristic) && charHandler.canRead()) { device.readCharacteristic(charHandler.characteristic); try { // TODO the ideal solution would be to use locks/conditions and timeouts - // between this code and `onCharacteristicReadComplete` but + // Kbetween this code and `onCharacteristicReadComplete` but // that would overcomplicate the code a bit and I plan // on implementing a better more generalized solution later Thread.sleep(50); @@ -97,7 +101,20 @@ public void initialize() { return; } } - } + if (charHandler.characteristic.canNotify()) { + // Enabled/Disable notifications dependent on if the channel is linked. + // TODO check why isLinked() is true for not linked channels + if (channelUids.stream().anyMatch(this::isLinked)) { + if (!device.isNotifying(charHandler.characteristic)) { + device.enableNotifications(charHandler.characteristic); + } + } else { + if (device.isNotifying(charHandler.characteristic)) { + device.disableNotifications(charHandler.characteristic); + } + } + } + }); } else { // if we are connected and still haven't been able to resolve the services, try disconnecting and // then connecting again @@ -117,6 +134,7 @@ public void dispose() { charHandlers.clear(); channelHandlers.clear(); + handlerToChannels.clear(); } @Override @@ -161,9 +179,11 @@ private void updateThingChannels() { logger.trace("{} processing characteristic {}", address, characteristic.getUuid()); CharacteristicHandler handler = getCharacteristicHandler(characteristic); List chans = handler.buildChannels(); - for (Channel channel : chans) { - channelHandlers.put(channel.getUID(), handler); + List chanUids = chans.stream().map(Channel::getUID).collect(Collectors.toList()); + for (ChannelUID channel : chanUids) { + channelHandlers.put(channel, handler); } + handlerToChannels.put(handler, chanUids); return chans.stream(); })// .collect(Collectors.toList()); @@ -341,8 +361,7 @@ public boolean canRead() { if (gattParser.isKnownCharacteristic(charUUID)) { return gattParser.isValidForRead(charUUID); } - // TODO: need to evaluate this from characteristic properties, but such properties aren't support yet - return true; + return characteristic.canRead(); } public boolean canWrite() { @@ -350,8 +369,7 @@ public boolean canWrite() { if (gattParser.isKnownCharacteristic(charUUID)) { return gattParser.isValidForWrite(charUUID); } - // TODO: need to evaluate this from characteristic properties, but such properties aren't support yet - return true; + return characteristic.canWrite(); } private boolean isAdvanced() { diff --git a/bundles/org.openhab.binding.bluetooth/src/main/java/org/openhab/binding/bluetooth/BeaconBluetoothHandler.java b/bundles/org.openhab.binding.bluetooth/src/main/java/org/openhab/binding/bluetooth/BeaconBluetoothHandler.java index deb1cce3279a8..c1602e52d216a 100644 --- a/bundles/org.openhab.binding.bluetooth/src/main/java/org/openhab/binding/bluetooth/BeaconBluetoothHandler.java +++ b/bundles/org.openhab.binding.bluetooth/src/main/java/org/openhab/binding/bluetooth/BeaconBluetoothHandler.java @@ -19,7 +19,6 @@ import javax.measure.quantity.Power; -import org.apache.commons.lang.StringUtils; import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.Nullable; import org.openhab.binding.bluetooth.BluetoothDevice.ConnectionState; @@ -193,7 +192,7 @@ protected void updateAdapterLocation() { if (device != null) { BluetoothAdapter adapter = device.getAdapter(); String location = adapter.getLocation(); - if (location != null || StringUtils.isBlank(location)) { + if (location != null && !location.isBlank()) { updateState(BluetoothBindingConstants.CHANNEL_TYPE_ADAPTER_LOCATION, new StringType(location)); } else { updateState(BluetoothBindingConstants.CHANNEL_TYPE_ADAPTER_LOCATION, UnDefType.NULL); diff --git a/bundles/org.openhab.binding.bluetooth/src/main/java/org/openhab/binding/bluetooth/BluetoothCharacteristic.java b/bundles/org.openhab.binding.bluetooth/src/main/java/org/openhab/binding/bluetooth/BluetoothCharacteristic.java index 0a014ecb36e93..dd591acc723f6 100644 --- a/bundles/org.openhab.binding.bluetooth/src/main/java/org/openhab/binding/bluetooth/BluetoothCharacteristic.java +++ b/bundles/org.openhab.binding.bluetooth/src/main/java/org/openhab/binding/bluetooth/BluetoothCharacteristic.java @@ -31,6 +31,7 @@ * * @author Chris Jackson - Initial contribution * @author Kai Kreuzer - Cleaned up code + * @author Peter Rosenberg - Improve properties support */ public class BluetoothCharacteristic { public static final int FORMAT_UINT8 = 0x11; @@ -142,6 +143,16 @@ public int getInstanceId() { return instance; } + /** + * Set the raw properties. The individual properties are represented as bits inside + * of this int value. + * + * @param properties of this Characteristic + */ + public void setProperties(int properties) { + this.properties = properties; + } + /** * Returns the properties of this characteristic. * @@ -152,6 +163,46 @@ public int getProperties() { return properties; } + /** + * Returns if the given characteristics property is enabled or not. + * + * @param property one of the Constants BluetoothCharacteristic.PROPERTY_XX + * @return true if this characteristic has the given property enabled, false if properties not set or + * the given property is not enabled. + */ + public boolean hasPropertyEnabled(int property) { + return (properties & property) != 0; + } + + /** + * Returns if notifications can be enabled on this characteristic. + * + * @return true if notifications can be enabled, false if notifications are not supported, characteristic is missing + * on device or notifications are not supported. + */ + public boolean canNotify() { + return hasPropertyEnabled(BluetoothCharacteristic.PROPERTY_NOTIFY); + } + + /** + * Returns if the value can be read on this characteristic. + * + * @return true if the value can be read, false otherwise. + */ + public boolean canRead() { + return hasPropertyEnabled(BluetoothCharacteristic.PROPERTY_READ); + } + + /** + * Returns if the value can be written on this characteristic. + * + * @return true if the value can be written with of without a response, false otherwise. + */ + public boolean canWrite() { + return hasPropertyEnabled(BluetoothCharacteristic.PROPERTY_WRITE) + || hasPropertyEnabled(BluetoothCharacteristic.PROPERTY_WRITE_NO_RESPONSE); + } + /** * Returns the permissions for this characteristic. */ diff --git a/bundles/org.openhab.binding.bluetooth/src/main/java/org/openhab/binding/bluetooth/BluetoothDevice.java b/bundles/org.openhab.binding.bluetooth/src/main/java/org/openhab/binding/bluetooth/BluetoothDevice.java index f9dfde0862b5d..ba1186829f12b 100644 --- a/bundles/org.openhab.binding.bluetooth/src/main/java/org/openhab/binding/bluetooth/BluetoothDevice.java +++ b/bundles/org.openhab.binding.bluetooth/src/main/java/org/openhab/binding/bluetooth/BluetoothDevice.java @@ -28,6 +28,7 @@ * @author Chris Jackson - Initial contribution * @author Kai Kreuzer - Refactored class to use Integer instead of int, fixed bugs, diverse improvements * @author Connor Petty - Made most of the methods abstract + * @author Peter Rosenberg - Improve notifications */ @NonNullByDefault public abstract class BluetoothDevice { @@ -249,6 +250,15 @@ public BluetoothAdapter getAdapter() { */ public abstract boolean writeCharacteristic(BluetoothCharacteristic characteristic); + /** + * Returns if notification is enabled for the given characteristic. + * + * @param characteristic the {@link BluetoothCharacteristic} to check if notifications are enabled. + * @return true if notification is enabled, false if notification is disabled, characteristic is missing on device + * or notifications are not supported. + */ + public abstract boolean isNotifying(BluetoothCharacteristic characteristic); + /** * Enables notifications for a characteristic. Only a single read or write operation can be requested at once. * Attempting to perform an operation when one is already in progress will result in subsequent calls returning diff --git a/bundles/org.openhab.binding.bluetooth/src/main/java/org/openhab/binding/bluetooth/DelegateBluetoothDevice.java b/bundles/org.openhab.binding.bluetooth/src/main/java/org/openhab/binding/bluetooth/DelegateBluetoothDevice.java index bb4c37029bf1b..074d9a8f39e7e 100644 --- a/bundles/org.openhab.binding.bluetooth/src/main/java/org/openhab/binding/bluetooth/DelegateBluetoothDevice.java +++ b/bundles/org.openhab.binding.bluetooth/src/main/java/org/openhab/binding/bluetooth/DelegateBluetoothDevice.java @@ -112,6 +112,12 @@ public boolean writeCharacteristic(BluetoothCharacteristic characteristic) { return delegate != null && delegate.writeCharacteristic(characteristic); } + @Override + public boolean isNotifying(BluetoothCharacteristic characteristic) { + BluetoothDevice delegate = getDelegate(); + return delegate != null ? delegate.isNotifying(characteristic) : false; + } + @Override public boolean enableNotifications(BluetoothCharacteristic characteristic) { BluetoothDevice delegate = getDelegate(); diff --git a/bundles/org.openhab.binding.bluetooth/src/main/java/org/openhab/binding/bluetooth/discovery/internal/BluetoothDiscoveryService.java b/bundles/org.openhab.binding.bluetooth/src/main/java/org/openhab/binding/bluetooth/discovery/internal/BluetoothDiscoveryService.java index af31f910b25e9..1f096e0fb7408 100644 --- a/bundles/org.openhab.binding.bluetooth/src/main/java/org/openhab/binding/bluetooth/discovery/internal/BluetoothDiscoveryService.java +++ b/bundles/org.openhab.binding.bluetooth/src/main/java/org/openhab/binding/bluetooth/discovery/internal/BluetoothDiscoveryService.java @@ -133,6 +133,13 @@ public void stopScan() { for (BluetoothAdapter adapter : adapters) { adapter.scanStop(); } + + // The method `removeOlderResults()` removes the Things from listeners like `Inbox`. + // We therefore need to reset `latestSnapshot` so that the Things are notified again next time. + // Results newer than `getTimestampOfLastScan()` will also be notified again but do not lead to duplicates. + discoveryCaches.values().forEach(discoveryCache -> { + discoveryCache.latestSnapshot.putValue(null); + }); removeOlderResults(getTimestampOfLastScan()); } @@ -155,10 +162,6 @@ private static ThingUID createThingUIDWithBridge(DiscoveryResult result, Bluetoo private static DiscoveryResult copyWithNewBridge(DiscoveryResult result, BluetoothAdapter adapter) { String label = result.getLabel(); - String adapterLabel = adapter.getLabel(); - if (adapterLabel != null) { - label = adapterLabel + " - " + label; - } return DiscoveryResultBuilder.create(createThingUIDWithBridge(result, adapter))// .withBridge(adapter.getUID())// @@ -294,6 +297,12 @@ private void publishDiscoveryResult(BluetoothAdapter adapter, DiscoveryResult re discoveryResults.put(adapter, results); } + /** + * Called when a new discovery is published and thus requires the old discovery to be removed first. + * + * @param adapter to get the results to be removed + * @param result unused + */ private void retractDiscoveryResult(BluetoothAdapter adapter, DiscoveryResult result) { Set results = discoveryResults.remove(adapter); if (results != null) { diff --git a/bundles/org.openhab.binding.bluetooth/src/test/java/org/openhab/binding/bluetooth/CharacteristicPropertiesTest.java b/bundles/org.openhab.binding.bluetooth/src/test/java/org/openhab/binding/bluetooth/CharacteristicPropertiesTest.java new file mode 100644 index 0000000000000..3d40537f6d550 --- /dev/null +++ b/bundles/org.openhab.binding.bluetooth/src/test/java/org/openhab/binding/bluetooth/CharacteristicPropertiesTest.java @@ -0,0 +1,101 @@ +/** + * Copyright (c) 2010-2021 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.bluetooth; + +import static org.junit.jupiter.api.Assertions.*; + +import java.util.UUID; + +import org.junit.jupiter.api.Test; + +/** + * Tests {@link BluetoothCharacteristic}. + * + * @author Peter Rosenberg - Initial contribution + */ +public class CharacteristicPropertiesTest { + private BluetoothCharacteristic characteristic = new BluetoothCharacteristic(UUID.randomUUID(), 0); + + @Test + public void testAllSupportedProperties() { + // given + // when + int properties = 0; + properties |= BluetoothCharacteristic.PROPERTY_BROADCAST; + properties |= BluetoothCharacteristic.PROPERTY_READ; + properties |= BluetoothCharacteristic.PROPERTY_WRITE_NO_RESPONSE; + properties |= BluetoothCharacteristic.PROPERTY_WRITE; + properties |= BluetoothCharacteristic.PROPERTY_NOTIFY; + properties |= BluetoothCharacteristic.PROPERTY_INDICATE; + characteristic.setProperties(properties); + + // then + assertTrue(characteristic.hasPropertyEnabled(BluetoothCharacteristic.PROPERTY_BROADCAST), "Broastcast not set"); + assertTrue(characteristic.hasPropertyEnabled(BluetoothCharacteristic.PROPERTY_READ), "Read not set"); + assertTrue(characteristic.hasPropertyEnabled(BluetoothCharacteristic.PROPERTY_WRITE_NO_RESPONSE), + "Write not response not set"); + assertTrue(characteristic.hasPropertyEnabled(BluetoothCharacteristic.PROPERTY_WRITE), "Write not set"); + assertTrue(characteristic.hasPropertyEnabled(BluetoothCharacteristic.PROPERTY_NOTIFY), "Notify not set"); + assertTrue(characteristic.hasPropertyEnabled(BluetoothCharacteristic.PROPERTY_INDICATE), "Indicate not set"); + assertFalse(characteristic.hasPropertyEnabled(BluetoothCharacteristic.PROPERTY_SIGNED_WRITE), + "Signed write set"); + assertFalse(characteristic.hasPropertyEnabled(BluetoothCharacteristic.PROPERTY_EXTENDED_PROPS), + "Extended props set"); + } + + @Test + public void testNoProperties() { + // given + // when + int properties = 0; + characteristic.setProperties(properties); + + // then + assertFalse(characteristic.hasPropertyEnabled(BluetoothCharacteristic.PROPERTY_BROADCAST), + "Broastcast not set"); + assertFalse(characteristic.hasPropertyEnabled(BluetoothCharacteristic.PROPERTY_READ), "Read not set"); + assertFalse(characteristic.hasPropertyEnabled(BluetoothCharacteristic.PROPERTY_WRITE_NO_RESPONSE), + "Write not response not set"); + assertFalse(characteristic.hasPropertyEnabled(BluetoothCharacteristic.PROPERTY_WRITE), "Write not set"); + assertFalse(characteristic.hasPropertyEnabled(BluetoothCharacteristic.PROPERTY_NOTIFY), "Notify not set"); + assertFalse(characteristic.hasPropertyEnabled(BluetoothCharacteristic.PROPERTY_INDICATE), "Indicate not set"); + assertFalse(characteristic.hasPropertyEnabled(BluetoothCharacteristic.PROPERTY_SIGNED_WRITE), + "Signed write set"); + assertFalse(characteristic.hasPropertyEnabled(BluetoothCharacteristic.PROPERTY_EXTENDED_PROPS), + "Extended props set"); + } + + @Test + public void testSomeSupportedProperties() { + // given + // when + int properties = 0; + properties |= BluetoothCharacteristic.PROPERTY_READ; + properties |= BluetoothCharacteristic.PROPERTY_NOTIFY; + characteristic.setProperties(properties); + + // then + assertFalse(characteristic.hasPropertyEnabled(BluetoothCharacteristic.PROPERTY_BROADCAST), + "Broastcast not set"); + assertTrue(characteristic.hasPropertyEnabled(BluetoothCharacteristic.PROPERTY_READ), "Read not set"); + assertFalse(characteristic.hasPropertyEnabled(BluetoothCharacteristic.PROPERTY_WRITE_NO_RESPONSE), + "Write not response not set"); + assertFalse(characteristic.hasPropertyEnabled(BluetoothCharacteristic.PROPERTY_WRITE), "Write not set"); + assertTrue(characteristic.hasPropertyEnabled(BluetoothCharacteristic.PROPERTY_NOTIFY), "Notify not set"); + assertFalse(characteristic.hasPropertyEnabled(BluetoothCharacteristic.PROPERTY_INDICATE), "Indicate not set"); + assertFalse(characteristic.hasPropertyEnabled(BluetoothCharacteristic.PROPERTY_SIGNED_WRITE), + "Signed write set"); + assertFalse(characteristic.hasPropertyEnabled(BluetoothCharacteristic.PROPERTY_EXTENDED_PROPS), + "Extended props set"); + } +} diff --git a/bundles/org.openhab.binding.bluetooth/src/test/java/org/openhab/binding/bluetooth/MockBluetoothDevice.java b/bundles/org.openhab.binding.bluetooth/src/test/java/org/openhab/binding/bluetooth/MockBluetoothDevice.java index d5170cd071df5..892362f86c1d2 100644 --- a/bundles/org.openhab.binding.bluetooth/src/test/java/org/openhab/binding/bluetooth/MockBluetoothDevice.java +++ b/bundles/org.openhab.binding.bluetooth/src/test/java/org/openhab/binding/bluetooth/MockBluetoothDevice.java @@ -102,6 +102,11 @@ public boolean enableNotifications(BluetoothCharacteristic characteristic) { return false; } + @Override + public boolean isNotifying(BluetoothCharacteristic characteristic) { + return false; + } + @Override public boolean disableNotifications(BluetoothCharacteristic characteristic) { return false; diff --git a/bundles/org.openhab.binding.bluetooth/src/test/java/org/openhab/binding/bluetooth/TestUtils.java b/bundles/org.openhab.binding.bluetooth/src/test/java/org/openhab/binding/bluetooth/TestUtils.java index fc2bbd8fc5818..c87ebec3a6788 100644 --- a/bundles/org.openhab.binding.bluetooth/src/test/java/org/openhab/binding/bluetooth/TestUtils.java +++ b/bundles/org.openhab.binding.bluetooth/src/test/java/org/openhab/binding/bluetooth/TestUtils.java @@ -12,7 +12,7 @@ */ package org.openhab.binding.bluetooth; -import org.apache.commons.lang.RandomStringUtils; +import org.apache.commons.lang3.RandomStringUtils; import org.eclipse.jdt.annotation.NonNullByDefault; import org.openhab.core.thing.ThingUID; diff --git a/bundles/org.openhab.binding.bluetooth/src/test/java/org/openhab/binding/bluetooth/discovery/internal/BluetoothDiscoveryServiceTest.java b/bundles/org.openhab.binding.bluetooth/src/test/java/org/openhab/binding/bluetooth/discovery/internal/BluetoothDiscoveryServiceTest.java index 141146d4911d8..b05b6e4e46eca 100644 --- a/bundles/org.openhab.binding.bluetooth/src/test/java/org/openhab/binding/bluetooth/discovery/internal/BluetoothDiscoveryServiceTest.java +++ b/bundles/org.openhab.binding.bluetooth/src/test/java/org/openhab/binding/bluetooth/discovery/internal/BluetoothDiscoveryServiceTest.java @@ -23,7 +23,7 @@ import java.util.concurrent.atomic.AtomicInteger; import java.util.function.BiConsumer; -import org.apache.commons.lang.RandomStringUtils; +import org.apache.commons.lang3.RandomStringUtils; import org.eclipse.jdt.annotation.NonNull; import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.Nullable; diff --git a/bundles/org.openhab.binding.bosesoundtouch/src/main/java/org/openhab/binding/bosesoundtouch/internal/ContentItem.java b/bundles/org.openhab.binding.bosesoundtouch/src/main/java/org/openhab/binding/bosesoundtouch/internal/ContentItem.java index d119580182331..84c5a40e355f4 100644 --- a/bundles/org.openhab.binding.bosesoundtouch/src/main/java/org/openhab/binding/bosesoundtouch/internal/ContentItem.java +++ b/bundles/org.openhab.binding.bosesoundtouch/src/main/java/org/openhab/binding/bosesoundtouch/internal/ContentItem.java @@ -15,7 +15,7 @@ import java.util.HashMap; import java.util.Map; -import org.apache.commons.lang.StringEscapeUtils; +import org.apache.commons.lang3.StringEscapeUtils; import org.openhab.core.types.StateOption; import com.google.gson.annotations.Expose; diff --git a/bundles/org.openhab.binding.bsblan/README.md b/bundles/org.openhab.binding.bsblan/README.md index feb29159049d8..c9b48a222d055 100644 --- a/bundles/org.openhab.binding.bsblan/README.md +++ b/bundles/org.openhab.binding.bsblan/README.md @@ -1,6 +1,6 @@ # BSB-LAN Binding -This binding uses the REST API of [BSB-LPB-PPS-LAN](https://github.com/fredlcore/bsb_lan) to obtain data from the device. +This binding uses the REST API of [BSB-LPB-PPS-LAN](https://github.com/fredlcore/BSB-LAN) to obtain data from the device. ## Supported Things diff --git a/bundles/org.openhab.binding.bsblan/src/main/java/org/openhab/binding/bsblan/internal/api/BsbLanApiCaller.java b/bundles/org.openhab.binding.bsblan/src/main/java/org/openhab/binding/bsblan/internal/api/BsbLanApiCaller.java index 62946ffd7a17c..50214b06767bc 100644 --- a/bundles/org.openhab.binding.bsblan/src/main/java/org/openhab/binding/bsblan/internal/api/BsbLanApiCaller.java +++ b/bundles/org.openhab.binding.bsblan/src/main/java/org/openhab/binding/bsblan/internal/api/BsbLanApiCaller.java @@ -12,16 +12,15 @@ */ package org.openhab.binding.bsblan.internal.api; -import static org.openhab.binding.bsblan.internal.BsbLanBindingConstants.*; +import static org.openhab.binding.bsblan.internal.BsbLanBindingConstants.API_TIMEOUT; import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.InputStream; import java.nio.charset.Charset; -import java.util.HashSet; import java.util.Set; +import java.util.stream.Collectors; -import org.apache.commons.lang.StringUtils; import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.Nullable; import org.openhab.binding.bsblan.internal.api.dto.BsbLanApiContentDTO; @@ -50,17 +49,14 @@ public BsbLanApiCaller(BsbLanBridgeConfiguration config) { } public @Nullable BsbLanApiParameterQueryResponseDTO queryParameter(Integer parameterId) { - Set parameters = new HashSet<>(); - - parameters.add(parameterId); - return queryParameters(parameters); + return queryParameters(Set.of(parameterId)); } public @Nullable BsbLanApiParameterQueryResponseDTO queryParameters(Set parameterIds) { // note: make the request even if parameterIds is empty as // thing OFFLINE/ONLINE detection relies on a response - - String apiPath = String.format("/JQ=%s", StringUtils.join(parameterIds, ",")); + String apiPath = String.format("/JQ=%s", + parameterIds.stream().map(String::valueOf).collect(Collectors.joining(","))); return makeRestCall(BsbLanApiParameterQueryResponseDTO.class, "GET", apiPath, null); } @@ -96,21 +92,21 @@ public boolean setParameter(Integer parameterId, String value, BsbLanApiParamete } private String createApiBaseUrl() { - final String host = StringUtils.trimToEmpty(bridgeConfig.host); - final String username = StringUtils.trimToEmpty(bridgeConfig.username); - final String password = StringUtils.trimToEmpty(bridgeConfig.password); - final String passkey = StringUtils.trimToEmpty(bridgeConfig.passkey); + final String host = bridgeConfig.host == null ? "" : bridgeConfig.host.trim(); + final String username = bridgeConfig.username == null ? "" : bridgeConfig.username.trim(); + final String password = bridgeConfig.password == null ? "" : bridgeConfig.password.trim(); + final String passkey = bridgeConfig.passkey == null ? "" : bridgeConfig.passkey.trim(); StringBuilder url = new StringBuilder(); url.append("http://"); - if (StringUtils.isNotBlank(username) && StringUtils.isNotBlank(password)) { + if (!username.isBlank() && !password.isBlank()) { url.append(username).append(":").append(password).append("@"); } url.append(host); if (bridgeConfig.port != 80) { url.append(":").append(bridgeConfig.port); } - if (StringUtils.isNotBlank(passkey)) { + if (!passkey.isBlank()) { url.append("/").append(passkey); } return url.toString(); @@ -134,7 +130,7 @@ private String createApiBaseUrl() { if (request != null) { String content = BsbLanApiContentConverter.toJson(request); logger.trace("api request content: '{}'", content); - if (StringUtils.isNotBlank(content)) { + if (!content.isBlank()) { contentStream = new ByteArrayInputStream(content.getBytes(Charset.forName("UTF-8"))); contentType = "application/json"; } diff --git a/bundles/org.openhab.binding.bsblan/src/main/java/org/openhab/binding/bsblan/internal/handler/BsbLanBridgeHandler.java b/bundles/org.openhab.binding.bsblan/src/main/java/org/openhab/binding/bsblan/internal/handler/BsbLanBridgeHandler.java index 0c81607a6f62b..b07b76f0ea540 100644 --- a/bundles/org.openhab.binding.bsblan/src/main/java/org/openhab/binding/bsblan/internal/handler/BsbLanBridgeHandler.java +++ b/bundles/org.openhab.binding.bsblan/src/main/java/org/openhab/binding/bsblan/internal/handler/BsbLanBridgeHandler.java @@ -18,9 +18,8 @@ import java.util.Set; import java.util.concurrent.ScheduledFuture; import java.util.concurrent.TimeUnit; -import java.util.stream.*; +import java.util.stream.Collectors; -import org.apache.commons.lang.StringUtils; import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.Nullable; import org.openhab.binding.bsblan.internal.api.BsbLanApiCaller; @@ -78,7 +77,8 @@ public void initialize() { bridgeConfig = getConfigAs(BsbLanBridgeConfiguration.class); // validate 'host' configuration - if (StringUtils.isBlank(bridgeConfig.host)) { + String host = bridgeConfig.host; + if (host == null || host.isBlank()) { updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, "Parameter 'host' is mandatory and must be configured"); return; @@ -128,8 +128,10 @@ private void doRefresh() { BsbLanApiCaller apiCaller = new BsbLanApiCaller(bridgeConfig); // refresh all parameters - Set parameterIds = things.stream().filter(thing -> thing instanceof BsbLanParameterHandler) - .map(thing -> (BsbLanParameterHandler) thing).map(thing -> thing.getParameterId()) + Set parameterIds = things.stream() // + .filter(thing -> thing instanceof BsbLanParameterHandler) // + .map(thing -> (BsbLanParameterHandler) thing) // + .map(thing -> thing.getParameterId()) // .collect(Collectors.toSet()); cachedParameterQueryResponse = apiCaller.queryParameters(parameterIds); diff --git a/bundles/org.openhab.binding.bsblan/src/main/java/org/openhab/binding/bsblan/internal/helper/BsbLanParameterConverter.java b/bundles/org.openhab.binding.bsblan/src/main/java/org/openhab/binding/bsblan/internal/helper/BsbLanParameterConverter.java index a0d285a3e3348..e9d24f19c9e09 100644 --- a/bundles/org.openhab.binding.bsblan/src/main/java/org/openhab/binding/bsblan/internal/helper/BsbLanParameterConverter.java +++ b/bundles/org.openhab.binding.bsblan/src/main/java/org/openhab/binding/bsblan/internal/helper/BsbLanParameterConverter.java @@ -14,10 +14,11 @@ import static org.openhab.binding.bsblan.internal.BsbLanBindingConstants.*; -import org.apache.commons.lang.StringEscapeUtils; +import org.apache.commons.lang3.StringEscapeUtils; import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.Nullable; import org.openhab.binding.bsblan.internal.api.dto.BsbLanApiParameterDTO; +import org.openhab.binding.bsblan.internal.handler.BsbLanParameterHandler; import org.openhab.core.library.types.DecimalType; import org.openhab.core.library.types.OnOffType; import org.openhab.core.library.types.StringType; @@ -74,7 +75,7 @@ private static State getStateForDescriptionChannel(BsbLanApiParameterDTO paramet } private static State getStateForUnitChannel(BsbLanApiParameterDTO parameter) { - String value = StringEscapeUtils.unescapeHtml(parameter.unit); + String value = StringEscapeUtils.unescapeHtml4(parameter.unit); return new StringType(value); } @@ -110,7 +111,7 @@ private static State getStateForSwitchValueChannel(BsbLanApiParameterDTO paramet /** * Converts a Command back to a value which is sent to the BSB-LAN device afterwards. - * + * * @param channelId * @param command * @return null if conversion fails or channel is readonly. diff --git a/bundles/org.openhab.binding.bsblan/src/test/java/org/openhab/binding/bsblan/internal/api/BsbLanApiContentConverterTests.java b/bundles/org.openhab.binding.bsblan/src/test/java/org/openhab/binding/bsblan/internal/api/BsbLanApiContentConverterTests.java index 9309cf3fb4d72..4e5abe9f1a189 100644 --- a/bundles/org.openhab.binding.bsblan/src/test/java/org/openhab/binding/bsblan/internal/api/BsbLanApiContentConverterTests.java +++ b/bundles/org.openhab.binding.bsblan/src/test/java/org/openhab/binding/bsblan/internal/api/BsbLanApiContentConverterTests.java @@ -61,8 +61,7 @@ public void serializeBsbLanApiParameterSetRequest() { String serializedRequest = BsbLanApiContentConverter.toJson(request); // verify serialized content - JsonParser parser = new JsonParser(); - JsonObject json = parser.parse(serializedRequest).getAsJsonObject(); + JsonObject json = JsonParser.parseString(serializedRequest).getAsJsonObject(); // Although specifying the parameter as int (which would be nicer) also seems to work, // we use a String here as this is the way it is noted in the documentation. diff --git a/bundles/org.openhab.binding.bticinosmarther/src/main/java/org/openhab/binding/bticinosmarther/internal/account/SmartherAccountService.java b/bundles/org.openhab.binding.bticinosmarther/src/main/java/org/openhab/binding/bticinosmarther/internal/account/SmartherAccountService.java index b3914acb496c1..30fe6f84f5f12 100644 --- a/bundles/org.openhab.binding.bticinosmarther/src/main/java/org/openhab/binding/bticinosmarther/internal/account/SmartherAccountService.java +++ b/bundles/org.openhab.binding.bticinosmarther/src/main/java/org/openhab/binding/bticinosmarther/internal/account/SmartherAccountService.java @@ -22,13 +22,13 @@ import java.util.Map; import java.util.Optional; import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.Nullable; -import org.eclipse.jetty.util.ConcurrentHashSet; import org.openhab.binding.bticinosmarther.internal.api.dto.Notification; import org.openhab.binding.bticinosmarther.internal.api.dto.Sender; import org.openhab.binding.bticinosmarther.internal.api.exception.SmartherGatewayException; @@ -61,7 +61,7 @@ public class SmartherAccountService { private final Logger logger = LoggerFactory.getLogger(SmartherAccountService.class); - private final Set handlers = new ConcurrentHashSet<>(); + private final Set handlers = ConcurrentHashMap.newKeySet(); private @Nullable HttpService httpService; private @Nullable BundleContext bundleContext; diff --git a/bundles/org.openhab.binding.bticinosmarther/src/main/java/org/openhab/binding/bticinosmarther/internal/api/SmartherApiConnector.java b/bundles/org.openhab.binding.bticinosmarther/src/main/java/org/openhab/binding/bticinosmarther/internal/api/SmartherApiConnector.java index 7117f3207860d..dcfe93dbc1b7e 100644 --- a/bundles/org.openhab.binding.bticinosmarther/src/main/java/org/openhab/binding/bticinosmarther/internal/api/SmartherApiConnector.java +++ b/bundles/org.openhab.binding.bticinosmarther/src/main/java/org/openhab/binding/bticinosmarther/internal/api/SmartherApiConnector.java @@ -84,7 +84,6 @@ public class SmartherApiConnector { private final Logger logger = LoggerFactory.getLogger(SmartherApiConnector.class); - private final JsonParser parser = new JsonParser(); private final HttpClient httpClient; private final ScheduledExecutorService scheduler; @@ -305,7 +304,7 @@ private boolean processResponse(ContentResponse response) throws SmartherGateway private String processErrorState(ContentResponse response) throws SmartherTokenExpiredException, SmartherAuthorizationException, SmartherInvalidResponseException { try { - final JsonElement element = parser.parse(response.getContentAsString()); + final JsonElement element = JsonParser.parseString(response.getContentAsString()); if (element.isJsonObject()) { final JsonObject object = element.getAsJsonObject(); diff --git a/bundles/org.openhab.binding.bticinosmarther/src/main/resources/OH-INF/config/config.xml b/bundles/org.openhab.binding.bticinosmarther/src/main/resources/OH-INF/config/config.xml index 9b91ae8dcde0d..cf4aca17c6cab 100644 --- a/bundles/org.openhab.binding.bticinosmarther/src/main/resources/OH-INF/config/config.xml +++ b/bundles/org.openhab.binding.bticinosmarther/src/main/resources/OH-INF/config/config.xml @@ -24,47 +24,44 @@ - + This is the Subscription Key provided by BTicino/Legrand when you subscribe to Smarther - v2.0 product. Go to https://developer.legrand.com/tutorials/getting-started/ - true + pattern="[0-9a-f]{8}[-]([0-9a-f]{4}[-]){3}[0-9a-f]{12}" required="true"> This is the Client ID provided by BTicino/Legrand when you add a new Application to your developer account. Go to https://developer.legrand.com/tutorials/create-an-application/ - true - + This is the Client Secret provided by BTicino/Legrand when you add a new Application to your developer account. - true password - + ON = the bridge subscribes each of its locations to receive C2C notifications upon changes on each of its modules' status or sensors data - temperature, humidity (requires a public https endpoint has been set as "First Reply Url" when registering the Application on Legrand's development portal); OFF = for each module connected to this bridge, status+sensors data are requested to Smarther API gateway on a periodical basis and whenever new settings are applied (period can be changed via module's "Status Refresh Period" parameter). - false true true - + This is the frequency the Smarther API gateway is called to update bridge status. There are limits to the number of requests that can be sent to the Smarther API gateway. The more often you poll, the faster locations are updated - at the risk of running out of your request quota. - false true Minutes 1440 @@ -88,56 +85,52 @@ + pattern="[0-9a-f]{8}[-]([0-9a-f]{4}[-]){3}[0-9a-f]{12}" required="true"> This is the Plant Id of the location the Chronothermostat module is installed in, provided by Smarther API. - true + pattern="[0-9a-f]{8}[-]([0-9a-f]{4}[-]){3}[0-9a-f]{12}" required="true"> This is the Module Id of the Chronothermostat module, provided by Smarther API. - true - + ON = the module settings are automatically updated according to the module status whenever it changes (e.g. polling, notification, etc.). OFF = the module settings are aligned to the module status only upon module initialization. - false true false - + This is the frequency the Smarther API gateway is called to refresh Programs list used in "automatic" mode. There are limits to the number of requests that can be sent to the Smarther API gateway. The more often you poll, the faster locations are updated - at the risk of running out of your request quota. - false true Hours 12 - + This is the number of days to be displayed in module settings, as options list for "End Date" field in "manual" mode (e.g. 1 = only "Today" is displayed, 5 = "Today" + "Tomorrow" + following 3 days are displayed). - false true 5 - + This is the frequency the Smarther API gateway is called to update module status and sensors data. There are limits to the number of requests that can be sent to the Smarther API gateway. The more often you poll, the faster locations are updated - at the risk of running out of your request quota. - false true Minutes 60 diff --git a/bundles/org.openhab.binding.buienradar/src/main/java/org/openhab/binding/buienradar/internal/BuienradarHandler.java b/bundles/org.openhab.binding.buienradar/src/main/java/org/openhab/binding/buienradar/internal/BuienradarHandler.java index 025e675bc884e..8fcca83298b23 100644 --- a/bundles/org.openhab.binding.buienradar/src/main/java/org/openhab/binding/buienradar/internal/BuienradarHandler.java +++ b/bundles/org.openhab.binding.buienradar/src/main/java/org/openhab/binding/buienradar/internal/BuienradarHandler.java @@ -26,7 +26,6 @@ import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; -import org.apache.commons.lang.StringUtils; import org.eclipse.jdt.annotation.NonNullByDefault; import org.openhab.binding.buienradar.internal.buienradarapi.BuienradarPredictionAPI; import org.openhab.binding.buienradar.internal.buienradarapi.Prediction; @@ -81,7 +80,7 @@ public void initialize() { this.config = getConfigAs(BuienradarConfiguration.class); boolean configValid = true; - if (StringUtils.trimToNull(config.location) == null) { + if (config.location == null || config.location.isBlank()) { updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, "@text/offline.conf-error-missing-location"); configValid = false; diff --git a/bundles/org.openhab.binding.caddx/src/main/java/org/openhab/binding/caddx/internal/handler/CaddxBridgeHandler.java b/bundles/org.openhab.binding.caddx/src/main/java/org/openhab/binding/caddx/internal/handler/CaddxBridgeHandler.java index 33bc24e4cceb1..73a6657e75c56 100644 --- a/bundles/org.openhab.binding.caddx/src/main/java/org/openhab/binding/caddx/internal/handler/CaddxBridgeHandler.java +++ b/bundles/org.openhab.binding.caddx/src/main/java/org/openhab/binding/caddx/internal/handler/CaddxBridgeHandler.java @@ -65,7 +65,6 @@ public class CaddxBridgeHandler extends BaseBridgeHandler implements CaddxPanelListener { private final Logger logger = LoggerFactory.getLogger(CaddxBridgeHandler.class); - static final byte[] DISCOVERY_PARTITION_STATUS_REQUEST_0 = { 0x26, 0x00 }; static final byte[] DISCOVERY_ZONES_SNAPSHOT_REQUEST_00 = { 0x25, 0x00 }; // 1 - 16 static final byte[] DISCOVERY_ZONES_SNAPSHOT_REQUEST_10 = { 0x25, 0x01 }; // 17 - 32 static final byte[] DISCOVERY_ZONES_SNAPSHOT_REQUEST_20 = { 0x25, 0x02 }; // 33 - 48 @@ -156,8 +155,13 @@ public void initialize() { comm.transmit(new CaddxMessage(DISCOVERY_ZONES_SNAPSHOT_REQUEST_B0, false)); // Send discovery commands for the partitions - comm.transmit(new CaddxMessage(DISCOVERY_PARTITION_STATUS_REQUEST_0, false)); comm.transmit(new CaddxMessage(DISCOVERY_PARTITIONS_SNAPSHOT_REQUEST, false)); + + // Send status commands to the zones and partitions + thingZonesMap.forEach((k, v) -> sendCommand(CaddxBindingConstants.ZONE_STATUS_REQUEST, + k.subtract(BigDecimal.ONE).toString())); + thingPartitionsMap.forEach((k, v) -> sendCommand(CaddxBindingConstants.PARTITION_STATUS_REQUEST, + k.subtract(BigDecimal.ONE).toString())); } // list all channels diff --git a/bundles/org.openhab.binding.caddx/src/main/java/org/openhab/binding/caddx/internal/handler/LogEventMessage.java b/bundles/org.openhab.binding.caddx/src/main/java/org/openhab/binding/caddx/internal/handler/LogEventMessage.java index f15c1c97e0ef8..f6e8d66662340 100644 --- a/bundles/org.openhab.binding.caddx/src/main/java/org/openhab/binding/caddx/internal/handler/LogEventMessage.java +++ b/bundles/org.openhab.binding.caddx/src/main/java/org/openhab/binding/caddx/internal/handler/LogEventMessage.java @@ -56,9 +56,6 @@ public String toString() { int eventType = Integer.parseInt(type); logger.trace("eventType received: {}", eventType); LogEventType logEventType = LogEventType.valueOfLogEventType(eventType); - if (logEventType == null) { - return "Unknown log event type received"; - } // Date sb.append(String.format("%02d", Integer.parseInt(day))).append('-') @@ -66,23 +63,27 @@ public String toString() { .append(String.format("%02d", Integer.parseInt(hour))).append(':') .append(String.format("%02d", Integer.parseInt(minute))).append(' '); - sb.append(logEventType.description); - if (logEventType.isPartitionValid) { - sb.append(" Partition ").append(Integer.parseInt(partition) + 1); - } + if (logEventType == null) { + sb.append("Unknown log event type"); + } else { + sb.append(logEventType.description); + if (logEventType.isPartitionValid) { + sb.append(" Partition ").append(Integer.parseInt(partition) + 1); + } - switch (logEventType.zud) { - case None: - break; - case Zone: - sb.append(" Zone ").append(Integer.parseInt(zud) + 1); - break; - case User: - sb.append(" User ").append(Integer.parseInt(zud) + 1); - break; - case Device: - sb.append(" Device ").append(zud); - break; + switch (logEventType.zud) { + case NONE: + break; + case ZONE: + sb.append(" Zone ").append(Integer.parseInt(zud) + 1); + break; + case USER: + sb.append(" User ").append(Integer.parseInt(zud) + 1); + break; + case DEVICE: + sb.append(" Device ").append(zud); + break; + } } return sb.toString(); diff --git a/bundles/org.openhab.binding.caddx/src/main/java/org/openhab/binding/caddx/internal/handler/LogEventType.java b/bundles/org.openhab.binding.caddx/src/main/java/org/openhab/binding/caddx/internal/handler/LogEventType.java index dd6e1205b008e..2e429b9f01c57 100644 --- a/bundles/org.openhab.binding.caddx/src/main/java/org/openhab/binding/caddx/internal/handler/LogEventType.java +++ b/bundles/org.openhab.binding.caddx/src/main/java/org/openhab/binding/caddx/internal/handler/LogEventType.java @@ -25,67 +25,67 @@ */ @NonNullByDefault public enum LogEventType { - ALARM(0, ZoneUserDevice.Zone, true, "Alarm"), - ALARM_RESTORE(1, ZoneUserDevice.Zone, true, "Alarm restore"), - BYPASS(2, ZoneUserDevice.Zone, true, "Bypass"), - BYPASS_RESTORE(3, ZoneUserDevice.Zone, true, "Bypass restore"), - TAMPER(4, ZoneUserDevice.Zone, true, "Tamper"), - TAMPER_RESTORE(5, ZoneUserDevice.Zone, true, "Tamper restore"), - TROUBLE(6, ZoneUserDevice.Zone, true, "Trouble"), - TROUBLE_RESTORE(7, ZoneUserDevice.Zone, true, "Trouble restore"), - TX_LOW_BATTERY(8, ZoneUserDevice.Zone, true, "TX low battery"), - TX_LOW_BATTERY_RESTORE(9, ZoneUserDevice.Zone, true, "TX low battery restore"), - ZONE_LOST(10, ZoneUserDevice.Zone, true, "Zone lost"), - ZONE_LOST_RESTORE(11, ZoneUserDevice.Zone, true, "Zone lost restore"), - START_OF_CROSS_TIME(12, ZoneUserDevice.Zone, true, "Start of cross time"), - SPECIAL_EXPANSION_EVENT(17, ZoneUserDevice.None, false, "Special expansion event"), - DURESS(18, ZoneUserDevice.None, true, "Duress"), - MANUAL_FIRE(19, ZoneUserDevice.None, true, "Manual fire"), - AUXILIARY2_PANIC(20, ZoneUserDevice.None, true, "Auxiliary 2 panic"), - PANIC(22, ZoneUserDevice.None, true, "Panic"), - KEYPAD_TAMPER(23, ZoneUserDevice.None, true, "Keypad tamper"), - CONTROL_BOX_TAMPER(24, ZoneUserDevice.Device, false, "Control box tamper"), - CONTROL_BOX_TAMPER_RESTORE(25, ZoneUserDevice.Device, false, "Control box tamper restore"), - AC_FAIL(26, ZoneUserDevice.Device, false, "AC fail"), - AC_FAIL_RESTORE(27, ZoneUserDevice.Device, false, "AC fail restore"), - LOW_BATTERY(28, ZoneUserDevice.Device, false, "Low battery"), - LOW_BATTERY_RESTORE(29, ZoneUserDevice.Device, false, "Low battery restore"), - OVER_CURRENT(30, ZoneUserDevice.Device, false, "Over-current"), - OVER_CURRENT_RESTORE(31, ZoneUserDevice.Device, false, "Over-current restore"), - SIREN_TAMPER(32, ZoneUserDevice.Device, false, "Siren tamper"), - SIREN_TAMPER_RESTORE(33, ZoneUserDevice.Device, false, "Siren tamper restore"), - TELEPHONE_FAULT(34, ZoneUserDevice.None, false, "Telephone fault"), - TELEPHONE_FAULT_RESTORE(35, ZoneUserDevice.None, false, "Telephone fault restore"), - EXPANDER_TROUBLE(36, ZoneUserDevice.Device, false, "Expander trouble"), - EXPANDER_TROUBLE_RESTORE(37, ZoneUserDevice.Device, false, "Expander trouble restore"), - FAIL_TO_COMMUNICATE(38, ZoneUserDevice.None, false, "Fail to communicate"), - LOG_FULL(39, ZoneUserDevice.None, false, "Log full"), - OPENING(40, ZoneUserDevice.User, true, "Opening"), - CLOSING(41, ZoneUserDevice.User, true, "Closing"), - EXIT_ERROR(42, ZoneUserDevice.User, true, "Exit error"), - RECENT_CLOSING(43, ZoneUserDevice.User, true, "Recent closing"), - AUTO_TEST(44, ZoneUserDevice.None, false, "Auto-test"), - START_PROGRAM(45, ZoneUserDevice.None, false, "Start program"), - END_PROGRAM(46, ZoneUserDevice.None, false, "End program"), - START_DOWNLOAD(47, ZoneUserDevice.None, false, "Start download"), - END_DOWNLOAD(48, ZoneUserDevice.None, false, "End download"), - CANCEL(49, ZoneUserDevice.User, true, "Cancel"), - GROUND_FAULT(50, ZoneUserDevice.None, false, "Ground fault"), - GROUND_FAULT_RESTORE(51, ZoneUserDevice.None, false, "Ground fault restore"), - MANUAL_TEST(52, ZoneUserDevice.None, false, "Manual test"), - CLOSED_WITH_ZONES_BYPASSED(53, ZoneUserDevice.User, true, "Closed with zones bypassed"), - START_OF_LISTEN_IN(54, ZoneUserDevice.None, false, "Start of listen in"), - TECHNICIAN_ON_SITE(55, ZoneUserDevice.None, false, "Technician on site"), - TECHNICIAN_LEFT(56, ZoneUserDevice.None, false, "Technician left"), - CONTROL_POWER_UP(57, ZoneUserDevice.None, false, "Control power up"), - FIRST_TO_OPEN(120, ZoneUserDevice.User, true, "First to open"), - LAST_TO_CLOSE(121, ZoneUserDevice.User, true, "Last toC close"), - PIN_ENTERED_WITH_BIT7_SET(122, ZoneUserDevice.User, true, "PIN entered with bit 7 set"), - BEGIN_WALK_TEST(123, ZoneUserDevice.None, false, "Begin walk-test"), - END_WALK_TEST(124, ZoneUserDevice.None, false, "End walk-test"), - RE_EXIT(125, ZoneUserDevice.None, true, "Re-exit"), - OUTPUT_TRIP(126, ZoneUserDevice.User, false, "Output trip"), - DATA_LOST(127, ZoneUserDevice.None, false, "Data Lost"); + ALARM(0, ZoneUserDevice.ZONE, true, "Alarm"), + ALARM_RESTORE(1, ZoneUserDevice.ZONE, true, "Alarm restore"), + BYPASS(2, ZoneUserDevice.ZONE, true, "Bypass"), + BYPASS_RESTORE(3, ZoneUserDevice.ZONE, true, "Bypass restore"), + TAMPER(4, ZoneUserDevice.ZONE, true, "Tamper"), + TAMPER_RESTORE(5, ZoneUserDevice.ZONE, true, "Tamper restore"), + TROUBLE(6, ZoneUserDevice.ZONE, true, "Trouble"), + TROUBLE_RESTORE(7, ZoneUserDevice.ZONE, true, "Trouble restore"), + TX_LOW_BATTERY(8, ZoneUserDevice.ZONE, true, "TX low battery"), + TX_LOW_BATTERY_RESTORE(9, ZoneUserDevice.ZONE, true, "TX low battery restore"), + ZONE_LOST(10, ZoneUserDevice.ZONE, true, "Zone lost"), + ZONE_LOST_RESTORE(11, ZoneUserDevice.ZONE, true, "Zone lost restore"), + START_OF_CROSS_TIME(12, ZoneUserDevice.ZONE, true, "Start of cross time"), + SPECIAL_EXPANSION_EVENT(17, ZoneUserDevice.NONE, false, "Special expansion event"), + DURESS(18, ZoneUserDevice.NONE, true, "Duress"), + MANUAL_FIRE(19, ZoneUserDevice.NONE, true, "Manual fire"), + AUXILIARY2_PANIC(20, ZoneUserDevice.NONE, true, "Auxiliary 2 panic"), + PANIC(22, ZoneUserDevice.NONE, true, "Panic"), + KEYPAD_TAMPER(23, ZoneUserDevice.NONE, true, "Keypad tamper"), + CONTROL_BOX_TAMPER(24, ZoneUserDevice.DEVICE, false, "Control box tamper"), + CONTROL_BOX_TAMPER_RESTORE(25, ZoneUserDevice.DEVICE, false, "Control box tamper restore"), + AC_FAIL(26, ZoneUserDevice.DEVICE, false, "AC fail"), + AC_FAIL_RESTORE(27, ZoneUserDevice.DEVICE, false, "AC fail restore"), + LOW_BATTERY(28, ZoneUserDevice.DEVICE, false, "Low battery"), + LOW_BATTERY_RESTORE(29, ZoneUserDevice.DEVICE, false, "Low battery restore"), + OVER_CURRENT(30, ZoneUserDevice.DEVICE, false, "Over-current"), + OVER_CURRENT_RESTORE(31, ZoneUserDevice.DEVICE, false, "Over-current restore"), + SIREN_TAMPER(32, ZoneUserDevice.DEVICE, false, "Siren tamper"), + SIREN_TAMPER_RESTORE(33, ZoneUserDevice.DEVICE, false, "Siren tamper restore"), + TELEPHONE_FAULT(34, ZoneUserDevice.NONE, false, "Telephone fault"), + TELEPHONE_FAULT_RESTORE(35, ZoneUserDevice.NONE, false, "Telephone fault restore"), + EXPANDER_TROUBLE(36, ZoneUserDevice.DEVICE, false, "Expander trouble"), + EXPANDER_TROUBLE_RESTORE(37, ZoneUserDevice.DEVICE, false, "Expander trouble restore"), + FAIL_TO_COMMUNICATE(38, ZoneUserDevice.NONE, false, "Fail to communicate"), + LOG_FULL(39, ZoneUserDevice.NONE, false, "Log full"), + OPENING(40, ZoneUserDevice.USER, true, "Opening"), + CLOSING(41, ZoneUserDevice.USER, true, "Closing"), + EXIT_ERROR(42, ZoneUserDevice.USER, true, "Exit error"), + RECENT_CLOSING(43, ZoneUserDevice.USER, true, "Recent closing"), + AUTO_TEST(44, ZoneUserDevice.NONE, false, "Auto-test"), + START_PROGRAM(45, ZoneUserDevice.NONE, false, "Start program"), + END_PROGRAM(46, ZoneUserDevice.NONE, false, "End program"), + START_DOWNLOAD(47, ZoneUserDevice.NONE, false, "Start download"), + END_DOWNLOAD(48, ZoneUserDevice.NONE, false, "End download"), + CANCEL(49, ZoneUserDevice.USER, true, "Cancel"), + GROUND_FAULT(50, ZoneUserDevice.NONE, false, "Ground fault"), + GROUND_FAULT_RESTORE(51, ZoneUserDevice.NONE, false, "Ground fault restore"), + MANUAL_TEST(52, ZoneUserDevice.NONE, false, "Manual test"), + CLOSED_WITH_ZONES_BYPASSED(53, ZoneUserDevice.USER, true, "Closed with zones bypassed"), + START_OF_LISTEN_IN(54, ZoneUserDevice.NONE, false, "Start of listen in"), + TECHNICIAN_ON_SITE(55, ZoneUserDevice.NONE, false, "Technician on site"), + TECHNICIAN_LEFT(56, ZoneUserDevice.NONE, false, "Technician left"), + CONTROL_POWER_UP(57, ZoneUserDevice.NONE, false, "Control power up"), + FIRST_TO_OPEN(120, ZoneUserDevice.USER, true, "First to open"), + LAST_TO_CLOSE(121, ZoneUserDevice.USER, true, "Last toC close"), + PIN_ENTERED_WITH_BIT7_SET(122, ZoneUserDevice.USER, true, "PIN entered with bit 7 set"), + BEGIN_WALK_TEST(123, ZoneUserDevice.NONE, false, "Begin walk-test"), + END_WALK_TEST(124, ZoneUserDevice.NONE, false, "End walk-test"), + RE_EXIT(125, ZoneUserDevice.NONE, true, "Re-exit"), + OUTPUT_TRIP(126, ZoneUserDevice.USER, false, "Output trip"), + DATA_LOST(127, ZoneUserDevice.NONE, false, "Data Lost"); private static final Map BY_LOG_EVENT_TYPE = new HashMap<>(); public final int eventType; diff --git a/bundles/org.openhab.binding.caddx/src/main/java/org/openhab/binding/caddx/internal/handler/ThingHandlerPanel.java b/bundles/org.openhab.binding.caddx/src/main/java/org/openhab/binding/caddx/internal/handler/ThingHandlerPanel.java index ee529e2df7822..3c3053f418437 100644 --- a/bundles/org.openhab.binding.caddx/src/main/java/org/openhab/binding/caddx/internal/handler/ThingHandlerPanel.java +++ b/bundles/org.openhab.binding.caddx/src/main/java/org/openhab/binding/caddx/internal/handler/ThingHandlerPanel.java @@ -67,7 +67,7 @@ public void updateChannel(ChannelUID channelUID, String data) { @Override public void handleCommand(ChannelUID channelUID, Command command) { - logger.trace("handleCommand(): Command Received - {} {}.", channelUID, command); + logger.debug("handleCommand(): Command Received - {} {}.", channelUID, command); String cmd = null; String data = null; @@ -167,10 +167,12 @@ private void handleLogEventMessage(CaddxMessage message) { // get the channel id from the map HashMap logMap = panelLogMessagesMap; - String id = logMap.get(eventNumberString); - if (logMap != null && id != null) { - ChannelUID channelUID = new ChannelUID(getThing().getUID(), id); - updateChannel(channelUID, logEventMessage.toString()); + if (logMap != null) { + String id = logMap.get(eventNumberString); + if (id != null) { + ChannelUID channelUID = new ChannelUID(getThing().getUID(), id); + updateChannel(channelUID, logEventMessage.toString()); + } } if (communicatorStackPointer != null && eventNumberString.equals(communicatorStackPointer)) { diff --git a/bundles/org.openhab.binding.caddx/src/main/java/org/openhab/binding/caddx/internal/handler/ThingHandlerZone.java b/bundles/org.openhab.binding.caddx/src/main/java/org/openhab/binding/caddx/internal/handler/ThingHandlerZone.java index b8d21f9673571..b8bff044e8f81 100644 --- a/bundles/org.openhab.binding.caddx/src/main/java/org/openhab/binding/caddx/internal/handler/ThingHandlerZone.java +++ b/bundles/org.openhab.binding.caddx/src/main/java/org/openhab/binding/caddx/internal/handler/ThingHandlerZone.java @@ -52,7 +52,6 @@ public ThingHandlerZone(Thing thing) { @Override public void updateChannel(ChannelUID channelUID, String data) { if (channelUID.getId().equals(CaddxBindingConstants.ZONE_NAME)) { - getThing().setLabel(data); updateState(channelUID, new StringType(data)); logger.trace(" updateChannel: {} = {}", channelUID, data); @@ -71,7 +70,7 @@ public void updateChannel(ChannelUID channelUID, String data) { @Override public void handleCommand(ChannelUID channelUID, Command command) { - logger.trace("handleCommand(): Command Received - {} {}.", channelUID, command); + logger.debug("handleCommand(): Command Received - {} {}.", channelUID, command); String cmd1 = null; String cmd2 = null; @@ -124,9 +123,9 @@ public void caddxEventReceived(CaddxEvent event, Thing thing) { logger.trace(" updateChannel: {} = {}", channelUID, value); } } - - updateStatus(ThingStatus.ONLINE); } + + updateStatus(ThingStatus.ONLINE); } @Override diff --git a/bundles/org.openhab.binding.caddx/src/main/java/org/openhab/binding/caddx/internal/handler/ZoneUserDevice.java b/bundles/org.openhab.binding.caddx/src/main/java/org/openhab/binding/caddx/internal/handler/ZoneUserDevice.java index 0efca7c5a77e0..7257e6c9f499a 100644 --- a/bundles/org.openhab.binding.caddx/src/main/java/org/openhab/binding/caddx/internal/handler/ZoneUserDevice.java +++ b/bundles/org.openhab.binding.caddx/src/main/java/org/openhab/binding/caddx/internal/handler/ZoneUserDevice.java @@ -21,8 +21,8 @@ */ @NonNullByDefault enum ZoneUserDevice { - None, - Zone, - User, - Device + NONE, + ZONE, + USER, + DEVICE } diff --git a/bundles/org.openhab.binding.chromecast/README.md b/bundles/org.openhab.binding.chromecast/README.md index 489dec5441dc4..35d3185bad52b 100644 --- a/bundles/org.openhab.binding.chromecast/README.md +++ b/bundles/org.openhab.binding.chromecast/README.md @@ -144,3 +144,19 @@ sitemap chromecast label="Chromecasts" { } } ``` + +## Rule Action + +This binding includes rule actions for casting media. + +* `playURL(String url)` +* `playURL(String url, String mimeType)` + +Examples: + +``` +val castActions = getActions("chromecast","chromecast:chromecast:29fcf535da") +val success = castActions.playURL("http://192.168.1.160:81/mjpg/front1/video.mjpg") +val success2 = castActions.playURL("http://192.168.1.160:81/mjpg/front1/video.mjpg", "image/jpeg") + +``` \ No newline at end of file diff --git a/bundles/org.openhab.binding.chromecast/pom.xml b/bundles/org.openhab.binding.chromecast/pom.xml index 8b68799f1aae8..ed879fa475bd6 100644 --- a/bundles/org.openhab.binding.chromecast/pom.xml +++ b/bundles/org.openhab.binding.chromecast/pom.xml @@ -16,7 +16,6 @@ jackson-core,jackson-annotations,jackson-databind - 2.9.10 @@ -25,6 +24,12 @@ api-v2 0.11.3 compile + + + com.fasterxml.jackson.core + * + + com.google.protobuf diff --git a/bundles/org.openhab.binding.chromecast/src/main/java/org/openhab/binding/chromecast/internal/ChromecastCommander.java b/bundles/org.openhab.binding.chromecast/src/main/java/org/openhab/binding/chromecast/internal/ChromecastCommander.java index 7b2f407836666..2a031a4bd2def 100644 --- a/bundles/org.openhab.binding.chromecast/src/main/java/org/openhab/binding/chromecast/internal/ChromecastCommander.java +++ b/bundles/org.openhab.binding.chromecast/src/main/java/org/openhab/binding/chromecast/internal/ChromecastCommander.java @@ -219,7 +219,7 @@ private void handleMute(final Command command) { } } - void playMedia(@Nullable String title, @Nullable String url, @Nullable String mimeType) { + public void playMedia(@Nullable String title, @Nullable String url, @Nullable String mimeType) { try { if (chromeCast.isAppAvailable(MEDIA_PLAYER)) { if (!chromeCast.isAppRunning(MEDIA_PLAYER)) { diff --git a/bundles/org.openhab.binding.chromecast/src/main/java/org/openhab/binding/chromecast/internal/action/ChromecastActions.java b/bundles/org.openhab.binding.chromecast/src/main/java/org/openhab/binding/chromecast/internal/action/ChromecastActions.java new file mode 100644 index 0000000000000..ba7e07aae5305 --- /dev/null +++ b/bundles/org.openhab.binding.chromecast/src/main/java/org/openhab/binding/chromecast/internal/action/ChromecastActions.java @@ -0,0 +1,94 @@ +/** + * Copyright (c) 2010-2021 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.chromecast.internal.action; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; +import org.openhab.binding.chromecast.internal.handler.ChromecastHandler; +import org.openhab.core.automation.annotation.ActionInput; +import org.openhab.core.automation.annotation.ActionOutput; +import org.openhab.core.automation.annotation.RuleAction; +import org.openhab.core.thing.binding.ThingActions; +import org.openhab.core.thing.binding.ThingActionsScope; +import org.openhab.core.thing.binding.ThingHandler; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * The {@link ChromecastActions} class defines rule actions for playing URLs + * + * @author Scott Hanson - Added Actions + */ +@ThingActionsScope(name = "chromecast") +@NonNullByDefault +public class ChromecastActions implements ThingActions { + + private final Logger logger = LoggerFactory.getLogger(ChromecastActions.class); + + private @Nullable ChromecastHandler handler; + + @RuleAction(label = "@text/playURLActionLabel", description = "@text/playURLActionDescription") + public @ActionOutput(name = "success", type = "java.lang.Boolean") Boolean playURL( + @ActionInput(name = "url") @Nullable String url) { + if (url == null) { + logger.warn("Cannot Play as URL is missing."); + return false; + } + + final ChromecastHandler handler = this.handler; + if (handler == null) { + logger.warn("Handler is null, cannot play."); + return false; + } else { + return handler.playURL(url, null); + } + } + + @RuleAction(label = "@text/playURLTypeActionLabel", description = "@text/playURLTypeActionDescription") + public @ActionOutput(name = "success", type = "java.lang.Boolean") Boolean playURL( + @ActionInput(name = "url") @Nullable String url, + @ActionInput(name = "mediaType") @Nullable String mediaType) { + if (url == null) { + logger.warn("Cannot Play as URL is missing."); + return false; + } + + final ChromecastHandler handler = this.handler; + if (handler == null) { + logger.warn("Handler is null, cannot tweet."); + return false; + } else { + return handler.playURL(url, mediaType); + } + } + + public static boolean playURL(ThingActions actions, @Nullable String url) { + return ((ChromecastActions) actions).playURL(url); + } + + public static boolean playURL(ThingActions actions, @Nullable String url, @Nullable String mediaType) { + return ((ChromecastActions) actions).playURL(url, mediaType); + } + + @Override + public void setThingHandler(@Nullable ThingHandler handler) { + if (handler instanceof ChromecastHandler) { + this.handler = (ChromecastHandler) handler; + } + } + + @Override + public @Nullable ThingHandler getThingHandler() { + return handler; + } +} diff --git a/bundles/org.openhab.binding.chromecast/src/main/java/org/openhab/binding/chromecast/internal/handler/ChromecastHandler.java b/bundles/org.openhab.binding.chromecast/src/main/java/org/openhab/binding/chromecast/internal/handler/ChromecastHandler.java index 664fbd267f4dd..b68ea53779e2e 100644 --- a/bundles/org.openhab.binding.chromecast/src/main/java/org/openhab/binding/chromecast/internal/handler/ChromecastHandler.java +++ b/bundles/org.openhab.binding.chromecast/src/main/java/org/openhab/binding/chromecast/internal/handler/ChromecastHandler.java @@ -13,6 +13,7 @@ package org.openhab.binding.chromecast.internal.handler; import java.io.IOException; +import java.util.Collection; import java.util.Collections; import java.util.Locale; import java.util.Set; @@ -26,6 +27,7 @@ import org.openhab.binding.chromecast.internal.ChromecastEventReceiver; import org.openhab.binding.chromecast.internal.ChromecastScheduler; import org.openhab.binding.chromecast.internal.ChromecastStatusUpdater; +import org.openhab.binding.chromecast.internal.action.ChromecastActions; import org.openhab.binding.chromecast.internal.config.ChromecastConfig; import org.openhab.core.audio.AudioFormat; import org.openhab.core.audio.AudioHTTPServer; @@ -39,6 +41,7 @@ import org.openhab.core.thing.ThingStatus; import org.openhab.core.thing.ThingStatusDetail; import org.openhab.core.thing.binding.BaseThingHandler; +import org.openhab.core.thing.binding.ThingHandlerService; import org.openhab.core.types.Command; import org.openhab.core.types.State; import org.slf4j.Logger; @@ -53,6 +56,7 @@ * @author Markus Rathgeb, Kai Kreuzer - Initial contribution * @author Daniel Walters - Online status fix, handle playuri channel and refactor play media code * @author Jason Holmes - Media Status. Refactor the monolith into separate classes. + * @author Scott Hanson - Added Actions. */ @NonNullByDefault public class ChromecastHandler extends BaseThingHandler implements AudioSink { @@ -205,6 +209,20 @@ public void setVolume(PercentType percentType) throws IOException { } } + @Override + public Collection> getServices() { + return Collections.singletonList(ChromecastActions.class); + } + + public boolean playURL(String url, @Nullable String mediaType) { + Coordinator localCoordinator = coordinator; + if (localCoordinator != null) { + localCoordinator.commander.playMedia(null, url, mediaType); + return true; + } + return false; + } + private static class Coordinator { private final Logger logger = LoggerFactory.getLogger(Coordinator.class); diff --git a/bundles/org.openhab.binding.chromecast/src/main/resources/OH-INF/i18n/chromecast.properties b/bundles/org.openhab.binding.chromecast/src/main/resources/OH-INF/i18n/chromecast.properties new file mode 100644 index 0000000000000..a6cb3a91f7baf --- /dev/null +++ b/bundles/org.openhab.binding.chromecast/src/main/resources/OH-INF/i18n/chromecast.properties @@ -0,0 +1,6 @@ +# actions +playURLActionLabel = play a URL +playURLActionDescription = Plays a URL. + +playURLTypeActionLabel = play a URL with a media type +playURLTypeActionDescription = Plays a URL with a defined media type attribute. \ No newline at end of file diff --git a/bundles/org.openhab.binding.cm11a/src/main/java/org/openhab/binding/cm11a/internal/X10Interface.java b/bundles/org.openhab.binding.cm11a/src/main/java/org/openhab/binding/cm11a/internal/X10Interface.java index 60a800c93c09c..e9ef9043f93c5 100644 --- a/bundles/org.openhab.binding.cm11a/src/main/java/org/openhab/binding/cm11a/internal/X10Interface.java +++ b/bundles/org.openhab.binding.cm11a/src/main/java/org/openhab/binding/cm11a/internal/X10Interface.java @@ -357,7 +357,7 @@ public boolean sendFunction(String address, int function, int dims) throws Inval */ public static boolean validateAddress(String address) { return (!(address.length() < 2 || address.length() > 3 - || !HOUSE_CODES.containsKey(new Character(address.charAt(0))) + || !HOUSE_CODES.containsKey(Character.valueOf(address.charAt(0))) || !DEVICE_CODES.containsKey(Integer.parseInt(address.substring(1))))); } diff --git a/bundles/org.openhab.binding.coolmasternet/src/main/java/org/openhab/binding/coolmasternet/internal/handler/HVACHandler.java b/bundles/org.openhab.binding.coolmasternet/src/main/java/org/openhab/binding/coolmasternet/internal/handler/HVACHandler.java index 509e224b6f0f1..4eb327b1147c4 100644 --- a/bundles/org.openhab.binding.coolmasternet/src/main/java/org/openhab/binding/coolmasternet/internal/handler/HVACHandler.java +++ b/bundles/org.openhab.binding.coolmasternet/src/main/java/org/openhab/binding/coolmasternet/internal/handler/HVACHandler.java @@ -136,7 +136,7 @@ public void handleCommand(final ChannelUID channelUID, final Command command) { if (command instanceof RefreshType) { final String currentTemp = query(controller, "a"); if (currentTemp != null) { - final Integer temp = new Integer(currentTemp); + final Integer temp = Integer.parseInt(currentTemp); final QuantityType value = new QuantityType<>(temp, controller.getUnit()); updateState(CURRENT_TEMP, value); } @@ -157,7 +157,7 @@ public void handleCommand(final ChannelUID channelUID, final Command command) { if (command instanceof RefreshType) { final String setTemp = query(controller, "t"); if (setTemp != null) { - final Integer temp = new Integer(setTemp); + final Integer temp = Integer.parseInt(setTemp); final QuantityType value = new QuantityType<>(temp, controller.getUnit()); updateState(SET_TEMP, value); } diff --git a/bundles/org.openhab.binding.daikin/src/main/java/org/openhab/binding/daikin/internal/DaikinHttpClientFactoryImpl.java b/bundles/org.openhab.binding.daikin/src/main/java/org/openhab/binding/daikin/internal/DaikinHttpClientFactoryImpl.java index 947a388929f67..b7729ef13492d 100644 --- a/bundles/org.openhab.binding.daikin/src/main/java/org/openhab/binding/daikin/internal/DaikinHttpClientFactoryImpl.java +++ b/bundles/org.openhab.binding.daikin/src/main/java/org/openhab/binding/daikin/internal/DaikinHttpClientFactoryImpl.java @@ -62,7 +62,7 @@ protected void deactivate() { private synchronized void initialize() { if (httpClient == null) { - httpClient = new HttpClient(new SslContextFactory(true)); + httpClient = new HttpClient(new SslContextFactory.Client(true)); try { httpClient.start(); logger.debug("Daikin http client started"); diff --git a/bundles/org.openhab.binding.deconz/src/main/resources/OH-INF/thing/sensor-thing-types.xml b/bundles/org.openhab.binding.deconz/src/main/resources/OH-INF/thing/sensor-thing-types.xml index 5f26c7ff46209..a379d52af7641 100644 --- a/bundles/org.openhab.binding.deconz/src/main/resources/OH-INF/thing/sensor-thing-types.xml +++ b/bundles/org.openhab.binding.deconz/src/main/resources/OH-INF/thing/sensor-thing-types.xml @@ -97,6 +97,7 @@ Number:Energy Current consumption + Energy @@ -242,6 +243,7 @@ Number:Temperature Current temperature + Temperature @@ -265,6 +267,7 @@ Number:Dimensionless Current humidity + Humidity @@ -288,6 +291,7 @@ Number:Pressure Current pressure + Pressure @@ -507,14 +511,16 @@ Number:Temperature - + Target temperature + Heating String Current mode + Heating diff --git a/bundles/org.openhab.binding.denonmarantz/src/main/java/org/openhab/binding/denonmarantz/internal/DenonMarantzState.java b/bundles/org.openhab.binding.denonmarantz/src/main/java/org/openhab/binding/denonmarantz/internal/DenonMarantzState.java index 8a2a4462313ea..9fdfc069b90b0 100644 --- a/bundles/org.openhab.binding.denonmarantz/src/main/java/org/openhab/binding/denonmarantz/internal/DenonMarantzState.java +++ b/bundles/org.openhab.binding.denonmarantz/src/main/java/org/openhab/binding/denonmarantz/internal/DenonMarantzState.java @@ -14,7 +14,6 @@ import java.math.BigDecimal; -import org.apache.commons.lang.StringUtils; import org.openhab.core.library.types.DecimalType; import org.openhab.core.library.types.OnOffType; import org.openhab.core.library.types.PercentType; @@ -184,7 +183,7 @@ public void setSurroundProgram(String surroundProgram) { } public void setNowPlayingArtist(String artist) { - StringType newVal = StringUtils.isBlank(artist) ? StringType.EMPTY : StringType.valueOf(artist); + StringType newVal = artist == null || artist.isBlank() ? StringType.EMPTY : StringType.valueOf(artist); if (!newVal.equals(this.artist)) { this.artist = newVal; handler.stateChanged(DenonMarantzBindingConstants.CHANNEL_NOW_PLAYING_ARTIST, this.artist); @@ -192,7 +191,7 @@ public void setNowPlayingArtist(String artist) { } public void setNowPlayingAlbum(String album) { - StringType newVal = StringUtils.isBlank(album) ? StringType.EMPTY : StringType.valueOf(album); + StringType newVal = album == null || album.isBlank() ? StringType.EMPTY : StringType.valueOf(album); if (!newVal.equals(this.album)) { this.album = newVal; handler.stateChanged(DenonMarantzBindingConstants.CHANNEL_NOW_PLAYING_ALBUM, this.album); @@ -200,7 +199,7 @@ public void setNowPlayingAlbum(String album) { } public void setNowPlayingTrack(String track) { - StringType newVal = StringUtils.isBlank(track) ? StringType.EMPTY : StringType.valueOf(track); + StringType newVal = track == null || track.isBlank() ? StringType.EMPTY : StringType.valueOf(track); if (!newVal.equals(this.track)) { this.track = newVal; handler.stateChanged(DenonMarantzBindingConstants.CHANNEL_NOW_PLAYING_TRACK, this.track); diff --git a/bundles/org.openhab.binding.denonmarantz/src/main/java/org/openhab/binding/denonmarantz/internal/connector/http/DenonMarantzHttpConnector.java b/bundles/org.openhab.binding.denonmarantz/src/main/java/org/openhab/binding/denonmarantz/internal/connector/http/DenonMarantzHttpConnector.java index dbee97969c73e..f43745c1dab0e 100644 --- a/bundles/org.openhab.binding.denonmarantz/src/main/java/org/openhab/binding/denonmarantz/internal/connector/http/DenonMarantzHttpConnector.java +++ b/bundles/org.openhab.binding.denonmarantz/src/main/java/org/openhab/binding/denonmarantz/internal/connector/http/DenonMarantzHttpConnector.java @@ -34,7 +34,6 @@ import javax.xml.stream.util.StreamReaderDelegate; import org.apache.commons.io.IOUtils; -import org.apache.commons.lang.StringUtils; import org.eclipse.jdt.annotation.Nullable; import org.eclipse.jetty.client.HttpClient; import org.eclipse.jetty.client.api.Response; @@ -169,7 +168,7 @@ public void dispose() { @Override protected void internalSendCommand(String command) { logger.debug("Sending command '{}'", command); - if (StringUtils.isBlank(command)) { + if (command == null || command.isBlank()) { logger.warn("Trying to send empty command"); return; } @@ -306,7 +305,7 @@ private T getDocument(String uri, Class response) throws IOException { String result = HttpUtil.executeUrl("GET", uri, REQUEST_TIMEOUT_MS); logger.trace("result of getDocument for uri '{}':\r\n{}", uri, result); - if (StringUtils.isNotBlank(result)) { + if (result != null && !result.isBlank()) { JAXBContext jc = JAXBContext.newInstance(response); XMLInputFactory xif = XMLInputFactory.newInstance(); xif.setProperty(XMLInputFactory.IS_SUPPORTING_EXTERNAL_ENTITIES, false); @@ -341,7 +340,7 @@ private T postDocument(String uri, Class response, S request) throws I ByteArrayInputStream inputStream = new ByteArrayInputStream(sw.toString().getBytes(StandardCharsets.UTF_8)); String result = HttpUtil.executeUrl("POST", uri, inputStream, CONTENT_TYPE_XML, REQUEST_TIMEOUT_MS); - if (StringUtils.isNotBlank(result)) { + if (result != null && !result.isBlank()) { JAXBContext jcResponse = JAXBContext.newInstance(response); @SuppressWarnings("unchecked") diff --git a/bundles/org.openhab.binding.denonmarantz/src/main/java/org/openhab/binding/denonmarantz/internal/connector/telnet/DenonMarantzTelnetClientThread.java b/bundles/org.openhab.binding.denonmarantz/src/main/java/org/openhab/binding/denonmarantz/internal/connector/telnet/DenonMarantzTelnetClientThread.java index 9eee0775b9163..6062c1ea7d59d 100644 --- a/bundles/org.openhab.binding.denonmarantz/src/main/java/org/openhab/binding/denonmarantz/internal/connector/telnet/DenonMarantzTelnetClientThread.java +++ b/bundles/org.openhab.binding.denonmarantz/src/main/java/org/openhab/binding/denonmarantz/internal/connector/telnet/DenonMarantzTelnetClientThread.java @@ -20,7 +20,6 @@ import java.net.Socket; import java.net.SocketTimeoutException; -import org.apache.commons.lang.StringUtils; import org.openhab.binding.denonmarantz.internal.config.DenonMarantzConfiguration; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -74,7 +73,7 @@ public void run() { break; } logger.trace("Received from {}: {}", config.getHost(), line); - if (!StringUtils.isBlank(line)) { + if (!line.isBlank()) { listener.receivedLine(line); } } catch (SocketTimeoutException e) { diff --git a/bundles/org.openhab.binding.denonmarantz/src/main/java/org/openhab/binding/denonmarantz/internal/connector/telnet/DenonMarantzTelnetConnector.java b/bundles/org.openhab.binding.denonmarantz/src/main/java/org/openhab/binding/denonmarantz/internal/connector/telnet/DenonMarantzTelnetConnector.java index da729e4cab85e..fbdb8d666f978 100644 --- a/bundles/org.openhab.binding.denonmarantz/src/main/java/org/openhab/binding/denonmarantz/internal/connector/telnet/DenonMarantzTelnetConnector.java +++ b/bundles/org.openhab.binding.denonmarantz/src/main/java/org/openhab/binding/denonmarantz/internal/connector/telnet/DenonMarantzTelnetConnector.java @@ -20,7 +20,7 @@ import java.util.concurrent.ScheduledExecutorService; import java.util.regex.Pattern; -import org.apache.commons.lang.StringUtils; +import org.apache.commons.lang3.StringUtils; import org.openhab.binding.denonmarantz.internal.DenonMarantzState; import org.openhab.binding.denonmarantz.internal.config.DenonMarantzConfiguration; import org.openhab.binding.denonmarantz.internal.connector.DenonMarantzConnector; @@ -263,7 +263,7 @@ private void processTitleCommand(String value) { @Override protected void internalSendCommand(String command) { logger.debug("Sending command '{}'", command); - if (StringUtils.isBlank(command)) { + if (command == null || command.isBlank()) { logger.warn("Trying to send empty command"); return; } diff --git a/bundles/org.openhab.binding.denonmarantz/src/main/java/org/openhab/binding/denonmarantz/internal/xml/adapters/StringAdapter.java b/bundles/org.openhab.binding.denonmarantz/src/main/java/org/openhab/binding/denonmarantz/internal/xml/adapters/StringAdapter.java index eb90b60d0dd75..011e75c56fd2c 100644 --- a/bundles/org.openhab.binding.denonmarantz/src/main/java/org/openhab/binding/denonmarantz/internal/xml/adapters/StringAdapter.java +++ b/bundles/org.openhab.binding.denonmarantz/src/main/java/org/openhab/binding/denonmarantz/internal/xml/adapters/StringAdapter.java @@ -14,7 +14,7 @@ import javax.xml.bind.annotation.adapters.XmlAdapter; -import org.apache.commons.lang.StringUtils; +import org.apache.commons.lang3.StringUtils; /** * Adapter to clean up string values diff --git a/bundles/org.openhab.binding.digitalstrom/src/main/java/org/openhab/binding/digitalstrom/internal/DigitalSTROMHandlerFactory.java b/bundles/org.openhab.binding.digitalstrom/src/main/java/org/openhab/binding/digitalstrom/internal/DigitalSTROMHandlerFactory.java index 6fa388fe73856..6b4d2dc48cc9b 100644 --- a/bundles/org.openhab.binding.digitalstrom/src/main/java/org/openhab/binding/digitalstrom/internal/DigitalSTROMHandlerFactory.java +++ b/bundles/org.openhab.binding.digitalstrom/src/main/java/org/openhab/binding/digitalstrom/internal/DigitalSTROMHandlerFactory.java @@ -18,7 +18,6 @@ import java.util.Map; import java.util.concurrent.ConcurrentHashMap; -import org.apache.commons.lang.StringUtils; import org.openhab.binding.digitalstrom.internal.discovery.DiscoveryServiceManager; import org.openhab.binding.digitalstrom.internal.handler.BridgeHandler; import org.openhab.binding.digitalstrom.internal.handler.CircuitHandler; @@ -139,8 +138,9 @@ protected ThingHandler createHandler(Thing thing) { private ThingUID getDeviceUID(ThingTypeUID thingTypeUID, ThingUID thingUID, Configuration configuration, ThingUID bridgeUID) { - if (thingUID == null && StringUtils.isNotBlank((String) configuration.get(DEVICE_DSID))) { - return new ThingUID(thingTypeUID, bridgeUID, configuration.get(DEVICE_DSID).toString()); + String id = (String) configuration.get(DEVICE_DSID); + if (thingUID == null && id != null && !id.isBlank()) { + return new ThingUID(thingTypeUID, bridgeUID, id); } return thingUID; } @@ -208,7 +208,8 @@ private ThingUID getBridgeThingUID(ThingTypeUID thingTypeUID, ThingUID thingUID, return thingUID; } String dSID; - if (StringUtils.isBlank((String) configuration.get(DS_ID))) { + String configValue = (String) configuration.get(DS_ID); + if (configValue == null || configValue.isBlank()) { dSID = getDSSid(configuration); if (dSID != null) { configuration.put(DS_ID, dSID); @@ -225,13 +226,15 @@ private ThingUID getBridgeThingUID(ThingTypeUID thingTypeUID, ThingUID thingUID, private String getDSSid(Configuration configuration) { String dSID = null; - if (StringUtils.isNotBlank((String) configuration.get(HOST))) { - String host = configuration.get(HOST).toString(); + String hostConfigValue = (String) configuration.get(HOST); + if (hostConfigValue != null && !hostConfigValue.isBlank()) { + String host = hostConfigValue; String applicationToken = null; String user = null; String pw = null; - if (StringUtils.isNotBlank((String) configuration.get(APPLICATION_TOKEN))) { + String atConfigValue = (String) configuration.get(APPLICATION_TOKEN); + if (atConfigValue != null && !atConfigValue.isBlank()) { applicationToken = configuration.get(APPLICATION_TOKEN).toString(); } @@ -249,8 +252,9 @@ private String getDSSid(Configuration configuration) { } private boolean checkUserPassword(Configuration configuration) { - return StringUtils.isNotBlank((String) configuration.get(USER_NAME)) - && StringUtils.isNotBlank((String) configuration.get(PASSWORD)); + String userName = (String) configuration.get(USER_NAME); + String password = (String) configuration.get(PASSWORD); + return userName != null && !userName.isBlank() && password != null && !password.isBlank(); } @Override diff --git a/bundles/org.openhab.binding.digitalstrom/src/main/java/org/openhab/binding/digitalstrom/internal/discovery/DeviceDiscoveryService.java b/bundles/org.openhab.binding.digitalstrom/src/main/java/org/openhab/binding/digitalstrom/internal/discovery/DeviceDiscoveryService.java index d713dd3d13aa8..820dcf1267669 100644 --- a/bundles/org.openhab.binding.digitalstrom/src/main/java/org/openhab/binding/digitalstrom/internal/discovery/DeviceDiscoveryService.java +++ b/bundles/org.openhab.binding.digitalstrom/src/main/java/org/openhab/binding/digitalstrom/internal/discovery/DeviceDiscoveryService.java @@ -21,16 +21,17 @@ import java.util.List; import java.util.Map; -import org.apache.commons.lang.StringUtils; import org.openhab.binding.digitalstrom.internal.DigitalSTROMBindingConstants; import org.openhab.binding.digitalstrom.internal.handler.BridgeHandler; import org.openhab.binding.digitalstrom.internal.lib.structure.devices.Circuit; import org.openhab.binding.digitalstrom.internal.lib.structure.devices.Device; import org.openhab.binding.digitalstrom.internal.lib.structure.devices.GeneralDeviceInformation; +import org.openhab.binding.digitalstrom.internal.lib.structure.devices.deviceparameters.constants.OutputModeEnum; import org.openhab.binding.digitalstrom.internal.providers.DsDeviceThingTypeProvider; 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.ThingTypeUID; import org.openhab.core.thing.ThingUID; import org.slf4j.Logger; @@ -126,10 +127,8 @@ private void onDeviceAddedInternal(GeneralDeviceInformation device) { if (thingUID != null) { Map properties = new HashMap<>(1); properties.put(DigitalSTROMBindingConstants.DEVICE_DSID, device.getDSID().getValue()); - String deviceName = null; - if (StringUtils.isNotBlank(device.getName())) { - deviceName = device.getName(); - } else { + String deviceName = device.getName(); + if (deviceName == null || deviceName.isBlank()) { // if no name is set, the dSID will be used as name deviceName = device.getDSID().getValue(); } diff --git a/bundles/org.openhab.binding.digitalstrom/src/main/java/org/openhab/binding/digitalstrom/internal/discovery/ZoneTemperatureControlDiscoveryService.java b/bundles/org.openhab.binding.digitalstrom/src/main/java/org/openhab/binding/digitalstrom/internal/discovery/ZoneTemperatureControlDiscoveryService.java index 4d52e673e15eb..83c4251fb2a02 100644 --- a/bundles/org.openhab.binding.digitalstrom/src/main/java/org/openhab/binding/digitalstrom/internal/discovery/ZoneTemperatureControlDiscoveryService.java +++ b/bundles/org.openhab.binding.digitalstrom/src/main/java/org/openhab/binding/digitalstrom/internal/discovery/ZoneTemperatureControlDiscoveryService.java @@ -20,9 +20,9 @@ import java.util.HashSet; import java.util.Map; -import org.apache.commons.lang.StringUtils; import org.openhab.binding.digitalstrom.internal.DigitalSTROMBindingConstants; import org.openhab.binding.digitalstrom.internal.handler.BridgeHandler; +import org.openhab.binding.digitalstrom.internal.handler.ZoneTemperatureControlHandler; import org.openhab.binding.digitalstrom.internal.lib.climate.jsonresponsecontainer.impl.TemperatureControlStatus; import org.openhab.core.config.discovery.AbstractDiscoveryService; import org.openhab.core.config.discovery.DiscoveryResult; @@ -103,7 +103,7 @@ private void internalConfigChanged(TemperatureControlStatus tempControlStatus) { Map properties = new HashMap<>(); properties.put(DigitalSTROMBindingConstants.ZONE_ID, tempControlStatus.getZoneID()); String zoneName = tempControlStatus.getZoneName(); - if (StringUtils.isBlank(zoneName)) { + if (zoneName == null || zoneName.isBlank()) { zoneName = tempControlStatus.getZoneID().toString(); } DiscoveryResult discoveryResult = DiscoveryResultBuilder.create(thingUID).withProperties(properties) diff --git a/bundles/org.openhab.binding.digitalstrom/src/main/java/org/openhab/binding/digitalstrom/internal/handler/BridgeHandler.java b/bundles/org.openhab.binding.digitalstrom/src/main/java/org/openhab/binding/digitalstrom/internal/handler/BridgeHandler.java index 22f81a09a885b..f13295f339d51 100644 --- a/bundles/org.openhab.binding.digitalstrom/src/main/java/org/openhab/binding/digitalstrom/internal/handler/BridgeHandler.java +++ b/bundles/org.openhab.binding.digitalstrom/src/main/java/org/openhab/binding/digitalstrom/internal/handler/BridgeHandler.java @@ -25,7 +25,6 @@ import java.util.concurrent.ScheduledFuture; import java.util.concurrent.TimeUnit; -import org.apache.commons.lang.StringUtils; import org.openhab.binding.digitalstrom.internal.DigitalSTROMBindingConstants; import org.openhab.binding.digitalstrom.internal.lib.climate.jsonresponsecontainer.impl.TemperatureControlStatus; import org.openhab.binding.digitalstrom.internal.lib.config.Config; @@ -198,9 +197,10 @@ public void run() { if (versions != null) { properties.putAll(versions); } - if (StringUtils.isBlank(getThing().getProperties().get(DigitalSTROMBindingConstants.SERVER_CERT)) - && StringUtils.isNotBlank(config.getCert())) { - properties.put(DigitalSTROMBindingConstants.SERVER_CERT, config.getCert()); + String certProperty = getThing().getProperties().get(DigitalSTROMBindingConstants.SERVER_CERT); + String certConfig = config.getCert(); + if ((certProperty == null || certProperty.isBlank()) && (certConfig != null && !certConfig.isBlank())) { + properties.put(DigitalSTROMBindingConstants.SERVER_CERT, certConfig); } logger.debug("update properties"); updateProperties(properties); @@ -235,8 +235,12 @@ public void initialize() { } private boolean checkLoginConfig(Config config) { - if ((StringUtils.isNotBlank(config.getUserName()) && StringUtils.isNotBlank(config.getPassword())) - || StringUtils.isNotBlank(config.getAppToken())) { + String userName = config.getUserName(); + String password = config.getPassword(); + String appToken = config.getAppToken(); + + if (((userName != null && !userName.isBlank()) && (password != null && !password.isBlank())) + || (appToken != null && !appToken.isBlank())) { return true; } onConnectionStateChange(CONNECTION_LOST, NO_USER_PASSWORD); @@ -296,8 +300,9 @@ private Config loadAndCheckConfig() { updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, excText + " have to be a number."); return null; } - if (StringUtils.isNotBlank(getThing().getProperties().get(DigitalSTROMBindingConstants.SERVER_CERT))) { - config.setCert(getThing().getProperties().get(DigitalSTROMBindingConstants.SERVER_CERT)); + String servertCert = getThing().getProperties().get(DigitalSTROMBindingConstants.SERVER_CERT); + if (servertCert != null && !servertCert.isBlank()) { + config.setCert(servertCert); } return config; } @@ -307,8 +312,9 @@ private Config loadAndCheckConnectionData(Configuration thingConfig) { this.config = new Config(); } // load and check connection and authorization data - if (StringUtils.isNotBlank((String) thingConfig.get(HOST))) { - config.setHost(thingConfig.get(HOST).toString()); + String host = (String) thingConfig.get(HOST); + if (host != null && !host.isBlank()) { + config.setHost(host); } else { updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, "The connection to the digitalSTROM-Server can't established, because the host address is missing. Please set the host address."); diff --git a/bundles/org.openhab.binding.digitalstrom/src/main/java/org/openhab/binding/digitalstrom/internal/handler/CircuitHandler.java b/bundles/org.openhab.binding.digitalstrom/src/main/java/org/openhab/binding/digitalstrom/internal/handler/CircuitHandler.java index ede88d34826e0..625e319aa3fc3 100644 --- a/bundles/org.openhab.binding.digitalstrom/src/main/java/org/openhab/binding/digitalstrom/internal/handler/CircuitHandler.java +++ b/bundles/org.openhab.binding.digitalstrom/src/main/java/org/openhab/binding/digitalstrom/internal/handler/CircuitHandler.java @@ -16,7 +16,6 @@ import java.util.Map; import java.util.Set; -import org.apache.commons.lang.StringUtils; import org.openhab.binding.digitalstrom.internal.DigitalSTROMBindingConstants; import org.openhab.binding.digitalstrom.internal.lib.listener.DeviceStatusListener; import org.openhab.binding.digitalstrom.internal.lib.structure.devices.Circuit; @@ -77,8 +76,8 @@ public CircuitHandler(Thing thing) { @Override public void initialize() { logger.debug("Initializing CircuitHandler."); - if (StringUtils.isNotBlank((String) getConfig().get(DigitalSTROMBindingConstants.DEVICE_DSID))) { - dSID = getConfig().get(DigitalSTROMBindingConstants.DEVICE_DSID).toString(); + dSID = (String) getConfig().get(DigitalSTROMBindingConstants.DEVICE_DSID); + if (dSID != null && !dSID.isBlank()) { final Bridge bridge = getBridge(); if (bridge != null) { bridgeStatusChanged(bridge.getStatusInfo()); diff --git a/bundles/org.openhab.binding.digitalstrom/src/main/java/org/openhab/binding/digitalstrom/internal/handler/DeviceHandler.java b/bundles/org.openhab.binding.digitalstrom/src/main/java/org/openhab/binding/digitalstrom/internal/handler/DeviceHandler.java index a76d75d5d71e2..2225d95d6d371 100644 --- a/bundles/org.openhab.binding.digitalstrom/src/main/java/org/openhab/binding/digitalstrom/internal/handler/DeviceHandler.java +++ b/bundles/org.openhab.binding.digitalstrom/src/main/java/org/openhab/binding/digitalstrom/internal/handler/DeviceHandler.java @@ -15,6 +15,7 @@ import static org.openhab.binding.digitalstrom.internal.DigitalSTROMBindingConstants.*; import java.math.BigDecimal; +import java.math.RoundingMode; import java.util.ArrayList; import java.util.HashSet; import java.util.Iterator; @@ -23,7 +24,6 @@ import java.util.Map; import java.util.Set; -import org.apache.commons.lang.StringUtils; import org.openhab.binding.digitalstrom.internal.DigitalSTROMBindingConstants; import org.openhab.binding.digitalstrom.internal.lib.GeneralLibConstance; import org.openhab.binding.digitalstrom.internal.lib.config.Config; @@ -108,8 +108,8 @@ public DeviceHandler(Thing thing) { @Override public void initialize() { logger.debug("Initializing DeviceHandler."); - if (StringUtils.isNotBlank((String) getConfig().get(DigitalSTROMBindingConstants.DEVICE_DSID))) { - dSID = getConfig().get(DigitalSTROMBindingConstants.DEVICE_DSID).toString(); + dSID = (String) getConfig().get(DigitalSTROMBindingConstants.DEVICE_DSID); + if (dSID != null && !dSID.isBlank()) { final Bridge bridge = getBridge(); if (bridge != null) { bridgeStatusChanged(bridge.getStatusInfo()); @@ -411,7 +411,7 @@ private int fromValueToPercent(int value, int max) { if (value <= 0 || max <= 0) { return 0; } - int percentValue = new BigDecimal(value * ((float) 100 / max)).setScale(0, BigDecimal.ROUND_HALF_UP).intValue(); + int percentValue = new BigDecimal(value * ((float) 100 / max)).setScale(0, RoundingMode.HALF_UP).intValue(); return percentValue < 0 ? 0 : percentValue > 100 ? 100 : percentValue; } diff --git a/bundles/org.openhab.binding.digitalstrom/src/main/java/org/openhab/binding/digitalstrom/internal/lib/manager/impl/ConnectionManagerImpl.java b/bundles/org.openhab.binding.digitalstrom/src/main/java/org/openhab/binding/digitalstrom/internal/lib/manager/impl/ConnectionManagerImpl.java index e12f4e0c81573..dd6f002dc0e26 100644 --- a/bundles/org.openhab.binding.digitalstrom/src/main/java/org/openhab/binding/digitalstrom/internal/lib/manager/impl/ConnectionManagerImpl.java +++ b/bundles/org.openhab.binding.digitalstrom/src/main/java/org/openhab/binding/digitalstrom/internal/lib/manager/impl/ConnectionManagerImpl.java @@ -14,7 +14,6 @@ import java.net.HttpURLConnection; -import org.apache.commons.lang.StringUtils; import org.openhab.binding.digitalstrom.internal.lib.config.Config; import org.openhab.binding.digitalstrom.internal.lib.listener.ConnectionListener; import org.openhab.binding.digitalstrom.internal.lib.manager.ConnectionManager; @@ -286,8 +285,9 @@ public String getSessionToken() { @Override public String getNewSessionToken() { if (this.genAppToken) { - if (StringUtils.isNotBlank(config.getAppToken())) { - sessionToken = this.digitalSTROMClient.loginApplication(config.getAppToken()); + String token = config.getAppToken(); + if (token != null && !token.isBlank()) { + sessionToken = this.digitalSTROMClient.loginApplication(token); } else if (codeIsAuthentificationFaild()) { onNotAuthenticated(); } @@ -379,8 +379,9 @@ public boolean connectionEstablished() { private void onNotAuthenticated() { String applicationToken = null; boolean isAuthenticated = false; - if (StringUtils.isNotBlank(config.getAppToken())) { - sessionToken = digitalSTROMClient.loginApplication(config.getAppToken()); + String token = config.getAppToken(); + if (token != null && !token.isBlank()) { + sessionToken = digitalSTROMClient.loginApplication(token); if (sessionToken != null) { isAuthenticated = true; } else { @@ -425,7 +426,7 @@ private void onNotAuthenticated() { logger.debug( "no application-token for application {} found, generate a application-token {}", config.getApplicationName(), applicationToken); - if (StringUtils.isNotBlank(applicationToken)) { + if (applicationToken != null && !applicationToken.isBlank()) { // enable applicationToken if (!digitalSTROMClient.enableApplicationToken(applicationToken, digitalSTROMClient.login(config.getUserName(), config.getPassword()))) { @@ -464,10 +465,9 @@ private void onNotAuthenticated() { } private boolean checkUserPassword() { - if (StringUtils.isNotBlank(config.getUserName()) && StringUtils.isNotBlank(config.getPassword())) { - return true; - } - return false; + String userName = config.getUserName(); + String password = config.getPassword(); + return userName != null && !userName.isBlank() && password != null && !password.isBlank(); } /** @@ -509,8 +509,9 @@ public String getApplicationToken() { @Override public boolean removeApplicationToken() { - if (StringUtils.isNotBlank(config.getAppToken())) { - return digitalSTROMClient.revokeToken(config.getAppToken(), null); + String token = config.getAppToken(); + if (token != null && !token.isBlank()) { + return digitalSTROMClient.revokeToken(token, null); } return true; } diff --git a/bundles/org.openhab.binding.digitalstrom/src/main/java/org/openhab/binding/digitalstrom/internal/lib/serverconnection/impl/DsAPIImpl.java b/bundles/org.openhab.binding.digitalstrom/src/main/java/org/openhab/binding/digitalstrom/internal/lib/serverconnection/impl/DsAPIImpl.java index 2e78f85ab2e5d..1da43ca30b705 100644 --- a/bundles/org.openhab.binding.digitalstrom/src/main/java/org/openhab/binding/digitalstrom/internal/lib/serverconnection/impl/DsAPIImpl.java +++ b/bundles/org.openhab.binding.digitalstrom/src/main/java/org/openhab/binding/digitalstrom/internal/lib/serverconnection/impl/DsAPIImpl.java @@ -22,7 +22,6 @@ import java.util.Map.Entry; import java.util.Set; -import org.apache.commons.lang.StringUtils; import org.openhab.binding.digitalstrom.internal.lib.GeneralLibConstance; import org.openhab.binding.digitalstrom.internal.lib.climate.jsonresponsecontainer.BaseSensorValues; import org.openhab.binding.digitalstrom.internal.lib.climate.jsonresponsecontainer.impl.AssignedSensors; @@ -134,12 +133,13 @@ private boolean checkBlankField(JsonObject obj, String key) { } private boolean checkRequiredZone(Integer zoneID, String zoneName) { - return zoneID != null && zoneID > -1 || StringUtils.isNotBlank(zoneName); + return zoneID != null && zoneID > -1 || (zoneName != null && !zoneName.isBlank()); } private boolean checkRequiredDevice(DSID dsid, String dSUID, String name) { - return StringUtils.isNotBlank(SimpleRequestBuilder.objectToString(dsid)) || StringUtils.isNotBlank(name) - || StringUtils.isNotBlank(dSUID); + String objectString = SimpleRequestBuilder.objectToString(dsid); + return (objectString != null && !objectString.isBlank()) || (name != null && !name.isBlank()) + || (dSUID != null && !dSUID.isBlank()); } @Override @@ -411,7 +411,7 @@ public boolean undoDeviceScene(String token, DSID dSID, String dSUID, String nam @Override public boolean subscribeEvent(String token, String name, Integer subscriptionID, int connectionTimeout, int readTimeout) { - if (StringUtils.isNotBlank(name) && SimpleRequestBuilder.objectToString(subscriptionID) != null) { + if ((name != null && !name.isBlank()) && SimpleRequestBuilder.objectToString(subscriptionID) != null) { String response; response = transport.execute( SimpleRequestBuilder.buildNewJsonRequest(ClassKeys.EVENT).addFunction(FunctionKeys.SUBSCRIBE) @@ -428,7 +428,7 @@ public boolean subscribeEvent(String token, String name, Integer subscriptionID, @Override public boolean unsubscribeEvent(String token, String name, Integer subscriptionID, int connectionTimeout, int readTimeout) { - if (StringUtils.isNotBlank(name) && SimpleRequestBuilder.objectToString(subscriptionID) != null) { + if (name != null && !name.isBlank() && SimpleRequestBuilder.objectToString(subscriptionID) != null) { String response; response = transport.execute( SimpleRequestBuilder.buildNewJsonRequest(ClassKeys.EVENT).addFunction(FunctionKeys.UNSUBSCRIBE) @@ -586,7 +586,7 @@ public List getMeterList(String token) { @Override public String loginApplication(String loginToken) { - if (StringUtils.isNotBlank(loginToken)) { + if (loginToken != null && !loginToken.isBlank()) { String response = transport.execute(SimpleRequestBuilder.buildNewRequest(InterfaceKeys.JSON) .addRequestClass(ClassKeys.SYSTEM).addFunction(FunctionKeys.LOGIN_APPLICATION) .addParameter(ParameterKeys.LOGIN_TOKEN, loginToken).buildRequestString()); diff --git a/bundles/org.openhab.binding.digitalstrom/src/main/java/org/openhab/binding/digitalstrom/internal/lib/serverconnection/impl/HttpTransportImpl.java b/bundles/org.openhab.binding.digitalstrom/src/main/java/org/openhab/binding/digitalstrom/internal/lib/serverconnection/impl/HttpTransportImpl.java index b22a31aa19aea..f8a7369764d5f 100644 --- a/bundles/org.openhab.binding.digitalstrom/src/main/java/org/openhab/binding/digitalstrom/internal/lib/serverconnection/impl/HttpTransportImpl.java +++ b/bundles/org.openhab.binding.digitalstrom/src/main/java/org/openhab/binding/digitalstrom/internal/lib/serverconnection/impl/HttpTransportImpl.java @@ -43,7 +43,7 @@ import javax.net.ssl.X509TrustManager; import org.apache.commons.io.IOUtils; -import org.apache.commons.lang.StringUtils; +import org.apache.commons.lang3.StringUtils; import org.openhab.binding.digitalstrom.internal.lib.config.Config; import org.openhab.binding.digitalstrom.internal.lib.manager.ConnectionManager; import org.openhab.binding.digitalstrom.internal.lib.serverconnection.HttpTransport; @@ -194,13 +194,14 @@ private void init(String uri, int connectTimeout, int readTimeout, boolean exept if (config != null) { cert = config.getCert(); logger.debug("generate SSLcontext from config cert"); - if (StringUtils.isNotBlank(cert)) { + if (cert != null && !cert.isBlank()) { sslSocketFactory = generateSSLContextFromPEMCertString(cert); } else { - if (StringUtils.isNotBlank(config.getTrustCertPath())) { + String trustCertPath = config.getTrustCertPath(); + if (trustCertPath != null && !trustCertPath.isBlank()) { logger.debug("generate SSLcontext from config cert path"); - cert = readPEMCertificateStringFromFile(config.getTrustCertPath()); - if (StringUtils.isNotBlank(cert)) { + cert = readPEMCertificateStringFromFile(trustCertPath); + if (cert != null && !cert.isBlank()) { sslSocketFactory = generateSSLContextFromPEMCertString(cert); } } else { @@ -355,7 +356,7 @@ private String addSessionToken(String request, String sessionToken) { private HttpsURLConnection getConnection(String request, int connectTimeout, int readTimeout) throws IOException { String correctedRequest = request; - if (StringUtils.isNotBlank(correctedRequest)) { + if (correctedRequest != null && !correctedRequest.isBlank()) { correctedRequest = fixRequest(correctedRequest); URL url = new URL(this.uri + correctedRequest); HttpsURLConnection connection = (HttpsURLConnection) url.openConnection(); @@ -415,7 +416,7 @@ public int getSensordataReadTimeout() { } private String readPEMCertificateStringFromFile(String path) { - if (StringUtils.isBlank(path)) { + if (path == null || path.isBlank()) { logger.error("Path is empty."); } else { File dssCert = new File(path); @@ -446,9 +447,9 @@ private String readPEMCertificateStringFromFile(String path) { @Override public String writePEMCertFile(String path) { - String correctedPath = StringUtils.trimToEmpty(path); + String correctedPath = path == null ? "" : path.trim(); File certFilePath; - if (StringUtils.isNotBlank(correctedPath)) { + if (!correctedPath.isBlank()) { certFilePath = new File(correctedPath); boolean pathExists = certFilePath.exists(); if (!pathExists) { @@ -485,7 +486,7 @@ public String writePEMCertFile(String path) { } private SSLSocketFactory generateSSLContextFromPEMCertString(String pemCert) { - if (StringUtils.isNotBlank(pemCert) && pemCert.startsWith(BEGIN_CERT)) { + if (pemCert != null && !pemCert.isBlank() && pemCert.startsWith(BEGIN_CERT)) { try { InputStream certInputStream = IOUtils.toInputStream(pemCert); final X509Certificate trustedCert = (X509Certificate) CertificateFactory.getInstance("X.509") diff --git a/bundles/org.openhab.binding.digitalstrom/src/main/java/org/openhab/binding/digitalstrom/internal/lib/serverconnection/impl/JSONResponseHandler.java b/bundles/org.openhab.binding.digitalstrom/src/main/java/org/openhab/binding/digitalstrom/internal/lib/serverconnection/impl/JSONResponseHandler.java index 27507006fcabd..89b523fd3cdb5 100644 --- a/bundles/org.openhab.binding.digitalstrom/src/main/java/org/openhab/binding/digitalstrom/internal/lib/serverconnection/impl/JSONResponseHandler.java +++ b/bundles/org.openhab.binding.digitalstrom/src/main/java/org/openhab/binding/digitalstrom/internal/lib/serverconnection/impl/JSONResponseHandler.java @@ -63,7 +63,7 @@ public static boolean checkResponse(JsonObject jsonResponse) { public static JsonObject toJsonObject(String jsonResponse) { if (jsonResponse != null && !jsonResponse.trim().equals("")) { try { - return (JsonObject) new JsonParser().parse(jsonResponse); + return (JsonObject) JsonParser.parseString(jsonResponse); } catch (JsonParseException e) { LOGGER.error("An JsonParseException occurred by parsing jsonRequest: {}", jsonResponse, e); } diff --git a/bundles/org.openhab.binding.digitalstrom/src/main/java/org/openhab/binding/digitalstrom/internal/lib/serverconnection/simpledsrequestbuilder/SimpleRequestBuilder.java b/bundles/org.openhab.binding.digitalstrom/src/main/java/org/openhab/binding/digitalstrom/internal/lib/serverconnection/simpledsrequestbuilder/SimpleRequestBuilder.java index 6adc3a15ef935..3ba6161922aa1 100644 --- a/bundles/org.openhab.binding.digitalstrom/src/main/java/org/openhab/binding/digitalstrom/internal/lib/serverconnection/simpledsrequestbuilder/SimpleRequestBuilder.java +++ b/bundles/org.openhab.binding.digitalstrom/src/main/java/org/openhab/binding/digitalstrom/internal/lib/serverconnection/simpledsrequestbuilder/SimpleRequestBuilder.java @@ -15,7 +15,6 @@ import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; -import org.apache.commons.lang.NullArgumentException; import org.openhab.binding.digitalstrom.internal.lib.serverconnection.simpledsrequestbuilder.constants.ExeptionConstants; import org.openhab.binding.digitalstrom.internal.lib.serverconnection.simpledsrequestbuilder.constants.InterfaceKeys; import org.openhab.binding.digitalstrom.internal.lib.serverconnection.simpledsrequestbuilder.constants.ParameterKeys; @@ -61,7 +60,7 @@ private SimpleRequestBuilder() { * @return simpleRequestBuilder with chosen interface * @throws NullArgumentException if the interfaceKey is null */ - public static SimpleRequestBuilder buildNewRequest(String interfaceKey) throws NullArgumentException { + public static SimpleRequestBuilder buildNewRequest(String interfaceKey) throws IllegalArgumentException { if (builder == null) { builder = new SimpleRequestBuilder(); } @@ -78,14 +77,13 @@ public static SimpleRequestBuilder buildNewRequest(String interfaceKey) throws N * @throws IllegalArgumentException if a requestClass is already chosen * @throws NullArgumentException if the requestClassKey is null */ - public static SimpleRequestBuilder buildNewJsonRequest(String requestClassKey) - throws NullArgumentException, IllegalArgumentException { + public static SimpleRequestBuilder buildNewJsonRequest(String requestClassKey) throws IllegalArgumentException { return buildNewRequest(InterfaceKeys.JSON).addRequestClass(requestClassKey); } private SimpleRequestBuilder buildNewRequestInt(String interfaceKey) { if (interfaceKey == null) { - throw new NullArgumentException("interfaceKey"); + throw new IllegalArgumentException("interfaceKey is null"); } request = "/" + interfaceKey + "/"; classIsChosen = false; @@ -102,8 +100,7 @@ private SimpleRequestBuilder buildNewRequestInt(String interfaceKey) { * @throws IllegalArgumentException if a requestClass is already chosen * @throws NullArgumentException if the requestClassKey is null */ - public SimpleRequestBuilder addRequestClass(String requestClassKey) - throws IllegalArgumentException, NullArgumentException { + public SimpleRequestBuilder addRequestClass(String requestClassKey) throws IllegalArgumentException { return builder.addRequestClassInt(requestClassKey); } @@ -115,7 +112,7 @@ private SimpleRequestBuilder addRequestClassInt(String requestClassKey) { if (!classIsChosen) { throw new IllegalArgumentException(ExeptionConstants.CLASS_ALREADY_ADDED); } else { - throw new NullArgumentException("requestClassKey"); + throw new IllegalArgumentException("requestClassKey is null"); } } return this; @@ -129,7 +126,7 @@ private SimpleRequestBuilder addRequestClassInt(String requestClassKey) { * @throws IllegalArgumentException if a function is already chosen * @throws NullArgumentException if the functionKey is null */ - public SimpleRequestBuilder addFunction(String functionKey) throws IllegalArgumentException, NullArgumentException { + public SimpleRequestBuilder addFunction(String functionKey) throws IllegalArgumentException { return builder.addFunctionInt(functionKey); } @@ -142,7 +139,7 @@ private SimpleRequestBuilder addFunctionInt(String functionKey) { functionIsChosen = true; request = request + functionKey; } else { - throw new NullArgumentException("functionKey"); + throw new IllegalArgumentException("functionKey is null"); } } else { throw new IllegalArgumentException(ExeptionConstants.FUNCTION_ALLREADY_ADDED); @@ -160,7 +157,7 @@ private SimpleRequestBuilder addFunctionInt(String functionKey) { * @throws NullArgumentException if the parameterKey is null */ public SimpleRequestBuilder addParameter(String parameterKey, String parameterValue) - throws IllegalArgumentException, NullArgumentException { + throws IllegalArgumentException { return builder.addParameterInt(parameterKey, parameterValue); } @@ -175,7 +172,7 @@ public SimpleRequestBuilder addParameter(String parameterKey, String parameterVa * @throws NullArgumentException if the parameterKey is null */ public SimpleRequestBuilder addDefaultZoneParameter(String sessionToken, Integer zoneID, String zoneName) - throws IllegalArgumentException, NullArgumentException { + throws IllegalArgumentException { return addParameter(ParameterKeys.TOKEN, sessionToken).addParameter(ParameterKeys.ID, objectToString(zoneID)) .addParameter(ParameterKeys.NAME, zoneName); } @@ -191,7 +188,7 @@ public SimpleRequestBuilder addDefaultZoneParameter(String sessionToken, Integer * @throws NullArgumentException if the parameterKey is null */ public SimpleRequestBuilder addDefaultGroupParameter(String sessionToken, Short groupID, String groupName) - throws IllegalArgumentException, NullArgumentException { + throws IllegalArgumentException { return addParameter(ParameterKeys.TOKEN, sessionToken) .addParameter(ParameterKeys.GROUP_ID, objectToString(groupID)) .addParameter(ParameterKeys.GROUP_NAME, groupName); @@ -210,7 +207,7 @@ public SimpleRequestBuilder addDefaultGroupParameter(String sessionToken, Short * @throws NullArgumentException if the parameterKey is null */ public SimpleRequestBuilder addDefaultZoneGroupParameter(String sessionToken, Integer zoneID, String zoneName, - Short groupID, String groupName) throws IllegalArgumentException, NullArgumentException { + Short groupID, String groupName) throws IllegalArgumentException { return addDefaultZoneParameter(sessionToken, zoneID, zoneName) .addParameter(ParameterKeys.GROUP_ID, objectToString(groupID)) .addParameter(ParameterKeys.GROUP_NAME, groupName); @@ -228,7 +225,7 @@ public SimpleRequestBuilder addDefaultZoneGroupParameter(String sessionToken, In * @throws NullArgumentException if the parameterKey is null */ public SimpleRequestBuilder addDefaultDeviceParameter(String sessionToken, DSID dsid, String dSUID, String name) - throws IllegalArgumentException, NullArgumentException { + throws IllegalArgumentException { return addParameter(ParameterKeys.TOKEN, sessionToken).addParameter(ParameterKeys.DSID, objectToString(dsid)) .addParameter(ParameterKeys.DSUID, dSUID).addParameter(ParameterKeys.NAME, name); } @@ -236,7 +233,7 @@ public SimpleRequestBuilder addDefaultDeviceParameter(String sessionToken, DSID private SimpleRequestBuilder addParameterInt(String parameterKey, String parameterValue) { if (allRight()) { if (parameterKey == null) { - throw new NullArgumentException("parameterKey"); + throw new IllegalArgumentException("parameterKey is null"); } if (parameterValue != null) { if (!parameterIsAdded) { diff --git a/bundles/org.openhab.binding.digitalstrom/src/main/java/org/openhab/binding/digitalstrom/internal/lib/structure/devices/impl/DeviceImpl.java b/bundles/org.openhab.binding.digitalstrom/src/main/java/org/openhab/binding/digitalstrom/internal/lib/structure/devices/impl/DeviceImpl.java index 2f5096b2b4fae..4f7969ca8d7e3 100644 --- a/bundles/org.openhab.binding.digitalstrom/src/main/java/org/openhab/binding/digitalstrom/internal/lib/structure/devices/impl/DeviceImpl.java +++ b/bundles/org.openhab.binding.digitalstrom/src/main/java/org/openhab/binding/digitalstrom/internal/lib/structure/devices/impl/DeviceImpl.java @@ -22,7 +22,6 @@ import java.util.Map.Entry; import java.util.Set; -import org.apache.commons.lang.StringUtils; import org.openhab.binding.digitalstrom.internal.DigitalSTROMBindingConstants; import org.openhab.binding.digitalstrom.internal.lib.GeneralLibConstance; import org.openhab.binding.digitalstrom.internal.lib.config.Config; @@ -1695,7 +1694,7 @@ public void saveConfigSceneSpecificationIntoDevice(Map propertri short sceneID = Short.parseShort((String) key .subSequence(DigitalSTROMBindingConstants.DEVICE_SCENE.length(), key.length())); sceneSave = propertries.get(key); - if (StringUtils.isNotBlank(sceneSave)) { + if (sceneSave != null && !sceneSave.isBlank()) { logger.debug("Find saved scene configuration for device with dSID {} and sceneID {}", dsid, key); String[] sceneParm = sceneSave.replace(" ", "").split(","); diff --git a/bundles/org.openhab.binding.digitalstrom/src/main/java/org/openhab/binding/digitalstrom/internal/lib/structure/scene/InternalScene.java b/bundles/org.openhab.binding.digitalstrom/src/main/java/org/openhab/binding/digitalstrom/internal/lib/structure/scene/InternalScene.java index 2bd98d08d4b36..430271a593da6 100644 --- a/bundles/org.openhab.binding.digitalstrom/src/main/java/org/openhab/binding/digitalstrom/internal/lib/structure/scene/InternalScene.java +++ b/bundles/org.openhab.binding.digitalstrom/src/main/java/org/openhab/binding/digitalstrom/internal/lib/structure/scene/InternalScene.java @@ -16,7 +16,6 @@ import java.util.LinkedList; import java.util.List; -import org.apache.commons.lang.StringUtils; import org.openhab.binding.digitalstrom.internal.lib.listener.SceneStatusListener; import org.openhab.binding.digitalstrom.internal.lib.structure.devices.Device; import org.openhab.binding.digitalstrom.internal.lib.structure.scene.constants.SceneTypes; @@ -71,7 +70,7 @@ public InternalScene(Integer zoneID, Short groupID, Short sceneID, String sceneN this.zoneID = zoneID; } this.internalSceneID = this.zoneID + "-" + this.groupID + "-" + this.sceneID; - if (StringUtils.isBlank(sceneName)) { + if (sceneName == null || sceneName.isBlank()) { this.sceneName = this.internalSceneID; } else { this.sceneName = sceneName; diff --git a/bundles/org.openhab.binding.digitalstrom/src/main/java/org/openhab/binding/digitalstrom/internal/providers/BaseDsI18n.java b/bundles/org.openhab.binding.digitalstrom/src/main/java/org/openhab/binding/digitalstrom/internal/providers/BaseDsI18n.java index d8046cc7343cd..64057c1a97648 100644 --- a/bundles/org.openhab.binding.digitalstrom/src/main/java/org/openhab/binding/digitalstrom/internal/providers/BaseDsI18n.java +++ b/bundles/org.openhab.binding.digitalstrom/src/main/java/org/openhab/binding/digitalstrom/internal/providers/BaseDsI18n.java @@ -13,8 +13,9 @@ package org.openhab.binding.digitalstrom.internal.providers; import java.util.Locale; +import java.util.stream.Collectors; +import java.util.stream.Stream; -import org.apache.commons.lang.StringUtils; import org.openhab.core.i18n.TranslationProvider; import org.osgi.framework.Bundle; import org.osgi.service.component.ComponentContext; @@ -132,6 +133,9 @@ protected String getDescText(String key, Locale locale) { * @return key */ public static String buildIdentifier(Object... parts) { - return StringUtils.join(parts, SEPERATOR).toLowerCase(); + return Stream.of(parts) // + .map(Object::toString) // + .map(String::toLowerCase) // + .collect(Collectors.joining(SEPERATOR)); } } diff --git a/bundles/org.openhab.binding.digitalstrom/src/main/resources/OH-INF/config/bridgeConfig.xml b/bundles/org.openhab.binding.digitalstrom/src/main/resources/OH-INF/config/bridgeConfig.xml index 8d6101b051203..2523b325a329f 100644 --- a/bundles/org.openhab.binding.digitalstrom/src/main/resources/OH-INF/config/bridgeConfig.xml +++ b/bundles/org.openhab.binding.digitalstrom/src/main/resources/OH-INF/config/bridgeConfig.xml @@ -14,54 +14,46 @@ @text/dss_param_group_general_desc - + @text/dss_param_app_token_desc - false - + network-address @text/dss_param_ip_address_desc - true - + @text/dss_param_username_desc - false - + @text/dss_param_password_desc - false password - + 120 @text/dss_param_sensor_inter_desc - false - + 30 @text/dss_param_total_power_inter_desc - false - + 7 @text/dss_param_trash_delete_desc - false - + true @text/dss_param_sensor_wait_label 60 - false diff --git a/bundles/org.openhab.binding.digitalstrom/src/main/resources/OH-INF/config/deviceConfig.xml b/bundles/org.openhab.binding.digitalstrom/src/main/resources/OH-INF/config/deviceConfig.xml index da12533fbfeaa..77194eabc16b7 100644 --- a/bundles/org.openhab.binding.digitalstrom/src/main/resources/OH-INF/config/deviceConfig.xml +++ b/bundles/org.openhab.binding.digitalstrom/src/main/resources/OH-INF/config/deviceConfig.xml @@ -15,7 +15,7 @@ @text/device_param_group_sensor_desc - + @text/device_param_active_power_desc @@ -25,9 +25,8 @@ never - false - + @text/device_param_electric_meter_desc @@ -37,9 +36,8 @@ never - false - + @text/device_param_output_current_desc @@ -49,21 +47,18 @@ never - false - + @text/device_param_dsid_desc - true - + @text/device_param_dsid_desc - true diff --git a/bundles/org.openhab.binding.digitalstrom/src/main/resources/OH-INF/thing/AppartmentScene.xml b/bundles/org.openhab.binding.digitalstrom/src/main/resources/OH-INF/thing/AppartmentScene.xml index 9712bf3b55050..dad19d4731ab2 100644 --- a/bundles/org.openhab.binding.digitalstrom/src/main/resources/OH-INF/thing/AppartmentScene.xml +++ b/bundles/org.openhab.binding.digitalstrom/src/main/resources/OH-INF/thing/AppartmentScene.xml @@ -16,10 +16,9 @@ - + @text/param_scene_id_desc - true diff --git a/bundles/org.openhab.binding.digitalstrom/src/main/resources/OH-INF/thing/GroupScene.xml b/bundles/org.openhab.binding.digitalstrom/src/main/resources/OH-INF/thing/GroupScene.xml index b597c64008968..4ef0adf92157c 100644 --- a/bundles/org.openhab.binding.digitalstrom/src/main/resources/OH-INF/thing/GroupScene.xml +++ b/bundles/org.openhab.binding.digitalstrom/src/main/resources/OH-INF/thing/GroupScene.xml @@ -17,22 +17,19 @@ - + @text/param_scene_zone_id_desc - false - + @text/param_scene_group_id_desc - false - + @text/param_scene_id_desc - false diff --git a/bundles/org.openhab.binding.digitalstrom/src/main/resources/OH-INF/thing/NamedScene.xml b/bundles/org.openhab.binding.digitalstrom/src/main/resources/OH-INF/thing/NamedScene.xml index 8edcfeac41096..521b57cdbb39e 100644 --- a/bundles/org.openhab.binding.digitalstrom/src/main/resources/OH-INF/thing/NamedScene.xml +++ b/bundles/org.openhab.binding.digitalstrom/src/main/resources/OH-INF/thing/NamedScene.xml @@ -17,22 +17,19 @@ - + @text/param_scene_zone_id_desc - false - + @text/param_scene_group_id_desc - false - + @text/param_scene_id_desc - false diff --git a/bundles/org.openhab.binding.digitalstrom/src/main/resources/OH-INF/thing/ZoneScene.xml b/bundles/org.openhab.binding.digitalstrom/src/main/resources/OH-INF/thing/ZoneScene.xml index e2dc0471bdc8a..7dc9ff80577fa 100644 --- a/bundles/org.openhab.binding.digitalstrom/src/main/resources/OH-INF/thing/ZoneScene.xml +++ b/bundles/org.openhab.binding.digitalstrom/src/main/resources/OH-INF/thing/ZoneScene.xml @@ -17,16 +17,14 @@ - + @text/param_scene_zone_id_desc - false - + @text/param_scene_id_desc - false diff --git a/bundles/org.openhab.binding.digitalstrom/src/main/resources/OH-INF/thing/ZoneTemperatureControl.xml b/bundles/org.openhab.binding.digitalstrom/src/main/resources/OH-INF/thing/ZoneTemperatureControl.xml index 32346f11ece82..3b053d010993d 100644 --- a/bundles/org.openhab.binding.digitalstrom/src/main/resources/OH-INF/thing/ZoneTemperatureControl.xml +++ b/bundles/org.openhab.binding.digitalstrom/src/main/resources/OH-INF/thing/ZoneTemperatureControl.xml @@ -13,10 +13,9 @@ - + @text/zoneTemperatureControl_parm_zoneID_desc - true diff --git a/bundles/org.openhab.binding.dmx/src/main/java/org/openhab/binding/dmx/internal/multiverse/BaseDmxChannel.java b/bundles/org.openhab.binding.dmx/src/main/java/org/openhab/binding/dmx/internal/multiverse/BaseDmxChannel.java index 88292e44bada4..2abd1fa9e5698 100644 --- a/bundles/org.openhab.binding.dmx/src/main/java/org/openhab/binding/dmx/internal/multiverse/BaseDmxChannel.java +++ b/bundles/org.openhab.binding.dmx/src/main/java/org/openhab/binding/dmx/internal/multiverse/BaseDmxChannel.java @@ -95,9 +95,10 @@ public int compareTo(BaseDmxChannel otherDmxChannel) { if (otherDmxChannel == null) { return -1; } - int universeCompare = new Integer(getUniverseId()).compareTo(new Integer(otherDmxChannel.getUniverseId())); + int universeCompare = Integer.valueOf(getUniverseId()) + .compareTo(Integer.valueOf(otherDmxChannel.getUniverseId())); if (universeCompare == 0) { - return new Integer(getChannelId()).compareTo(new Integer(otherDmxChannel.getChannelId())); + return Integer.valueOf(getChannelId()).compareTo(Integer.valueOf(otherDmxChannel.getChannelId())); } else { return universeCompare; } diff --git a/bundles/org.openhab.binding.dmx/src/main/resources/OH-INF/thing/artnet-bridge.xml b/bundles/org.openhab.binding.dmx/src/main/resources/OH-INF/thing/artnet-bridge.xml index ce5b2db5bfc84..fad3cc7d8f419 100644 --- a/bundles/org.openhab.binding.dmx/src/main/resources/OH-INF/thing/artnet-bridge.xml +++ b/bundles/org.openhab.binding.dmx/src/main/resources/OH-INF/thing/artnet-bridge.xml @@ -10,11 +10,10 @@ - + Network addresses of ArtNet receivers, format: address[:port][, address[:port], ...]. Default port is 6454. - true diff --git a/bundles/org.openhab.binding.dmx/src/main/resources/OH-INF/thing/chaser-thing.xml b/bundles/org.openhab.binding.dmx/src/main/resources/OH-INF/thing/chaser-thing.xml index a7ad88b25feba..5313518fcf0ec 100644 --- a/bundles/org.openhab.binding.dmx/src/main/resources/OH-INF/thing/chaser-thing.xml +++ b/bundles/org.openhab.binding.dmx/src/main/resources/OH-INF/thing/chaser-thing.xml @@ -16,21 +16,18 @@ - + Format is channel[,channel, ...] or channel[/width] - true resume old actions after this completes false - false - + fadeTime:value[, ...]:holdTime - true diff --git a/bundles/org.openhab.binding.dmx/src/main/resources/OH-INF/thing/color-thing.xml b/bundles/org.openhab.binding.dmx/src/main/resources/OH-INF/thing/color-thing.xml index b2ba8c8a86098..1bebec0c08ad5 100644 --- a/bundles/org.openhab.binding.dmx/src/main/resources/OH-INF/thing/color-thing.xml +++ b/bundles/org.openhab.binding.dmx/src/main/resources/OH-INF/thing/color-thing.xml @@ -18,10 +18,9 @@ - + Format is channel[,channel, ...] or channel[/width], has to be multiple of three - true @@ -37,7 +36,6 @@ Format is "value[, value, ...]", has to be a multiple of three. If less values than channels are defined, they are reused from the beginning. - false true @@ -49,7 +47,6 @@ If enabled, values are stored on OFF command and restored on ON command - false false diff --git a/bundles/org.openhab.binding.dmx/src/main/resources/OH-INF/thing/dimmer-thing.xml b/bundles/org.openhab.binding.dmx/src/main/resources/OH-INF/thing/dimmer-thing.xml index 7e107d9b01ccd..d4c1695ba1fbd 100644 --- a/bundles/org.openhab.binding.dmx/src/main/resources/OH-INF/thing/dimmer-thing.xml +++ b/bundles/org.openhab.binding.dmx/src/main/resources/OH-INF/thing/dimmer-thing.xml @@ -16,10 +16,9 @@ - + Format is channel[,channel, ...] or channel[/width] - true @@ -36,7 +35,6 @@ Format is "value[, value, ...]". If less values than channels are defined, they are reused from the beginning. - false true @@ -49,7 +47,6 @@ If enabled, values are stored on OFF command and restored on ON command - false false diff --git a/bundles/org.openhab.binding.dmx/src/main/resources/OH-INF/thing/lib485-bridge.xml b/bundles/org.openhab.binding.dmx/src/main/resources/OH-INF/thing/lib485-bridge.xml index 70fe9f7175395..0bd1ea68ef571 100644 --- a/bundles/org.openhab.binding.dmx/src/main/resources/OH-INF/thing/lib485-bridge.xml +++ b/bundles/org.openhab.binding.dmx/src/main/resources/OH-INF/thing/lib485-bridge.xml @@ -10,10 +10,9 @@ - + Network address of bridge, format: address[:port]. Default port is 9020. - true localhost diff --git a/bundles/org.openhab.binding.dmx/src/main/resources/OH-INF/thing/sacn-bridge.xml b/bundles/org.openhab.binding.dmx/src/main/resources/OH-INF/thing/sacn-bridge.xml index c594138c59f35..723c7e6112ac7 100644 --- a/bundles/org.openhab.binding.dmx/src/main/resources/OH-INF/thing/sacn-bridge.xml +++ b/bundles/org.openhab.binding.dmx/src/main/resources/OH-INF/thing/sacn-bridge.xml @@ -10,11 +10,10 @@ - + Set UDP mode to multicast (default)/unicast - true @@ -25,24 +24,20 @@ Network addresses of sACN/E1.31 receivers, format: address[:port][, address[:port], ...]. Default port is 5568. - false Network address of the sending host, format: address[:port]. Default port is 0 (random) - false true ID of DMX universe (1-63999) 1 - false Suppress re-transmission and refresh every 800ms or send every packet. - false @@ -53,12 +48,10 @@ List of channels that should use LED dim curve. Format is channel[,channel, ...] or channel[/width]. - false true DMX refresh rate in Hz - false 30 true diff --git a/bundles/org.openhab.binding.dmx/src/main/resources/OH-INF/thing/tunablewhite-thing.xml b/bundles/org.openhab.binding.dmx/src/main/resources/OH-INF/thing/tunablewhite-thing.xml index 52076d7ecf8be..2476b86aa6fc3 100644 --- a/bundles/org.openhab.binding.dmx/src/main/resources/OH-INF/thing/tunablewhite-thing.xml +++ b/bundles/org.openhab.binding.dmx/src/main/resources/OH-INF/thing/tunablewhite-thing.xml @@ -18,10 +18,9 @@ - + Format is channel[,channel, ...] or channel[/width], has to be an even number - true @@ -38,7 +37,6 @@ Format is "value[, value, ...]", has to be a multiple of two. If less values than channels are defined, they are reused from the beginning. - false true @@ -51,7 +49,6 @@ If enabled, values are stored on OFF command and restored on ON command - false false diff --git a/bundles/org.openhab.binding.ecobee/src/main/java/org/openhab/binding/ecobee/internal/api/EcobeeApi.java b/bundles/org.openhab.binding.ecobee/src/main/java/org/openhab/binding/ecobee/internal/api/EcobeeApi.java index e77c5334e015f..93c28d5e3f982 100644 --- a/bundles/org.openhab.binding.ecobee/src/main/java/org/openhab/binding/ecobee/internal/api/EcobeeApi.java +++ b/bundles/org.openhab.binding.ecobee/src/main/java/org/openhab/binding/ecobee/internal/api/EcobeeApi.java @@ -26,7 +26,7 @@ import java.util.Set; import java.util.concurrent.TimeoutException; -import org.apache.commons.lang.exception.ExceptionUtils; +import org.apache.commons.lang3.exception.ExceptionUtils; import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.Nullable; import org.eclipse.jetty.client.HttpClient; @@ -168,16 +168,16 @@ private boolean isAuthorized() { ecobeeAuth.doAuthorization(); } catch (OAuthException | IOException | RuntimeException e) { if (logger.isDebugEnabled()) { - logger.info("API: Got exception trying to get access token from OAuth service", e); + logger.warn("API: Got exception trying to get access token from OAuth service", e); } else { - logger.info("API: Got {} trying to get access token from OAuth service: {}", + logger.warn("API: Got {} trying to get access token from OAuth service: {}", e.getClass().getSimpleName(), e.getMessage()); } } catch (EcobeeAuthException e) { if (logger.isDebugEnabled()) { - logger.info("API: The Ecobee authorization process threw an exception", e); + logger.warn("API: The Ecobee authorization process threw an exception", e); } else { - logger.info("API: The Ecobee authorization process threw an exception: {}", e.getMessage()); + logger.warn("API: The Ecobee authorization process threw an exception: {}", e.getMessage()); } ecobeeAuth.setState(EcobeeAuthState.NEED_PIN); } catch (OAuthResponseException e) { @@ -189,12 +189,12 @@ private boolean isAuthorized() { private void handleOAuthException(OAuthResponseException e) { if ("invalid_grant".equalsIgnoreCase(e.getError())) { // Usually indicates that the refresh token is no longer valid and will require reauthorization - logger.warn("API: Received 'invalid_grant' error response. Please reauthorize application with Ecobee"); + logger.debug("API: Received 'invalid_grant' error response. Please reauthorize application with Ecobee"); deleteOAuthClientService(); createOAuthClientService(); } else { // Other errors may not require reauthorization and/or may not apply - logger.warn("API: Exception getting access token: error='{}', description='{}'", e.getError(), + logger.debug("API: Exception getting access token: error='{}', description='{}'", e.getError(), e.getErrorDescription()); } } @@ -285,7 +285,7 @@ private String buildQueryUrl(String baseUrl, String requestJson) throws Unsuppor } catch (IOException e) { logIOException(e); } catch (EcobeeAuthException e) { - logger.info("API: Unable to execute GET: {}", e.getMessage()); + logger.debug("API: Unable to execute GET: {}", e.getMessage()); } return response; } @@ -306,7 +306,7 @@ private boolean executePost(String url, String json) { } catch (IOException e) { logIOException(e); } catch (EcobeeAuthException e) { - logger.info("API: Unable to execute POST: {}", e.getMessage()); + logger.debug("API: Unable to execute POST: {}", e.getMessage()); } return false; } @@ -318,22 +318,21 @@ private void logIOException(Exception e) { logger.debug("API: Call to Ecobee API failed with exception: {}: {}", rootCause.getClass().getSimpleName(), rootCause.getMessage()); } else { - // What's left are unexpected errors that should be logged as INFO with a full stack trace - logger.info("API: Call to Ecobee API failed", e); + // What's left are unexpected errors that should be logged as WARN with a full stack trace + logger.warn("API: Call to Ecobee API failed", e); } } private void logJSException(Exception e, String response) { // The API sometimes returns an HTML page complaining of an SSL error - // Otherwise, this probably should be INFO level logger.debug("API: JsonSyntaxException parsing response: {}", response, e); } private boolean isSuccess(@Nullable AbstractResponseDTO response) { if (response == null) { - logger.info("API: Ecobee API returned null response"); + logger.debug("API: Ecobee API returned null response"); } else if (response.status.code.intValue() != 0) { - logger.info("API: Ecobee API returned unsuccessful status: code={}, message={}", response.status.code, + logger.debug("API: Ecobee API returned unsuccessful status: code={}, message={}", response.status.code, response.status.message); if (response.status.code == ECOBEE_DEAUTHORIZED_TOKEN) { // Token has been deauthorized, so restart the authorization process from the beginning @@ -342,11 +341,11 @@ private boolean isSuccess(@Nullable AbstractResponseDTO response) { createOAuthClientService(); } else if (response.status.code == ECOBEE_TOKEN_EXPIRED) { // Check isAuthorized again to see if we can get a valid token - logger.info("API: Unable to complete API call because token is expired"); + logger.debug("API: Unable to complete API call because token is expired"); if (isAuthorized()) { return true; } else { - logger.warn("API: isAuthorized was NOT successful on second try"); + logger.debug("API: isAuthorized was NOT successful on second try"); } } } else { diff --git a/bundles/org.openhab.binding.ecobee/src/main/java/org/openhab/binding/ecobee/internal/api/EcobeeAuth.java b/bundles/org.openhab.binding.ecobee/src/main/java/org/openhab/binding/ecobee/internal/api/EcobeeAuth.java index e3aa1853277e2..06958b89d12cf 100644 --- a/bundles/org.openhab.binding.ecobee/src/main/java/org/openhab/binding/ecobee/internal/api/EcobeeAuth.java +++ b/bundles/org.openhab.binding.ecobee/src/main/java/org/openhab/binding/ecobee/internal/api/EcobeeAuth.java @@ -18,8 +18,7 @@ import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; -import org.apache.commons.lang.StringUtils; -import org.apache.commons.lang.exception.ExceptionUtils; +import org.apache.commons.lang3.exception.ExceptionUtils; import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.Nullable; import org.eclipse.jetty.client.HttpClient; @@ -133,8 +132,9 @@ private void authorize() throws EcobeeAuthException { logger.debug("EcobeeAuth: Got null authorize response from Ecobee API"); setState(EcobeeAuthState.NEED_PIN); } else { - if (StringUtils.isNotEmpty(authResponse.error)) { - throw new EcobeeAuthException(authResponse.error + ": " + authResponse.errorDescription); + String error = authResponse.error; + if (error != null && !error.isEmpty()) { + throw new EcobeeAuthException(error + ": " + authResponse.errorDescription); } code = authResponse.code; writeLogMessage(authResponse.pin, authResponse.expiresIn); @@ -172,8 +172,9 @@ private void getTokens() throws EcobeeAuthException { setState(isPinExpired() ? EcobeeAuthState.NEED_PIN : EcobeeAuthState.NEED_TOKEN); return; } - if (StringUtils.isNotEmpty(tokenResponse.error)) { - throw new EcobeeAuthException(tokenResponse.error + ": " + tokenResponse.errorDescription); + String error = tokenResponse.error; + if (error != null && !error.isEmpty()) { + throw new EcobeeAuthException(error + ": " + tokenResponse.errorDescription); } AccessTokenResponse accessTokenResponse = new AccessTokenResponse(); accessTokenResponse.setRefreshToken(tokenResponse.refreshToken); diff --git a/bundles/org.openhab.binding.ecobee/src/main/java/org/openhab/binding/ecobee/internal/handler/EcobeeSensorThingHandler.java b/bundles/org.openhab.binding.ecobee/src/main/java/org/openhab/binding/ecobee/internal/handler/EcobeeSensorThingHandler.java index 7168910c1c7fc..760248fc8f273 100644 --- a/bundles/org.openhab.binding.ecobee/src/main/java/org/openhab/binding/ecobee/internal/handler/EcobeeSensorThingHandler.java +++ b/bundles/org.openhab.binding.ecobee/src/main/java/org/openhab/binding/ecobee/internal/handler/EcobeeSensorThingHandler.java @@ -17,7 +17,7 @@ import java.util.Map; import java.util.concurrent.ConcurrentHashMap; -import org.apache.commons.lang.WordUtils; +import org.apache.commons.lang3.text.WordUtils; import org.eclipse.jdt.annotation.NonNullByDefault; import org.openhab.binding.ecobee.internal.config.EcobeeSensorConfiguration; import org.openhab.binding.ecobee.internal.dto.thermostat.RemoteSensorCapabilityDTO; diff --git a/bundles/org.openhab.binding.ecobee/src/main/java/org/openhab/binding/ecobee/internal/handler/EcobeeThermostatBridgeHandler.java b/bundles/org.openhab.binding.ecobee/src/main/java/org/openhab/binding/ecobee/internal/handler/EcobeeThermostatBridgeHandler.java index 76541af49d686..f6b399bf6f867 100644 --- a/bundles/org.openhab.binding.ecobee/src/main/java/org/openhab/binding/ecobee/internal/handler/EcobeeThermostatBridgeHandler.java +++ b/bundles/org.openhab.binding.ecobee/src/main/java/org/openhab/binding/ecobee/internal/handler/EcobeeThermostatBridgeHandler.java @@ -25,7 +25,7 @@ import javax.measure.Unit; -import org.apache.commons.lang.WordUtils; +import org.apache.commons.lang3.text.WordUtils; import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.Nullable; import org.openhab.binding.ecobee.internal.action.EcobeeActions; diff --git a/bundles/org.openhab.binding.elerotransmitterstick/src/main/java/org/openhab/binding/elerotransmitterstick/internal/stick/SerialConnection.java b/bundles/org.openhab.binding.elerotransmitterstick/src/main/java/org/openhab/binding/elerotransmitterstick/internal/stick/SerialConnection.java index 16573c9828dfa..71cd206469f40 100644 --- a/bundles/org.openhab.binding.elerotransmitterstick/src/main/java/org/openhab/binding/elerotransmitterstick/internal/stick/SerialConnection.java +++ b/bundles/org.openhab.binding.elerotransmitterstick/src/main/java/org/openhab/binding/elerotransmitterstick/internal/stick/SerialConnection.java @@ -17,7 +17,7 @@ import java.util.List; import java.util.TooManyListenersException; -import org.apache.commons.lang.ArrayUtils; +import org.apache.commons.lang3.ArrayUtils; import org.openhab.core.io.transport.serial.PortInUseException; import org.openhab.core.io.transport.serial.SerialPort; import org.openhab.core.io.transport.serial.SerialPortEvent; diff --git a/bundles/org.openhab.binding.enigma2/src/main/java/org/openhab/binding/enigma2/internal/Enigma2Client.java b/bundles/org.openhab.binding.enigma2/src/main/java/org/openhab/binding/enigma2/internal/Enigma2Client.java index b695105c2be0d..f636b1d9dfe90 100644 --- a/bundles/org.openhab.binding.enigma2/src/main/java/org/openhab/binding/enigma2/internal/Enigma2Client.java +++ b/bundles/org.openhab.binding.enigma2/src/main/java/org/openhab/binding/enigma2/internal/Enigma2Client.java @@ -24,7 +24,6 @@ import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.parsers.ParserConfigurationException; -import org.apache.commons.lang.StringUtils; import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.Nullable; import org.eclipse.jetty.util.UrlEncoded; @@ -94,7 +93,7 @@ public Enigma2Client(String host, @Nullable String user, @Nullable String passwo } catch (ParserConfigurationException e) { logger.warn("Failed setting parser features against XXE attacks!", e); } - if (StringUtils.isNotEmpty(user) && StringUtils.isNotEmpty(password)) { + if (user != null && !user.isEmpty() && password != null && !password.isEmpty()) { this.host = "http://" + user + ":" + password + "@" + host; } else { this.host = "http://" + host; diff --git a/bundles/org.openhab.binding.enocean/README.md b/bundles/org.openhab.binding.enocean/README.md index e6ff99b0a1686..38600c86d2e4b 100644 --- a/bundles/org.openhab.binding.enocean/README.md +++ b/bundles/org.openhab.binding.enocean/README.md @@ -126,7 +126,21 @@ The corresponding channels are created dynamically, too. If the actuator supports UTE teach-in, the corresponding thing can be created and paired automatically. First you have to **start the discovery scan for a gateway**. Then press the teach-in button of the actuator. -If the EEP of the actuator is known, the binding sends an UTE teach-in response with a new SenderId and creates a new thing with its channels. +If the EEP of the actuator is known, the binding sends an UTE teach-in response with a new SenderId and creates a new thing with its channels. + +This binding supports so called smart acknowlegde (SMACK) devices too. +Before you can pair a SMACK device you have to configure your gateway bridge as a SMACK postmaster. +If this option is enabled you can pair up to 20 SMACK devices with your gateway. + +Communication between your gateway and a SMACK device is handled through mailboxes. +A mailbox is created for each paired SMACK device and deleted after teach out. +You can see the paired SMACK devices and their mailbox index in the gateway properties. +SMACK devices send periodically status updates followed by a response request. +Whenever such a request is received a `requestAnswer` event is triggered for channel `statusRequestEvent`. +Afterwards you have 100ms time to recalculate your items states and update them. +A message with the updated item states is built, put into the corresponding mailbox and automatically sent upon request of the device. +Pairing and unpairing can be done through a discovery scan. +The corresponding thing of an unpaired device gets disabled, you have to delete it manually if you want to. If the actuator does not support UTE teach-ins, you have to create, configure and choose the right EEP of the thing manually. It is important to link the teach-in channel of this thing to a switch item. @@ -158,6 +172,8 @@ If you change the SenderId of your thing, you have to pair again the thing with | | espVersion | ESP Version of gateway | ESP3, ESP2 | | | rs485 | If gateway is directly connected to a RS485 bus the BaseId is set to 0x00 | true, false | | rs485BaseId | Override BaseId 0x00 if your bus contains a telegram duplicator (FTD14 for ex) | 4 byte hex value | +| | enableSmack | Enables SMACK pairing and handling of SMACK messages | true, false | +| | sendTeachOuts | Defines if a repeated teach in request should be answered with a learned in or teach out response | true, false | | pushButton | receivingEEPId | EEP used for receiving msg | F6_01_01, D2_03_0A | | | enoceanId | EnOceanId of device this thing belongs to | hex value as string | | rockerSwitch | receivingEEPId | | F6_02_01, F6_02_02 | @@ -300,6 +316,7 @@ The channels of a thing are determined automatically based on the chosen EEP. | rssi | Number | Received Signal Strength Indication (dBm) of last received message | | repeatCount | Number | Number of repeaters involved in the transmission of the telegram | | lastReceived | DateTime | Date and time the last telegram was received | +| statusRequestEvent | Trigger | Emits event 'requestAnswer' | Items linked to bi-directional actuators (actuator sends status messages back) should always disable the `autoupdate`. This is especially true for Eltako rollershutter, as their position is calculated out of the current position and the moving time. diff --git a/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/EnOceanBindingConstants.java b/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/EnOceanBindingConstants.java index 4ae4bf2240bca..c6b3814c5edbc 100644 --- a/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/EnOceanBindingConstants.java +++ b/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/EnOceanBindingConstants.java @@ -177,7 +177,7 @@ public class EnOceanBindingConstants { public static final String CHANNEL_WAKEUPCYCLE = "wakeUpCycle"; public static final String CHANNEL_SERVICECOMMAND = "serviceCommand"; public static final String CHANNEL_STATUS_REQUEST_EVENT = "statusRequestEvent"; - public static final String CHANNEL_SEND_COMMAND = "sendCommand"; + public static final String VIRTUALCHANNEL_SEND_COMMAND = "sendCommand"; public static final String CHANNEL_VENTILATIONOPERATIONMODE = "ventilationOperationMode"; public static final String CHANNEL_FIREPLACESAFETYMODE = "fireplaceSafetyMode"; @@ -293,7 +293,8 @@ public class EnOceanBindingConstants { Map.entry(CHANNEL_INDOORAIRANALYSIS, new EnOceanChannelDescription(new ChannelTypeUID(BINDING_ID, CHANNEL_INDOORAIRANALYSIS), CoreItemFactory.STRING)), - Map.entry(CHANNEL_SETPOINT, + Map.entry( + CHANNEL_SETPOINT, new EnOceanChannelDescription(new ChannelTypeUID(BINDING_ID, CHANNEL_SETPOINT), CoreItemFactory.NUMBER)), Map.entry(CHANNEL_CONTACT, @@ -444,13 +445,6 @@ public class EnOceanBindingConstants { new EnOceanChannelDescription(new ChannelTypeUID(BINDING_ID, CHANNEL_SERVICECOMMAND), CoreItemFactory.NUMBER)), - Map.entry(CHANNEL_STATUS_REQUEST_EVENT, - new EnOceanChannelDescription(new ChannelTypeUID(BINDING_ID, CHANNEL_STATUS_REQUEST_EVENT), null, - "", false, true)), - Map.entry(CHANNEL_SEND_COMMAND, - new EnOceanChannelDescription(new ChannelTypeUID(BINDING_ID, CHANNEL_SEND_COMMAND), - CoreItemFactory.SWITCH)), - Map.entry(CHANNEL_VENTILATIONOPERATIONMODE, new EnOceanChannelDescription(new ChannelTypeUID(BINDING_ID, CHANNEL_VENTILATIONOPERATIONMODE), CoreItemFactory.STRING)), @@ -527,6 +521,10 @@ public class EnOceanBindingConstants { CoreItemFactory.NUMBER + ItemUtil.EXTENSION_SEPARATOR + Dimensionless.class.getSimpleName())), + Map.entry(CHANNEL_STATUS_REQUEST_EVENT, + new EnOceanChannelDescription(new ChannelTypeUID(BINDING_ID, CHANNEL_STATUS_REQUEST_EVENT), null, + "", false, true)), + Map.entry(CHANNEL_REPEATERMODE, new EnOceanChannelDescription( new ChannelTypeUID(BINDING_ID, CHANNEL_REPEATERMODE), CoreItemFactory.STRING))); @@ -536,11 +534,8 @@ public class EnOceanBindingConstants { public static final String REPEATERMODE_LEVEL_2 = "LEVEL2"; // Bridge config properties - public static final String SENDERID = "senderId"; public static final String PATH = "path"; - public static final String HOST = "host"; - public static final String RS485 = "rs485"; - public static final String NEXTSENDERID = "nextSenderId"; + public static final String PARAMETER_NEXT_SENDERID = "nextSenderId"; // Bridge properties public static final String PROPERTY_BASE_ID = "Base ID"; @@ -551,13 +546,12 @@ public class EnOceanBindingConstants { public static final String PROPERTY_DESCRIPTION = "Description"; // Thing properties - public static final String PROPERTY_ENOCEAN_ID = "enoceanId"; + public static final String PROPERTY_SENDINGENOCEAN_ID = "SendingEnoceanId"; // Thing config parameter public static final String PARAMETER_SENDERIDOFFSET = "senderIdOffset"; public static final String PARAMETER_SENDINGEEPID = "sendingEEPId"; public static final String PARAMETER_RECEIVINGEEPID = "receivingEEPId"; - public static final String PARAMETER_EEPID = "eepId"; public static final String PARAMETER_BROADCASTMESSAGES = "broadcastMessages"; public static final String PARAMETER_ENOCEANID = "enoceanId"; diff --git a/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/EnOceanHandlerFactory.java b/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/EnOceanHandlerFactory.java index 3ac9ea809cced..bbc82fff8d2b9 100644 --- a/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/EnOceanHandlerFactory.java +++ b/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/EnOceanHandlerFactory.java @@ -28,6 +28,7 @@ import org.openhab.core.io.transport.serial.SerialPortManager; import org.openhab.core.thing.Bridge; import org.openhab.core.thing.Thing; +import org.openhab.core.thing.ThingManager; import org.openhab.core.thing.ThingTypeUID; import org.openhab.core.thing.ThingUID; import org.openhab.core.thing.binding.BaseThingHandlerFactory; @@ -60,6 +61,9 @@ public class EnOceanHandlerFactory extends BaseThingHandlerFactory { @Reference ItemChannelLinkRegistry itemChannelLinkRegistry; + @Reference + ThingManager thingManager; + @Override public boolean supportsThingType(ThingTypeUID thingTypeUID) { return SUPPORTED_THING_TYPES_UIDS.contains(thingTypeUID); @@ -96,7 +100,7 @@ protected void removeHandler(ThingHandler thingHandler) { } private void registerDeviceDiscoveryService(EnOceanBridgeHandler handler) { - EnOceanDeviceDiscoveryService discoveryService = new EnOceanDeviceDiscoveryService(handler); + EnOceanDeviceDiscoveryService discoveryService = new EnOceanDeviceDiscoveryService(handler, thingManager); discoveryService.activate(); this.discoveryServiceRegs.put(handler.getThing().getUID(), bundleContext.registerService(DiscoveryService.class.getName(), discoveryService, new Hashtable<>())); diff --git a/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/config/EnOceanActuatorConfig.java b/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/config/EnOceanActuatorConfig.java index 461e2e0c95d5d..eb9291abcafca 100644 --- a/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/config/EnOceanActuatorConfig.java +++ b/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/config/EnOceanActuatorConfig.java @@ -19,7 +19,7 @@ public class EnOceanActuatorConfig extends EnOceanBaseConfig { public int channel; - public int senderIdOffset = -1; + public Integer senderIdOffset = null; public String manufacturerId; public String teachInType; diff --git a/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/config/EnOceanBaseConfig.java b/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/config/EnOceanBaseConfig.java index 49a0cdae330df..100d83a3b8633 100644 --- a/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/config/EnOceanBaseConfig.java +++ b/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/config/EnOceanBaseConfig.java @@ -17,15 +17,23 @@ import java.util.ArrayList; import java.util.List; +import org.eclipse.jdt.annotation.NonNullByDefault; import org.openhab.core.util.HexUtils; /** * * @author Daniel Weber - Initial contribution */ +@NonNullByDefault public class EnOceanBaseConfig { + /** + * EnOceanId of the physical device + */ public String enoceanId; + /** + * EEP used/send by physical device + */ public List receivingEEPId = new ArrayList<>(); public boolean receivingSIGEEP = false; diff --git a/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/config/EnOceanBridgeConfig.java b/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/config/EnOceanBridgeConfig.java index baad63a36ed47..6e6bc671a4461 100644 --- a/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/config/EnOceanBridgeConfig.java +++ b/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/config/EnOceanBridgeConfig.java @@ -46,10 +46,16 @@ public static ESPVersion getESPVersion(String espVersion) { public boolean rs485; public String rs485BaseId; - public int nextSenderId = 0; + public Integer nextSenderId; + + public boolean enableSmack; + public boolean sendTeachOuts; public EnOceanBridgeConfig() { espVersion = "ESP3"; + sendTeachOuts = false; + enableSmack = true; + nextSenderId = null; } public ESPVersion getESPVersion() { diff --git a/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/config/EnOceanChannelTransformationConfig.java b/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/config/EnOceanChannelTransformationConfig.java index 36471ec37be02..81c701f4579e4 100644 --- a/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/config/EnOceanChannelTransformationConfig.java +++ b/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/config/EnOceanChannelTransformationConfig.java @@ -12,12 +12,19 @@ */ package org.openhab.binding.enocean.internal.config; +import org.openhab.core.config.core.Configuration; + /** * * @author Daniel Weber - Initial contribution */ -public class EnOceanChannelTransformationConfig { +public class EnOceanChannelTransformationConfig extends Configuration { public String transformationType; public String transformationFunction; + + public EnOceanChannelTransformationConfig() { + put("transformationType", ""); + put("transformationFunction", ""); + } } diff --git a/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/discovery/EnOceanDeviceDiscoveryService.java b/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/discovery/EnOceanDeviceDiscoveryService.java index 030e601e576bf..0373c474ba586 100644 --- a/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/discovery/EnOceanDeviceDiscoveryService.java +++ b/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/discovery/EnOceanDeviceDiscoveryService.java @@ -25,9 +25,14 @@ import org.openhab.binding.enocean.internal.messages.BasePacket; import org.openhab.binding.enocean.internal.messages.ERP1Message; import org.openhab.binding.enocean.internal.messages.ERP1Message.RORG; -import org.openhab.binding.enocean.internal.transceiver.PacketListener; +import org.openhab.binding.enocean.internal.messages.EventMessage; +import org.openhab.binding.enocean.internal.messages.EventMessage.EventMessageType; +import org.openhab.binding.enocean.internal.messages.Responses.SMACKTeachInResponse; +import org.openhab.binding.enocean.internal.transceiver.TeachInListener; import org.openhab.core.config.discovery.AbstractDiscoveryService; import org.openhab.core.config.discovery.DiscoveryResultBuilder; +import org.openhab.core.thing.Thing; +import org.openhab.core.thing.ThingManager; import org.openhab.core.thing.ThingTypeUID; import org.openhab.core.thing.ThingUID; import org.openhab.core.util.HexUtils; @@ -39,15 +44,16 @@ * * @author Daniel Weber - Initial contribution */ - -public class EnOceanDeviceDiscoveryService extends AbstractDiscoveryService implements PacketListener { +public class EnOceanDeviceDiscoveryService extends AbstractDiscoveryService implements TeachInListener { private final Logger logger = LoggerFactory.getLogger(EnOceanDeviceDiscoveryService.class); private EnOceanBridgeHandler bridgeHandler; + private ThingManager thingManager; - public EnOceanDeviceDiscoveryService(EnOceanBridgeHandler bridgeHandler) { + public EnOceanDeviceDiscoveryService(EnOceanBridgeHandler bridgeHandler, ThingManager thingManager) { super(null, 60, false); this.bridgeHandler = bridgeHandler; + this.thingManager = thingManager; } /** @@ -102,72 +108,139 @@ public void packetReceived(BasePacket packet) { } String enoceanId = HexUtils.bytesToHex(eep.getSenderId()); - ThingTypeUID thingTypeUID = eep.getThingTypeUID(); - ThingUID thingUID = new ThingUID(thingTypeUID, bridgeHandler.getThing().getUID(), enoceanId); - - int senderIdOffset = 0; - boolean broadcastMessages = true; - - // check for bidirectional communication => do not use broadcast in this case - if (msg.getRORG() == RORG.UTE && (msg.getPayload(1, 1)[0] - & UTEResponse.CommunicationType_MASK) == UTEResponse.CommunicationType_MASK) { - broadcastMessages = false; - } - - // if ute => send response if needed - if (msg.getRORG() == RORG.UTE && (msg.getPayload(1, 1)[0] & UTEResponse.ResponseNeeded_MASK) == 0) { - logger.info("Sending UTE response to {}", enoceanId); - senderIdOffset = sendTeachInResponse(msg, enoceanId); - } - - // if 4BS teach in variation 3 => send response - if ((eep instanceof _4BSMessage) && ((_4BSMessage) eep).isTeachInVariation3Supported()) { - logger.info("Sending 4BS teach in variation 3 response to {}", enoceanId); - senderIdOffset = sendTeachInResponse(msg, enoceanId); - } - DiscoveryResultBuilder discoveryResultBuilder = DiscoveryResultBuilder.create(thingUID) - .withRepresentationProperty(enoceanId).withBridge(bridgeHandler.getThing().getUID()); + bridgeHandler.getThing().getThings().stream() + .filter(t -> t.getConfiguration().getProperties().getOrDefault(PARAMETER_ENOCEANID, EMPTYENOCEANID) + .toString().equals(enoceanId)) + .findFirst().ifPresentOrElse(t -> { + // If repeated learn is not allowed => send teach out + // otherwise do nothing + if (bridgeHandler.sendTeachOuts()) { + sendTeachOutResponse(msg, enoceanId, t); + thingManager.setEnabled(t.getUID(), false); + } + }, () -> { + Integer senderIdOffset = null; + boolean broadcastMessages = true; + + // check for bidirectional communication => do not use broadcast in this case + if (msg.getRORG() == RORG.UTE && (msg.getPayload(1, 1)[0] + & UTEResponse.CommunicationType_MASK) == UTEResponse.CommunicationType_MASK) { + broadcastMessages = false; + } + + if (msg.getRORG() == RORG.UTE && (msg.getPayload(1, 1)[0] & UTEResponse.ResponseNeeded_MASK) == 0) { + // if ute => send response if needed + logger.debug("Sending UTE response to {}", enoceanId); + senderIdOffset = sendTeachInResponse(msg, enoceanId); + if (senderIdOffset == null) { + return; + } + } else if ((eep instanceof _4BSMessage) && ((_4BSMessage) eep).isTeachInVariation3Supported()) { + // if 4BS teach in variation 3 => send response + logger.debug("Sending 4BS teach in variation 3 response to {}", enoceanId); + senderIdOffset = sendTeachInResponse(msg, enoceanId); + if (senderIdOffset == null) { + return; + } + } + + createDiscoveryResult(eep, broadcastMessages, senderIdOffset); + }); + } - eep.addConfigPropertiesTo(discoveryResultBuilder); - discoveryResultBuilder.withProperty(PARAMETER_BROADCASTMESSAGES, broadcastMessages); - discoveryResultBuilder.withProperty(PARAMETER_ENOCEANID, enoceanId); + @Override + public void eventReceived(EventMessage event) { + if (event.getEventMessageType() == EventMessageType.SA_CONFIRM_LEARN) { + EEP eep = EEPFactory.buildEEPFromTeachInSMACKEvent(event); + if (eep == null) { + return; + } - if (senderIdOffset > 0) { - // advance config with new device id - discoveryResultBuilder.withProperty(PARAMETER_SENDERIDOFFSET, senderIdOffset); + SMACKTeachInResponse response = EEPFactory.buildResponseFromSMACKTeachIn(event, + bridgeHandler.sendTeachOuts()); + if (response != null) { + bridgeHandler.sendMessage(response, null); + + if (response.isTeachIn()) { + // SenderIdOffset will be determined during Thing init + createDiscoveryResult(eep, false, -1); + } else if (response.isTeachOut()) { + // disable already teached in thing + bridgeHandler.getThing().getThings().stream() + .filter(t -> t.getConfiguration().getProperties() + .getOrDefault(PARAMETER_ENOCEANID, EMPTYENOCEANID).toString() + .equals(HexUtils.bytesToHex(eep.getSenderId()))) + .findFirst().ifPresentOrElse(t -> { + thingManager.setEnabled(t.getUID(), false); + logger.info("Disable thing with id {}", t.getUID()); + }, () -> { + logger.info("Thing for EnOceanId {} already deleted", + HexUtils.bytesToHex(eep.getSenderId())); + }); + } + } } - - thingDiscovered(discoveryResultBuilder.build()); - - // As we only support sensors to be teached in, we do not need to send a teach in response => 4bs - // bidirectional teach in proc is not supported yet - // this is true except for UTE teach in => we always have to send a response here } - private int sendTeachInResponse(ERP1Message msg, String enoceanId) { - int offset; + private Integer sendTeachInResponse(ERP1Message msg, String enoceanId) { // get new sender Id - offset = bridgeHandler.getNextSenderId(enoceanId); - if (offset > 0) { + Integer offset = bridgeHandler.getNextSenderId(enoceanId); + if (offset != null) { byte[] newSenderId = bridgeHandler.getBaseId(); newSenderId[3] += offset; // send response - EEP response = EEPFactory.buildResponseEEPFromTeachInERP1(msg, newSenderId); + EEP response = EEPFactory.buildResponseEEPFromTeachInERP1(msg, newSenderId, true); if (response != null) { bridgeHandler.sendMessage(response.getERP1Message(), null); - logger.info("Teach in response for {} with new senderId {} (= offset {}) sent", enoceanId, + logger.debug("Teach in response for {} with new senderId {} (= offset {}) sent", enoceanId, HexUtils.bytesToHex(newSenderId), offset); } else { logger.warn("Teach in response for enoceanId {} not supported!", enoceanId); } + } else { + logger.warn("Could not get new SenderIdOffset"); } return offset; } + private void sendTeachOutResponse(ERP1Message msg, String enoceanId, Thing thing) { + byte[] senderId = bridgeHandler.getBaseId(); + senderId[3] += (byte) thing.getConfiguration().getProperties().getOrDefault(PARAMETER_SENDERIDOFFSET, 0); + + // send response + EEP response = EEPFactory.buildResponseEEPFromTeachInERP1(msg, senderId, false); + if (response != null) { + bridgeHandler.sendMessage(response.getERP1Message(), null); + logger.debug("Teach out response for thing {} with EnOceanId {} sent", thing.getUID().getId(), enoceanId); + } else { + logger.warn("Teach out response for enoceanId {} not supported!", enoceanId); + } + } + + protected void createDiscoveryResult(EEP eep, boolean broadcastMessages, Integer senderIdOffset) { + String enoceanId = HexUtils.bytesToHex(eep.getSenderId()); + ThingTypeUID thingTypeUID = eep.getThingTypeUID(); + ThingUID thingUID = new ThingUID(thingTypeUID, bridgeHandler.getThing().getUID(), enoceanId); + + DiscoveryResultBuilder discoveryResultBuilder = DiscoveryResultBuilder.create(thingUID) + .withRepresentationProperty(PARAMETER_ENOCEANID).withProperty(PARAMETER_ENOCEANID, enoceanId) + .withProperty(PARAMETER_BROADCASTMESSAGES, broadcastMessages) + .withBridge(bridgeHandler.getThing().getUID()); + + eep.addConfigPropertiesTo(discoveryResultBuilder); + + if (senderIdOffset != null) { + // advance config with new device id + discoveryResultBuilder.withProperty(PARAMETER_SENDERIDOFFSET, senderIdOffset); + } + + thingDiscovered(discoveryResultBuilder.build()); + } + @Override - public long getSenderIdToListenTo() { + public long getEnOceanIdToListenTo() { // we just want teach in msg, so return zero here return 0; } diff --git a/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/A5_02/A5_02.java b/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/A5_02/A5_02.java index 30c8522f7c2d2..957296c15f37f 100644 --- a/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/A5_02/A5_02.java +++ b/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/A5_02/A5_02.java @@ -20,7 +20,6 @@ import org.openhab.core.library.types.QuantityType; import org.openhab.core.library.unit.SIUnits; import org.openhab.core.types.State; -import org.openhab.core.types.UnDefType; /** * @@ -51,9 +50,6 @@ protected int getUnscaledTemperatureValue() { @Override protected State convertToStateImpl(String channelId, String channelTypeId, Function getCurrentStateFunc, Configuration config) { - if (!isValid()) { - return UnDefType.UNDEF; - } double scaledTemp = getScaledMin() - (((getUnscaledMin() - getUnscaledTemperatureValue()) * (getScaledMin() - getScaledMax())) diff --git a/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/A5_04/A5_04.java b/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/A5_04/A5_04.java index fa4241caf9548..355d6365b356e 100644 --- a/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/A5_04/A5_04.java +++ b/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/A5_04/A5_04.java @@ -62,9 +62,6 @@ protected int getUnscaledHumidityValue() { @Override protected State convertToStateImpl(String channelId, String channelTypeId, Function getCurrentStateFunc, Configuration config) { - if (!isValid()) { - return UnDefType.UNDEF; - } if (channelId.equals(CHANNEL_TEMPERATURE)) { double scaledTemp = getScaledTemperatureMin() diff --git a/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/A5_07/A5_07.java b/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/A5_07/A5_07.java index ac78bbf02e832..56f074f799ae9 100644 --- a/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/A5_07/A5_07.java +++ b/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/A5_07/A5_07.java @@ -53,9 +53,6 @@ protected State getSupplyVoltage(int value) { @Override protected State convertToStateImpl(String channelId, String channelTypeId, Function getCurrentStateFunc, Configuration config) { - if (!isValid()) { - return UnDefType.UNDEF; - } if (channelId.equals(CHANNEL_ILLUMINATION)) { return getIllumination(); diff --git a/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/A5_08/A5_08.java b/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/A5_08/A5_08.java index 5f6a54c806dd1..cf471e7fd7136 100644 --- a/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/A5_08/A5_08.java +++ b/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/A5_08/A5_08.java @@ -71,9 +71,6 @@ protected int getUnscaledIlluminationValue() { @Override protected State convertToStateImpl(String channelId, String channelTypeId, Function getCurrentStateFunc, Configuration config) { - if (!isValid()) { - return UnDefType.UNDEF; - } if (channelId.equals(CHANNEL_TEMPERATURE)) { double scaledTemp = getScaledTemperatureMin() diff --git a/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/A5_10/A5_10.java b/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/A5_10/A5_10.java index 99def10649e15..465ce91d2528f 100644 --- a/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/A5_10/A5_10.java +++ b/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/A5_10/A5_10.java @@ -40,9 +40,6 @@ public A5_10(ERP1Message packet) { @Override protected State convertToStateImpl(String channelId, String channelTypeId, Function getCurrentStateFunc, Configuration config) { - if (!isValid()) { - return UnDefType.UNDEF; - } switch (channelId) { case CHANNEL_FANSPEEDSTAGE: diff --git a/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/A5_20/A5_20_04.java b/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/A5_20/A5_20_04.java index 1c54b86f78c95..058a34b760163 100644 --- a/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/A5_20/A5_20_04.java +++ b/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/A5_20/A5_20_04.java @@ -169,7 +169,7 @@ private byte getSer(Function getCurrentStateFunc) { @Override protected void convertFromCommandImpl(String channelId, String channelTypeId, Command command, Function getCurrentStateFunc, Configuration config) { - if (CHANNEL_SEND_COMMAND.equals(channelId) && (command.equals(OnOffType.ON))) { + if (VIRTUALCHANNEL_SEND_COMMAND.equals(channelId)) { byte db3 = getPos(getCurrentStateFunc); byte db2 = getTsp(getCurrentStateFunc); byte db1 = (byte) (0x00 | getMc(getCurrentStateFunc) | getWuc(getCurrentStateFunc)); diff --git a/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/Base/PTM200Message.java b/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/Base/PTM200Message.java index ce476f4f85237..ead041877797c 100644 --- a/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/Base/PTM200Message.java +++ b/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/Base/PTM200Message.java @@ -55,9 +55,6 @@ protected void convertFromCommandImpl(String channelId, String channelTypeId, Co @Override protected State convertToStateImpl(String channelId, String channelTypeId, Function getCurrentStateFunc, Configuration config) { - if (!isValid()) { - return UnDefType.UNDEF; - } switch (channelId) { case CHANNEL_GENERAL_SWITCHING: @@ -77,4 +74,9 @@ protected State convertToStateImpl(String channelId, String channelTypeId, return UnDefType.UNDEF; } + + @Override + public boolean isValidForTeachIn() { + return false; + } } diff --git a/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/Base/UTEResponse.java b/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/Base/UTEResponse.java index 41cdcb1a88462..21dba840f65b3 100644 --- a/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/Base/UTEResponse.java +++ b/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/Base/UTEResponse.java @@ -27,11 +27,12 @@ public class UTEResponse extends _VLDMessage { public static final byte ResponseNeeded_MASK = 0x40; public static final byte TeachIn_NotSpecified = 0x20; - public UTEResponse(ERP1Message packet) { + public UTEResponse(ERP1Message packet, boolean teachIn) { int dataLength = packet.getPayload().length - ESP3_SENDERID_LENGTH - ESP3_RORG_LENGTH - ESP3_STATUS_LENGTH; setData(packet.getPayload(ESP3_RORG_LENGTH, dataLength)); - bytes[0] = (byte) 0x91; // bidirectional communication, teach in accepted, teach in response + bytes[0] = (byte) (teachIn ? 0x91 : 0xA1); // bidirectional communication, teach in accepted or teach out, teach + // in response setStatus((byte) 0x80); setSuppressRepeating(true); diff --git a/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/Base/_4BSTeachInVariation3Response.java b/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/Base/_4BSTeachInVariation3Response.java index 96d1200b0c4eb..651a786af8d72 100644 --- a/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/Base/_4BSTeachInVariation3Response.java +++ b/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/Base/_4BSTeachInVariation3Response.java @@ -23,11 +23,11 @@ */ public class _4BSTeachInVariation3Response extends _4BSMessage { - public _4BSTeachInVariation3Response(ERP1Message packet) { + public _4BSTeachInVariation3Response(ERP1Message packet, boolean teachIn) { byte[] payload = packet.getPayload(ESP3_RORG_LENGTH, RORG._4BS.getDataLength()); - payload[3] = (byte) 0xF0; // telegram with EEP number and Manufacturer ID, - // EEP supported, Sender ID stored, Response + payload[3] = (byte) (teachIn ? 0xF0 : 0xD0); // telegram with EEP number and Manufacturer ID, + // EEP supported, Sender ID stored or deleted, Response setData(payload); setDestinationId(packet.getSenderId()); diff --git a/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/Base/_RPSMessage.java b/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/Base/_RPSMessage.java index 4f6afd303d39e..3dd44acc26e6d 100644 --- a/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/Base/_RPSMessage.java +++ b/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/Base/_RPSMessage.java @@ -51,4 +51,6 @@ public EEP setStatus(byte status) { return this; } + + public abstract boolean isValidForTeachIn(); } diff --git a/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/D2_05/D2_05_00.java b/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/D2_05/D2_05_00.java index c6d54180a628a..018b3ea06335b 100644 --- a/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/D2_05/D2_05_00.java +++ b/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/D2_05/D2_05_00.java @@ -98,7 +98,8 @@ protected byte getChannel() { @Override public void addConfigPropertiesTo(DiscoveryResultBuilder discoveredThingResultBuilder) { - discoveredThingResultBuilder.withProperty(PARAMETER_EEPID, getEEPType().getId()); + discoveredThingResultBuilder.withProperty(PARAMETER_SENDINGEEPID, getEEPType().getId()) + .withProperty(PARAMETER_RECEIVINGEEPID, getEEPType().getId()); } @Override diff --git a/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/EEPFactory.java b/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/EEPFactory.java index 63e9c0fcf3f43..c84a1a753f7f7 100644 --- a/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/EEPFactory.java +++ b/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/EEPFactory.java @@ -15,18 +15,24 @@ import static org.openhab.binding.enocean.internal.messages.ESP3Packet.*; import java.lang.reflect.InvocationTargetException; +import java.util.Arrays; import org.openhab.binding.enocean.internal.eep.Base.UTEResponse; import org.openhab.binding.enocean.internal.eep.Base._4BSMessage; import org.openhab.binding.enocean.internal.eep.Base._4BSTeachInVariation3Response; +import org.openhab.binding.enocean.internal.eep.Base._RPSMessage; import org.openhab.binding.enocean.internal.eep.D5_00.D5_00_01; import org.openhab.binding.enocean.internal.eep.F6_01.F6_01_01; import org.openhab.binding.enocean.internal.eep.F6_02.F6_02_01; +import org.openhab.binding.enocean.internal.eep.F6_05.F6_05_02; import org.openhab.binding.enocean.internal.eep.F6_10.F6_10_00; import org.openhab.binding.enocean.internal.eep.F6_10.F6_10_00_EltakoFPE; import org.openhab.binding.enocean.internal.eep.F6_10.F6_10_01; import org.openhab.binding.enocean.internal.messages.ERP1Message; import org.openhab.binding.enocean.internal.messages.ERP1Message.RORG; +import org.openhab.binding.enocean.internal.messages.EventMessage; +import org.openhab.binding.enocean.internal.messages.EventMessage.EventMessageType; +import org.openhab.binding.enocean.internal.messages.Responses.SMACKTeachInResponse; import org.openhab.core.util.HexUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -45,8 +51,9 @@ public static EEP createEEP(EEPType eepType) { if (cl == null) { throw new IllegalArgumentException("Message " + eepType + " not implemented"); } - return cl.newInstance(); - } catch (IllegalAccessException | InstantiationException e) { + return cl.getDeclaredConstructor().newInstance(); + } catch (IllegalAccessException | InstantiationException | IllegalArgumentException | InvocationTargetException + | NoSuchMethodException | SecurityException e) { throw new IllegalArgumentException(e); } } @@ -69,6 +76,21 @@ public static EEP buildEEP(EEPType eepType, ERP1Message packet) { } } + private static EEPType getGenericEEPTypeFor(byte rorg) { + logger.info("Received unsupported EEP teach in, trying to fallback to generic thing"); + RORG r = RORG.getRORG(rorg); + if (r == RORG._4BS) { + logger.info("Fallback to 4BS generic thing"); + return EEPType.Generic4BS; + } else if (r == RORG.VLD) { + logger.info("Fallback to VLD generic thing"); + return EEPType.GenericVLD; + } else { + logger.info("Fallback not possible"); + return null; + } + } + public static EEP buildEEPFromTeachInERP1(ERP1Message msg) { if (!msg.getIsTeachIn() && !(msg.getRORG() == RORG.RPS)) { return null; @@ -77,38 +99,48 @@ public static EEP buildEEPFromTeachInERP1(ERP1Message msg) { switch (msg.getRORG()) { case RPS: try { - EEP result = new F6_01_01(msg); - if (result.isValid()) { // check if t21 is set, nu not set, and data == 0x10 or 0x00 + _RPSMessage result = new F6_10_00(msg); + if (result.isValidForTeachIn()) { + return result; + } + } catch (Exception e) { + } + + try { + _RPSMessage result = new F6_10_01(msg); + if (result.isValidForTeachIn()) { return result; } } catch (Exception e) { } try { - EEP result = new F6_02_01(msg); - if (result.isValid()) { // check if highest bit is not set + _RPSMessage result = new F6_02_01(msg); + if (result.isValidForTeachIn()) { return result; } } catch (Exception e) { } try { - EEP result = new F6_10_00(msg); - if (result.isValid()) { + _RPSMessage result = new F6_05_02(msg); + if (result.isValidForTeachIn()) { return result; } } catch (Exception e) { } + try { - EEP result = new F6_10_00_EltakoFPE(msg); - if (result.isValid()) { // check if data == 0x10 or 0x00 + _RPSMessage result = new F6_01_01(msg); + if (result.isValidForTeachIn()) { return result; } } catch (Exception e) { } + try { - EEP result = new F6_10_01(msg); - if (result.isValid()) { + _RPSMessage result = new F6_10_00_EltakoFPE(msg); + if (result.isValidForTeachIn()) { return result; } } catch (Exception e) { @@ -120,8 +152,8 @@ public static EEP buildEEPFromTeachInERP1(ERP1Message msg) { case _4BS: { int db_0 = msg.getPayload()[4]; if ((db_0 & _4BSMessage.LRN_Type_Mask) == 0) { // Variation 1 - logger.info("Received 4BS Teach In variation 1 without EEP"); - return null; + logger.info("Received 4BS Teach In variation 1 without EEP, fallback to generic thing"); + return buildEEP(EEPType.Generic4BS, msg); } byte db_3 = msg.getPayload()[1]; @@ -132,19 +164,21 @@ public static EEP buildEEPFromTeachInERP1(ERP1Message msg) { int type = ((db_3 & 0b11) << 5) + ((db_2 & 0xFF) >>> 3); int manufId = ((db_2 & 0b111) << 8) + (db_1 & 0xff); - logger.info("Received 4BS Teach In with EEP A5-{}-{} and manufacturerID {}", + logger.debug("Received 4BS Teach In with EEP A5-{}-{} and manufacturerID {}", HexUtils.bytesToHex(new byte[] { (byte) func }), HexUtils.bytesToHex(new byte[] { (byte) type }), HexUtils.bytesToHex(new byte[] { (byte) manufId })); EEPType eepType = EEPType.getType(RORG._4BS, func, type, manufId); if (eepType == null) { - logger.debug("Received unsupported EEP teach in, fallback to generic thing"); - eepType = EEPType.Generic4BS; + eepType = getGenericEEPTypeFor(RORG._4BS.getValue()); } - return buildEEP(eepType, msg); + if (eepType != null) { + return buildEEP(eepType, msg); + } } + break; case UTE: { byte[] payload = msg.getPayload(); @@ -161,38 +195,58 @@ public static EEP buildEEPFromTeachInERP1(ERP1Message msg) { EEPType eepType = EEPType.getType(RORG.getRORG(rorg), func, type, manufId); if (eepType == null) { - logger.info("Received unsupported EEP teach in, fallback to generic thing"); - RORG r = RORG.getRORG(rorg); - if (r == RORG._4BS) { - eepType = EEPType.Generic4BS; - } else if (r == RORG.VLD) { - eepType = EEPType.GenericVLD; - } else { - return null; - } + eepType = getGenericEEPTypeFor(rorg); } - return buildEEP(eepType, msg); + if (eepType != null) { + return buildEEP(eepType, msg); + } } - case Unknown: - case VLD: - case MSC: - case SIG: + break; + default: return null; } return null; } - public static EEP buildResponseEEPFromTeachInERP1(ERP1Message msg, byte[] senderId) { + public static EEP buildEEPFromTeachInSMACKEvent(EventMessage event) { + if (event.getEventMessageType() != EventMessageType.SA_CONFIRM_LEARN) { + return null; + } + + byte[] payload = event.getPayload(); + byte manufIdMSB = payload[2]; + byte manufIdLSB = payload[3]; + int manufId = ((manufIdMSB & 0b111) << 8) + (manufIdLSB & 0xff); + + byte rorg = payload[4]; + int func = payload[5] & 0xFF; + int type = payload[6] & 0xFF; + + byte[] senderId = Arrays.copyOfRange(payload, 12, 12 + 4); + + logger.debug("Received SMACK Teach In with EEP {}-{}-{} and manufacturerID {}", + HexUtils.bytesToHex(new byte[] { (byte) rorg }), HexUtils.bytesToHex(new byte[] { (byte) func }), + HexUtils.bytesToHex(new byte[] { (byte) type }), HexUtils.bytesToHex(new byte[] { (byte) manufId })); + + EEPType eepType = EEPType.getType(RORG.getRORG(rorg), func, type, manufId); + if (eepType == null) { + eepType = getGenericEEPTypeFor(rorg); + } + + return createEEP(eepType).setSenderId(senderId); + } + + public static EEP buildResponseEEPFromTeachInERP1(ERP1Message msg, byte[] senderId, boolean teachIn) { switch (msg.getRORG()) { case UTE: - EEP result = new UTEResponse(msg); + EEP result = new UTEResponse(msg, teachIn); result.setSenderId(senderId); return result; case _4BS: - result = new _4BSTeachInVariation3Response(msg); + result = new _4BSTeachInVariation3Response(msg, teachIn); result.setSenderId(senderId); return result; @@ -200,4 +254,31 @@ public static EEP buildResponseEEPFromTeachInERP1(ERP1Message msg, byte[] sender return null; } } + + public static SMACKTeachInResponse buildResponseFromSMACKTeachIn(EventMessage event, boolean sendTeachOuts) { + SMACKTeachInResponse response = new SMACKTeachInResponse(); + + byte priority = event.getPayload()[1]; + if ((priority & 0b1001) == 0b1001) { + logger.debug("gtw is already postmaster"); + if (sendTeachOuts) { + logger.debug("Repeated learn is not allow hence send teach out"); + response.setTeachOutResponse(); + } else { + logger.debug("Send a repeated learn in"); + response.setRepeatedTeachInResponse(); + } + } else if ((priority & 0b100) == 0) { + logger.debug("no place for further mailbox"); + response.setNoPlaceForFurtherMailbox(); + } else if ((priority & 0b10) == 0) { + logger.debug("rssi is not good enough"); + response.setBadRSSI(); + } else if ((priority & 0b1) == 0b1) { + logger.debug("gtw is candidate for postmaster => teach in"); + response.setTeachIn(); + } + + return response; + } } diff --git a/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/EEPType.java b/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/EEPType.java index 451c853303fdd..26c461ae8257c 100644 --- a/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/EEPType.java +++ b/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/EEPType.java @@ -20,6 +20,7 @@ import org.eclipse.jdt.annotation.NonNull; import org.openhab.binding.enocean.internal.EnOceanChannelDescription; +import org.openhab.binding.enocean.internal.config.EnOceanChannelTransformationConfig; import org.openhab.binding.enocean.internal.eep.A5_02.A5_02_01; import org.openhab.binding.enocean.internal.eep.A5_02.A5_02_02; import org.openhab.binding.enocean.internal.eep.A5_02.A5_02_03; @@ -164,15 +165,9 @@ public enum EEPType { UTEResponse(RORG.UTE, 0, 0, false, UTEResponse.class, null), _4BSTeachInVariation3Response(RORG._4BS, 0, 0, false, _4BSTeachInVariation3Response.class, null), - GenericRPS(RORG.RPS, 0xFF, 0xFF, false, GenericRPS.class, THING_TYPE_GENERICTHING, CHANNEL_GENERIC_SWITCH, - CHANNEL_GENERIC_ROLLERSHUTTER, CHANNEL_GENERIC_DIMMER, CHANNEL_GENERIC_NUMBER, CHANNEL_GENERIC_STRING, - CHANNEL_GENERIC_COLOR, CHANNEL_GENERIC_TEACHINCMD), - Generic4BS(RORG._4BS, 0xFF, 0xFF, false, Generic4BS.class, THING_TYPE_GENERICTHING, CHANNEL_GENERIC_SWITCH, - CHANNEL_GENERIC_ROLLERSHUTTER, CHANNEL_GENERIC_DIMMER, CHANNEL_GENERIC_NUMBER, CHANNEL_GENERIC_STRING, - CHANNEL_GENERIC_COLOR, CHANNEL_GENERIC_TEACHINCMD, CHANNEL_VIBRATION), - GenericVLD(RORG.VLD, 0xFF, 0xFF, false, GenericVLD.class, THING_TYPE_GENERICTHING, CHANNEL_GENERIC_SWITCH, - CHANNEL_GENERIC_ROLLERSHUTTER, CHANNEL_GENERIC_DIMMER, CHANNEL_GENERIC_NUMBER, CHANNEL_GENERIC_STRING, - CHANNEL_GENERIC_COLOR, CHANNEL_GENERIC_TEACHINCMD), + GenericRPS(RORG.RPS, 0xFF, 0xFF, false, GenericRPS.class, THING_TYPE_GENERICTHING), + Generic4BS(RORG._4BS, 0xFF, 0xFF, false, Generic4BS.class, THING_TYPE_GENERICTHING, CHANNEL_VIBRATION), + GenericVLD(RORG.VLD, 0xFF, 0xFF, false, GenericVLD.class, THING_TYPE_GENERICTHING), PTM200(RORG.RPS, 0x00, 0x00, false, PTM200Message.class, null, CHANNEL_GENERAL_SWITCHING, CHANNEL_ROLLERSHUTTER, CHANNEL_CONTACT), @@ -391,8 +386,8 @@ public enum EEPType { // UniversalCommand(RORG._4BS, 0x3f, 0x7f, false, A5_3F_7F_Universal.class, THING_TYPE_UNIVERSALACTUATOR, // CHANNEL_GENERIC_ROLLERSHUTTER, CHANNEL_GENERIC_LIGHT_SWITCHING, CHANNEL_GENERIC_DIMMER, CHANNEL_TEACHINCMD), - EltakoFSB(RORG._4BS, 0x3f, 0x7f, false, "EltakoFSB", 0, A5_3F_7F_EltakoFSB.class, THING_TYPE_ROLLERSHUTTER, 0, - new Hashtable() { + EltakoFSB(RORG._4BS, 0x3f, 0x7f, false, false, "EltakoFSB", 0, A5_3F_7F_EltakoFSB.class, THING_TYPE_ROLLERSHUTTER, + 0, new Hashtable() { private static final long serialVersionUID = 1L; { put(CHANNEL_ROLLERSHUTTER, new Configuration()); @@ -404,10 +399,10 @@ public enum EEPType { } }), - Thermostat(RORG._4BS, 0x20, 0x04, false, A5_20_04.class, THING_TYPE_THERMOSTAT, CHANNEL_VALVE_POSITION, + Thermostat(RORG._4BS, 0x20, 0x04, false, true, A5_20_04.class, THING_TYPE_THERMOSTAT, CHANNEL_VALVE_POSITION, CHANNEL_BUTTON_LOCK, CHANNEL_DISPLAY_ORIENTATION, CHANNEL_TEMPERATURE_SETPOINT, CHANNEL_TEMPERATURE, CHANNEL_FEED_TEMPERATURE, CHANNEL_MEASUREMENT_CONTROL, CHANNEL_FAILURE_CODE, CHANNEL_WAKEUPCYCLE, - CHANNEL_SERVICECOMMAND, CHANNEL_STATUS_REQUEST_EVENT, CHANNEL_SEND_COMMAND), + CHANNEL_SERVICECOMMAND), SwitchWithEnergyMeasurment_00(RORG.VLD, 0x01, 0x00, true, D2_01_00.class, THING_TYPE_MEASUREMENTSWITCH, CHANNEL_GENERAL_SWITCHING, CHANNEL_TOTALUSAGE), @@ -512,23 +507,36 @@ public enum EEPType { private boolean supportsRefresh; + private boolean requestsResponse; + EEPType(RORG rorg, int func, int type, boolean supportsRefresh, Class eepClass, ThingTypeUID thingTypeUID, String... channelIds) { this(rorg, func, type, supportsRefresh, eepClass, thingTypeUID, -1, channelIds); } + EEPType(RORG rorg, int func, int type, boolean supportsRefresh, boolean requestsResponse, + Class eepClass, ThingTypeUID thingTypeUID, String... channelIds) { + this(rorg, func, type, supportsRefresh, requestsResponse, eepClass, thingTypeUID, -1, channelIds); + } + EEPType(RORG rorg, int func, int type, boolean supportsRefresh, String manufactorSuffix, int manufId, Class eepClass, ThingTypeUID thingTypeUID, String... channelIds) { - this(rorg, func, type, supportsRefresh, manufactorSuffix, manufId, eepClass, thingTypeUID, 0, channelIds); + this(rorg, func, type, supportsRefresh, false, manufactorSuffix, manufId, eepClass, thingTypeUID, 0, + channelIds); } EEPType(RORG rorg, int func, int type, boolean supportsRefresh, Class eepClass, ThingTypeUID thingTypeUID, int command, String... channelIds) { - this(rorg, func, type, supportsRefresh, "", 0, eepClass, thingTypeUID, command, channelIds); + this(rorg, func, type, supportsRefresh, false, "", 0, eepClass, thingTypeUID, command, channelIds); } - EEPType(RORG rorg, int func, int type, boolean supportsRefresh, String manufactorSuffix, int manufId, + EEPType(RORG rorg, int func, int type, boolean supportsRefresh, boolean requestsResponse, Class eepClass, ThingTypeUID thingTypeUID, int command, String... channelIds) { + this(rorg, func, type, supportsRefresh, requestsResponse, "", 0, eepClass, thingTypeUID, command, channelIds); + } + + EEPType(RORG rorg, int func, int type, boolean supportsRefresh, boolean requestsResponse, String manufactorSuffix, + int manufId, Class eepClass, ThingTypeUID thingTypeUID, int command, String... channelIds) { this.rorg = rorg; this.func = func; this.type = type; @@ -538,24 +546,18 @@ public enum EEPType { this.manufactorSuffix = manufactorSuffix; this.manufactorId = manufId; this.supportsRefresh = supportsRefresh; + this.requestsResponse = requestsResponse; for (String id : channelIds) { this.channelIdsWithConfig.put(id, new Configuration()); this.supportedChannels.put(id, CHANNELID2CHANNELDESCRIPTION.get(id)); } - this.channelIdsWithConfig.put(CHANNEL_RSSI, new Configuration()); - this.supportedChannels.put(CHANNEL_RSSI, CHANNELID2CHANNELDESCRIPTION.get(CHANNEL_RSSI)); - - this.channelIdsWithConfig.put(CHANNEL_REPEATCOUNT, new Configuration()); - this.supportedChannels.put(CHANNEL_REPEATCOUNT, CHANNELID2CHANNELDESCRIPTION.get(CHANNEL_REPEATCOUNT)); - - this.channelIdsWithConfig.put(CHANNEL_LASTRECEIVED, new Configuration()); - this.supportedChannels.put(CHANNEL_LASTRECEIVED, CHANNELID2CHANNELDESCRIPTION.get(CHANNEL_LASTRECEIVED)); + addDefaultChannels(); } - EEPType(RORG rorg, int func, int type, boolean supportsRefresh, String manufactorSuffix, int manufId, - Class eepClass, ThingTypeUID thingTypeUID, int command, + EEPType(RORG rorg, int func, int type, boolean supportsRefresh, boolean requestsResponse, String manufactorSuffix, + int manufId, Class eepClass, ThingTypeUID thingTypeUID, int command, Hashtable channelConfigs) { this.rorg = rorg; this.func = func; @@ -567,11 +569,46 @@ public enum EEPType { this.manufactorSuffix = manufactorSuffix; this.manufactorId = manufId; this.supportsRefresh = supportsRefresh; + this.requestsResponse = requestsResponse; for (String id : channelConfigs.keySet()) { this.supportedChannels.put(id, CHANNELID2CHANNELDESCRIPTION.get(id)); } + addDefaultChannels(); + } + + private void addDefaultChannels() { + + if (THING_TYPE_GENERICTHING.equals(this.thingTypeUID)) { + this.channelIdsWithConfig.put(CHANNEL_GENERIC_SWITCH, new EnOceanChannelTransformationConfig()); + this.supportedChannels.put(CHANNEL_GENERIC_SWITCH, + CHANNELID2CHANNELDESCRIPTION.get(CHANNEL_GENERIC_SWITCH)); + + this.channelIdsWithConfig.put(CHANNEL_GENERIC_ROLLERSHUTTER, new EnOceanChannelTransformationConfig()); + this.supportedChannels.put(CHANNEL_GENERIC_ROLLERSHUTTER, + CHANNELID2CHANNELDESCRIPTION.get(CHANNEL_GENERIC_ROLLERSHUTTER)); + + this.channelIdsWithConfig.put(CHANNEL_GENERIC_DIMMER, new EnOceanChannelTransformationConfig()); + this.supportedChannels.put(CHANNEL_GENERIC_DIMMER, + CHANNELID2CHANNELDESCRIPTION.get(CHANNEL_GENERIC_DIMMER)); + + this.channelIdsWithConfig.put(CHANNEL_GENERIC_NUMBER, new EnOceanChannelTransformationConfig()); + this.supportedChannels.put(CHANNEL_GENERIC_NUMBER, + CHANNELID2CHANNELDESCRIPTION.get(CHANNEL_GENERIC_NUMBER)); + + this.channelIdsWithConfig.put(CHANNEL_GENERIC_STRING, new EnOceanChannelTransformationConfig()); + this.supportedChannels.put(CHANNEL_GENERIC_STRING, + CHANNELID2CHANNELDESCRIPTION.get(CHANNEL_GENERIC_STRING)); + + this.channelIdsWithConfig.put(CHANNEL_GENERIC_COLOR, new EnOceanChannelTransformationConfig()); + this.supportedChannels.put(CHANNEL_GENERIC_COLOR, CHANNELID2CHANNELDESCRIPTION.get(CHANNEL_GENERIC_COLOR)); + + this.channelIdsWithConfig.put(CHANNEL_GENERIC_TEACHINCMD, new EnOceanChannelTransformationConfig()); + this.supportedChannels.put(CHANNEL_GENERIC_TEACHINCMD, + CHANNELID2CHANNELDESCRIPTION.get(CHANNEL_GENERIC_TEACHINCMD)); + } + this.channelIdsWithConfig.put(CHANNEL_RSSI, new Configuration()); this.supportedChannels.put(CHANNEL_RSSI, CHANNELID2CHANNELDESCRIPTION.get(CHANNEL_RSSI)); @@ -580,6 +617,12 @@ public enum EEPType { this.channelIdsWithConfig.put(CHANNEL_LASTRECEIVED, new Configuration()); this.supportedChannels.put(CHANNEL_LASTRECEIVED, CHANNELID2CHANNELDESCRIPTION.get(CHANNEL_LASTRECEIVED)); + + if (requestsResponse) { + this.channelIdsWithConfig.put(CHANNEL_STATUS_REQUEST_EVENT, new Configuration()); + this.supportedChannels.put(CHANNEL_STATUS_REQUEST_EVENT, + CHANNELID2CHANNELDESCRIPTION.get(CHANNEL_STATUS_REQUEST_EVENT)); + } } public Class getEEPClass() { @@ -602,6 +645,10 @@ public boolean getSupportsRefresh() { return supportsRefresh; } + public boolean getRequstesResponse() { + return requestsResponse; + } + public Map GetSupportedChannels() { return Collections.unmodifiableMap(supportedChannels); } @@ -614,7 +661,7 @@ public boolean isChannelSupported(Channel channel) { } public boolean isChannelSupported(String channelId, String channelTypeId) { - return supportedChannels.containsKey(channelId) + return supportedChannels.containsKey(channelId) || VIRTUALCHANNEL_SEND_COMMAND.equals(channelId) || supportedChannels.values().stream().anyMatch(c -> c.channelTypeUID.getId().equals(channelTypeId)); } diff --git a/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/F6_01/F6_01_01.java b/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/F6_01/F6_01_01.java index ce19528af36f0..b69aa8d4d6386 100644 --- a/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/F6_01/F6_01_01.java +++ b/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/F6_01/F6_01_01.java @@ -34,9 +34,6 @@ public F6_01_01(ERP1Message packet) { @Override protected String convertToEventImpl(String channelId, String channelTypeId, String lastEvent, Configuration config) { - if (!isValid()) { - return null; - } return getBit(bytes[0], 4) ? CommonTriggerEvents.PRESSED : CommonTriggerEvents.RELEASED; } @@ -45,4 +42,10 @@ protected String convertToEventImpl(String channelId, String channelTypeId, Stri protected boolean validateData(byte[] bytes) { return super.validateData(bytes) && !getBit(bytes[0], 7); } + + @Override + public boolean isValidForTeachIn() { + // just treat press as teach in, ignore release + return t21 && !nu && bytes[0] == 0x10; + } } diff --git a/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/F6_02/F6_02_01.java b/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/F6_02/F6_02_01.java index 2af97fdc06f67..8152c5e90a30a 100644 --- a/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/F6_02/F6_02_01.java +++ b/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/F6_02/F6_02_01.java @@ -55,9 +55,6 @@ public F6_02_01(ERP1Message packet) { @Override protected String convertToEventImpl(String channelId, String channelTypeId, String lastEvent, Configuration config) { - if (!isValid()) { - return null; - } if (t21 && nu) { byte dir1 = channelId.equals(CHANNEL_ROCKERSWITCH_CHANNELA) ? A0 : B0; @@ -112,11 +109,6 @@ protected State convertToStateImpl(String channelId, String channelTypeId, // this method is used by the classic device listener channels to convert an rocker switch message into an // appropriate item update State currentState = getCurrentStateFunc.apply(channelId); - - if (!isValid()) { - return UnDefType.UNDEF; - } - if (t21 && nu) { EnOceanChannelVirtualRockerSwitchConfig c = config.as(EnOceanChannelVirtualRockerSwitchConfig.class); byte dir1 = c.getChannel() == Channel.ChannelA ? A0 : B0; @@ -179,4 +171,22 @@ private State inverse(UpDownType currentState) { protected boolean validateData(byte[] bytes) { return super.validateData(bytes) && !getBit(bytes[0], 7); } + + @Override + public boolean isValidForTeachIn() { + if (t21) { + // just treat press as teach in => DB0.4 has to be set + if (!getBit(bytes[0], 4)) { + return false; + } + // DB0.7 is never set for rocker switch message + if (getBit(bytes[0], 7)) { + return false; + } + } else { + return false; + } + + return true; + } } diff --git a/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/F6_02/F6_02_02.java b/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/F6_02/F6_02_02.java index 29b643cb01a1a..76e169785171a 100644 --- a/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/F6_02/F6_02_02.java +++ b/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/F6_02/F6_02_02.java @@ -52,9 +52,6 @@ public F6_02_02(ERP1Message packet) { @Override protected String convertToEventImpl(String channelId, String channelTypeId, String lastEvent, Configuration config) { - if (!isValid()) { - return null; - } if (t21 && nu) { byte dir1 = channelId.equals(CHANNEL_ROCKERSWITCH_CHANNELA) ? AI : BI; @@ -109,11 +106,6 @@ protected State convertToStateImpl(String channelId, String channelTypeId, // this method is used by the classic device listener channels to convert an rocker switch message into an // appropriate item update State currentState = getCurrentStateFunc.apply(channelId); - - if (!isValid()) { - return UnDefType.UNDEF; - } - if (t21 && nu) { EnOceanChannelVirtualRockerSwitchConfig c = config.as(EnOceanChannelVirtualRockerSwitchConfig.class); byte dir1 = c.getChannel() == Channel.ChannelA ? AI : BI; @@ -171,4 +163,9 @@ private State inverse(OnOffType currentState) { private State inverse(UpDownType currentState) { return currentState == UpDownType.UP ? UpDownType.DOWN : UpDownType.UP; } + + @Override + public boolean isValidForTeachIn() { + return false; // Never treat a message as F6-02-02, let user decide which orientation of rocker switch is used + } } diff --git a/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/F6_05/F6_05_02.java b/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/F6_05/F6_05_02.java index 50855ab2e92cc..4fafc5b68ac6e 100644 --- a/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/F6_05/F6_05_02.java +++ b/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/F6_05/F6_05_02.java @@ -44,9 +44,6 @@ public F6_05_02(ERP1Message packet) { @Override protected State convertToStateImpl(String channelId, String channelTypeId, Function getCurrentStateFunc, Configuration config) { - if (!isValid()) { - return UnDefType.UNDEF; - } switch (channelId) { case CHANNEL_SMOKEDETECTION: @@ -62,4 +59,10 @@ protected State convertToStateImpl(String channelId, String channelTypeId, protected boolean validateData(byte[] bytes) { return super.validateData(bytes) && (bytes[0] == ALARM_OFF || bytes[0] == ALARM_ON || bytes[0] == ENERGY_LOW); } + + @Override + public boolean isValidForTeachIn() { + // just treat the first message with ALARM_ON as teach in + return !t21 && !nu && bytes[0] == ALARM_ON; + } } diff --git a/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/F6_10/F6_10_00.java b/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/F6_10/F6_10_00.java index 43fc0f75344d4..498a807458fdb 100644 --- a/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/F6_10/F6_10_00.java +++ b/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/F6_10/F6_10_00.java @@ -47,9 +47,6 @@ public F6_10_00(ERP1Message packet) { @Override protected State convertToStateImpl(String channelId, String channelTypeId, Function getCurrentStateFunc, Configuration config) { - if (!isValid()) { - return UnDefType.UNDEF; - } byte data = (byte) (bytes[0] & 0xF0); @@ -82,4 +79,9 @@ protected State convertToStateImpl(String channelId, String channelTypeId, protected boolean validateData(byte[] bytes) { return super.validateData(bytes) && getBit(bytes[0], 7) && getBit(bytes[0], 6); } + + @Override + public boolean isValidForTeachIn() { + return t21 && !nu && getBit(bytes[0], 7) && getBit(bytes[0], 6); + } } diff --git a/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/F6_10/F6_10_00_EltakoFPE.java b/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/F6_10/F6_10_00_EltakoFPE.java index 8a381ea9e1600..ee9cd485f9c9b 100644 --- a/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/F6_10/F6_10_00_EltakoFPE.java +++ b/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/F6_10/F6_10_00_EltakoFPE.java @@ -61,4 +61,10 @@ protected boolean validateData(byte[] bytes) { // FPE just sends 0b00010000 or 0b00000000 value, so we apply mask 0b11101111 return super.validateData(bytes) && ((bytes[0] & (byte) 0xEF) == (byte) 0x00); } + + @Override + public boolean isValidForTeachIn() { + // just treat CLOSED as teach in + return bytes[0] == CLOSED; + } } diff --git a/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/F6_10/F6_10_01.java b/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/F6_10/F6_10_01.java index 51ed9306212f0..3773cafe49c8d 100644 --- a/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/F6_10/F6_10_01.java +++ b/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/F6_10/F6_10_01.java @@ -47,9 +47,6 @@ public F6_10_01(ERP1Message packet) { @Override protected State convertToStateImpl(String channelId, String channelTypeId, Function getCurrentStateFunc, Configuration config) { - if (!isValid()) { - return UnDefType.UNDEF; - } byte data = (byte) (bytes[0] & 0x0F); @@ -82,4 +79,10 @@ protected State convertToStateImpl(String channelId, String channelTypeId, protected boolean validateData(byte[] bytes) { return super.validateData(bytes) && getBit(bytes[0], 6) && getBit(bytes[0], 3) && getBit(bytes[0], 2); } + + @Override + public boolean isValidForTeachIn() { + return !getBit(bytes[0], 7) && getBit(bytes[0], 6) && !getBit(bytes[0], 5) && !getBit(bytes[0], 4) + && getBit(bytes[0], 3) && getBit(bytes[0], 2); + } } diff --git a/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/Generic/GenericEEP.java b/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/Generic/GenericEEP.java index 178ae890b8689..d930e8206400b 100644 --- a/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/Generic/GenericEEP.java +++ b/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/Generic/GenericEEP.java @@ -12,7 +12,7 @@ */ package org.openhab.binding.enocean.internal.eep.Generic; -import static org.openhab.binding.enocean.internal.EnOceanBindingConstants.PARAMETER_EEPID; +import static org.openhab.binding.enocean.internal.EnOceanBindingConstants.*; import static org.openhab.binding.enocean.internal.messages.ESP3Packet.*; import java.lang.reflect.InvocationTargetException; @@ -161,6 +161,7 @@ protected boolean validateData(byte[] bytes) { @Override public void addConfigPropertiesTo(DiscoveryResultBuilder discoveredThingResultBuilder) { - discoveredThingResultBuilder.withProperty(PARAMETER_EEPID, getEEPType().getId()); + discoveredThingResultBuilder.withProperty(PARAMETER_SENDINGEEPID, getEEPType().getId()) + .withProperty(PARAMETER_RECEIVINGEEPID, getEEPType().getId()); } } diff --git a/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/handler/EnOceanBaseActuatorHandler.java b/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/handler/EnOceanBaseActuatorHandler.java index 590769153791d..be2398ac0cf6c 100644 --- a/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/handler/EnOceanBaseActuatorHandler.java +++ b/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/handler/EnOceanBaseActuatorHandler.java @@ -28,6 +28,7 @@ import org.openhab.binding.enocean.internal.eep.EEPType; import org.openhab.binding.enocean.internal.messages.BasePacket; import org.openhab.core.config.core.Configuration; +import org.openhab.core.library.types.OnOffType; import org.openhab.core.thing.Channel; import org.openhab.core.thing.ChannelUID; import org.openhab.core.thing.Thing; @@ -68,8 +69,8 @@ public EnOceanBaseActuatorHandler(Thing thing, ItemChannelLinkRegistry itemChann * @param senderIdOffset to be validated * @return true if senderIdOffset is between ]0;128[ and is not used yet */ - private boolean validateSenderIdOffset(int senderIdOffset) { - if (senderIdOffset == -1) { + private boolean validateSenderIdOffset(Integer senderIdOffset) { + if (senderIdOffset == null) { return true; } @@ -157,26 +158,24 @@ boolean validateConfig() { } private boolean initializeIdForSending() { - // Generic things are treated as actuator things, however to support also generic sensors one can define a - // senderIdOffset of -1 - // TODO: seperate generic actuators from generic sensors? - String thingTypeId = this.getThing().getThingTypeUID().getId(); - String genericThingTypeId = THING_TYPE_GENERICTHING.getId(); - - if (getConfiguration().senderIdOffset == -1 && thingTypeId.equals(genericThingTypeId)) { - return true; - } - EnOceanBridgeHandler bridgeHandler = getBridgeHandler(); if (bridgeHandler == null) { return false; } - // if senderIdOffset is not set (=> defaults to -1) or set to -1, the next free senderIdOffset is determined - if (getConfiguration().senderIdOffset == -1) { + // Generic things are treated as actuator things, however to support also generic sensors one can omit + // senderIdOffset + // TODO: seperate generic actuators from generic sensors? + if ((getConfiguration().senderIdOffset == null + && THING_TYPE_GENERICTHING.equals(this.getThing().getThingTypeUID()))) { + return true; + } + + // if senderIdOffset is not set, the next free senderIdOffset is determined + if (getConfiguration().senderIdOffset == null) { Configuration updateConfig = editConfiguration(); getConfiguration().senderIdOffset = bridgeHandler.getNextSenderId(thing); - if (getConfiguration().senderIdOffset == -1) { + if (getConfiguration().senderIdOffset == null) { configurationErrorDescription = "Could not get a free sender Id from Bridge"; return false; } @@ -185,12 +184,10 @@ private boolean initializeIdForSending() { } byte[] baseId = bridgeHandler.getBaseId(); - baseId[3] = (byte) ((baseId[3] & 0xFF) + getConfiguration().senderIdOffset); + baseId[3] = (byte) ((baseId[3] + getConfiguration().senderIdOffset) & 0xFF); this.senderId = baseId; - - this.updateProperty(PROPERTY_ENOCEAN_ID, HexUtils.bytesToHex(this.senderId)); + this.updateProperty(PROPERTY_SENDINGENOCEAN_ID, HexUtils.bytesToHex(this.senderId)); bridgeHandler.addSender(getConfiguration().senderIdOffset, thing); - return true; } @@ -203,6 +200,22 @@ private void refreshStates() { } } + @Override + protected void sendRequestResponse() { + sendMessage(VIRTUALCHANNEL_SEND_COMMAND, VIRTUALCHANNEL_SEND_COMMAND, OnOffType.ON, null); + } + + protected void sendMessage(String channelId, String channelTypeId, Command command, Configuration channelConfig) { + EEP eep = EEPFactory.createEEP(sendingEEPType); + if (eep.convertFromCommand(channelId, channelTypeId, command, id -> getCurrentState(id), channelConfig) + .hasData()) { + BasePacket msg = eep.setSenderId(senderId).setDestinationId(destinationId) + .setSuppressRepeating(getConfiguration().suppressRepeating).getERP1Message(); + + getBridgeHandler().sendMessage(msg, null); + } + } + @Override public void handleCommand(ChannelUID channelUID, Command command) { // We must have a valid sendingEEPType and sender id to send commands @@ -237,16 +250,7 @@ public void handleCommand(ChannelUID channelUID, Command command) { try { Configuration channelConfig = channel.getConfiguration(); - - EEP eep = EEPFactory.createEEP(sendingEEPType); - if (eep.convertFromCommand(channelId, channelTypeId, command, id -> getCurrentState(id), channelConfig) - .hasData()) { - BasePacket msg = eep.setSenderId(senderId).setDestinationId(destinationId) - .setSuppressRepeating(getConfiguration().suppressRepeating).getERP1Message(); - - getBridgeHandler().sendMessage(msg, null); - } - + sendMessage(channelId, channelTypeId, command, channelConfig); } catch (IllegalArgumentException e) { logger.warn("Exception while sending telegram!", e); } @@ -254,11 +258,16 @@ public void handleCommand(ChannelUID channelUID, Command command) { @Override public void handleRemoval() { - if (getConfiguration().senderIdOffset > 0) { - EnOceanBridgeHandler bridgeHandler = getBridgeHandler(); - if (bridgeHandler != null) { + + EnOceanBridgeHandler bridgeHandler = getBridgeHandler(); + if (bridgeHandler != null) { + if (getConfiguration().senderIdOffset != null && getConfiguration().senderIdOffset > 0) { bridgeHandler.removeSender(getConfiguration().senderIdOffset); } + + if (bridgeHandler.isSmackClient(this.thing)) { + logger.warn("Removing smack client (ThingId: {}) without teach out!", this.thing.getUID().getId()); + } } super.handleRemoval(); diff --git a/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/handler/EnOceanBaseSensorHandler.java b/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/handler/EnOceanBaseSensorHandler.java index d533c1e558c4a..736b37be23671 100644 --- a/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/handler/EnOceanBaseSensorHandler.java +++ b/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/handler/EnOceanBaseSensorHandler.java @@ -19,6 +19,8 @@ import java.util.Comparator; import java.util.Hashtable; import java.util.Set; +import java.util.concurrent.ScheduledFuture; +import java.util.concurrent.TimeUnit; import java.util.function.Predicate; import org.openhab.binding.enocean.internal.config.EnOceanBaseConfig; @@ -57,6 +59,8 @@ public class EnOceanBaseSensorHandler extends EnOceanBaseThingHandler implements protected final Hashtable receivingEEPTypes = new Hashtable<>(); + protected ScheduledFuture responseFuture = null; + public EnOceanBaseSensorHandler(Thing thing, ItemChannelLinkRegistry itemChannelLinkRegistry) { super(thing, itemChannelLinkRegistry); } @@ -104,7 +108,7 @@ boolean validateConfig() { } @Override - public long getSenderIdToListenTo() { + public long getEnOceanIdToListenTo() { return Long.parseLong(config.enoceanId, 16); } @@ -129,6 +133,10 @@ protected Predicate channelFilter(EEPType eepType, byte[] senderId) { }; } + protected void sendRequestResponse() { + throw new UnsupportedOperationException("Sensor cannot send responses"); + } + @Override public void packetReceived(BasePacket packet) { ERP1Message msg = (ERP1Message) packet; @@ -175,6 +183,15 @@ public void packetReceived(BasePacket packet) { break; } }); + + if (receivingEEPType.getRequstesResponse()) { + // fire trigger for receive + triggerChannel(prepareAnswer, "requestAnswer"); + // Send response after 100ms + if (responseFuture == null || responseFuture.isDone()) { + responseFuture = scheduler.schedule(this::sendRequestResponse, 100, TimeUnit.MILLISECONDS); + } + } } } } diff --git a/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/handler/EnOceanBaseThingHandler.java b/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/handler/EnOceanBaseThingHandler.java index 20ccfa80b5062..f561db01859cf 100644 --- a/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/handler/EnOceanBaseThingHandler.java +++ b/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/handler/EnOceanBaseThingHandler.java @@ -12,6 +12,8 @@ */ package org.openhab.binding.enocean.internal.handler; +import static org.openhab.binding.enocean.internal.EnOceanBindingConstants.*; + import java.util.AbstractMap.SimpleEntry; import java.util.Collection; import java.util.Hashtable; @@ -63,18 +65,25 @@ public abstract class EnOceanBaseThingHandler extends ConfigStatusThingHandler { private ItemChannelLinkRegistry itemChannelLinkRegistry; + protected @NonNull ChannelUID prepareAnswer; + public EnOceanBaseThingHandler(Thing thing, ItemChannelLinkRegistry itemChannelLinkRegistry) { super(thing); this.itemChannelLinkRegistry = itemChannelLinkRegistry; + prepareAnswer = new ChannelUID(thing.getUID(), CHANNEL_STATUS_REQUEST_EVENT); } - @SuppressWarnings("null") @Override public void initialize() { logger.debug("Initializing enocean base thing handler."); this.gateway = null; // reset gateway in case we change the bridge this.config = null; - initializeThing((getBridge() == null) ? null : getBridge().getStatus()); + Bridge bridge = getBridge(); + if (bridge == null) { + updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, "A bridge is required"); + } else { + initializeThing(bridge.getStatus()); + } } private void initializeThing(ThingStatus bridgeStatus) { @@ -143,6 +152,10 @@ protected void updateChannels() { String channelId = entry.getKey(); EnOceanChannelDescription cd = entry.getValue().GetSupportedChannels().get(channelId); + if (cd == null) { + return; + } + // if we do not need to auto create channel => skip if (!cd.autoCreate) { return; diff --git a/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/handler/EnOceanBridgeHandler.java b/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/handler/EnOceanBridgeHandler.java index c888f6a60be2d..e793366f26698 100644 --- a/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/handler/EnOceanBridgeHandler.java +++ b/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/handler/EnOceanBridgeHandler.java @@ -15,30 +15,35 @@ import static org.openhab.binding.enocean.internal.EnOceanBindingConstants.*; import java.io.IOException; -import java.math.BigDecimal; +import java.util.Arrays; import java.util.Collection; import java.util.LinkedList; import java.util.Set; import java.util.concurrent.ScheduledFuture; import java.util.concurrent.TimeUnit; +import java.util.stream.Collectors; import org.openhab.binding.enocean.internal.EnOceanConfigStatusMessage; import org.openhab.binding.enocean.internal.config.EnOceanBaseConfig; import org.openhab.binding.enocean.internal.config.EnOceanBridgeConfig; +import org.openhab.binding.enocean.internal.config.EnOceanBridgeConfig.ESPVersion; import org.openhab.binding.enocean.internal.messages.BasePacket; -import org.openhab.binding.enocean.internal.messages.BaseResponse; import org.openhab.binding.enocean.internal.messages.ESP3PacketFactory; -import org.openhab.binding.enocean.internal.messages.RDBaseIdResponse; -import org.openhab.binding.enocean.internal.messages.RDRepeaterResponse; -import org.openhab.binding.enocean.internal.messages.RDVersionResponse; import org.openhab.binding.enocean.internal.messages.Response; import org.openhab.binding.enocean.internal.messages.Response.ResponseType; +import org.openhab.binding.enocean.internal.messages.Responses.BaseResponse; +import org.openhab.binding.enocean.internal.messages.Responses.RDBaseIdResponse; +import org.openhab.binding.enocean.internal.messages.Responses.RDLearnedClientsResponse; +import org.openhab.binding.enocean.internal.messages.Responses.RDLearnedClientsResponse.LearnedClient; +import org.openhab.binding.enocean.internal.messages.Responses.RDRepeaterResponse; +import org.openhab.binding.enocean.internal.messages.Responses.RDVersionResponse; import org.openhab.binding.enocean.internal.transceiver.EnOceanESP2Transceiver; import org.openhab.binding.enocean.internal.transceiver.EnOceanESP3Transceiver; import org.openhab.binding.enocean.internal.transceiver.EnOceanTransceiver; import org.openhab.binding.enocean.internal.transceiver.PacketListener; import org.openhab.binding.enocean.internal.transceiver.ResponseListener; import org.openhab.binding.enocean.internal.transceiver.ResponseListenerIgnoringTimeouts; +import org.openhab.binding.enocean.internal.transceiver.TeachInListener; import org.openhab.binding.enocean.internal.transceiver.TransceiverErrorListener; import org.openhab.core.config.core.Configuration; import org.openhab.core.config.core.status.ConfigStatusMessage; @@ -76,9 +81,12 @@ public class EnOceanBridgeHandler extends ConfigStatusBridgeHandler implements T private byte[] baseId = null; private Thing[] sendingThings = new Thing[128]; - private int nextSenderId = 0; private SerialPortManager serialPortManager; + private boolean smackAvailable = false; + private boolean sendTeachOuts = true; + private Set smackClients = Set.of(); + public EnOceanBridgeHandler(Bridge bridge, SerialPortManager serialPortManager) { super(bridge); this.serialPortManager = serialPortManager; @@ -157,13 +165,6 @@ public void initialize() { updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, "SerialPortManager could not be found"); } else { - Object devId = getConfig().get(NEXTSENDERID); - if (devId != null) { - nextSenderId = ((BigDecimal) devId).intValue(); - } else { - nextSenderId = 0; - } - if (connectorTask == null || connectorTask.isDone()) { connectorTask = scheduler.scheduleWithFixedDelay(new Runnable() { @Override @@ -187,9 +188,12 @@ private synchronized void initTransceiver() { switch (c.getESPVersion()) { case ESP2: transceiver = new EnOceanESP2Transceiver(c.path, this, scheduler, serialPortManager); + smackAvailable = false; + sendTeachOuts = false; break; case ESP3: transceiver = new EnOceanESP3Transceiver(c.path, this, scheduler, serialPortManager); + sendTeachOuts = c.sendTeachOuts; break; default: break; @@ -200,6 +204,7 @@ private synchronized void initTransceiver() { updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_PENDING, "starting rx thread..."); transceiver.StartReceiving(scheduler); + logger.info("EnOceanSerialTransceiver RX thread up and running"); if (c.rs485) { if (c.rs485BaseId != null && !c.rs485BaseId.isEmpty()) { @@ -238,6 +243,28 @@ public void responseReceived(RDBaseIdResponse response) { } } }); + + if (c.getESPVersion() == ESPVersion.ESP3) { + logger.debug("set postmaster mailboxes"); + transceiver.sendBasePacket(ESP3PacketFactory.SA_WR_POSTMASTER((byte) (c.enableSmack ? 20 : 0)), + new ResponseListenerIgnoringTimeouts() { + + @Override + public void responseReceived(BaseResponse response) { + + logger.debug("received response for postmaster mailboxes"); + if (response.isOK()) { + updateProperty("Postmaster mailboxes:", + Integer.toString(c.enableSmack ? 20 : 0)); + smackAvailable = c.enableSmack; + refreshProperties(); + } else { + updateProperty("Postmaster mailboxes:", "Not supported"); + smackAvailable = false; + } + } + }); + } } logger.debug("request version info"); @@ -283,7 +310,7 @@ public Collection getConfigStatus() { Collection configStatusMessages = new LinkedList<>(); // The serial port must be provided - String path = (String) getThing().getConfiguration().get(PATH); + String path = getThing().getConfiguration().as(EnOceanBridgeConfig.class).path; if (path == null || path.isEmpty()) { configStatusMessages.add(ConfigStatusMessage.Builder.error(PATH) .withMessageKeySuffix(EnOceanConfigStatusMessage.PORT_MISSING.getMessageKey()).withArguments(PATH) @@ -297,30 +324,33 @@ public byte[] getBaseId() { return baseId.clone(); } - public int getNextSenderId(Thing sender) { - // TODO: change id to enoceanId + public boolean isSmackClient(Thing sender) { + return smackClients.contains(sender.getConfiguration().as(EnOceanBaseConfig.class).enoceanId); + } + + public Integer getNextSenderId(Thing sender) { return getNextSenderId(sender.getConfiguration().as(EnOceanBaseConfig.class).enoceanId); } - public int getNextSenderId(String senderId) { - if (nextSenderId != 0 && sendingThings[nextSenderId] == null) { - int result = nextSenderId; - Configuration config = getConfig(); - config.put(NEXTSENDERID, null); - updateConfiguration(config); - nextSenderId = 0; + public Integer getNextSenderId(String enoceanId) { + EnOceanBridgeConfig config = getConfigAs(EnOceanBridgeConfig.class); - return result; + if (config.nextSenderId != null && sendingThings[config.nextSenderId] == null) { + Configuration c = this.editConfiguration(); + c.put(PARAMETER_NEXT_SENDERID, null); + updateConfiguration(c); + + return config.nextSenderId; } - for (byte i = 1; i < sendingThings.length; i++) { + for (int i = 1; i < sendingThings.length; i++) { if (sendingThings[i] == null || sendingThings[i].getConfiguration().as(EnOceanBaseConfig.class).enoceanId - .equalsIgnoreCase(senderId)) { + .equalsIgnoreCase(enoceanId)) { return i; } } - return -1; + return null; } public boolean existsSender(int id, Thing sender) { @@ -345,7 +375,7 @@ public void sendMessage(BasePacket message, ResponseListene } public void addPacketListener(PacketListener listener) { - addPacketListener(listener, listener.getSenderIdToListenTo()); + addPacketListener(listener, listener.getEnOceanIdToListenTo()); } public void addPacketListener(PacketListener listener, long senderIdToListenTo) { @@ -355,7 +385,7 @@ public void addPacketListener(PacketListener listener, long senderIdToListenTo) } public void removePacketListener(PacketListener listener) { - removePacketListener(listener, listener.getSenderIdToListenTo()); + removePacketListener(listener, listener.getEnOceanIdToListenTo()); } public void removePacketListener(PacketListener listener, long senderIdToListenTo) { @@ -364,12 +394,74 @@ public void removePacketListener(PacketListener listener, long senderIdToListenT } } - public void startDiscovery(PacketListener teachInListener) { + public void startDiscovery(TeachInListener teachInListener) { transceiver.startDiscovery(teachInListener); + + if (smackAvailable) { + // activate smack teach in + logger.debug("activate smack teach in"); + try { + transceiver.sendBasePacket(ESP3PacketFactory.SA_WR_LEARNMODE(true), + new ResponseListenerIgnoringTimeouts() { + + @Override + public void responseReceived(BaseResponse response) { + + if (response.isOK()) { + logger.debug("Smack teach in activated"); + } + } + }); + } catch (IOException e) { + updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, + "Smack packet could not be send: " + e.getMessage()); + } + } } public void stopDiscovery() { transceiver.stopDiscovery(); + + try { + transceiver.sendBasePacket(ESP3PacketFactory.SA_WR_LEARNMODE(false), null); + refreshProperties(); + } catch (IOException e) { + updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, + "Smack packet could not be send: " + e.getMessage()); + } + } + + private void refreshProperties() { + if (getThing().getStatus() == ThingStatus.ONLINE && smackAvailable) { + + logger.debug("request learned smack clients"); + try { + transceiver.sendBasePacket(ESP3PacketFactory.SA_RD_LEARNEDCLIENTS, + new ResponseListenerIgnoringTimeouts() { + + @Override + public void responseReceived(RDLearnedClientsResponse response) { + + logger.debug("received response for learned smack clients"); + if (response.isValid() && response.isOK()) { + LearnedClient[] clients = response.getLearnedClients(); + updateProperty("Learned smart ack clients", Integer.toString(clients.length)); + updateProperty("Smart ack clients", + Arrays.stream(clients) + .map(x -> String.format("%s (MB Idx: %d)", + HexUtils.bytesToHex(x.clientId), x.mailboxIndex)) + .collect(Collectors.joining(", "))); + smackClients = Arrays.stream(clients).map(x -> HexUtils.bytesToHex(x.clientId)) + .collect(Collectors.toSet()); + } + } + }); + } catch (IOException e) { + updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, + "Smack packet could not be send: " + e.getMessage()); + + } + } } @Override @@ -378,4 +470,8 @@ public void ErrorOccured(Throwable exception) { transceiver = null; updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, exception.getMessage()); } + + public boolean sendTeachOuts() { + return sendTeachOuts; + } } diff --git a/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/handler/EnOceanClassicDeviceHandler.java b/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/handler/EnOceanClassicDeviceHandler.java index 412cd9ad8d8fe..08a1e499e82d4 100644 --- a/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/handler/EnOceanClassicDeviceHandler.java +++ b/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/handler/EnOceanClassicDeviceHandler.java @@ -71,7 +71,7 @@ void initializeConfig() { } @Override - public long getSenderIdToListenTo() { + public long getEnOceanIdToListenTo() { return 0; } diff --git a/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/messages/ESP3PacketFactory.java b/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/messages/ESP3PacketFactory.java index 73551cdc83bbe..f410d6e1aba8a 100644 --- a/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/messages/ESP3PacketFactory.java +++ b/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/messages/ESP3PacketFactory.java @@ -13,8 +13,10 @@ package org.openhab.binding.enocean.internal.messages; import org.openhab.binding.enocean.internal.EnOceanBindingConstants; +import org.openhab.binding.enocean.internal.Helper; import org.openhab.binding.enocean.internal.messages.BasePacket.ESPPacketType; import org.openhab.binding.enocean.internal.messages.CCMessage.CCMessageType; +import org.openhab.binding.enocean.internal.messages.SAMessage.SAMessageType; import org.openhab.core.library.types.StringType; /** @@ -42,6 +44,28 @@ public static BasePacket CO_WR_REPEATER(StringType level) { } } + public static BasePacket SA_WR_LEARNMODE(boolean activate) { + return new SAMessage(SAMessageType.SA_WR_LEARNMODE, + new byte[] { SAMessageType.SA_WR_LEARNMODE.getValue(), (byte) (activate ? 1 : 0), 0, 0, 0, 0, 0 }); + } + + public final static BasePacket SA_RD_LEARNEDCLIENTS = new SAMessage(SAMessageType.SA_RD_LEARNEDCLIENTS); + + public static BasePacket SA_RD_MAILBOX_STATUS(byte[] clientId, byte[] controllerId) { + return new SAMessage(SAMessageType.SA_RD_MAILBOX_STATUS, + Helper.concatAll(new byte[] { SAMessageType.SA_RD_MAILBOX_STATUS.getValue() }, clientId, controllerId)); + } + + public static BasePacket SA_WR_POSTMASTER(byte mailboxes) { + return new SAMessage(SAMessageType.SA_WR_POSTMASTER, + new byte[] { SAMessageType.SA_WR_POSTMASTER.getValue(), mailboxes }); + } + + public static BasePacket SA_WR_CLIENTLEARNRQ(byte manu1, byte manu2, byte rorg, byte func, byte type) { + return new SAMessage(SAMessageType.SA_WR_CLIENTLEARNRQ, + new byte[] { SAMessageType.SA_WR_CLIENTLEARNRQ.getValue(), manu1, manu2, rorg, func, type }); + } + public static BasePacket BuildPacket(int dataLength, int optionalDataLength, byte packetType, byte[] payload) { ESPPacketType type = ESPPacketType.getPacketType(packetType); @@ -50,6 +74,8 @@ public static BasePacket BuildPacket(int dataLength, int optionalDataLength, byt return new Response(dataLength, optionalDataLength, payload); case RADIO_ERP1: return new ERP1Message(dataLength, optionalDataLength, payload); + case EVENT: + return new EventMessage(dataLength, optionalDataLength, payload); default: return null; } diff --git a/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/messages/EventMessage.java b/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/messages/EventMessage.java new file mode 100644 index 0000000000000..4cacceba47052 --- /dev/null +++ b/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/messages/EventMessage.java @@ -0,0 +1,65 @@ +/** + * Copyright (c) 2010-2021 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.enocean.internal.messages; + +import java.util.stream.Stream; + +/** + * + * @author Daniel Weber - Initial contribution + */ +public class EventMessage extends BasePacket { + + public enum EventMessageType { + UNKNOWN((byte) 0x00, 1), + SA_RECLAIM_NOT_SUCCESSFUL((byte) 0x01, 1), + SA_CONFIRM_LEARN((byte) 0x02, 17), + SA_LEARN_ACK((byte) 0x03, 4), + CO_READY((byte) 0x04, 2), + CO_EVENT_SECUREDEVICES((byte) 0x05, 6), + CO_DUTYCYCLE_LIMIT((byte) 0x06, 2), + CO_TRANSMIT_FAILED((byte) 0x07, 2); + + private byte value; + private int dataLength; + + EventMessageType(byte value, int dataLength) { + this.value = value; + this.dataLength = dataLength; + } + + public byte getValue() { + return this.value; + } + + public int getDataLength() { + return dataLength; + } + + public static EventMessageType getEventMessageType(byte value) { + return Stream.of(EventMessageType.values()).filter(t -> t.value == value).findFirst().orElse(UNKNOWN); + } + } + + private EventMessageType type; + + EventMessage(int dataLength, int optionalDataLength, byte[] payload) { + super(dataLength, optionalDataLength, ESPPacketType.EVENT, payload); + + type = EventMessageType.getEventMessageType(payload[0]); + } + + public EventMessageType getEventMessageType() { + return type; + } +} diff --git a/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/messages/Response.java b/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/messages/Response.java index 2c8a0699fe366..e7b8f2094d30a 100644 --- a/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/messages/Response.java +++ b/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/messages/Response.java @@ -57,7 +57,7 @@ public static ResponseType getResponsetype(byte value) { protected ResponseType responseType; protected boolean _isValid = false; - protected Response(int dataLength, int optionalDataLength, byte[] payload) { + public Response(int dataLength, int optionalDataLength, byte[] payload) { super(dataLength, optionalDataLength, ESPPacketType.RESPONSE, payload); try { diff --git a/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/messages/BaseResponse.java b/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/messages/Responses/BaseResponse.java similarity index 85% rename from bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/messages/BaseResponse.java rename to bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/messages/Responses/BaseResponse.java index 63de3f5cdd984..56a4ccbbf869d 100644 --- a/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/messages/BaseResponse.java +++ b/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/messages/Responses/BaseResponse.java @@ -10,9 +10,10 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.binding.enocean.internal.messages; +package org.openhab.binding.enocean.internal.messages.Responses; import org.openhab.binding.enocean.internal.Helper; +import org.openhab.binding.enocean.internal.messages.Response; /** * diff --git a/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/messages/RDBaseIdResponse.java b/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/messages/Responses/RDBaseIdResponse.java similarity index 91% rename from bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/messages/RDBaseIdResponse.java rename to bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/messages/Responses/RDBaseIdResponse.java index e2220c053f863..b2a8a0da72ea2 100644 --- a/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/messages/RDBaseIdResponse.java +++ b/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/messages/Responses/RDBaseIdResponse.java @@ -10,9 +10,10 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.binding.enocean.internal.messages; +package org.openhab.binding.enocean.internal.messages.Responses; import org.openhab.binding.enocean.internal.Helper; +import org.openhab.binding.enocean.internal.messages.Response; /** * diff --git a/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/messages/Responses/RDLearnedClientsResponse.java b/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/messages/Responses/RDLearnedClientsResponse.java new file mode 100644 index 0000000000000..481915212bd31 --- /dev/null +++ b/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/messages/Responses/RDLearnedClientsResponse.java @@ -0,0 +1,61 @@ +/** + * Copyright (c) 2010-2021 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.enocean.internal.messages.Responses; + +import java.util.Arrays; + +import org.openhab.binding.enocean.internal.Helper; +import org.openhab.binding.enocean.internal.messages.Response; + +/** + * + * @author Daniel Weber - Initial contribution + */ +public class RDLearnedClientsResponse extends Response { + + public class LearnedClient { + public byte[] clientId; + public byte[] controllerId; + public int mailboxIndex; + } + + LearnedClient[] learnedClients; + + public RDLearnedClientsResponse(Response response) { + this(response.getPayload().length, response.getOptionalPayload().length, + Helper.concatAll(response.getPayload(), response.getOptionalPayload())); + } + + RDLearnedClientsResponse(int dataLength, int optionalDataLength, byte[] payload) { + super(dataLength, optionalDataLength, payload); + + if (payload == null || ((payload.length - 1) % 9) != 0) { + return; + } else { + _isValid = true; + } + + learnedClients = new LearnedClient[(payload.length - 1) / 9]; + for (int i = 0; i < learnedClients.length; i++) { + LearnedClient client = new LearnedClient(); + client.clientId = Arrays.copyOfRange(payload, 1 + i * 9, 1 + i * 9 + 4); + client.controllerId = Arrays.copyOfRange(payload, 5 + i * 9, 5 + i * 9 + 4); + client.mailboxIndex = payload[9 + i * 9] & 0xFF; + learnedClients[i] = client; + } + } + + public LearnedClient[] getLearnedClients() { + return learnedClients; + } +} diff --git a/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/messages/RDRepeaterResponse.java b/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/messages/Responses/RDRepeaterResponse.java similarity index 93% rename from bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/messages/RDRepeaterResponse.java rename to bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/messages/Responses/RDRepeaterResponse.java index cecc623b86dcb..440068ba4dcfd 100644 --- a/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/messages/RDRepeaterResponse.java +++ b/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/messages/Responses/RDRepeaterResponse.java @@ -10,11 +10,12 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.binding.enocean.internal.messages; +package org.openhab.binding.enocean.internal.messages.Responses; import static org.openhab.binding.enocean.internal.EnOceanBindingConstants.*; import org.eclipse.jdt.annotation.NonNull; +import org.openhab.binding.enocean.internal.messages.Response; import org.openhab.core.library.types.StringType; /** diff --git a/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/messages/RDVersionResponse.java b/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/messages/Responses/RDVersionResponse.java similarity index 94% rename from bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/messages/RDVersionResponse.java rename to bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/messages/Responses/RDVersionResponse.java index 1374c56e01970..daedc9aa9ae01 100644 --- a/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/messages/RDVersionResponse.java +++ b/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/messages/Responses/RDVersionResponse.java @@ -10,11 +10,12 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.binding.enocean.internal.messages; +package org.openhab.binding.enocean.internal.messages.Responses; import java.util.Arrays; import org.eclipse.jdt.annotation.NonNull; +import org.openhab.binding.enocean.internal.messages.Response; import org.openhab.core.util.HexUtils; /** diff --git a/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/messages/Responses/SMACKTeachInResponse.java b/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/messages/Responses/SMACKTeachInResponse.java new file mode 100644 index 0000000000000..a827922d300fb --- /dev/null +++ b/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/messages/Responses/SMACKTeachInResponse.java @@ -0,0 +1,65 @@ +/** + * Copyright (c) 2010-2021 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.enocean.internal.messages.Responses; + +import org.openhab.binding.enocean.internal.messages.Response; + +/** + * + * @author Daniel Weber - Initial contribution + */ +public class SMACKTeachInResponse extends Response { + + // set response time to 250ms + static final byte RESPONSE_TIME_HVALUE = 0; + static final byte RESPONSE_TIME_LVALUE = (byte) 0xFA; + + static final byte TEACH_IN = 0x00; + static final byte TEACH_OUT = 0x20; + static final byte REPEATED_TEACH_IN = 0x01; + static final byte NOPLACE_FOR_MAILBOX = 0x12; + static final byte BAD_RSSI = 0x14; + + public SMACKTeachInResponse() { + super(4, 0, new byte[] { Response.ResponseType.RET_OK.getValue(), RESPONSE_TIME_HVALUE, RESPONSE_TIME_LVALUE, + TEACH_IN }); + } + + public void setTeachOutResponse() { + data[3] = TEACH_OUT; + } + + public boolean isTeachOut() { + return data[3] == TEACH_OUT; + } + + public void setRepeatedTeachInResponse() { + data[3] = REPEATED_TEACH_IN; + } + + public void setNoPlaceForFurtherMailbox() { + data[3] = NOPLACE_FOR_MAILBOX; + } + + public void setBadRSSI() { + data[3] = BAD_RSSI; + } + + public void setTeachIn() { + data[3] = TEACH_IN; + } + + public boolean isTeachIn() { + return data[3] == TEACH_IN; + } +} diff --git a/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/messages/SAMessage.java b/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/messages/SAMessage.java new file mode 100644 index 0000000000000..e63087ee58717 --- /dev/null +++ b/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/messages/SAMessage.java @@ -0,0 +1,64 @@ +/** + * Copyright (c) 2010-2021 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.enocean.internal.messages; + +/** + * + * @author Daniel Weber - Initial contribution + */ +public class SAMessage extends BasePacket { + + public enum SAMessageType { + SA_WR_LEARNMODE((byte) 0x01, 7), + SA_RD_LEARNMODE((byte) 0x02, 1), + SA_WR_LEARNCONFIRM((byte) 0x03, 1), + SA_WR_CLIENTLEARNRQ((byte) 0x04, 6), + SA_WR_RESET((byte) 0x05, 1), + SA_RD_LEARNEDCLIENTS((byte) 0x06, 1), + SA_WR_RECLAIMS((byte) 0x07, 1), + SA_WR_POSTMASTER((byte) 0x08, 2), + SA_RD_MAILBOX_STATUS((byte) 0x09, 9); + + private byte value; + private int dataLength; + + SAMessageType(byte value, int dataLength) { + this.value = value; + this.dataLength = dataLength; + } + + public byte getValue() { + return this.value; + } + + public int getDataLength() { + return dataLength; + } + } + + private SAMessageType type; + + public SAMessage(SAMessageType type) { + this(type, new byte[] { type.getValue() }); + } + + public SAMessage(SAMessageType type, byte[] payload) { + super(type.getDataLength(), 0, ESPPacketType.SMART_ACK_COMMAND, payload); + + this.type = type; + } + + public SAMessageType getSAMessageType() { + return type; + } +} diff --git a/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/transceiver/EnOceanESP3Transceiver.java b/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/transceiver/EnOceanESP3Transceiver.java index db15a3fec0c5f..28e8b04209630 100644 --- a/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/transceiver/EnOceanESP3Transceiver.java +++ b/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/transceiver/EnOceanESP3Transceiver.java @@ -18,8 +18,6 @@ import org.openhab.binding.enocean.internal.EnOceanException; import org.openhab.binding.enocean.internal.messages.BasePacket; -import org.openhab.binding.enocean.internal.messages.ERP1Message; -import org.openhab.binding.enocean.internal.messages.ERP1Message.RORG; import org.openhab.binding.enocean.internal.messages.ESP3Packet; import org.openhab.binding.enocean.internal.messages.ESP3PacketFactory; import org.openhab.binding.enocean.internal.messages.Response; @@ -138,21 +136,8 @@ protected void processMessage(byte firstByte) { HexUtils.bytesToHex(packet.getPayload())); break; case EVENT: - logger.debug("Event occured: {}", HexUtils.bytesToHex(packet.getPayload())); - break; - case RADIO_ERP1: { - ERP1Message msg = (ERP1Message) packet; - logger.debug("{} with RORG {} for {} payload {} received", - packet.getPacketType().name(), msg.getRORG().name(), - HexUtils.bytesToHex(msg.getSenderId()), HexUtils.bytesToHex( - Arrays.copyOf(dataBuffer, dataLength + optionalLength))); - - if (msg.getRORG() != RORG.Unknown) { - informListeners(msg); - } else { - logger.debug("Received unknown RORG"); - } - } + case RADIO_ERP1: + informListeners(packet); break; case RADIO_ERP2: break; diff --git a/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/transceiver/EnOceanTransceiver.java b/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/transceiver/EnOceanTransceiver.java index df729a1dbc0e2..55be4988b0259 100644 --- a/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/transceiver/EnOceanTransceiver.java +++ b/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/transceiver/EnOceanTransceiver.java @@ -27,9 +27,13 @@ import org.openhab.binding.enocean.internal.EnOceanBindingConstants; import org.openhab.binding.enocean.internal.EnOceanException; +import org.openhab.binding.enocean.internal.Helper; import org.openhab.binding.enocean.internal.messages.BasePacket; +import org.openhab.binding.enocean.internal.messages.BasePacket.ESPPacketType; import org.openhab.binding.enocean.internal.messages.ERP1Message; import org.openhab.binding.enocean.internal.messages.ERP1Message.RORG; +import org.openhab.binding.enocean.internal.messages.EventMessage; +import org.openhab.binding.enocean.internal.messages.EventMessage.EventMessageType; import org.openhab.binding.enocean.internal.messages.Response; import org.openhab.core.io.transport.serial.PortInUseException; import org.openhab.core.io.transport.serial.SerialPort; @@ -138,7 +142,8 @@ private synchronized void send() throws IOException { Request currentRequest = null; protected Map> listeners; - protected PacketListener teachInListener; + protected HashSet eventListeners; + protected TeachInListener teachInListener; protected InputStream inputStream; protected OutputStream outputStream; @@ -151,6 +156,7 @@ public EnOceanTransceiver(String path, TransceiverErrorListener errorListener, S requestQueue = new RequestQueue(scheduler); listeners = new HashMap<>(); + eventListeners = new HashSet<>(); teachInListener = null; this.errorListener = errorListener; @@ -192,6 +198,7 @@ public void run() { } }); } + logger.info("EnOceanSerialTransceiver RX thread started"); } public void ShutDown() { @@ -266,36 +273,65 @@ protected int read(byte[] buffer, int length) { } } - protected void informListeners(ERP1Message msg) { + protected void informListeners(BasePacket packet) { try { - byte[] senderId = msg.getSenderId(); + if (packet.getPacketType() == ESPPacketType.RADIO_ERP1) { + ERP1Message msg = (ERP1Message) packet; + byte[] senderId = msg.getSenderId(); + byte[] d = Helper.concatAll(msg.getPayload(), msg.getOptionalPayload()); + + logger.debug("{} with RORG {} for {} payload {} received", packet.getPacketType().name(), + msg.getRORG().name(), HexUtils.bytesToHex(msg.getSenderId()), HexUtils.bytesToHex(d)); + + if (msg.getRORG() != RORG.Unknown) { + if (senderId != null) { + if (filteredDeviceId != null && senderId[0] == filteredDeviceId[0] + && senderId[1] == filteredDeviceId[1] && senderId[2] == filteredDeviceId[2]) { + // filter away own messages which are received through a repeater + return; + } - if (senderId != null) { - if (filteredDeviceId != null && senderId[0] == filteredDeviceId[0] && senderId[1] == filteredDeviceId[1] - && senderId[2] == filteredDeviceId[2]) { - // filter away own messages which are received through a repeater - return; - } + if (teachInListener != null && (msg.getIsTeachIn() || msg.getRORG() == RORG.RPS)) { + logger.info("Received teach in message from {}", HexUtils.bytesToHex(msg.getSenderId())); + teachInListener.packetReceived(msg); + return; + } else if (teachInListener == null && msg.getIsTeachIn()) { + logger.info("Discard message because this is a teach-in telegram from {}!", + HexUtils.bytesToHex(msg.getSenderId())); + return; + } - if (teachInListener != null) { - if (msg.getIsTeachIn() || (msg.getRORG() == RORG.RPS)) { - logger.info("Received teach in message from {}", HexUtils.bytesToHex(msg.getSenderId())); - teachInListener.packetReceived(msg); - return; + long s = Long.parseLong(HexUtils.bytesToHex(senderId), 16); + HashSet pl = listeners.get(s); + if (pl != null) { + pl.forEach(l -> l.packetReceived(msg)); + } } } else { - if (msg.getIsTeachIn()) { - logger.info("Discard message because this is a teach-in telegram from {}!", - HexUtils.bytesToHex(msg.getSenderId())); + logger.debug("Received unknown RORG"); + } + } else if (packet.getPacketType() == ESPPacketType.EVENT) { + EventMessage event = (EventMessage) packet; + + byte[] d = Helper.concatAll(packet.getPayload(), packet.getOptionalPayload()); + logger.debug("{} with type {} payload {} received", ESPPacketType.EVENT.name(), + event.getEventMessageType().name(), HexUtils.bytesToHex(d)); + + if (event.getEventMessageType() == EventMessageType.SA_CONFIRM_LEARN) { + byte[] senderId = event.getPayload(EventMessageType.SA_CONFIRM_LEARN.getDataLength() - 5, 4); + + if (teachInListener != null) { + logger.info("Received smart teach in from {}", HexUtils.bytesToHex(senderId)); + teachInListener.eventReceived(event); + return; + } else { + logger.info("Discard message because this is a smart teach-in telegram from {}!", + HexUtils.bytesToHex(senderId)); return; } } - long s = Long.parseLong(HexUtils.bytesToHex(senderId), 16); - HashSet pl = listeners.get(s); - if (pl != null) { - pl.forEach(l -> l.packetReceived(msg)); - } + eventListeners.forEach(l -> l.eventReceived(event)); } } catch (Exception e) { logger.error("Exception in informListeners", e); @@ -354,7 +390,15 @@ public void removePacketListener(PacketListener listener, long senderIdToListenT } } - public void startDiscovery(PacketListener teachInListener) { + public void addEventMessageListener(EventListener listener) { + eventListeners.add(listener); + } + + public void removeEventMessageListener(EventListener listener) { + eventListeners.remove(listener); + } + + public void startDiscovery(TeachInListener teachInListener) { this.teachInListener = teachInListener; } diff --git a/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/transceiver/EventListener.java b/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/transceiver/EventListener.java new file mode 100644 index 0000000000000..2a27bfb14f15b --- /dev/null +++ b/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/transceiver/EventListener.java @@ -0,0 +1,23 @@ +/** + * Copyright (c) 2010-2021 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.enocean.internal.transceiver; + +import org.openhab.binding.enocean.internal.messages.EventMessage; + +/** + * + * @author Daniel Weber - Initial contribution + */ +public interface EventListener { + public void eventReceived(EventMessage event); +} diff --git a/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/transceiver/PacketListener.java b/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/transceiver/PacketListener.java index 04e55fdba857b..da161441aa87a 100644 --- a/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/transceiver/PacketListener.java +++ b/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/transceiver/PacketListener.java @@ -22,5 +22,5 @@ public interface PacketListener { public void packetReceived(BasePacket packet); - public long getSenderIdToListenTo(); + public long getEnOceanIdToListenTo(); } diff --git a/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/transceiver/TeachInListener.java b/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/transceiver/TeachInListener.java new file mode 100644 index 0000000000000..06a26c41800c0 --- /dev/null +++ b/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/transceiver/TeachInListener.java @@ -0,0 +1,21 @@ +/** + * Copyright (c) 2010-2021 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.enocean.internal.transceiver; + +/** + * + * @author Daniel Weber - Initial contribution + */ +public interface TeachInListener extends PacketListener, EventListener { + +} diff --git a/bundles/org.openhab.binding.enocean/src/main/resources/OH-INF/config/config.xml b/bundles/org.openhab.binding.enocean/src/main/resources/OH-INF/config/config.xml index 199039a8eafb1..ebd49c604dd7b 100644 --- a/bundles/org.openhab.binding.enocean/src/main/resources/OH-INF/config/config.xml +++ b/bundles/org.openhab.binding.enocean/src/main/resources/OH-INF/config/config.xml @@ -5,15 +5,13 @@ xsi:schemaLocation="https://openhab.org/schemas/config-description/v1.0.0 https://openhab.org/schemas/config-description-1.0.0.xsd"> - + The type of the transformation, e.g. "MAP" - true - + The transformation function, e.g. for transformation type map => filename of mapping file - true diff --git a/bundles/org.openhab.binding.enocean/src/main/resources/OH-INF/thing/AutomatedMeterSensor.xml b/bundles/org.openhab.binding.enocean/src/main/resources/OH-INF/thing/AutomatedMeterSensor.xml index 20db15ce26eb8..11d4daae595a2 100644 --- a/bundles/org.openhab.binding.enocean/src/main/resources/OH-INF/thing/AutomatedMeterSensor.xml +++ b/bundles/org.openhab.binding.enocean/src/main/resources/OH-INF/thing/AutomatedMeterSensor.xml @@ -14,12 +14,11 @@ Sensor for different meters like energy measurement (EEP: A5-12) - + EnOceanId of device this thing belongs to - true - + EEP which is used by panel @@ -29,7 +28,6 @@ true - true diff --git a/bundles/org.openhab.binding.enocean/src/main/resources/OH-INF/thing/CentralCommand.xml b/bundles/org.openhab.binding.enocean/src/main/resources/OH-INF/thing/CentralCommand.xml index 7115374ab58f8..31a5b2c166697 100644 --- a/bundles/org.openhab.binding.enocean/src/main/resources/OH-INF/thing/CentralCommand.xml +++ b/bundles/org.openhab.binding.enocean/src/main/resources/OH-INF/thing/CentralCommand.xml @@ -13,30 +13,28 @@ Controls a switching or dimming actuator (EEP: A5-38) - + EnOceanId of device this thing belongs to - true - + Id is used to generate the EnOcean Id (Int between [1-127]). If not specified the next free Id will be determined by bridge - + true - true true - + @@ -44,7 +42,6 @@ true - true diff --git a/bundles/org.openhab.binding.enocean/src/main/resources/OH-INF/thing/ClassicDevice.xml b/bundles/org.openhab.binding.enocean/src/main/resources/OH-INF/thing/ClassicDevice.xml index 6d24ab2084156..4fa6e9da97390 100644 --- a/bundles/org.openhab.binding.enocean/src/main/resources/OH-INF/thing/ClassicDevice.xml +++ b/bundles/org.openhab.binding.enocean/src/main/resources/OH-INF/thing/ClassicDevice.xml @@ -20,12 +20,12 @@ - + Id is used to generate the EnOcean Id (Int between [1-127]). If not specified the next free Id will be determined by bridge - + EEP which is used to control the device @@ -34,9 +34,8 @@ true F6_02_01 - true - + EEP which is used by rocker switch listener(s) @@ -45,7 +44,6 @@ true F6_02_01 - true diff --git a/bundles/org.openhab.binding.enocean/src/main/resources/OH-INF/thing/Contact.xml b/bundles/org.openhab.binding.enocean/src/main/resources/OH-INF/thing/Contact.xml index 57f6069ea4945..648cb2c6b6253 100644 --- a/bundles/org.openhab.binding.enocean/src/main/resources/OH-INF/thing/Contact.xml +++ b/bundles/org.openhab.binding.enocean/src/main/resources/OH-INF/thing/Contact.xml @@ -13,12 +13,11 @@ Single input contact sensor (EEP: D5-00) - + EnOceanId of device this thing belongs to - true - + EEP which is used by contact @@ -30,7 +29,6 @@ D5_00_01 2 true - true diff --git a/bundles/org.openhab.binding.enocean/src/main/resources/OH-INF/thing/EnvironmentalSensor.xml b/bundles/org.openhab.binding.enocean/src/main/resources/OH-INF/thing/EnvironmentalSensor.xml index aae17def9b664..0962fb9f6128d 100644 --- a/bundles/org.openhab.binding.enocean/src/main/resources/OH-INF/thing/EnvironmentalSensor.xml +++ b/bundles/org.openhab.binding.enocean/src/main/resources/OH-INF/thing/EnvironmentalSensor.xml @@ -13,19 +13,17 @@ Sensor for different environmental sensors like a weather station (EEP: A5-13) - + EnOceanId of device this thing belongs to - true - + EEP which is used by sensor true - true diff --git a/bundles/org.openhab.binding.enocean/src/main/resources/OH-INF/thing/GenericThing.xml b/bundles/org.openhab.binding.enocean/src/main/resources/OH-INF/thing/GenericThing.xml index 2fa60566e40e8..67cfc54e72a63 100644 --- a/bundles/org.openhab.binding.enocean/src/main/resources/OH-INF/thing/GenericThing.xml +++ b/bundles/org.openhab.binding.enocean/src/main/resources/OH-INF/thing/GenericThing.xml @@ -13,17 +13,16 @@ Thing whose EEP is unsupported. Use a TRANSFORM to convert things messages. - + EnOceanId of device this thing belongs to - true - + Id is used to generate the EnOcean Id (Int between [1-127]). If not specified the next free Id will be determined by bridge - + @@ -31,9 +30,8 @@ true - true - + @@ -41,7 +39,6 @@ true - true diff --git a/bundles/org.openhab.binding.enocean/src/main/resources/OH-INF/thing/HeatRecoveryVentilation.xml b/bundles/org.openhab.binding.enocean/src/main/resources/OH-INF/thing/HeatRecoveryVentilation.xml index cd58af2cbf2fa..46d461cf8e5fb 100644 --- a/bundles/org.openhab.binding.enocean/src/main/resources/OH-INF/thing/HeatRecoveryVentilation.xml +++ b/bundles/org.openhab.binding.enocean/src/main/resources/OH-INF/thing/HeatRecoveryVentilation.xml @@ -13,18 +13,16 @@ Sensor and actuator to control heat recovery ventilation units (EEP: D2-50) - + EnOceanId of device this thing belongs to - true - + Id is used to generate the EnOcean Id (Int between [1-127]). If not specified the next free Id will be determined by bridge - true - + @@ -33,13 +31,12 @@ true - true false - + EEP which is used by Ventilation Unit @@ -49,7 +46,6 @@ true - true diff --git a/bundles/org.openhab.binding.enocean/src/main/resources/OH-INF/thing/LightSensor.xml b/bundles/org.openhab.binding.enocean/src/main/resources/OH-INF/thing/LightSensor.xml index 632067232aeb4..00d978f5ed546 100644 --- a/bundles/org.openhab.binding.enocean/src/main/resources/OH-INF/thing/LightSensor.xml +++ b/bundles/org.openhab.binding.enocean/src/main/resources/OH-INF/thing/LightSensor.xml @@ -13,12 +13,11 @@ Sensor which sends light data (EEP: A5-06) - + EnOceanId of device this thing belongs to - true - + EEP which is used by sensor @@ -26,7 +25,6 @@ true - true diff --git a/bundles/org.openhab.binding.enocean/src/main/resources/OH-INF/thing/LightTemperatureOccupancySensor.xml b/bundles/org.openhab.binding.enocean/src/main/resources/OH-INF/thing/LightTemperatureOccupancySensor.xml index c67a1fbdc3918..c1c36d1c6daec 100644 --- a/bundles/org.openhab.binding.enocean/src/main/resources/OH-INF/thing/LightTemperatureOccupancySensor.xml +++ b/bundles/org.openhab.binding.enocean/src/main/resources/OH-INF/thing/LightTemperatureOccupancySensor.xml @@ -13,12 +13,11 @@ Sensor which sends light, temperature and occupancy data (EEP: A5-08) - + EnOceanId of device this thing belongs to - true - + EEP which is used by sensor @@ -28,7 +27,6 @@ true - true diff --git a/bundles/org.openhab.binding.enocean/src/main/resources/OH-INF/thing/MeasurementSwitch.xml b/bundles/org.openhab.binding.enocean/src/main/resources/OH-INF/thing/MeasurementSwitch.xml index 0f095d5ed6e18..abe36f166243e 100644 --- a/bundles/org.openhab.binding.enocean/src/main/resources/OH-INF/thing/MeasurementSwitch.xml +++ b/bundles/org.openhab.binding.enocean/src/main/resources/OH-INF/thing/MeasurementSwitch.xml @@ -13,23 +13,21 @@ Electronic switches and dimmers with energy measurement and local control (EEP: D2-01) - + EnOceanId of device this thing belongs to - true - + Id is used to generate the EnOcean Id (Int between [1-127]). If not specified the next free Id will be determined by bridge - true Time in seconds after a refresh is triggerd, 0 to disable 300 - + @@ -55,13 +53,12 @@ true - true false - + @@ -89,7 +86,6 @@ 2 true - true diff --git a/bundles/org.openhab.binding.enocean/src/main/resources/OH-INF/thing/MechanicalHandle.xml b/bundles/org.openhab.binding.enocean/src/main/resources/OH-INF/thing/MechanicalHandle.xml index 95f18fad67553..775eab0a85be2 100644 --- a/bundles/org.openhab.binding.enocean/src/main/resources/OH-INF/thing/MechanicalHandle.xml +++ b/bundles/org.openhab.binding.enocean/src/main/resources/OH-INF/thing/MechanicalHandle.xml @@ -14,12 +14,11 @@ Mechanical handle sensor for window/door handles - + EnOceanId of device this thing belongs to - true - + EEP which is used by handle @@ -29,7 +28,6 @@ true - true diff --git a/bundles/org.openhab.binding.enocean/src/main/resources/OH-INF/thing/MultiFunctionSmokeDetector.xml b/bundles/org.openhab.binding.enocean/src/main/resources/OH-INF/thing/MultiFunctionSmokeDetector.xml index 3b367bf202237..e292562a738ac 100644 --- a/bundles/org.openhab.binding.enocean/src/main/resources/OH-INF/thing/MultiFunctionSmokeDetector.xml +++ b/bundles/org.openhab.binding.enocean/src/main/resources/OH-INF/thing/MultiFunctionSmokeDetector.xml @@ -13,12 +13,11 @@ Multi Function Sensor like a Smoke Detector (EEP: F6-05, D2-14) - + EnOceanId of device this thing belongs to - true - + EEP which is used by sensor @@ -26,7 +25,6 @@ - true diff --git a/bundles/org.openhab.binding.enocean/src/main/resources/OH-INF/thing/OccupancySensor.xml b/bundles/org.openhab.binding.enocean/src/main/resources/OH-INF/thing/OccupancySensor.xml index c6d093c2cc3b2..7f770684e64c2 100644 --- a/bundles/org.openhab.binding.enocean/src/main/resources/OH-INF/thing/OccupancySensor.xml +++ b/bundles/org.openhab.binding.enocean/src/main/resources/OH-INF/thing/OccupancySensor.xml @@ -13,12 +13,11 @@ Sensor which sends light, supply voltage and occupancy data (EEP: A5-07) - + EnOceanId of device this thing belongs to - true - + EEP which is used by sensor @@ -27,7 +26,6 @@ true - true diff --git a/bundles/org.openhab.binding.enocean/src/main/resources/OH-INF/thing/PushButton.xml b/bundles/org.openhab.binding.enocean/src/main/resources/OH-INF/thing/PushButton.xml index d743aece64f20..635d291363312 100644 --- a/bundles/org.openhab.binding.enocean/src/main/resources/OH-INF/thing/PushButton.xml +++ b/bundles/org.openhab.binding.enocean/src/main/resources/OH-INF/thing/PushButton.xml @@ -14,12 +14,11 @@ Represents a physical Push Button (EEP: F6-01, D2-03) - + EnOceanId of device this thing belongs to - true - + EEP which is used by push button @@ -28,7 +27,6 @@ true F6_01_01 - true diff --git a/bundles/org.openhab.binding.enocean/src/main/resources/OH-INF/thing/RockerSwitch.xml b/bundles/org.openhab.binding.enocean/src/main/resources/OH-INF/thing/RockerSwitch.xml index 505f02c2bbf92..c8652487d8b16 100644 --- a/bundles/org.openhab.binding.enocean/src/main/resources/OH-INF/thing/RockerSwitch.xml +++ b/bundles/org.openhab.binding.enocean/src/main/resources/OH-INF/thing/RockerSwitch.xml @@ -23,12 +23,11 @@ - + EnOceanId of device this thing belongs to - true - + EEP which is used by rocker switch @@ -36,7 +35,6 @@ true - true diff --git a/bundles/org.openhab.binding.enocean/src/main/resources/OH-INF/thing/Rollershutter.xml b/bundles/org.openhab.binding.enocean/src/main/resources/OH-INF/thing/Rollershutter.xml index 486ceebf0e2e4..b0f00d97962fb 100644 --- a/bundles/org.openhab.binding.enocean/src/main/resources/OH-INF/thing/Rollershutter.xml +++ b/bundles/org.openhab.binding.enocean/src/main/resources/OH-INF/thing/Rollershutter.xml @@ -13,12 +13,11 @@ Rollershutter like Eltako FSB14/61/71 or NodOn SIN-2-RS-01 (EEP: A5-37, D2-05) - + EnOceanId of device this thing belongs to - true - + Id is used to generate the EnOcean Id (Int between [1-127]). If not specified the next free Id will be determined by bridge @@ -28,7 +27,7 @@ Time in seconds after a refresh is triggered, 0 to disable 300 - + @@ -37,13 +36,12 @@ A5_3F_7F_EltakoFSB true - true true - + @@ -53,7 +51,6 @@ 4 true - true diff --git a/bundles/org.openhab.binding.enocean/src/main/resources/OH-INF/thing/RoomOperatingPanel.xml b/bundles/org.openhab.binding.enocean/src/main/resources/OH-INF/thing/RoomOperatingPanel.xml index e9829d5dc836f..c8a177104763d 100644 --- a/bundles/org.openhab.binding.enocean/src/main/resources/OH-INF/thing/RoomOperatingPanel.xml +++ b/bundles/org.openhab.binding.enocean/src/main/resources/OH-INF/thing/RoomOperatingPanel.xml @@ -13,12 +13,11 @@ Room operating panel with different kinds of sensors (EEP: A5-10) - + EnOceanId of device this thing belongs to - true - + EEP which is used by panel @@ -58,7 +57,6 @@ true - true diff --git a/bundles/org.openhab.binding.enocean/src/main/resources/OH-INF/thing/TemperatureHumiditySensor.xml b/bundles/org.openhab.binding.enocean/src/main/resources/OH-INF/thing/TemperatureHumiditySensor.xml index a057604fcfac4..fd7282fa92009 100644 --- a/bundles/org.openhab.binding.enocean/src/main/resources/OH-INF/thing/TemperatureHumiditySensor.xml +++ b/bundles/org.openhab.binding.enocean/src/main/resources/OH-INF/thing/TemperatureHumiditySensor.xml @@ -13,12 +13,11 @@ Sensor which sends temperature and humidity data (EEP: A5-04) - + EnOceanId of device this thing belongs to - true - + EEP which is used by sensor @@ -28,7 +27,6 @@ true - true diff --git a/bundles/org.openhab.binding.enocean/src/main/resources/OH-INF/thing/TemperatureSensor.xml b/bundles/org.openhab.binding.enocean/src/main/resources/OH-INF/thing/TemperatureSensor.xml index 5e233edca17f8..9f28e772caa04 100644 --- a/bundles/org.openhab.binding.enocean/src/main/resources/OH-INF/thing/TemperatureSensor.xml +++ b/bundles/org.openhab.binding.enocean/src/main/resources/OH-INF/thing/TemperatureSensor.xml @@ -13,12 +13,11 @@ Sensor which send temperature data (EEP: A5-02) - + EnOceanId of device this thing belongs to - true - + EEP which is used by panel @@ -49,7 +48,6 @@ true - true diff --git a/bundles/org.openhab.binding.enocean/src/main/resources/OH-INF/thing/Thermostat.xml b/bundles/org.openhab.binding.enocean/src/main/resources/OH-INF/thing/Thermostat.xml index 2be75d8e93033..750bbb27d650c 100644 --- a/bundles/org.openhab.binding.enocean/src/main/resources/OH-INF/thing/Thermostat.xml +++ b/bundles/org.openhab.binding.enocean/src/main/resources/OH-INF/thing/Thermostat.xml @@ -13,18 +13,16 @@ Sensor and actuator to control radiator thermostats - + EnOceanId of device this thing belongs to - true - + Id is used to generate the EnOcean Id (Int between [1-127]). If not specified the next free Id will be determined by bridge - true - + EEP which is used by sensor @@ -33,9 +31,8 @@ "A5_20_04" true - true - + "A5_20_04" true - true diff --git a/bundles/org.openhab.binding.enocean/src/main/resources/OH-INF/thing/bridge.xml b/bundles/org.openhab.binding.enocean/src/main/resources/OH-INF/thing/bridge.xml index afa99f504c7f8..4938404623f5d 100644 --- a/bundles/org.openhab.binding.enocean/src/main/resources/OH-INF/thing/bridge.xml +++ b/bundles/org.openhab.binding.enocean/src/main/resources/OH-INF/thing/bridge.xml @@ -15,12 +15,11 @@ - + Path to the EnOcean gateway - true - + true @@ -28,9 +27,13 @@ true - true ESP3 + + + Declare Gateway as a SMACK Postmaster and handle SMACK messages + true + true @@ -41,9 +44,15 @@ 00000000 - + + true + + Should a learned in or teach out response been send on a repeated smack teach in request + false + + true - + Defines the next device Id, if empty, the next device id is automatically determined diff --git a/bundles/org.openhab.binding.enocean/src/main/resources/OH-INF/thing/channels.xml b/bundles/org.openhab.binding.enocean/src/main/resources/OH-INF/thing/channels.xml index 9469d48b8c9af..941eb0d76487a 100644 --- a/bundles/org.openhab.binding.enocean/src/main/resources/OH-INF/thing/channels.xml +++ b/bundles/org.openhab.binding.enocean/src/main/resources/OH-INF/thing/channels.xml @@ -488,13 +488,6 @@ - - Switch - - You can send telegrams to the device by switching this channel on - thermostat - - Switch diff --git a/bundles/org.openhab.binding.enturno/src/main/java/org/openhab/binding/enturno/internal/EnturNoHandler.java b/bundles/org.openhab.binding.enturno/src/main/java/org/openhab/binding/enturno/internal/EnturNoHandler.java index 15ff8fbd77a81..2a701eaba3597 100644 --- a/bundles/org.openhab.binding.enturno/src/main/java/org/openhab/binding/enturno/internal/EnturNoHandler.java +++ b/bundles/org.openhab.binding.enturno/src/main/java/org/openhab/binding/enturno/internal/EnturNoHandler.java @@ -20,7 +20,6 @@ import java.util.concurrent.ScheduledFuture; import java.util.concurrent.TimeUnit; -import org.apache.commons.lang.StringUtils; import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.Nullable; import org.eclipse.jetty.client.HttpClient; @@ -95,14 +94,15 @@ public void initialize() { logger.debug("Stop place id: {}", stopId); boolean configValid = true; - if (StringUtils.trimToNull(stopId) == null) { + if (stopId == null || stopId.isBlank()) { updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, "@text/offline.conf-error-missing-stopId"); configValid = false; } - logger.debug("Line code: {}", config.getLineCode()); - if (StringUtils.trimToNull(config.getLineCode()) == null) { + String lineCode = config.getLineCode(); + logger.debug("Line code: {}", lineCode); + if (lineCode == null || lineCode.isBlank()) { updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, "@text/offline.conf-error-missing-lineCode"); configValid = false; diff --git a/bundles/org.openhab.binding.enturno/src/main/java/org/openhab/binding/enturno/internal/connection/EnturNoConnection.java b/bundles/org.openhab.binding.enturno/src/main/java/org/openhab/binding/enturno/internal/connection/EnturNoConnection.java index 6f852ac302221..5dc92dfd19bee 100644 --- a/bundles/org.openhab.binding.enturno/src/main/java/org/openhab/binding/enturno/internal/connection/EnturNoConnection.java +++ b/bundles/org.openhab.binding.enturno/src/main/java/org/openhab/binding/enturno/internal/connection/EnturNoConnection.java @@ -32,7 +32,7 @@ import java.util.concurrent.TimeoutException; import java.util.stream.Collectors; -import org.apache.commons.lang.StringUtils; +import org.apache.commons.lang3.StringUtils; import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.Nullable; import org.eclipse.jetty.client.HttpClient; @@ -77,7 +77,6 @@ public class EnturNoConnection { private final EnturNoHandler handler; private final HttpClient httpClient; - private final JsonParser parser = new JsonParser(); private final Gson gson = new Gson(); public EnturNoConnection(EnturNoHandler handler, HttpClient httpClient) { @@ -96,9 +95,9 @@ public EnturNoConnection(EnturNoHandler handler, HttpClient httpClient) { */ public synchronized List getEnturTimeTable(@Nullable String stopPlaceId, @Nullable String lineCode) throws JsonSyntaxException, EnturConfigurationException, EnturCommunicationException { - if (StringUtils.isBlank(stopPlaceId)) { + if (stopPlaceId == null || stopPlaceId.isBlank()) { throw new EnturConfigurationException("Stop place id cannot be empty or null"); - } else if (lineCode == null || StringUtils.isBlank(lineCode)) { + } else if (lineCode == null || lineCode.isBlank()) { throw new EnturConfigurationException("Line code cannot be empty or null"); } @@ -115,8 +114,9 @@ public synchronized List getEnturTimeTable(@Nullable String stopPla private Map getRequestParams(EnturNoConfiguration config) { Map params = new HashMap<>(); - params.put(PARAM_STOPID, StringUtils.trimToEmpty(config.getStopPlaceId())); - params.put(PARAM_START_DATE_TIME, StringUtils.trimToEmpty(LocalDateTime.now(ZoneId.of(TIME_ZONE)).toString())); + String stopPlaceId = config.getStopPlaceId(); + params.put(PARAM_STOPID, stopPlaceId == null ? "" : stopPlaceId.trim()); + params.put(PARAM_START_DATE_TIME, LocalDateTime.now(ZoneId.of(TIME_ZONE)).toString()); return params; } @@ -141,7 +141,7 @@ private String getResponse(String url, Map params) { int httpStatus = contentResponse.getStatus(); String content = contentResponse.getContentAsString(); - String errorMessage = StringUtils.EMPTY; + String errorMessage = ""; logger.trace("Entur response: status = {}, content = '{}'", httpStatus, content); switch (httpStatus) { case OK_200: @@ -167,7 +167,7 @@ private String getResponse(String url, Map params) { } private String getErrorMessage(String response) { - JsonObject jsonResponse = parser.parse(response).getAsJsonObject(); + JsonObject jsonResponse = JsonParser.parseString(response).getAsJsonObject(); if (jsonResponse.has(PROPERTY_MESSAGE)) { return jsonResponse.get(PROPERTY_MESSAGE).getAsString(); } diff --git a/bundles/org.openhab.binding.epsonprojector/src/main/java/org/openhab/binding/epsonprojector/internal/handler/EpsonProjectorHandler.java b/bundles/org.openhab.binding.epsonprojector/src/main/java/org/openhab/binding/epsonprojector/internal/handler/EpsonProjectorHandler.java index d9029b21de245..2d8cc2ca41497 100644 --- a/bundles/org.openhab.binding.epsonprojector/src/main/java/org/openhab/binding/epsonprojector/internal/handler/EpsonProjectorHandler.java +++ b/bundles/org.openhab.binding.epsonprojector/src/main/java/org/openhab/binding/epsonprojector/internal/handler/EpsonProjectorHandler.java @@ -156,10 +156,13 @@ private void updateChannelState(Channel channel) { State state = queryDataFromDevice(epsonCommand); if (state != null) { - updateStatus(ThingStatus.ONLINE); if (isLinked(channel.getUID())) { updateState(channel.getUID(), state); } + // the first valid response will cause the thing to go ONLINE + if (state != UnDefType.UNDEF) { + updateStatus(ThingStatus.ONLINE); + } } } catch (IllegalArgumentException e) { logger.warn("Unknown channel {}", channel.getUID().getId()); diff --git a/bundles/org.openhab.binding.evohome/src/main/java/org/openhab/binding/evohome/internal/handler/BaseEvohomeHandler.java b/bundles/org.openhab.binding.evohome/src/main/java/org/openhab/binding/evohome/internal/handler/BaseEvohomeHandler.java index e3255b4bac225..f959c01d88f7e 100644 --- a/bundles/org.openhab.binding.evohome/src/main/java/org/openhab/binding/evohome/internal/handler/BaseEvohomeHandler.java +++ b/bundles/org.openhab.binding.evohome/src/main/java/org/openhab/binding/evohome/internal/handler/BaseEvohomeHandler.java @@ -12,7 +12,6 @@ */ package org.openhab.binding.evohome.internal.handler; -import org.apache.commons.lang.StringUtils; import org.openhab.binding.evohome.internal.api.models.v2.response.Locations; import org.openhab.binding.evohome.internal.configuration.EvohomeThingConfiguration; import org.openhab.core.thing.Bridge; @@ -132,7 +131,7 @@ private void checkConfig() { if (configuration == null) { updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, "Configuration is missing or corrupted"); - } else if (StringUtils.isEmpty(configuration.id)) { + } else if (configuration.id == null || configuration.id.isEmpty()) { updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, "Id not configured"); } } diff --git a/bundles/org.openhab.binding.evohome/src/main/java/org/openhab/binding/evohome/internal/handler/EvohomeAccountBridgeHandler.java b/bundles/org.openhab.binding.evohome/src/main/java/org/openhab/binding/evohome/internal/handler/EvohomeAccountBridgeHandler.java index 06e563f776ab4..5c64f717afa7c 100644 --- a/bundles/org.openhab.binding.evohome/src/main/java/org/openhab/binding/evohome/internal/handler/EvohomeAccountBridgeHandler.java +++ b/bundles/org.openhab.binding.evohome/src/main/java/org/openhab/binding/evohome/internal/handler/EvohomeAccountBridgeHandler.java @@ -22,7 +22,6 @@ import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; -import org.apache.commons.lang.StringUtils; import org.eclipse.jetty.client.HttpClient; import org.openhab.binding.evohome.internal.RunnableWithTimeout; import org.openhab.binding.evohome.internal.api.EvohomeApiClient; @@ -190,9 +189,9 @@ private boolean checkConfig() { if (configuration == null) { errorMessage = "Configuration is missing or corrupted"; - } else if (StringUtils.isEmpty(configuration.username)) { + } else if (configuration.username == null || configuration.username.isEmpty()) { errorMessage = "Username not configured"; - } else if (StringUtils.isEmpty(configuration.password)) { + } else if (configuration.password == null || configuration.password.isEmpty()) { errorMessage = "Password not configured"; } else { return true; diff --git a/bundles/org.openhab.binding.exec/src/main/feature/feature.xml b/bundles/org.openhab.binding.exec/src/main/feature/feature.xml index ff73780e211b3..4bc0c8c65475f 100644 --- a/bundles/org.openhab.binding.exec/src/main/feature/feature.xml +++ b/bundles/org.openhab.binding.exec/src/main/feature/feature.xml @@ -4,7 +4,7 @@ openhab-runtime-base - mvn:${project.groupId}/org.openhab.addons.features.karaf.openhab-addons-external/${project.version}/cfg/exec.whitelist + mvn:org.openhab.addons.features.karaf/org.openhab.addons.features.karaf.openhab-addons-external/${project.version}/cfg/exec.whitelist mvn:org.openhab.addons.bundles/org.openhab.binding.exec/${project.version} diff --git a/bundles/org.openhab.binding.exec/src/main/java/org/openhab/binding/exec/internal/handler/ExecHandler.java b/bundles/org.openhab.binding.exec/src/main/java/org/openhab/binding/exec/internal/handler/ExecHandler.java index 33c70ce165df6..10d700467cfd9 100644 --- a/bundles/org.openhab.binding.exec/src/main/java/org/openhab/binding/exec/internal/handler/ExecHandler.java +++ b/bundles/org.openhab.binding.exec/src/main/java/org/openhab/binding/exec/internal/handler/ExecHandler.java @@ -29,7 +29,7 @@ import java.util.regex.Pattern; import java.util.regex.PatternSyntaxException; -import org.apache.commons.lang.StringUtils; +import org.apache.commons.lang3.StringUtils; import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.Nullable; import org.openhab.binding.exec.internal.ExecWhitelistWatchService; diff --git a/bundles/org.openhab.binding.feican/src/main/java/org/openhab/binding/feican/internal/Commands.java b/bundles/org.openhab.binding.feican/src/main/java/org/openhab/binding/feican/internal/Commands.java index 49da817f56a51..5284de881c6ce 100644 --- a/bundles/org.openhab.binding.feican/src/main/java/org/openhab/binding/feican/internal/Commands.java +++ b/bundles/org.openhab.binding.feican/src/main/java/org/openhab/binding/feican/internal/Commands.java @@ -13,6 +13,7 @@ package org.openhab.binding.feican.internal; import java.math.BigDecimal; +import java.math.RoundingMode; import org.eclipse.jdt.annotation.NonNullByDefault; import org.openhab.core.library.types.HSBType; @@ -82,7 +83,7 @@ public byte[] color(HSBType color) { */ private byte convertColorPercentToByte(PercentType percent) { return percent.toBigDecimal().multiply(BigDecimal.valueOf(255)) - .divide(BigDecimal.valueOf(100), 2, BigDecimal.ROUND_HALF_UP).byteValue(); + .divide(BigDecimal.valueOf(100), 2, RoundingMode.HALF_UP).byteValue(); } /** diff --git a/bundles/org.openhab.binding.foobot/src/main/java/org/openhab/binding/foobot/internal/FoobotApiConnector.java b/bundles/org.openhab.binding.foobot/src/main/java/org/openhab/binding/foobot/internal/FoobotApiConnector.java index 61d95b998a740..48e12ed582754 100644 --- a/bundles/org.openhab.binding.foobot/src/main/java/org/openhab/binding/foobot/internal/FoobotApiConnector.java +++ b/bundles/org.openhab.binding.foobot/src/main/java/org/openhab/binding/foobot/internal/FoobotApiConnector.java @@ -25,7 +25,6 @@ import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; -import org.apache.commons.lang.StringUtils; import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.Nullable; import org.eclipse.jetty.client.HttpClient; @@ -156,7 +155,7 @@ protected String request(String url, String apiKey) throws FoobotApiException { apiKeyLimitRemaining = API_RATE_LIMIT_EXCEEDED; throw new FoobotApiException(response.getStatus(), API_RATE_LIMIT_EXCEEDED_MESSAGE); case HttpStatus.OK_200: - if (StringUtils.trimToNull(content) == null) { + if (content == null || content.isBlank()) { throw new FoobotApiException(0, "No data returned"); } return content; diff --git a/bundles/org.openhab.binding.foobot/src/main/java/org/openhab/binding/foobot/internal/handler/FoobotAccountHandler.java b/bundles/org.openhab.binding.foobot/src/main/java/org/openhab/binding/foobot/internal/handler/FoobotAccountHandler.java index 4684b7456749d..0bece5240d56f 100644 --- a/bundles/org.openhab.binding.foobot/src/main/java/org/openhab/binding/foobot/internal/handler/FoobotAccountHandler.java +++ b/bundles/org.openhab.binding.foobot/src/main/java/org/openhab/binding/foobot/internal/handler/FoobotAccountHandler.java @@ -15,12 +15,14 @@ import static org.openhab.binding.foobot.internal.FoobotBindingConstants.*; import java.time.Duration; -import java.util.*; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.List; import java.util.concurrent.ScheduledFuture; import java.util.concurrent.TimeUnit; import java.util.stream.Collectors; -import org.apache.commons.lang.StringUtils; import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.Nullable; import org.openhab.binding.foobot.internal.FoobotApiConnector; @@ -93,10 +95,12 @@ public void initialize() { final FoobotAccountConfiguration accountConfig = getConfigAs(FoobotAccountConfiguration.class); final List missingParams = new ArrayList<>(); - if (StringUtils.trimToNull(accountConfig.apiKey) == null) { + String apiKey = accountConfig.apiKey; + if (apiKey.isBlank()) { missingParams.add("'apikey'"); } - if (StringUtils.trimToNull(accountConfig.username) == null) { + String username = accountConfig.username; + if (username.isBlank()) { missingParams.add("'username'"); } @@ -104,13 +108,13 @@ public void initialize() { final boolean oneParam = missingParams.size() == 1; final String errorMsg = String.format( "Parameter%s [%s] %s mandatory and must be configured and not be empty", oneParam ? "" : "s", - StringUtils.join(missingParams, ", "), oneParam ? "is" : "are"); + String.join(", ", missingParams), oneParam ? "is" : "are"); updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, errorMsg); return; } - username = accountConfig.username; - connector.setApiKey(accountConfig.apiKey); + this.username = username; + connector.setApiKey(apiKey); refreshInterval = accountConfig.refreshInterval; if (this.refreshInterval < MINIMUM_REFRESH_PERIOD_MINUTES) { logger.warn( @@ -118,8 +122,7 @@ public void initialize() { accountConfig.refreshInterval, MINIMUM_REFRESH_PERIOD_MINUTES, DEFAULT_REFRESH_PERIOD_MINUTES); refreshInterval = DEFAULT_REFRESH_PERIOD_MINUTES; } - logger.debug("Foobot Account bridge starting... user: {}, refreshInterval: {}", accountConfig.username, - refreshInterval); + logger.debug("Foobot Account bridge starting... user: {}, refreshInterval: {}", username, refreshInterval); updateStatus(ThingStatus.UNKNOWN, ThingStatusDetail.NONE, "Wait to get associated devices"); diff --git a/bundles/org.openhab.binding.foobot/src/main/java/org/openhab/binding/foobot/internal/handler/FoobotDeviceHandler.java b/bundles/org.openhab.binding.foobot/src/main/java/org/openhab/binding/foobot/internal/handler/FoobotDeviceHandler.java index 46302f7cac200..a9855e1467d9f 100644 --- a/bundles/org.openhab.binding.foobot/src/main/java/org/openhab/binding/foobot/internal/handler/FoobotDeviceHandler.java +++ b/bundles/org.openhab.binding.foobot/src/main/java/org/openhab/binding/foobot/internal/handler/FoobotDeviceHandler.java @@ -21,7 +21,6 @@ import javax.measure.Unit; -import org.apache.commons.lang.StringUtils; import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.Nullable; import org.openhab.binding.foobot.internal.FoobotApiConnector; @@ -93,7 +92,7 @@ public void initialize() { logger.debug("Initializing Foobot handler."); uuid = (String) getConfig().get(FoobotBindingConstants.CONFIG_UUID); - if (StringUtils.trimToNull(uuid) == null) { + if (uuid.isBlank()) { updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, "Parameter 'uuid' is mandatory and must be configured"); return; diff --git a/bundles/org.openhab.binding.fronius/src/main/java/org/openhab/binding/fronius/internal/handler/FroniusBridgeHandler.java b/bundles/org.openhab.binding.fronius/src/main/java/org/openhab/binding/fronius/internal/handler/FroniusBridgeHandler.java index 16662d873dfda..274157f0a3fe0 100644 --- a/bundles/org.openhab.binding.fronius/src/main/java/org/openhab/binding/fronius/internal/handler/FroniusBridgeHandler.java +++ b/bundles/org.openhab.binding.fronius/src/main/java/org/openhab/binding/fronius/internal/handler/FroniusBridgeHandler.java @@ -18,7 +18,6 @@ import java.util.concurrent.ScheduledFuture; import java.util.concurrent.TimeUnit; -import org.apache.commons.lang.StringUtils; import org.openhab.binding.fronius.internal.FroniusBridgeConfiguration; import org.openhab.core.io.net.http.HttpUtil; import org.openhab.core.thing.Bridge; @@ -62,10 +61,13 @@ public void initialize() { boolean validConfig = true; String errorMsg = null; - if (StringUtils.trimToNull(config.hostname) == null) { + + String hostname = config.hostname; + if (hostname == null || hostname.isBlank()) { errorMsg = "Parameter 'hostname' is mandatory and must be configured"; validConfig = false; } + if (config.refreshInterval != null && config.refreshInterval <= 0) { errorMsg = "Parameter 'refresh' must be at least 1 second"; validConfig = false; diff --git a/bundles/org.openhab.binding.fronius/src/main/java/org/openhab/binding/fronius/internal/handler/FroniusMeterHandler.java b/bundles/org.openhab.binding.fronius/src/main/java/org/openhab/binding/fronius/internal/handler/FroniusMeterHandler.java index 22e62e8706c06..9e590f0f7912c 100644 --- a/bundles/org.openhab.binding.fronius/src/main/java/org/openhab/binding/fronius/internal/handler/FroniusMeterHandler.java +++ b/bundles/org.openhab.binding.fronius/src/main/java/org/openhab/binding/fronius/internal/handler/FroniusMeterHandler.java @@ -14,7 +14,7 @@ import java.util.Map; -import org.apache.commons.lang.StringUtils; +import org.apache.commons.lang3.StringUtils; import org.openhab.binding.fronius.internal.FroniusBaseDeviceConfiguration; import org.openhab.binding.fronius.internal.FroniusBindingConstants; import org.openhab.binding.fronius.internal.FroniusBridgeConfiguration; diff --git a/bundles/org.openhab.binding.fronius/src/main/java/org/openhab/binding/fronius/internal/handler/FroniusSymoInverterHandler.java b/bundles/org.openhab.binding.fronius/src/main/java/org/openhab/binding/fronius/internal/handler/FroniusSymoInverterHandler.java index e156d7d9bd831..ff8592f8d20c2 100644 --- a/bundles/org.openhab.binding.fronius/src/main/java/org/openhab/binding/fronius/internal/handler/FroniusSymoInverterHandler.java +++ b/bundles/org.openhab.binding.fronius/src/main/java/org/openhab/binding/fronius/internal/handler/FroniusSymoInverterHandler.java @@ -12,7 +12,7 @@ */ package org.openhab.binding.fronius.internal.handler; -import org.apache.commons.lang.StringUtils; +import org.apache.commons.lang3.StringUtils; import org.openhab.binding.fronius.internal.FroniusBaseDeviceConfiguration; import org.openhab.binding.fronius.internal.FroniusBindingConstants; import org.openhab.binding.fronius.internal.FroniusBridgeConfiguration; diff --git a/bundles/org.openhab.binding.fsinternetradio/src/main/java/org/openhab/binding/fsinternetradio/internal/handler/FSInternetRadioHandler.java b/bundles/org.openhab.binding.fsinternetradio/src/main/java/org/openhab/binding/fsinternetradio/internal/handler/FSInternetRadioHandler.java index a24cb7f043172..548d00a951643 100644 --- a/bundles/org.openhab.binding.fsinternetradio/src/main/java/org/openhab/binding/fsinternetradio/internal/handler/FSInternetRadioHandler.java +++ b/bundles/org.openhab.binding.fsinternetradio/src/main/java/org/openhab/binding/fsinternetradio/internal/handler/FSInternetRadioHandler.java @@ -18,7 +18,6 @@ import java.math.BigDecimal; import java.util.concurrent.ScheduledFuture; -import org.apache.commons.lang.StringUtils; import org.eclipse.jetty.client.HttpClient; import org.openhab.binding.fsinternetradio.internal.radio.FrontierSiliconRadio; import org.openhab.core.library.types.DecimalType; @@ -128,7 +127,7 @@ public void initialize() { final BigDecimal port = (BigDecimal) getThing().getConfiguration().get(CONFIG_PROPERTY_PORT); final String pin = (String) getThing().getConfiguration().get(CONFIG_PROPERTY_PIN); - if (ip == null || StringUtils.isEmpty(pin) || port.intValue() == 0) { + if (ip == null || pin == null || pin.isEmpty() || port.intValue() == 0) { // configuration incomplete updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, "Configuration incomplete"); } else { diff --git a/bundles/org.openhab.binding.fsinternetradio/src/test/java/org/openhab/binding/fsinternetradio/test/FSInternetRadioHandlerJavaTest.java b/bundles/org.openhab.binding.fsinternetradio/src/test/java/org/openhab/binding/fsinternetradio/test/FSInternetRadioHandlerJavaTest.java index 479ff037ebbb8..0b28a407ab93f 100644 --- a/bundles/org.openhab.binding.fsinternetradio/src/test/java/org/openhab/binding/fsinternetradio/test/FSInternetRadioHandlerJavaTest.java +++ b/bundles/org.openhab.binding.fsinternetradio/src/test/java/org/openhab/binding/fsinternetradio/test/FSInternetRadioHandlerJavaTest.java @@ -21,9 +21,12 @@ import java.io.IOException; import java.math.BigDecimal; -import java.util.*; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Objects; -import org.apache.commons.lang.StringUtils; import org.eclipse.jdt.annotation.NonNull; import org.eclipse.jetty.client.HttpClient; import org.eclipse.jetty.servlet.ServletHolder; @@ -818,7 +821,7 @@ private boolean isConfigurationComplete(Configuration config) { BigDecimal port = (BigDecimal) config.get(FSInternetRadioBindingConstants.CONFIG_PROPERTY_PORT.toString()); String pin = (String) config.get(FSInternetRadioBindingConstants.CONFIG_PROPERTY_PIN.toString()); - if (ip == null || port.compareTo(BigDecimal.ZERO) == 0 || StringUtils.isEmpty(pin)) { + if (ip == null || port.compareTo(BigDecimal.ZERO) == 0 || pin == null || pin.isEmpty()) { return false; } return true; diff --git a/bundles/org.openhab.binding.ftpupload/README.md b/bundles/org.openhab.binding.ftpupload/README.md index 5d102e0588818..8ee0f6ac7baaa 100644 --- a/bundles/org.openhab.binding.ftpupload/README.md +++ b/bundles/org.openhab.binding.ftpupload/README.md @@ -18,10 +18,11 @@ Automatic discovery is not supported. The binding has the following configuration options: -| Parameter | Name | Description | Required | Default value | -|-------------|--------------|------------------------------------------------------------------------------------------------------------------------|----------|---------------| -| port | TCP Port | TCP port of the FTP server | no | 2121 | -| idleTimeout | Idle timeout | The number of seconds before an inactive client is disconnected. If this value is set to 0, the idle time is disabled. | no | 60 | +| Parameter | Name | Description | Required | Default value | +|--------------|---------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|----------|---------------| +| port | TCP Port | TCP port of the FTP server | no | 2121 | +| idleTimeout | Idle timeout | The number of seconds before an inactive client is disconnected. If this value is set to 0, the idle time is disabled. | no | 60 | +| passivePorts | Passive Ports | A string of passive ports, can contain a single port (as an integer), multiple ports seperated by commas (e.g. 123,124,125) or ranges of ports, including open ended ranges (e.g. 123-125, 30000-, -1023). Combinations for single ports and ranges is also supported. Empty (default) allows all ports as passive ports. | no | | ## Thing Configuration diff --git a/bundles/org.openhab.binding.ftpupload/src/main/java/org/openhab/binding/ftpupload/internal/FtpUploadHandlerFactory.java b/bundles/org.openhab.binding.ftpupload/src/main/java/org/openhab/binding/ftpupload/internal/FtpUploadHandlerFactory.java index b888e570a9053..21dd1e83b1375 100644 --- a/bundles/org.openhab.binding.ftpupload/src/main/java/org/openhab/binding/ftpupload/internal/FtpUploadHandlerFactory.java +++ b/bundles/org.openhab.binding.ftpupload/src/main/java/org/openhab/binding/ftpupload/internal/FtpUploadHandlerFactory.java @@ -18,7 +18,7 @@ import java.util.Dictionary; import java.util.Set; -import org.apache.commons.lang.StringUtils; +import org.apache.ftpserver.DataConnectionConfigurationFactory; import org.apache.ftpserver.FtpServerConfigurationException; import org.apache.ftpserver.ftplet.FtpException; import org.openhab.binding.ftpupload.internal.ftp.FtpServer; @@ -90,13 +90,14 @@ protected synchronized void deactivate(ComponentContext componentContext) { protected synchronized void modified(ComponentContext componentContext) { stopFtpServer(); Dictionary properties = componentContext.getProperties(); + DataConnectionConfigurationFactory dataConnectionConfigurationFactory = new DataConnectionConfigurationFactory(); int port = DEFAULT_PORT; int idleTimeout = DEFAULT_IDLE_TIMEOUT; if (properties.get("port") != null) { String strPort = properties.get("port").toString(); - if (StringUtils.isNotEmpty(strPort)) { + if (!strPort.isEmpty()) { try { port = Integer.valueOf(strPort); } catch (NumberFormatException e) { @@ -107,7 +108,7 @@ protected synchronized void modified(ComponentContext componentContext) { if (properties.get("idleTimeout") != null) { String strIdleTimeout = properties.get("idleTimeout").toString(); - if (StringUtils.isNotEmpty(strIdleTimeout)) { + if (!strIdleTimeout.isEmpty()) { try { idleTimeout = Integer.valueOf(strIdleTimeout); } catch (NumberFormatException e) { @@ -116,9 +117,21 @@ protected synchronized void modified(ComponentContext componentContext) { } } + if (properties.get("passivePorts") != null) { + String strPassivePorts = properties.get("passivePorts").toString(); + if (!strPassivePorts.isEmpty()) { + try { + dataConnectionConfigurationFactory.setPassivePorts(strPassivePorts); + } catch (IllegalArgumentException e) { + logger.warn("Invalid passive ports '{}' ({})", strPassivePorts, e.getMessage()); + } + } + } + try { logger.debug("Starting FTP server, port={}, idleTimeout={}", port, idleTimeout); - ftpServer.startServer(port, idleTimeout); + ftpServer.startServer(port, idleTimeout, + dataConnectionConfigurationFactory.createDataConnectionConfiguration()); } catch (FtpException | FtpServerConfigurationException e) { logger.warn("FTP server starting failed, reason: {}", e.getMessage()); } diff --git a/bundles/org.openhab.binding.ftpupload/src/main/java/org/openhab/binding/ftpupload/internal/ftp/FtpServer.java b/bundles/org.openhab.binding.ftpupload/src/main/java/org/openhab/binding/ftpupload/internal/ftp/FtpServer.java index df1444f85a912..0cec11fdad1a7 100644 --- a/bundles/org.openhab.binding.ftpupload/src/main/java/org/openhab/binding/ftpupload/internal/ftp/FtpServer.java +++ b/bundles/org.openhab.binding.ftpupload/src/main/java/org/openhab/binding/ftpupload/internal/ftp/FtpServer.java @@ -19,6 +19,7 @@ import java.util.List; import java.util.Map; +import org.apache.ftpserver.DataConnectionConfiguration; import org.apache.ftpserver.FtpServerConfigurationException; import org.apache.ftpserver.FtpServerFactory; import org.apache.ftpserver.ftplet.DefaultFtplet; @@ -48,6 +49,7 @@ public class FtpServer { private final Logger logger = LoggerFactory.getLogger(FtpServer.class); private int port; + private DataConnectionConfiguration dataConnectionConfiguration; int idleTimeout; private org.apache.ftpserver.FtpServer server; @@ -61,10 +63,12 @@ public FtpServer() { FTPUserManager = new FTPUserManager(); } - public void startServer(int port, int idleTimeout) throws FtpException { + public void startServer(int port, int idleTimeout, DataConnectionConfiguration dataConnectionConfiguration) + throws FtpException { stopServer(); this.port = port; this.idleTimeout = idleTimeout; + this.dataConnectionConfiguration = dataConnectionConfiguration; FTPUserManager.setIdleTimeout(idleTimeout); initServer(); } @@ -127,8 +131,10 @@ public void printStats() { private void initServer() throws FtpException { FtpServerFactory serverFactory = new FtpServerFactory(); ListenerFactory listenerFactory = new ListenerFactory(); + listenerFactory.setPort(port); listenerFactory.setIdleTimeout(idleTimeout); + listenerFactory.setDataConnectionConfiguration(dataConnectionConfiguration); Listener listener = listenerFactory.createListener(); diff --git a/bundles/org.openhab.binding.ftpupload/src/main/resources/OH-INF/binding/binding.xml b/bundles/org.openhab.binding.ftpupload/src/main/resources/OH-INF/binding/binding.xml index 891acf55fc0b4..f76a75aabaa59 100644 --- a/bundles/org.openhab.binding.ftpupload/src/main/resources/OH-INF/binding/binding.xml +++ b/bundles/org.openhab.binding.ftpupload/src/main/resources/OH-INF/binding/binding.xml @@ -18,5 +18,15 @@ time is disabled. 60 + + + A string of passive ports, can contain a single port (as an integer), multiple ports seperated by + commas + (e.g. 123,124,125) or ranges of ports, including open ended ranges (e.g. 123-125, 30000-, -1023). + Combinations for + single ports and ranges is also supported. Empty (default) allows all ports as passive ports. + + true + diff --git a/bundles/org.openhab.binding.globalcache/src/main/java/org/openhab/binding/globalcache/internal/handler/GlobalCacheHandler.java b/bundles/org.openhab.binding.globalcache/src/main/java/org/openhab/binding/globalcache/internal/handler/GlobalCacheHandler.java index 986b6db1c8a68..119fb83d0efa2 100644 --- a/bundles/org.openhab.binding.globalcache/src/main/java/org/openhab/binding/globalcache/internal/handler/GlobalCacheHandler.java +++ b/bundles/org.openhab.binding.globalcache/src/main/java/org/openhab/binding/globalcache/internal/handler/GlobalCacheHandler.java @@ -36,7 +36,6 @@ import java.util.concurrent.atomic.AtomicInteger; import java.util.regex.Pattern; -import org.apache.commons.lang.StringUtils; import org.eclipse.jdt.annotation.NonNull; import org.openhab.binding.globalcache.internal.GlobalCacheBindingConstants.CommandType; import org.openhab.binding.globalcache.internal.command.CommandGetstate; @@ -252,7 +251,7 @@ private String lookupCode(Command command) { } String mapFile = (String) thing.getConfiguration().get(THING_CONFIG_MAP_FILENAME); - if (StringUtils.isEmpty(mapFile)) { + if (mapFile == null || mapFile.isEmpty()) { logger.warn("MAP file is not defined in configuration of thing {}", thingID()); return null; } @@ -266,14 +265,13 @@ private String lookupCode(Command command) { String code; try { code = transformService.transform(mapFile, command.toString()); - } catch (TransformationException e) { logger.error("Failed to transform {} for thing {} using map file '{}', exception={}", command, thingID(), mapFile, e.getMessage()); return null; } - if (StringUtils.isEmpty(code)) { + if (code == null || code.isEmpty()) { logger.warn("No entry for {} in map file '{}' for thing {}", command, mapFile, thingID()); return null; } @@ -638,7 +636,7 @@ public ConnectionManager() { private String getIPAddress() { String ipAddress = ((GlobalCacheHandler) thing.getHandler()).getIP(); - if (StringUtils.isEmpty(ipAddress)) { + if (ipAddress == null || ipAddress.isEmpty()) { logger.debug("Handler for thing {} could not get IP address from config", thingID()); markThingOfflineWithError(ThingStatusDetail.OFFLINE.CONFIGURATION_ERROR, "IP address not set"); } @@ -912,7 +910,7 @@ private SerialPortReader startSerialPortReader(CommandType serialDevice, String if (Boolean.TRUE.equals(enableTwoWay)) { // Get the end of message delimiter from the config, URL decode it, and convert it to a byte array String endOfMessageString = (String) thing.getConfiguration().get(endOfMessageDelimiterConfig); - if (StringUtils.isNotEmpty(endOfMessageString)) { + if (endOfMessageString != null && !endOfMessageString.isEmpty()) { logger.debug("End of message is {} for thing {} {}", endOfMessageString, thingID(), serialDevice); byte[] endOfMessage; try { diff --git a/bundles/org.openhab.binding.goecharger/src/main/java/org/openhab/binding/goecharger/internal/handler/GoEChargerHandler.java b/bundles/org.openhab.binding.goecharger/src/main/java/org/openhab/binding/goecharger/internal/handler/GoEChargerHandler.java index 6b54975a07f05..39dfd9fdbfcaa 100644 --- a/bundles/org.openhab.binding.goecharger/src/main/java/org/openhab/binding/goecharger/internal/handler/GoEChargerHandler.java +++ b/bundles/org.openhab.binding.goecharger/src/main/java/org/openhab/binding/goecharger/internal/handler/GoEChargerHandler.java @@ -45,7 +45,7 @@ import javax.measure.quantity.ElectricCurrent; import javax.measure.quantity.Energy; -import org.apache.commons.lang.StringUtils; +import org.apache.commons.lang3.StringUtils; import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.Nullable; import org.eclipse.jetty.client.HttpClient; diff --git a/bundles/org.openhab.binding.gpio/README.md b/bundles/org.openhab.binding.gpio/README.md index e935a735f753e..622cf483582bd 100644 --- a/bundles/org.openhab.binding.gpio/README.md +++ b/bundles/org.openhab.binding.gpio/README.md @@ -22,12 +22,25 @@ sudo raspi-config -> Interfacing Options --> Remote GPIO --> YES --> OK --> Finish +Note: if you are setting this up on a Raspberry Pi without `raspi-config` you can create the service config file manually: + +``` +sudo mkdir -p /etc/systemd/system/pigpiod.service.d/ +sudo nano /etc/systemd/system/pigpiod.service.d/public.conf +``` + [Service] + ExecStart= + ExecStart=/usr/bin/pigpiod +``` +sudo systemctl daemon-reload +``` +Now that Remote GPIO is enabled, get the daemon going: ``` sudo systemctl enable pigpiod sudo systemctl start pigpiod ``` -Set `host` to the address of the pi and the `port` to the port of pigpio (default: 8888). +In openHAB, set `host` to the address of the pi and the `port` to the port of pigpio (default: 8888). ## Channels diff --git a/bundles/org.openhab.binding.gpstracker/src/main/resources/OH-INF/thing/tracker.xml b/bundles/org.openhab.binding.gpstracker/src/main/resources/OH-INF/thing/tracker.xml index 9221356941657..acda2134cd8b7 100644 --- a/bundles/org.openhab.binding.gpstracker/src/main/resources/OH-INF/thing/tracker.xml +++ b/bundles/org.openhab.binding.gpstracker/src/main/resources/OH-INF/thing/tracker.xml @@ -31,7 +31,7 @@ Number:Length GPS accuracy - + DateTime diff --git a/bundles/org.openhab.binding.gree/src/main/java/org/openhab/binding/gree/internal/handler/GreeHandler.java b/bundles/org.openhab.binding.gree/src/main/java/org/openhab/binding/gree/internal/handler/GreeHandler.java index 1869a1e54e10a..73ccda0c8ee46 100644 --- a/bundles/org.openhab.binding.gree/src/main/java/org/openhab/binding/gree/internal/handler/GreeHandler.java +++ b/bundles/org.openhab.binding.gree/src/main/java/org/openhab/binding/gree/internal/handler/GreeHandler.java @@ -16,6 +16,7 @@ import java.io.IOException; import java.math.BigDecimal; +import java.math.RoundingMode; import java.net.DatagramSocket; import java.time.Instant; import java.util.List; @@ -567,7 +568,7 @@ private String logInfo(String msgKey, Object... arg) { public static QuantityType toQuantityType(Number value, int digits, Unit unit) { BigDecimal bd = new BigDecimal(value.doubleValue()); - return new QuantityType<>(bd.setScale(digits, BigDecimal.ROUND_HALF_EVEN), unit); + return new QuantityType<>(bd.setScale(digits, RoundingMode.HALF_EVEN), unit); } private void stopRefreshTask() { diff --git a/bundles/org.openhab.binding.harmonyhub/src/main/java/org/openhab/binding/harmonyhub/internal/handler/HarmonyHubHandler.java b/bundles/org.openhab.binding.harmonyhub/src/main/java/org/openhab/binding/harmonyhub/internal/handler/HarmonyHubHandler.java index 43013400baa80..eb974d0f74133 100644 --- a/bundles/org.openhab.binding.harmonyhub/src/main/java/org/openhab/binding/harmonyhub/internal/handler/HarmonyHubHandler.java +++ b/bundles/org.openhab.binding.harmonyhub/src/main/java/org/openhab/binding/harmonyhub/internal/handler/HarmonyHubHandler.java @@ -25,7 +25,6 @@ import java.util.concurrent.ScheduledFuture; import java.util.concurrent.TimeUnit; -import org.apache.commons.lang.StringUtils; import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.Nullable; import org.openhab.binding.harmonyhub.internal.HarmonyHubHandlerFactory; @@ -263,9 +262,9 @@ private synchronized void connect() { // earlier versions required a name and used network discovery to find the hub and retrieve the host, // this section is to not break that and also update older configurations to use the host configuration // option instead of name - if (StringUtils.isBlank(host)) { + if (host == null || host.isBlank()) { host = getThing().getProperties().get(HUB_PROPERTY_HOST); - if (StringUtils.isNotBlank(host)) { + if (host != null && !host.isBlank()) { Configuration genericConfig = getConfig(); genericConfig.put(HUB_PROPERTY_HOST, host); updateConfiguration(genericConfig); diff --git a/bundles/org.openhab.binding.hdanywhere/src/main/java/org/openhab/binding/hdanywhere/internal/handler/Mhub4K431Handler.java b/bundles/org.openhab.binding.hdanywhere/src/main/java/org/openhab/binding/hdanywhere/internal/handler/Mhub4K431Handler.java index 417ee7a367a1e..79b114d45698d 100644 --- a/bundles/org.openhab.binding.hdanywhere/src/main/java/org/openhab/binding/hdanywhere/internal/handler/Mhub4K431Handler.java +++ b/bundles/org.openhab.binding.hdanywhere/src/main/java/org/openhab/binding/hdanywhere/internal/handler/Mhub4K431Handler.java @@ -12,8 +12,6 @@ */ package org.openhab.binding.hdanywhere.internal.handler; -import static org.apache.commons.lang.StringUtils.isNotBlank; - import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.InputStream; @@ -93,27 +91,25 @@ public void dispose() { String content = "{tag:ptn}"; InputStream stream = new ByteArrayInputStream(content.getBytes(StandardCharsets.UTF_8)); - if (isNotBlank(httpMethod) && isNotBlank(url)) { - String response = HttpUtil.executeUrl(httpMethod, url, null, stream, null, timeout); - response = response.trim(); - response = response.substring(1, response.length() - 1); + String response = HttpUtil.executeUrl(httpMethod, url, null, stream, null, timeout); + response = response.trim(); + response = response.substring(1, response.length() - 1); - if (response != null) { - updateStatus(ThingStatus.ONLINE); + if (response != null) { + updateStatus(ThingStatus.ONLINE); - java.lang.reflect.Type type = new TypeToken>() { - }.getType(); - Map map = gson.fromJson(response, type); + java.lang.reflect.Type type = new TypeToken>() { + }.getType(); + Map map = gson.fromJson(response, type); - String inputChannel = map.get("Inputchannel"); + String inputChannel = map.get("Inputchannel"); - for (int i = 0; i < numberOfPorts; i++) { - DecimalType decimalType = new DecimalType(String.valueOf(inputChannel.charAt(i))); - updateState(new ChannelUID(getThing().getUID(), Port.get(i + 1).channelID()), decimalType); - } - } else { - updateStatus(ThingStatus.OFFLINE); + for (int i = 0; i < numberOfPorts; i++) { + DecimalType decimalType = new DecimalType(String.valueOf(inputChannel.charAt(i))); + updateState(new ChannelUID(getThing().getUID(), Port.get(i + 1).channelID()), decimalType); } + } else { + updateStatus(ThingStatus.OFFLINE); } } catch (Exception e) { logger.debug("An exception occurred while polling the HDanwywhere matrix: '{}'", e.getMessage()); diff --git a/bundles/org.openhab.binding.hdanywhere/src/main/java/org/openhab/binding/hdanywhere/internal/handler/MultiroomPlusHandler.java b/bundles/org.openhab.binding.hdanywhere/src/main/java/org/openhab/binding/hdanywhere/internal/handler/MultiroomPlusHandler.java index 70a4672d5c52b..a2cdc8c9539c6 100644 --- a/bundles/org.openhab.binding.hdanywhere/src/main/java/org/openhab/binding/hdanywhere/internal/handler/MultiroomPlusHandler.java +++ b/bundles/org.openhab.binding.hdanywhere/src/main/java/org/openhab/binding/hdanywhere/internal/handler/MultiroomPlusHandler.java @@ -12,8 +12,6 @@ */ package org.openhab.binding.hdanywhere.internal.handler; -import static org.apache.commons.lang.StringUtils.isNotBlank; - import java.io.IOException; import java.math.BigDecimal; import java.util.concurrent.ScheduledFuture; @@ -69,24 +67,22 @@ public MultiroomPlusHandler(Thing thing) { String httpMethod = "GET"; String url = "http://" + host + "/status_show.shtml"; - if (isNotBlank(httpMethod) && isNotBlank(url)) { - String response = HttpUtil.executeUrl(httpMethod, url, null, null, null, timeout); + String response = HttpUtil.executeUrl(httpMethod, url, null, null, null, timeout); - if (response != null) { - updateStatus(ThingStatus.ONLINE); + if (response != null) { + updateStatus(ThingStatus.ONLINE); - for (int i = 1; i <= numberOfPorts; i++) { - Pattern p = Pattern.compile("var out" + i + "var = (.*);"); - Matcher m = p.matcher(response); + for (int i = 1; i <= numberOfPorts; i++) { + Pattern p = Pattern.compile("var out" + i + "var = (.*);"); + Matcher m = p.matcher(response); - while (m.find()) { - DecimalType decimalType = new DecimalType(m.group(1)); - updateState(new ChannelUID(getThing().getUID(), Port.get(i).channelID()), decimalType); - } + while (m.find()) { + DecimalType decimalType = new DecimalType(m.group(1)); + updateState(new ChannelUID(getThing().getUID(), Port.get(i).channelID()), decimalType); } - } else { - updateStatus(ThingStatus.OFFLINE); } + } else { + updateStatus(ThingStatus.OFFLINE); } } catch (Exception e) { logger.warn("An exception occurred while polling the HDanwywhere matrix: '{}'", e.getMessage()); diff --git a/bundles/org.openhab.binding.hdpowerview/pom.xml b/bundles/org.openhab.binding.hdpowerview/pom.xml index 3aabeea2eab87..e66e270a3f839 100644 --- a/bundles/org.openhab.binding.hdpowerview/pom.xml +++ b/bundles/org.openhab.binding.hdpowerview/pom.xml @@ -14,9 +14,15 @@ openHAB Add-ons :: Bundles :: Hunter Douglas PowerView Binding + + + !jcifs.* + + + - org.samba.jcifs + jcifs jcifs 1.3.17 compile diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/feature/feature.xml b/bundles/org.openhab.binding.hdpowerview/src/main/feature/feature.xml index 589480fba40c6..71ed90ed0ecf4 100644 --- a/bundles/org.openhab.binding.hdpowerview/src/main/feature/feature.xml +++ b/bundles/org.openhab.binding.hdpowerview/src/main/feature/feature.xml @@ -4,7 +4,6 @@ openhab-runtime-base - mvn:org.samba.jcifs/jcifs/1.3.17 mvn:org.openhab.addons.bundles/org.openhab.binding.hdpowerview/${project.version} diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/HDPowerViewHandlerFactory.java b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/HDPowerViewHandlerFactory.java index 6fb6c4747296b..8b4dc8aeb8be1 100644 --- a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/HDPowerViewHandlerFactory.java +++ b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/HDPowerViewHandlerFactory.java @@ -14,14 +14,14 @@ import java.util.Hashtable; -import javax.ws.rs.client.ClientBuilder; - import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.Nullable; +import org.eclipse.jetty.client.HttpClient; import org.openhab.binding.hdpowerview.internal.discovery.HDPowerViewShadeDiscoveryService; import org.openhab.binding.hdpowerview.internal.handler.HDPowerViewHubHandler; import org.openhab.binding.hdpowerview.internal.handler.HDPowerViewShadeHandler; import org.openhab.core.config.discovery.DiscoveryService; +import org.openhab.core.io.net.http.HttpClientFactory; import org.openhab.core.thing.Bridge; import org.openhab.core.thing.Thing; import org.openhab.core.thing.ThingTypeUID; @@ -41,11 +41,12 @@ @NonNullByDefault @Component(service = ThingHandlerFactory.class, configurationPid = "binding.hdpowerview") public class HDPowerViewHandlerFactory extends BaseThingHandlerFactory { - private final ClientBuilder clientBuilder; + + private final HttpClient httpClient; @Activate - public HDPowerViewHandlerFactory(@Reference ClientBuilder clientBuilder) { - this.clientBuilder = clientBuilder; + public HDPowerViewHandlerFactory(@Reference HttpClientFactory httpClientFactory) { + this.httpClient = httpClientFactory.getCommonHttpClient(); } @Override @@ -58,7 +59,7 @@ public boolean supportsThingType(ThingTypeUID thingTypeUID) { ThingTypeUID thingTypeUID = thing.getThingTypeUID(); if (thingTypeUID.equals(HDPowerViewBindingConstants.THING_TYPE_HUB)) { - HDPowerViewHubHandler handler = new HDPowerViewHubHandler((Bridge) thing, clientBuilder); + HDPowerViewHubHandler handler = new HDPowerViewHubHandler((Bridge) thing, httpClient); registerService(new HDPowerViewShadeDiscoveryService(handler)); return handler; } else if (thingTypeUID.equals(HDPowerViewBindingConstants.THING_TYPE_SHADE)) { diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/HDPowerViewWebTargets.java b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/HDPowerViewWebTargets.java index 94174ba16348a..4d40d9927bee2 100644 --- a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/HDPowerViewWebTargets.java +++ b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/HDPowerViewWebTargets.java @@ -13,17 +13,18 @@ package org.openhab.binding.hdpowerview.internal; import java.time.Instant; - -import javax.ws.rs.ProcessingException; -import javax.ws.rs.client.Client; -import javax.ws.rs.client.Entity; -import javax.ws.rs.client.Invocation; -import javax.ws.rs.client.WebTarget; -import javax.ws.rs.core.MediaType; -import javax.ws.rs.core.Response; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.TimeoutException; import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.Nullable; +import org.eclipse.jetty.client.HttpClient; +import org.eclipse.jetty.client.api.ContentResponse; +import org.eclipse.jetty.client.api.Request; +import org.eclipse.jetty.client.util.StringContentProvider; +import org.eclipse.jetty.http.HttpHeader; +import org.eclipse.jetty.http.HttpMethod; +import org.eclipse.jetty.http.HttpStatus; import org.openhab.binding.hdpowerview.internal.api.ShadePosition; import org.openhab.binding.hdpowerview.internal.api.requests.ShadeMove; import org.openhab.binding.hdpowerview.internal.api.requests.ShadeStop; @@ -45,14 +46,6 @@ @NonNullByDefault public class HDPowerViewWebTargets { - private static final String PUT = "PUT"; - private static final String GET = "GET"; - private static final String SCENE_ID = "sceneId"; - private static final String ID = "id"; - private static final String REFRESH = "refresh"; - private static final String CONN_HDR = "Connection"; - private static final String CONN_VAL = "close"; // versus "keep-alive" - private final Logger logger = LoggerFactory.getLogger(HDPowerViewWebTargets.class); /* @@ -64,128 +57,159 @@ public class HDPowerViewWebTargets { private final int maintenancePeriod = 300; private Instant maintenanceScheduledEnd = Instant.now().minusSeconds(2 * maintenancePeriod); - private WebTarget base; - private WebTarget shades; - private WebTarget shade; - private WebTarget sceneActivate; - private WebTarget scenes; + private final String base; + private final String shades; + private final String sceneActivate; + private final String scenes; private final Gson gson = new Gson(); + private final HttpClient httpClient; + + /** + * private helper class for passing http url query parameters + */ + private static class Query { + private final String key; + private final String value; + + private Query(String key, String value) { + this.key = key; + this.value = value; + } + + public static Query of(String key, String value) { + return new Query(key, value); + } + + public String getKey() { + return key; + } + + public String getValue() { + return value; + } + } /** * Initialize the web targets - * - * @param client the Javax RS client (the binding) + * + * @param httpClient the HTTP client (the binding) * @param ipAddress the IP address of the server (the hub) */ - public HDPowerViewWebTargets(Client client, String ipAddress) { - base = client.target("http://" + ipAddress + "/api"); - shades = base.path("shades/"); - shade = base.path("shades/{id}"); - sceneActivate = base.path("scenes"); - scenes = base.path("scenes/"); + public HDPowerViewWebTargets(HttpClient httpClient, String ipAddress) { + base = "http://" + ipAddress + "/api/"; + shades = base + "shades/"; + sceneActivate = base + "scenes"; + scenes = base + "scenes/"; + this.httpClient = httpClient; } /** * Fetches a JSON package that describes all shades in the hub, and wraps it in * a Shades class instance - * + * * @return Shades class instance * @throws JsonParseException if there is a JSON parsing error - * @throws ProcessingException if there is any processing error + * @throws HubProcessingException if there is any processing error * @throws HubMaintenanceException if the hub is down for maintenance */ - public @Nullable Shades getShades() throws JsonParseException, ProcessingException, HubMaintenanceException { - String json = invoke(shades.request().header(CONN_HDR, CONN_VAL).buildGet(), shades, null); + public @Nullable Shades getShades() throws JsonParseException, HubProcessingException, HubMaintenanceException { + String json = invoke(HttpMethod.GET, shades, null, null); return gson.fromJson(json, Shades.class); } /** * Instructs the hub to move a specific shade - * + * * @param shadeId id of the shade to be moved * @param position instance of ShadePosition containing the new position - * @throws ProcessingException if there is any processing error + * @throws HubProcessingException if there is any processing error * @throws HubMaintenanceException if the hub is down for maintenance */ - public void moveShade(int shadeId, ShadePosition position) throws ProcessingException, HubMaintenanceException { - WebTarget target = shade.resolveTemplate(ID, shadeId); + public void moveShade(int shadeId, ShadePosition position) throws HubProcessingException, HubMaintenanceException { String json = gson.toJson(new ShadeMove(shadeId, position)); - invoke(target.request().header(CONN_HDR, CONN_VAL) - .buildPut(Entity.entity(json, MediaType.APPLICATION_JSON_TYPE)), target, json); - return; + invoke(HttpMethod.PUT, shades + Integer.toString(shadeId), null, json); } /** * Fetches a JSON package that describes all scenes in the hub, and wraps it in * a Scenes class instance - * + * * @return Scenes class instance * @throws JsonParseException if there is a JSON parsing error - * @throws ProcessingException if there is any processing error + * @throws HubProcessingException if there is any processing error * @throws HubMaintenanceException if the hub is down for maintenance */ - public @Nullable Scenes getScenes() throws JsonParseException, ProcessingException, HubMaintenanceException { - String json = invoke(scenes.request().header(CONN_HDR, CONN_VAL).buildGet(), scenes, null); + public @Nullable Scenes getScenes() throws JsonParseException, HubProcessingException, HubMaintenanceException { + String json = invoke(HttpMethod.GET, scenes, null, null); return gson.fromJson(json, Scenes.class); } /** * Instructs the hub to execute a specific scene - * + * * @param sceneId id of the scene to be executed - * @throws ProcessingException if there is any processing error + * @throws HubProcessingException if there is any processing error * @throws HubMaintenanceException if the hub is down for maintenance */ - public void activateScene(int sceneId) throws ProcessingException, HubMaintenanceException { - WebTarget target = sceneActivate.queryParam(SCENE_ID, sceneId); - invoke(target.request().header(CONN_HDR, CONN_VAL).buildGet(), target, null); + public void activateScene(int sceneId) throws HubProcessingException, HubMaintenanceException { + invoke(HttpMethod.GET, sceneActivate, Query.of("sceneId", Integer.toString(sceneId)), null); } - private synchronized String invoke(Invocation invocation, WebTarget target, @Nullable String jsonCommand) - throws ProcessingException, HubMaintenanceException { + /** + * Invoke a call on the hub server to retrieve information or send a command + * + * @param method GET or PUT + * @param url the host url to be called + * @param query the http query parameter + * @param jsonCommand the request command content (as a json string) + * @return the response content (as a json string) + * @throws HubProcessingException + * @throws HubMaintenanceException + * @throws HubProcessingException + */ + private synchronized String invoke(HttpMethod method, String url, @Nullable Query query, + @Nullable String jsonCommand) throws HubMaintenanceException, HubProcessingException { if (logger.isTraceEnabled()) { - logger.trace("API command {} {}", jsonCommand == null ? GET : PUT, target.getUri()); + logger.trace("API command {} {}", method, url); if (jsonCommand != null) { logger.trace("JSON command = {}", jsonCommand); } } - Response response; + Request request = httpClient.newRequest(url).method(method).header("Connection", "close").accept("*/*"); + if (query != null) { + request.param(query.getKey(), query.getValue()); + } + if (jsonCommand != null) { + request.header(HttpHeader.CONTENT_TYPE, "application/json").content(new StringContentProvider(jsonCommand)); + } + ContentResponse response; try { - response = invocation.invoke(); - } catch (ProcessingException e) { + response = request.send(); + } catch (InterruptedException | TimeoutException | ExecutionException e) { if (Instant.now().isBefore(maintenanceScheduledEnd)) { // throw "softer" exception during maintenance window logger.debug("Hub still undergoing maintenance"); throw new HubMaintenanceException("Hub still undergoing maintenance"); } - throw e; + throw new HubProcessingException(String.format("%s: \"%s\"", e.getClass().getName(), e.getMessage())); } int statusCode = response.getStatus(); - if (statusCode == 423) { + if (statusCode == HttpStatus.LOCKED_423) { // set end of maintenance window, and throw a "softer" exception maintenanceScheduledEnd = Instant.now().plusSeconds(maintenancePeriod); logger.debug("Hub undergoing maintenance"); - if (response.hasEntity()) { - response.readEntity(String.class); - } - response.close(); throw new HubMaintenanceException("Hub undergoing maintenance"); } - if (statusCode != 200) { - logger.warn("Hub returned HTTP error '{}'", statusCode); - if (response.hasEntity()) { - response.readEntity(String.class); - } - response.close(); - throw new ProcessingException(String.format("HTTP %d error", statusCode)); + if (statusCode != HttpStatus.OK_200) { + logger.warn("Hub returned HTTP {} {}", statusCode, response.getReason()); + throw new HubProcessingException(String.format("HTTP %d error", statusCode)); } - if (!response.hasEntity()) { + String jsonResponse = response.getContentAsString(); + if ("".equals(jsonResponse)) { logger.warn("Hub returned no content"); - response.close(); - throw new ProcessingException("Missing response entity"); + throw new HubProcessingException("Missing response entity"); } - String jsonResponse = response.readEntity(String.class); if (logger.isTraceEnabled()) { logger.trace("JSON response = {}", jsonResponse); } @@ -195,15 +219,16 @@ private synchronized String invoke(Invocation invocation, WebTarget target, @Nul /** * Fetches a JSON package that describes a specific shade in the hub, and wraps it * in a Shade class instance - * + * * @param shadeId id of the shade to be fetched * @return Shade class instance - * @throws ProcessingException if there is any processing error + * @throws JsonParseException if there is a JSON parsing error + * @throws HubProcessingException if there is any processing error * @throws HubMaintenanceException if the hub is down for maintenance */ - public @Nullable Shade getShade(int shadeId) throws ProcessingException, HubMaintenanceException { - WebTarget target = shade.resolveTemplate(ID, shadeId); - String json = invoke(target.request().header(CONN_HDR, CONN_VAL).buildGet(), target, null); + public @Nullable Shade getShade(int shadeId) + throws JsonParseException, HubProcessingException, HubMaintenanceException { + String json = invoke(HttpMethod.GET, shades + Integer.toString(shadeId), null, null); return gson.fromJson(json, Shade.class); } @@ -211,30 +236,29 @@ private synchronized String invoke(Invocation invocation, WebTarget target, @Nul * Instructs the hub to do a hard refresh (discovery on the hubs RF network) on * a specific shade; fetches a JSON package that describes that shade, and wraps * it in a Shade class instance - * + * * @param shadeId id of the shade to be refreshed * @return Shade class instance - * @throws ProcessingException if there is any processing error + * @throws JsonParseException if there is a JSON parsing error + * @throws HubProcessingException if there is any processing error * @throws HubMaintenanceException if the hub is down for maintenance */ - public @Nullable Shade refreshShade(int shadeId) throws ProcessingException, HubMaintenanceException { - WebTarget target = shade.resolveTemplate(ID, shadeId).queryParam(REFRESH, true); - String json = invoke(target.request().header(CONN_HDR, CONN_VAL).buildGet(), target, null); + public @Nullable Shade refreshShade(int shadeId) + throws JsonParseException, HubProcessingException, HubMaintenanceException { + String json = invoke(HttpMethod.GET, shades + Integer.toString(shadeId), + Query.of("refresh", Boolean.toString(true)), null); return gson.fromJson(json, Shade.class); } /** * Tells the hub to stop movement of a specific shade - * + * * @param shadeId id of the shade to be stopped - * @throws ProcessingException if there is any processing error + * @throws HubProcessingException if there is any processing error * @throws HubMaintenanceException if the hub is down for maintenance */ - public void stopShade(int shadeId) throws ProcessingException, HubMaintenanceException { - WebTarget target = shade.resolveTemplate(ID, shadeId); + public void stopShade(int shadeId) throws HubProcessingException, HubMaintenanceException { String json = gson.toJson(new ShadeStop(shadeId)); - invoke(target.request().header(CONN_HDR, CONN_VAL) - .buildPut(Entity.entity(json, MediaType.APPLICATION_JSON_TYPE)), target, json); - return; + invoke(HttpMethod.PUT, shades + Integer.toString(shadeId), null, json); } } diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/HubProcessingException.java b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/HubProcessingException.java new file mode 100644 index 0000000000000..87594d1af770d --- /dev/null +++ b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/HubProcessingException.java @@ -0,0 +1,30 @@ +/** + * Copyright (c) 2010-2021 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.hdpowerview.internal; + +import org.eclipse.jdt.annotation.NonNullByDefault; + +/** + * The {@link HubProcessingException} is a custom exception for the HD PowerView hub + * + * @author Andrew Fiddian-Green - Initial contribution + */ +@NonNullByDefault +public class HubProcessingException extends Exception { + + private static final long serialVersionUID = 4307088023775166450L; + + public HubProcessingException(String message) { + super(message); + } +} diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/discovery/HDPowerViewShadeDiscoveryService.java b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/discovery/HDPowerViewShadeDiscoveryService.java index e733ff3c41eca..ad4ad4e390cf5 100644 --- a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/discovery/HDPowerViewShadeDiscoveryService.java +++ b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/discovery/HDPowerViewShadeDiscoveryService.java @@ -17,13 +17,12 @@ import java.util.concurrent.ScheduledFuture; import java.util.concurrent.TimeUnit; -import javax.ws.rs.ProcessingException; - import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.Nullable; import org.openhab.binding.hdpowerview.internal.HDPowerViewBindingConstants; import org.openhab.binding.hdpowerview.internal.HDPowerViewWebTargets; import org.openhab.binding.hdpowerview.internal.HubMaintenanceException; +import org.openhab.binding.hdpowerview.internal.HubProcessingException; import org.openhab.binding.hdpowerview.internal.api.responses.Shades; import org.openhab.binding.hdpowerview.internal.api.responses.Shades.ShadeData; import org.openhab.binding.hdpowerview.internal.config.HDPowerViewShadeConfiguration; @@ -85,7 +84,7 @@ private Runnable createScanner() { try { HDPowerViewWebTargets webTargets = hub.getWebTargets(); if (webTargets == null) { - throw new ProcessingException("Web targets not initialized"); + throw new HubProcessingException("Web targets not initialized"); } Shades shades = webTargets.getShades(); if (shades != null && shades.shadeData != null) { @@ -107,7 +106,7 @@ private Runnable createScanner() { } } } - } catch (ProcessingException | JsonParseException e) { + } catch (HubProcessingException | JsonParseException e) { logger.warn("Unexpected error: {}", e.getMessage()); } catch (HubMaintenanceException e) { // exceptions are logged in HDPowerViewWebTargets diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/handler/HDPowerViewHubHandler.java b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/handler/HDPowerViewHubHandler.java index b67d4a20d6755..0827a682e31e4 100644 --- a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/handler/HDPowerViewHubHandler.java +++ b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/handler/HDPowerViewHubHandler.java @@ -21,13 +21,14 @@ import java.util.concurrent.TimeUnit; import javax.ws.rs.ProcessingException; -import javax.ws.rs.client.ClientBuilder; import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.Nullable; +import org.eclipse.jetty.client.HttpClient; import org.openhab.binding.hdpowerview.internal.HDPowerViewBindingConstants; import org.openhab.binding.hdpowerview.internal.HDPowerViewWebTargets; import org.openhab.binding.hdpowerview.internal.HubMaintenanceException; +import org.openhab.binding.hdpowerview.internal.HubProcessingException; import org.openhab.binding.hdpowerview.internal.api.responses.Scenes; import org.openhab.binding.hdpowerview.internal.api.responses.Scenes.Scene; import org.openhab.binding.hdpowerview.internal.api.responses.Shades; @@ -63,7 +64,7 @@ public class HDPowerViewHubHandler extends BaseBridgeHandler { private final Logger logger = LoggerFactory.getLogger(HDPowerViewHubHandler.class); - private final ClientBuilder clientBuilder; + private final HttpClient httpClient; private long refreshInterval; private long hardRefreshInterval; @@ -75,9 +76,9 @@ public class HDPowerViewHubHandler extends BaseBridgeHandler { private final ChannelTypeUID sceneChannelTypeUID = new ChannelTypeUID(HDPowerViewBindingConstants.BINDING_ID, HDPowerViewBindingConstants.CHANNELTYPE_SCENE_ACTIVATE); - public HDPowerViewHubHandler(Bridge bridge, ClientBuilder clientBuilder) { + public HDPowerViewHubHandler(Bridge bridge, HttpClient httpClient) { super(bridge); - this.clientBuilder = clientBuilder; + this.httpClient = httpClient; } @Override @@ -98,7 +99,7 @@ public void handleCommand(ChannelUID channelUID, Command command) { webTargets.activateScene(Integer.parseInt(channelUID.getId())); } catch (HubMaintenanceException e) { // exceptions are logged in HDPowerViewWebTargets - } catch (NumberFormatException | ProcessingException e) { + } catch (NumberFormatException | HubProcessingException e) { logger.debug("Unexpected error {}", e.getMessage()); } } @@ -116,7 +117,7 @@ public void initialize() { return; } - webTargets = new HDPowerViewWebTargets(clientBuilder.build(), host); + webTargets = new HDPowerViewWebTargets(httpClient, host); refreshInterval = config.refresh; hardRefreshInterval = config.hardRefresh; schedulePoll(); @@ -178,7 +179,7 @@ private synchronized void poll() { pollScenes(); } catch (JsonParseException e) { logger.warn("Bridge returned a bad JSON response: {}", e.getMessage()); - } catch (ProcessingException e) { + } catch (HubProcessingException e) { logger.warn("Error connecting to bridge: {}", e.getMessage()); updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.BRIDGE_OFFLINE, e.getMessage()); } catch (HubMaintenanceException e) { @@ -186,7 +187,7 @@ private synchronized void poll() { } } - private void pollShades() throws JsonParseException, ProcessingException, HubMaintenanceException { + private void pollShades() throws JsonParseException, HubProcessingException, HubMaintenanceException { HDPowerViewWebTargets webTargets = this.webTargets; if (webTargets == null) { throw new ProcessingException("Web targets not initialized"); @@ -229,7 +230,7 @@ private void updateShadeThing(String shadeId, Thing thing, @Nullable ShadeData s thingHandler.onReceiveUpdate(shadeData); } - private void pollScenes() throws JsonParseException, ProcessingException, HubMaintenanceException { + private void pollScenes() throws JsonParseException, HubProcessingException, HubMaintenanceException { HDPowerViewWebTargets webTargets = this.webTargets; if (webTargets == null) { throw new ProcessingException("Web targets not initialized"); diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/handler/HDPowerViewShadeHandler.java b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/handler/HDPowerViewShadeHandler.java index 5bbf5fa294c0d..3856de9d7561f 100644 --- a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/handler/HDPowerViewShadeHandler.java +++ b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/handler/HDPowerViewShadeHandler.java @@ -19,12 +19,11 @@ import java.util.concurrent.ScheduledFuture; import java.util.concurrent.TimeUnit; -import javax.ws.rs.ProcessingException; - import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.Nullable; import org.openhab.binding.hdpowerview.internal.HDPowerViewWebTargets; import org.openhab.binding.hdpowerview.internal.HubMaintenanceException; +import org.openhab.binding.hdpowerview.internal.HubProcessingException; import org.openhab.binding.hdpowerview.internal.api.ActuatorClass; import org.openhab.binding.hdpowerview.internal.api.CoordinateSystem; import org.openhab.binding.hdpowerview.internal.api.ShadePosition; @@ -128,7 +127,7 @@ public void handleCommand(ChannelUID channelUID, Command command) { /** * Update the state of the channels based on the ShadeData provided - * + * * @param shadeData the ShadeData to be used; may be null */ protected void onReceiveUpdate(@Nullable ShadeData shadeData) { @@ -157,11 +156,11 @@ private void moveShade(ActuatorClass actuatorClass, CoordinateSystem coordSys, i try { HDPowerViewHubHandler bridge; if ((bridge = getBridgeHandler()) == null) { - throw new ProcessingException("Missing bridge handler"); + throw new HubProcessingException("Missing bridge handler"); } HDPowerViewWebTargets webTargets = bridge.getWebTargets(); if (webTargets == null) { - throw new ProcessingException("Web targets not initialized"); + throw new HubProcessingException("Web targets not initialized"); } int shadeId = getShadeId(); @@ -190,7 +189,7 @@ private void moveShade(ActuatorClass actuatorClass, CoordinateSystem coordSys, i webTargets.moveShade(shadeId, ShadePosition.create(ZERO_IS_CLOSED, primaryPercent, ZERO_IS_OPEN, newPercent)); } - } catch (ProcessingException | NumberFormatException e) { + } catch (HubProcessingException | NumberFormatException e) { logger.warn("Unexpected error: {}", e.getMessage()); return; } catch (HubMaintenanceException e) { @@ -211,16 +210,16 @@ private void stopShade() { try { HDPowerViewHubHandler bridge; if ((bridge = getBridgeHandler()) == null) { - throw new ProcessingException("Missing bridge handler"); + throw new HubProcessingException("Missing bridge handler"); } HDPowerViewWebTargets webTargets = bridge.getWebTargets(); if (webTargets == null) { - throw new ProcessingException("Web targets not initialized"); + throw new HubProcessingException("Web targets not initialized"); } int shadeId = getShadeId(); webTargets.stopShade(shadeId); requestRefreshShade(); - } catch (ProcessingException | NumberFormatException e) { + } catch (HubProcessingException | NumberFormatException e) { logger.warn("Unexpected error: {}", e.getMessage()); return; } catch (HubMaintenanceException e) { @@ -242,11 +241,11 @@ private void doRefreshShade() { try { HDPowerViewHubHandler bridge; if ((bridge = getBridgeHandler()) == null) { - throw new ProcessingException("Missing bridge handler"); + throw new HubProcessingException("Missing bridge handler"); } HDPowerViewWebTargets webTargets = bridge.getWebTargets(); if (webTargets == null) { - throw new ProcessingException("Web targets not initialized"); + throw new HubProcessingException("Web targets not initialized"); } int shadeId = getShadeId(); Shade shade = webTargets.refreshShade(shadeId); @@ -258,7 +257,7 @@ private void doRefreshShade() { } } } - } catch (ProcessingException | NumberFormatException e) { + } catch (HubProcessingException | NumberFormatException e) { logger.warn("Unexpected error: {}", e.getMessage()); } catch (HubMaintenanceException e) { // exceptions are logged in HDPowerViewWebTargets diff --git a/bundles/org.openhab.binding.hdpowerview/src/test/java/org/openhab/binding/hdpowerview/HDPowerViewJUnitTests.java b/bundles/org.openhab.binding.hdpowerview/src/test/java/org/openhab/binding/hdpowerview/HDPowerViewJUnitTests.java index cb0d41910896a..7f6891dd3d281 100644 --- a/bundles/org.openhab.binding.hdpowerview/src/test/java/org/openhab/binding/hdpowerview/HDPowerViewJUnitTests.java +++ b/bundles/org.openhab.binding.hdpowerview/src/test/java/org/openhab/binding/hdpowerview/HDPowerViewJUnitTests.java @@ -22,15 +22,13 @@ import java.util.List; import java.util.regex.Pattern; -import javax.ws.rs.ProcessingException; -import javax.ws.rs.client.Client; -import javax.ws.rs.client.ClientBuilder; - import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.Nullable; +import org.eclipse.jetty.client.HttpClient; import org.junit.jupiter.api.Test; import org.openhab.binding.hdpowerview.internal.HDPowerViewWebTargets; import org.openhab.binding.hdpowerview.internal.HubMaintenanceException; +import org.openhab.binding.hdpowerview.internal.HubProcessingException; import org.openhab.binding.hdpowerview.internal.api.CoordinateSystem; import org.openhab.binding.hdpowerview.internal.api.ShadePosition; import org.openhab.binding.hdpowerview.internal.api.responses.Scenes; @@ -76,7 +74,7 @@ private String loadJson(String fileName) { /** * Run a series of ONLINE tests on the communication with a hub - * + * * @param hubIPAddress must be a valid hub IP address to run the * tests on; or an INVALID IP address to * suppress the tests @@ -99,10 +97,18 @@ public void testOnlineCommunication() { boolean allowShadeMovementCommands = false; if (VALID_IP_V4_ADDRESS.matcher(hubIPAddress).matches()) { - // initialize stuff - Client client = ClientBuilder.newClient(); + // ==== initialize stuff ==== + HttpClient client = new HttpClient(); assertNotNull(client); - // client.register(new Logger()); + + // ==== start the client ==== + try { + client.start(); + assertTrue(client.isStarted()); + } catch (Exception e) { + fail(e.getMessage()); + } + HDPowerViewWebTargets webTargets = new HDPowerViewWebTargets(client, hubIPAddress); assertNotNull(webTargets); @@ -180,7 +186,7 @@ public void testOnlineCommunication() { String shadeName = shadexData.getName(); assertNotNull(shadeName); } - } catch (JsonParseException | ProcessingException | HubMaintenanceException e) { + } catch (JsonParseException | HubProcessingException | HubMaintenanceException e) { fail(e.getMessage()); } @@ -203,7 +209,7 @@ public void testOnlineCommunication() { String sceneName = scene.getName(); assertNotNull(sceneName); } - } catch (JsonParseException | ProcessingException | HubMaintenanceException e) { + } catch (JsonParseException | HubProcessingException | HubMaintenanceException e) { fail(e.getMessage()); } @@ -214,7 +220,7 @@ public void testOnlineCommunication() { assertNotEquals(0, shadeId); shade = webTargets.refreshShade(shadeId); assertNotNull(shade); - } catch (ProcessingException | HubMaintenanceException e) { + } catch (HubProcessingException | HubMaintenanceException e) { fail(e.getMessage()); } @@ -245,7 +251,7 @@ public void testOnlineCommunication() { if (allowShadeMovementCommands) { webTargets.moveShade(shadeId, newPos); } - } catch (ProcessingException | HubMaintenanceException e) { + } catch (HubProcessingException | HubMaintenanceException e) { fail(e.getMessage()); } @@ -254,7 +260,26 @@ public void testOnlineCommunication() { try { assertNotNull(sceneId); webTargets.activateScene(sceneId); - } catch (ProcessingException | HubMaintenanceException e) { + } catch (HubProcessingException | HubMaintenanceException e) { + fail(e.getMessage()); + } + } + + // ==== test stop command ==== + if (allowShadeMovementCommands) { + try { + assertNotNull(sceneId); + webTargets.stopShade(shadeId); + } catch (HubProcessingException | HubMaintenanceException e) { + fail(e.getMessage()); + } + } + + // ==== stop the client ==== + if (client.isRunning()) { + try { + client.stop(); + } catch (Exception e) { fail(e.getMessage()); } } diff --git a/bundles/org.openhab.binding.helios/src/main/java/org/openhab/binding/helios/internal/handler/HeliosHandler221.java b/bundles/org.openhab.binding.helios/src/main/java/org/openhab/binding/helios/internal/handler/HeliosHandler221.java index cca5cbf13353a..1b3d10b6cef67 100644 --- a/bundles/org.openhab.binding.helios/src/main/java/org/openhab/binding/helios/internal/handler/HeliosHandler221.java +++ b/bundles/org.openhab.binding.helios/src/main/java/org/openhab/binding/helios/internal/handler/HeliosHandler221.java @@ -130,7 +130,6 @@ public class HeliosHandler221 extends BaseThingHandler { private String ipAddress; // JSON variables - private JsonParser parser = new JsonParser(); private Gson gson = new Gson(); private ScheduledFuture logJob; @@ -203,7 +202,7 @@ public boolean verify(String hostname, javax.net.ssl.SSLSession sslSession) { return; } - JsonObject jsonObject = parser.parse(response.readEntity(String.class)).getAsJsonObject(); + JsonObject jsonObject = JsonParser.parseString(response.readEntity(String.class)).getAsJsonObject(); if (logger.isTraceEnabled()) { logger.trace("initialize() Request : {}", systemTarget.resolveTemplate("ip", ipAddress) @@ -303,7 +302,7 @@ private long subscribe() { } if (response != null) { - JsonObject jsonObject = parser.parse(response.readEntity(String.class)).getAsJsonObject(); + JsonObject jsonObject = JsonParser.parseString(response.readEntity(String.class)).getAsJsonObject(); if (logger.isTraceEnabled()) { logger.trace("subscribe() Request : {}", @@ -365,7 +364,7 @@ private void unsubscribe() { } if (response != null) { - JsonObject jsonObject = parser.parse(response.readEntity(String.class)).getAsJsonObject(); + JsonObject jsonObject = JsonParser.parseString(response.readEntity(String.class)).getAsJsonObject(); if (logger.isTraceEnabled()) { logger.trace("unsubscribe() Request : {}", @@ -426,7 +425,7 @@ private List pullLog(long logSubscriptionID) { } if (response != null) { - JsonObject jsonObject = parser.parse(response.readEntity(String.class)).getAsJsonObject(); + JsonObject jsonObject = JsonParser.parseString(response.readEntity(String.class)).getAsJsonObject(); if (logger.isTraceEnabled()) { logger.trace("pullLog() Request : {}", @@ -488,7 +487,7 @@ private List getSwitches() { } if (response != null) { - JsonObject jsonObject = parser.parse(response.readEntity(String.class)).getAsJsonObject(); + JsonObject jsonObject = JsonParser.parseString(response.readEntity(String.class)).getAsJsonObject(); if (logger.isTraceEnabled()) { logger.trace("getSwitches() Request : {}", switchTarget.resolveTemplate("ip", ipAddress) @@ -506,7 +505,7 @@ private List getSwitches() { getThing().getUID().toString()); String result = jsonObject.get("result").toString(); result = result.replace("switch", "id"); - JsonObject js = parser.parse(result).getAsJsonObject(); + JsonObject js = JsonParser.parseString(result).getAsJsonObject(); RESTSwitch[] switchArray = gson.fromJson(js.getAsJsonArray("ides"), RESTSwitch[].class); if (switchArray != null) { return Arrays.asList(switchArray); @@ -554,7 +553,7 @@ private void triggerSwitch(String id) { } if (response != null) { - JsonObject jsonObject = parser.parse(response.readEntity(String.class)).getAsJsonObject(); + JsonObject jsonObject = JsonParser.parseString(response.readEntity(String.class)).getAsJsonObject(); if (logger.isTraceEnabled()) { logger.trace("triggerSwitch() Request : {}", @@ -609,7 +608,7 @@ private void enableSwitch(String id, boolean flag) { } if (response != null) { - JsonObject jsonObject = parser.parse(response.readEntity(String.class)).getAsJsonObject(); + JsonObject jsonObject = JsonParser.parseString(response.readEntity(String.class)).getAsJsonObject(); if (logger.isTraceEnabled()) { logger.trace("enableSwitch() Request : {}", @@ -664,7 +663,7 @@ private List getPorts() { } if (response != null) { - JsonObject jsonObject = parser.parse(response.readEntity(String.class)).getAsJsonObject(); + JsonObject jsonObject = JsonParser.parseString(response.readEntity(String.class)).getAsJsonObject(); if (logger.isTraceEnabled()) { logger.trace("getPorts() Request : {}", portTarget.resolveTemplate("ip", ipAddress) @@ -732,7 +731,7 @@ private List getPorts() { } if (response != null) { - JsonObject jsonObject = parser.parse(response.readEntity(String.class)).getAsJsonObject(); + JsonObject jsonObject = JsonParser.parseString(response.readEntity(String.class)).getAsJsonObject(); if (logger.isTraceEnabled()) { logger.trace("configureRunnable Request : {}", systemTarget.resolveTemplate("ip", ipAddress) diff --git a/bundles/org.openhab.binding.heos/src/main/java/org/openhab/binding/heos/internal/api/HeosFacade.java b/bundles/org.openhab.binding.heos/src/main/java/org/openhab/binding/heos/internal/api/HeosFacade.java index 221783e907ccc..0aeecce3c3c89 100644 --- a/bundles/org.openhab.binding.heos/src/main/java/org/openhab/binding/heos/internal/api/HeosFacade.java +++ b/bundles/org.openhab.binding.heos/src/main/java/org/openhab/binding/heos/internal/api/HeosFacade.java @@ -23,7 +23,6 @@ import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.Nullable; -import org.jetbrains.annotations.NotNull; import org.openhab.binding.heos.internal.json.dto.HeosResponseObject; import org.openhab.binding.heos.internal.json.payload.BrowseResult; import org.openhab.binding.heos.internal.json.payload.Group; @@ -69,7 +68,6 @@ public List getPlaylists() throws IOException, ReadException { return getBrowseResults(PLAYLISTS_SID); } - @NotNull private List getBrowseResults(String sourceIdentifier) throws IOException, ReadException { HeosResponseObject response = browseSource(sourceIdentifier); logger.debug("Response: {}", response); @@ -353,7 +351,7 @@ public HeosResponseObject logIn(String name, String password) throws IOExc /** * Get all the players known by HEOS - * + * * @return */ public HeosResponseObject getPlayers() throws IOException, ReadException { @@ -362,7 +360,7 @@ public HeosResponseObject getPlayers() throws IOException, ReadExcepti /** * Get all the groups known by HEOS - * + * * @return */ public HeosResponseObject getGroups() throws IOException, ReadException { diff --git a/bundles/org.openhab.binding.heos/src/main/java/org/openhab/binding/heos/internal/handler/HeosThingBaseHandler.java b/bundles/org.openhab.binding.heos/src/main/java/org/openhab/binding/heos/internal/handler/HeosThingBaseHandler.java index ce625122fb269..afa7ea927e03b 100644 --- a/bundles/org.openhab.binding.heos/src/main/java/org/openhab/binding/heos/internal/handler/HeosThingBaseHandler.java +++ b/bundles/org.openhab.binding.heos/src/main/java/org/openhab/binding/heos/internal/handler/HeosThingBaseHandler.java @@ -14,9 +14,9 @@ import static org.openhab.binding.heos.internal.HeosBindingConstants.*; import static org.openhab.binding.heos.internal.handler.FutureUtil.cancel; -import static org.openhab.binding.heos.internal.json.dto.HeosCommandGroup.GROUP; -import static org.openhab.binding.heos.internal.json.dto.HeosCommandGroup.PLAYER; +import static org.openhab.binding.heos.internal.json.dto.HeosCommandGroup.*; import static org.openhab.binding.heos.internal.json.dto.HeosCommunicationAttribute.*; +import static org.openhab.binding.heos.internal.resources.HeosConstants.*; import static org.openhab.core.thing.ThingStatus.*; import java.io.IOException; @@ -30,7 +30,6 @@ import javax.measure.quantity.Time; -import org.apache.commons.lang.StringUtils; import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.Nullable; import org.openhab.binding.heos.internal.HeosChannelHandlerFactory; @@ -38,15 +37,31 @@ import org.openhab.binding.heos.internal.exception.HeosFunctionalException; import org.openhab.binding.heos.internal.exception.HeosNotConnectedException; import org.openhab.binding.heos.internal.exception.HeosNotFoundException; -import org.openhab.binding.heos.internal.json.dto.*; +import org.openhab.binding.heos.internal.json.dto.HeosCommandTuple; +import org.openhab.binding.heos.internal.json.dto.HeosCommunicationAttribute; +import org.openhab.binding.heos.internal.json.dto.HeosError; +import org.openhab.binding.heos.internal.json.dto.HeosEvent; +import org.openhab.binding.heos.internal.json.dto.HeosEventObject; +import org.openhab.binding.heos.internal.json.dto.HeosObject; +import org.openhab.binding.heos.internal.json.dto.HeosResponseObject; import org.openhab.binding.heos.internal.json.payload.Media; import org.openhab.binding.heos.internal.json.payload.Player; import org.openhab.binding.heos.internal.resources.HeosEventListener; import org.openhab.binding.heos.internal.resources.Telnet.ReadException; import org.openhab.core.io.net.http.HttpUtil; -import org.openhab.core.library.types.*; +import org.openhab.core.library.types.OnOffType; +import org.openhab.core.library.types.PercentType; +import org.openhab.core.library.types.PlayPauseType; +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.*; +import org.openhab.core.thing.Bridge; +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.ThingStatusInfo; import org.openhab.core.thing.binding.BaseThingHandler; import org.openhab.core.types.UnDefType; import org.slf4j.Logger; @@ -474,16 +489,17 @@ protected void handleThingMediaUpdate(Media info) { } private void handleImageUrl(Media info) { - if (StringUtils.isNotBlank(info.imageUrl)) { + String imageUrl = info.imageUrl; + if (imageUrl != null && !imageUrl.isBlank()) { try { - URL url = new URL(info.imageUrl); // checks if String is proper URL + URL url = new URL(imageUrl); // checks if String is proper URL RawType cover = HttpUtil.downloadImage(url.toString()); if (cover != null) { updateState(CH_ID_COVER, cover); return; } } catch (MalformedURLException e) { - logger.debug("Cover can't be loaded. No proper URL: {}", info.imageUrl, e); + logger.debug("Cover can't be loaded. No proper URL: {}", imageUrl, e); } } updateState(CH_ID_COVER, UnDefType.NULL); diff --git a/bundles/org.openhab.binding.homematic/README.md b/bundles/org.openhab.binding.homematic/README.md index 7eaf099bc175b..d0724f662fe21 100644 --- a/bundles/org.openhab.binding.homematic/README.md +++ b/bundles/org.openhab.binding.homematic/README.md @@ -289,10 +289,12 @@ Dimmer Light "Light [%d %%]" { channel="homematic:HM-LC-Dim1T-Pl-2:cc The GATEWAY-EXTRAS is a virtual device which contains a switch to reload all values from all devices and also a switch to put the gateway in the install mode to add new devices. If the gateway supports variables and scripts, you can handle them with this device too. + The type is generated: `GATEWAY-EXTRAS-[BRIDGE_ID]`. +Example: bridgeId=**ccu** -> type=GATEWAY-EXTRAS-**CCU** -**Example:** bridgeId=ccu, type=GATEWAY-EXTRAS-CCU -Address: fixed GWE00000000 +The address of the virtual device must be the default value `GWE00000000`. +Usage of a custom ID is not supported. ### RELOAD_ALL_FROM_GATEWAY diff --git a/bundles/org.openhab.binding.homematic/src/main/java/org/openhab/binding/homematic/internal/communicator/parser/CcuVariablesAndScriptsParser.java b/bundles/org.openhab.binding.homematic/src/main/java/org/openhab/binding/homematic/internal/communicator/parser/CcuVariablesAndScriptsParser.java index 17ff956ede68f..27d5dc559494d 100644 --- a/bundles/org.openhab.binding.homematic/src/main/java/org/openhab/binding/homematic/internal/communicator/parser/CcuVariablesAndScriptsParser.java +++ b/bundles/org.openhab.binding.homematic/src/main/java/org/openhab/binding/homematic/internal/communicator/parser/CcuVariablesAndScriptsParser.java @@ -50,7 +50,7 @@ public Void parse(TclScriptDataList resultList) throws IOException { if (dp.isIntegerType()) { dp.setMinValue(toInteger(entry.minValue)); dp.setMaxValue(toInteger(entry.maxValue)); - } else { + } else if (dp.isFloatType()) { dp.setMinValue(toDouble(entry.minValue)); dp.setMaxValue(toDouble(entry.maxValue)); } diff --git a/bundles/org.openhab.binding.http/src/main/java/org/openhab/binding/http/internal/HttpHandlerFactory.java b/bundles/org.openhab.binding.http/src/main/java/org/openhab/binding/http/internal/HttpHandlerFactory.java index 3318fb9d4385c..064984de0b398 100644 --- a/bundles/org.openhab.binding.http/src/main/java/org/openhab/binding/http/internal/HttpHandlerFactory.java +++ b/bundles/org.openhab.binding.http/src/main/java/org/openhab/binding/http/internal/HttpHandlerFactory.java @@ -12,7 +12,7 @@ */ package org.openhab.binding.http.internal; -import static org.openhab.binding.http.internal.HttpBindingConstants.*; +import static org.openhab.binding.http.internal.HttpBindingConstants.THING_TYPE_URL; import java.util.Set; @@ -59,8 +59,8 @@ public class HttpHandlerFactory extends BaseThingHandlerFactory @Activate public HttpHandlerFactory(@Reference HttpClientFactory httpClientFactory, @Reference HttpDynamicStateDescriptionProvider httpDynamicStateDescriptionProvider) { - this.secureClient = new HttpClient(new SslContextFactory()); - this.insecureClient = new HttpClient(new SslContextFactory(true)); + this.secureClient = new HttpClient(new SslContextFactory.Client()); + this.insecureClient = new HttpClient(new SslContextFactory.Client(true)); try { this.secureClient.start(); this.insecureClient.start(); diff --git a/bundles/org.openhab.binding.http/src/main/java/org/openhab/binding/http/internal/converter/PlayerItemConverter.java b/bundles/org.openhab.binding.http/src/main/java/org/openhab/binding/http/internal/converter/PlayerItemConverter.java index eb2ad57b3bc8d..4a358b04abea6 100644 --- a/bundles/org.openhab.binding.http/src/main/java/org/openhab/binding/http/internal/converter/PlayerItemConverter.java +++ b/bundles/org.openhab.binding.http/src/main/java/org/openhab/binding/http/internal/converter/PlayerItemConverter.java @@ -81,6 +81,16 @@ public String toString(Command command) { @Override public State toState(String string) { + if (string.equals(channelConfig.playValue)) { + return PlayPauseType.PLAY; + } else if (string.equals(channelConfig.pauseValue)) { + return PlayPauseType.PAUSE; + } else if (string.equals(channelConfig.rewindValue)) { + return RewindFastforwardType.REWIND; + } else if (string.equals(channelConfig.fastforwardValue)) { + return RewindFastforwardType.FASTFORWARD; + } + return UnDefType.UNDEF; } } diff --git a/bundles/org.openhab.binding.http/src/main/resources/OH-INF/config/config.xml b/bundles/org.openhab.binding.http/src/main/resources/OH-INF/config/config.xml index 456cebfd7721a..6a79246e350fc 100644 --- a/bundles/org.openhab.binding.http/src/main/resources/OH-INF/config/config.xml +++ b/bundles/org.openhab.binding.http/src/main/resources/OH-INF/config/config.xml @@ -7,11 +7,13 @@ - Transformation pattern used when receiving values. + Transformation pattern used when receiving values. Chain multiple transformations with the mathematical + intersection character "∩". - Transformation pattern used when sending values. + Transformation pattern used when sending values. Chain multiple transformations with the mathematical + intersection character "∩". @@ -44,11 +46,13 @@ - Transformation pattern used when receiving values. + Transformation pattern used when receiving values. Chain multiple transformations with the mathematical + intersection character "∩". - Transformation pattern used when sending values. + Transformation pattern used when sending values. Chain multiple transformations with the mathematical + intersection character "∩". @@ -113,11 +117,13 @@ - Transformation pattern used when receiving values. + Transformation pattern used when receiving values. Chain multiple transformations with the mathematical + intersection character "∩". - Transformation pattern used when sending values. + Transformation pattern used when sending values. Chain multiple transformations with the mathematical + intersection character "∩". @@ -158,11 +164,13 @@ - Transformation pattern used when receiving values. + Transformation pattern used when receiving values. Chain multiple transformations with the mathematical + intersection character "∩". - Transformation pattern used when sending values. + Transformation pattern used when sending values. Chain multiple transformations with the mathematical + intersection character "∩". @@ -230,11 +238,13 @@ - Transformation pattern used when receiving values. + Transformation pattern used when receiving values. Chain multiple transformations with the mathematical + intersection character "∩". - Transformation pattern used when sending values. + Transformation pattern used when sending values. Chain multiple transformations with the mathematical + intersection character "∩". @@ -272,11 +282,13 @@ - Transformation pattern used when receiving values. + Transformation pattern used when receiving values. Chain multiple transformations with the mathematical + intersection character "∩". - Transformation pattern used when sending values. + Transformation pattern used when sending values. Chain multiple transformations with the mathematical + intersection character "∩". @@ -333,11 +345,13 @@ - Transformation pattern used when receiving values. + Transformation pattern used when receiving values. Chain multiple transformations with the mathematical + intersection character "∩". - Transformation pattern used when sending values. + Transformation pattern used when sending values Chain multiple transformations with the mathematical + intersection character "∩".. @@ -386,11 +400,13 @@ - Transformation pattern used when receiving values. + Transformation pattern used when receiving values. Chain multiple transformations with the mathematical + intersection character "∩". - Transformation pattern used when sending values. + Transformation pattern used when sending values. Chain multiple transformations with the mathematical + intersection character "∩". diff --git a/bundles/org.openhab.binding.hue/src/main/java/org/openhab/binding/hue/internal/HueBridge.java b/bundles/org.openhab.binding.hue/src/main/java/org/openhab/binding/hue/internal/HueBridge.java index f914e758d04ce..14b25f8cf17f0 100644 --- a/bundles/org.openhab.binding.hue/src/main/java/org/openhab/binding/hue/internal/HueBridge.java +++ b/bundles/org.openhab.binding.hue/src/main/java/org/openhab/binding/hue/internal/HueBridge.java @@ -844,7 +844,7 @@ protected Result doNetwork(String address, String requestMethod, @Nullable Strin return super.doNetwork(address, requestMethod, body); } else { String extractedAddress = Util.quickMatch("^http://[^/]+(.+)$", address); - JsonElement commandBody = new JsonParser().parse(body); + JsonElement commandBody = body == null ? null : JsonParser.parseString(body); scheduleCommand = new ScheduleCommand(extractedAddress, requestMethod, commandBody); // Return a fake result that will cause an exception and the callback to end diff --git a/bundles/org.openhab.binding.hue/src/main/java/org/openhab/binding/hue/internal/discovery/HueBridgeDiscoveryParticipant.java b/bundles/org.openhab.binding.hue/src/main/java/org/openhab/binding/hue/internal/discovery/HueBridgeDiscoveryParticipant.java index d5bb4ce498354..7018c62917b3a 100644 --- a/bundles/org.openhab.binding.hue/src/main/java/org/openhab/binding/hue/internal/discovery/HueBridgeDiscoveryParticipant.java +++ b/bundles/org.openhab.binding.hue/src/main/java/org/openhab/binding/hue/internal/discovery/HueBridgeDiscoveryParticipant.java @@ -119,9 +119,11 @@ public long getRemovalGracePeriodSeconds(RemoteDevice device) { try { Configuration conf = configAdmin.getConfiguration("binding.hue"); Dictionary properties = conf.getProperties(); - Object property = properties.get(HueBindingConstants.REMOVAL_GRACE_PERIOD); - if (property != null) { - removalGracePeriodSeconds = Long.parseLong(property.toString()); + if (properties != null) { + Object property = properties.get(HueBindingConstants.REMOVAL_GRACE_PERIOD); + if (property != null) { + removalGracePeriodSeconds = Long.parseLong(property.toString()); + } } } catch (IOException | IllegalStateException | NumberFormatException e) { // fall through to pre-initialised (default) value diff --git a/bundles/org.openhab.binding.hue/src/test/java/org/openhab/binding/hue/internal/handler/HueLightHandlerTest.java b/bundles/org.openhab.binding.hue/src/test/java/org/openhab/binding/hue/internal/handler/HueLightHandlerTest.java index ea16da229cff9..58adabcb26909 100644 --- a/bundles/org.openhab.binding.hue/src/test/java/org/openhab/binding/hue/internal/handler/HueLightHandlerTest.java +++ b/bundles/org.openhab.binding.hue/src/test/java/org/openhab/binding/hue/internal/handler/HueLightHandlerTest.java @@ -425,9 +425,8 @@ protected Bridge getBridge() { } private void assertJson(String expected, String actual) { - JsonParser parser = new JsonParser(); - JsonElement jsonExpected = parser.parse(expected); - JsonElement jsonActual = parser.parse(actual); + JsonElement jsonExpected = JsonParser.parseString(expected); + JsonElement jsonActual = JsonParser.parseString(actual); assertEquals(jsonExpected, jsonActual); } } diff --git a/bundles/org.openhab.binding.hydrawise/src/main/java/org/openhab/binding/hydrawise/internal/HydrawiseHandler.java b/bundles/org.openhab.binding.hydrawise/src/main/java/org/openhab/binding/hydrawise/internal/HydrawiseHandler.java index ea64e615fb76e..932d888461519 100644 --- a/bundles/org.openhab.binding.hydrawise/src/main/java/org/openhab/binding/hydrawise/internal/HydrawiseHandler.java +++ b/bundles/org.openhab.binding.hydrawise/src/main/java/org/openhab/binding/hydrawise/internal/HydrawiseHandler.java @@ -16,11 +16,14 @@ import java.time.ZonedDateTime; import java.time.temporal.ChronoUnit; -import java.util.*; +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; +import java.util.Objects; +import java.util.Optional; import java.util.concurrent.ScheduledFuture; import java.util.concurrent.TimeUnit; -import org.apache.commons.lang.StringUtils; import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.Nullable; import org.openhab.binding.hydrawise.internal.api.HydrawiseAuthenticationException; @@ -210,8 +213,9 @@ protected void updateZones(LocalScheduleResponse status) { updateGroupState(group, CHANNEL_ZONE_TYPE, new DecimalType(r.type)); updateGroupState(group, CHANNEL_ZONE_TIME, r.runTimeSeconds != null ? new DecimalType(r.runTimeSeconds) : UnDefType.UNDEF); - if (StringUtils.isNotBlank(r.icon)) { - updateGroupState(group, CHANNEL_ZONE_ICON, new StringType(BASE_IMAGE_URL + r.icon)); + String icon = r.icon; + if (icon != null && !icon.isBlank()) { + updateGroupState(group, CHANNEL_ZONE_ICON, new StringType(BASE_IMAGE_URL + icon)); } if (r.time >= MAX_RUN_TIME) { updateGroupState(group, CHANNEL_ZONE_NEXT_RUN_TIME_TIME, UnDefType.UNDEF); diff --git a/bundles/org.openhab.binding.iammeter/src/main/java/org/openhab/binding/iammeter/internal/Iammeter3080THandler.java b/bundles/org.openhab.binding.iammeter/src/main/java/org/openhab/binding/iammeter/internal/Iammeter3080THandler.java index 4a2f380b0788e..3c8787453c16d 100644 --- a/bundles/org.openhab.binding.iammeter/src/main/java/org/openhab/binding/iammeter/internal/Iammeter3080THandler.java +++ b/bundles/org.openhab.binding.iammeter/src/main/java/org/openhab/binding/iammeter/internal/Iammeter3080THandler.java @@ -41,7 +41,7 @@ public Iammeter3080THandler(Thing thing) { @SuppressWarnings("null") @Override protected void resolveData(String response) { - JsonElement iammeterDataElement = new JsonParser().parse(response); + JsonElement iammeterDataElement = JsonParser.parseString(response); JsonObject iammeterData = iammeterDataElement.getAsJsonObject(); String keyWord = "Datas"; if (iammeterData.has("Datas") && iammeterData.has("SN")) { diff --git a/bundles/org.openhab.binding.iammeter/src/main/java/org/openhab/binding/iammeter/internal/IammeterHandler.java b/bundles/org.openhab.binding.iammeter/src/main/java/org/openhab/binding/iammeter/internal/IammeterHandler.java index c26360cdc6780..305a4862c3182 100644 --- a/bundles/org.openhab.binding.iammeter/src/main/java/org/openhab/binding/iammeter/internal/IammeterHandler.java +++ b/bundles/org.openhab.binding.iammeter/src/main/java/org/openhab/binding/iammeter/internal/IammeterHandler.java @@ -37,7 +37,7 @@ public IammeterHandler(Thing thing) { @Override protected void resolveData(String response) { - JsonElement iammeterDataElement = new JsonParser().parse(response); + JsonElement iammeterDataElement = JsonParser.parseString(response); JsonObject iammeterData = iammeterDataElement.getAsJsonObject(); String keyWord = "Data"; if (iammeterData.has("data") || (iammeterData.has("Data") && iammeterData.has("SN"))) { diff --git a/bundles/org.openhab.binding.iaqualink/src/main/java/org/openhab/binding/iaqualink/internal/handler/IAqualinkHandler.java b/bundles/org.openhab.binding.iaqualink/src/main/java/org/openhab/binding/iaqualink/internal/handler/IAqualinkHandler.java index 50ed769d62beb..c928bd6d04606 100644 --- a/bundles/org.openhab.binding.iaqualink/src/main/java/org/openhab/binding/iaqualink/internal/handler/IAqualinkHandler.java +++ b/bundles/org.openhab.binding.iaqualink/src/main/java/org/openhab/binding/iaqualink/internal/handler/IAqualinkHandler.java @@ -32,7 +32,6 @@ import javax.measure.Unit; import javax.measure.quantity.Temperature; -import org.apache.commons.lang.StringUtils; import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.Nullable; import org.eclipse.jetty.client.HttpClient; @@ -268,7 +267,7 @@ private void configure() { String confSerialId = configuration.serialId; String confApiKey = configuration.apiKey; - if (StringUtils.isNotBlank(confApiKey)) { + if (confApiKey != null && !confApiKey.isBlank()) { this.apiKey = confApiKey; } else { this.apiKey = DEFAULT_API_KEY; @@ -291,7 +290,7 @@ private void configure() { return; } - if (StringUtils.isNotBlank(confSerialId)) { + if (confSerialId != null && !confSerialId.isBlank()) { serialNumber = confSerialId.replaceAll("[^a-zA-Z0-9]", "").toLowerCase(); if (!Arrays.stream(devices).anyMatch(device -> device.getSerialNumber().equals(serialNumber))) { updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, @@ -438,8 +437,7 @@ private void updatedState(String name, @Nullable String value) { */ private State toState(String name, @Nullable String type, @Nullable String value) { try { - // @nullable checker does not recognize isBlank as checking null here, so must use == null to make happy - if (value == null || StringUtils.isBlank(value)) { + if (value == null || value.isBlank()) { return UnDefType.UNDEF; } diff --git a/bundles/org.openhab.binding.icalendar/README.md b/bundles/org.openhab.binding.icalendar/README.md index 66fc2b8303d24..7a6c3db6898b9 100644 --- a/bundles/org.openhab.binding.icalendar/README.md +++ b/bundles/org.openhab.binding.icalendar/README.md @@ -31,17 +31,17 @@ Each `calendar` thing requires the following configuration parameters: Each `eventfilter` thing requires a bridge of type `calendar` and has following configuration options: -| parameter name | description | optional | -|------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|--------------------------------------------| -| `maxEvents` | The count of expected results. | mandatory | -| `refreshTime` | The frequency in minutes the channels get refreshed. | mandatory (default available) | -| `datetimeUnit` | A unit for time settings in this filter. Valid values: `MINUTE`, `HOUR`, `DAY` and `WEEK`. | optional (required for time-based filtering) | -| `datetimeStart` | The start of the time frame where to search for events relative to current time. Combined with `datetimeUnit`. | optional | -| `datetimeEnd` | The end of the time frame where to search for events relative to current time. Combined with `datetimeUnit`. The value must be greater than `datetimeStart` to get results. | optional | -| `datetimeRound` | Whether to round the datetimes of start and end down to the earlier time unit. Example if set: current time is 13:00, timeunit is set to `DAY`. Resulting search will start and end at 0:00. | optional | -| `textEventField` | A field to filter the events text-based. Valid values: `SUMMARY`, `DESCRIPTION`, `COMMENT`, `CONTACT` and `LOCATION` (as described in RFC 5545). | optional/required for text-based filtering | -| `textEventValue` | The text to filter events with. | optional | -| `textValueType` | The type of the text to filter with. Valid values: `TEXT` (field must contain value), `REGEX` (field must match value, completely, dot matches all, case insensetive). | optional/required for text-based filtering | +| parameter name | description | optional | +|------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|--------------------------------------------| +| `maxEvents` | The count of expected results. | mandatory | +| `refreshTime` | The frequency in minutes the channels get refreshed. | mandatory (default available) | +| `datetimeUnit` | A unit for time settings in this filter. Valid values: `MINUTE`, `HOUR`, `DAY` and `WEEK`. | optional (required for time-based filtering) | +| `datetimeStart` | The start of the time frame where to search for events relative to current time. Combined with `datetimeUnit`. | optional | +| `datetimeEnd` | The end of the time frame where to search for events relative to current time. Combined with `datetimeUnit`. The value must be greater than `datetimeStart` to get results. | optional | +| `datetimeRound` | Whether to round the datetimes of start and end down to the earlier time unit. Example if set: current time is 13:00, timeunit is set to `DAY`. Resulting search will start and end at 0:00. | optional | +| `textEventField` | A field to filter the events text-based. Valid values: `SUMMARY`, `DESCRIPTION`, `COMMENT`, `CONTACT` and `LOCATION` (as described in RFC 5545). | optional/required for text-based filtering | +| `textEventValue` | The text to filter events with. | optional | +| `textValueType` | The type of the text to filter with. Valid values: `TEXT` (field must contain value, case insensitive), `REGEX` (field must match value, completely, dot matches all, usually case sensitive). | optional/required for text-based filtering | ## Channels @@ -167,11 +167,6 @@ BEGIN:Calendar_Test_Switch:ON END:Calendar_Test_Switch:OFF ``` -### Notes for Nextcloud - -The `url` should be: `https:///remote.php/dav/calendars//?export`, so the `?export` is important to get an `ical` file from the calendar. -Username and password for the nextcloud account have to be set as well. - ## Breaking changes In OH3 `calendar` was changed from Thing to Bridge. You need to recreate calendars (or replace `Thing` by `Bridge` in your `.things` file). diff --git a/bundles/org.openhab.binding.icalendar/pom.xml b/bundles/org.openhab.binding.icalendar/pom.xml index 0dd53f8983d4c..610195fd2cc59 100644 --- a/bundles/org.openhab.binding.icalendar/pom.xml +++ b/bundles/org.openhab.binding.icalendar/pom.xml @@ -11,7 +11,6 @@ openHAB Add-ons :: Bundles :: iCalendar Binding jackson-core,jackson-annotations,jackson-databind - 2.10.3 @@ -20,6 +19,12 @@ biweekly 0.6.4 compile + + + com.fasterxml.jackson.core + * + + diff --git a/bundles/org.openhab.binding.icloud/src/main/resources/OH-INF/i18n/iCloud_de.properties b/bundles/org.openhab.binding.icloud/src/main/resources/OH-INF/i18n/iCloud_de.properties new file mode 100644 index 0000000000000..2b6beab311a3f --- /dev/null +++ b/bundles/org.openhab.binding.icloud/src/main/resources/OH-INF/i18n/iCloud_de.properties @@ -0,0 +1,34 @@ +# Binding +icloud.binding.name=iCloud Binding +icloud.binding.description=Die Apple iCloud wird genutzt, um Daten wie den Ladezustand oder den Standort von einem oder mehreren Apple Geräten zu erhalten, die mit einem iCloud Account verknüpft sind. + +# Account Thing +icloud.account-thing.label=iCloud Account +icloud.account-thing.description=Der iCloud Account (Bridge) repräsentiert einen iCloud Account. Du benötigst mehrere iCloud Account Bridges, um mehrere iCloud Accounts zu verwalten. + +icloud.account-thing.parameter.apple-id.label=Apple-ID +icloud.account-thing.parameter.apple-id.description=Apple-ID (E-Mail Adresse), um Zugriff zur iCloud zu erhalten. +icloud.account-thing.parameter.password.label=Passwort +icloud.account-thing.parameter.password.description=Passwort der Apple-ID, um Zugriff zur iCloud zu erhalten. +icloud.account-thing.parameter.refresh.label=Aktualisierungszeit in Minuten +icloud.account-thing.parameter.refresh.description=Zeit in der die iCloud Informationen aktualisiert werden sollen. + +icloud.account-thing.property.owner=Besitzer + +# Device Thing +icloud.device-thing.label=iCloud Gerät +icloud.device-thing.description=Das iCloud Gerät (Thing) repräsentiert ein mit der iCloud verknüpftes Apple Gerät, wie zum Beispiel ein iPhone. Es muss mit einem iCloud Account (Bridge) verknüpft werden, um Aktualisierungen zu erhalten. Mehrere iCloud Geräte können mit einem iCloud Account (Bridge) verknüpft werden. + +icloud.device-thing.parameter.id.label=Geräte-ID + +icloud.device-thing.channel.battery-status.label=Ladezustand +icloud.device-thing.channel.battery-status.state.not-charging=Lädt nicht +icloud.device-thing.channel.battery-status.state.charged=Aufgeladen +icloud.device-thing.channel.battery-status.state.charging=Lädt +icloud.device-thing.channel.battery-status.state.unknown=Unbekannt +icloud.device-thing.channel.find-my-phone.label=Wo ist? +icloud.device-thing.channel.location.label=Standort +icloud.device-thing.channel.location-accuracy=Standort Genauigkeit +icloud.device-thing.channel.location-last-update=Letztes Standort Update + +icloud.device-thing.property.device-name=Gerätename diff --git a/bundles/org.openhab.binding.ihc/src/main/java/org/openhab/binding/ihc/internal/ChannelUtils.java b/bundles/org.openhab.binding.ihc/src/main/java/org/openhab/binding/ihc/internal/ChannelUtils.java index 0ca762a59df10..2bb837d5fc58d 100644 --- a/bundles/org.openhab.binding.ihc/src/main/java/org/openhab/binding/ihc/internal/ChannelUtils.java +++ b/bundles/org.openhab.binding.ihc/src/main/java/org/openhab/binding/ihc/internal/ChannelUtils.java @@ -19,7 +19,6 @@ import java.util.Set; import java.util.function.Predicate; -import org.apache.commons.lang.StringUtils; import org.openhab.binding.ihc.internal.config.ChannelParams; import org.openhab.binding.ihc.internal.ws.exeptions.ConversionException; import org.openhab.core.config.core.Configuration; @@ -203,7 +202,7 @@ private static void addChannelsFromProjectFile(Thing thing, NodeList nodes, Stri ChannelUID channelUID = new ChannelUID(thing.getUID(), group + resourceId); ChannelTypeUID type = new ChannelTypeUID(BINDING_ID, channelType); Configuration configuration = new Configuration(); - configuration.put(PARAM_RESOURCE_ID, new Integer(resourceId)); + configuration.put(PARAM_RESOURCE_ID, Integer.valueOf(resourceId)); Channel channel = ChannelBuilder.create(channelUID, acceptedItemType).withConfiguration(configuration) .withLabel(description).withType(type).build(); @@ -214,16 +213,16 @@ private static void addChannelsFromProjectFile(Thing thing, NodeList nodes, Stri private static String createDescription(String name1, String name2, String name3, String name4) { String description = ""; - if (StringUtils.isNotEmpty(name1)) { + if (name1 != null && !name1.isEmpty()) { description = name1; } - if (StringUtils.isNotEmpty(name2)) { + if (name2 != null && !name2.isEmpty()) { description += String.format(" - %s", name2); } - if (StringUtils.isNotEmpty(name3)) { + if (name3 != null && !name3.isEmpty()) { description += String.format(" - %s", name3); } - if (StringUtils.isNotEmpty(name4)) { + if (name4 != null && !name4.isEmpty()) { description += String.format(" - %s", name4); } return description; diff --git a/bundles/org.openhab.binding.ihc/src/main/java/org/openhab/binding/ihc/internal/ws/services/IhcResourceInteractionService.java b/bundles/org.openhab.binding.ihc/src/main/java/org/openhab/binding/ihc/internal/ws/services/IhcResourceInteractionService.java index 8f7b7a8ebd41d..93439b0aa6ac1 100644 --- a/bundles/org.openhab.binding.ihc/src/main/java/org/openhab/binding/ihc/internal/ws/services/IhcResourceInteractionService.java +++ b/bundles/org.openhab.binding.ihc/src/main/java/org/openhab/binding/ihc/internal/ws/services/IhcResourceInteractionService.java @@ -20,7 +20,6 @@ import javax.xml.xpath.XPathExpressionException; -import org.apache.commons.lang.StringUtils; import org.openhab.binding.ihc.internal.ws.datatypes.XPathUtils; import org.openhab.binding.ihc.internal.ws.exeptions.IhcExecption; import org.openhab.binding.ihc.internal.ws.http.IhcConnectionPool; @@ -92,12 +91,12 @@ private WSResourceValue parseResourceValue(Node n) throws XPathExpressionExcepti // parse resource id String resourceId = XPathUtils.getSpeficValueFromNode(n, "ns1:resourceID"); - if (StringUtils.isNotBlank(resourceId)) { + if (resourceId != null && !resourceId.isBlank()) { int id = Integer.parseInt(resourceId); // Parse floating point value String floatingPointValue = getValue(n, "floatingPointValue"); - if (StringUtils.isNotBlank(floatingPointValue)) { + if (floatingPointValue != null && !floatingPointValue.isBlank()) { String min = getValue(n, "minimumValue"); String max = getValue(n, "maximumValue"); return new WSFloatingPointValue(id, Double.valueOf(floatingPointValue), Double.valueOf(min), @@ -106,13 +105,13 @@ private WSResourceValue parseResourceValue(Node n) throws XPathExpressionExcepti // Parse boolean value String value = getValue(n, "value"); - if (StringUtils.isNotBlank(value)) { + if (value != null && !value.isBlank()) { return new WSBooleanValue(id, Boolean.valueOf(value)); } // Parse integer value String integer = getValue(n, "integer"); - if (StringUtils.isNotBlank(integer)) { + if (integer != null && !integer.isBlank()) { String min = getValue(n, "minimumValue"); String max = getValue(n, "maximumValue"); return new WSIntegerValue(id, Integer.valueOf(integer), Integer.valueOf(min), Integer.valueOf(max)); @@ -120,13 +119,13 @@ private WSResourceValue parseResourceValue(Node n) throws XPathExpressionExcepti // Parse timer value String milliseconds = getValue(n, "milliseconds"); - if (StringUtils.isNotBlank(milliseconds)) { + if (milliseconds != null && !milliseconds.isBlank()) { return new WSTimerValue(id, Integer.valueOf(milliseconds)); } // Parse time value String hours = getValue(n, "hours"); - if (StringUtils.isNotBlank(hours)) { + if (hours != null && !hours.isBlank()) { String minutes = getValue(n, "minutes"); String seconds = getValue(n, "seconds"); return new WSTimeValue(id, Integer.valueOf(hours), Integer.valueOf(minutes), Integer.valueOf(seconds)); @@ -134,7 +133,7 @@ private WSResourceValue parseResourceValue(Node n) throws XPathExpressionExcepti // Parse date value String year = getValue(n, "year"); - if (StringUtils.isNotBlank(year)) { + if (year != null && !year.isBlank()) { String month = getValue(n, "month"); String day = getValue(n, "day"); return new WSDateValue(id, Short.valueOf(year), Byte.valueOf(month), Byte.valueOf(day)); @@ -142,7 +141,7 @@ private WSResourceValue parseResourceValue(Node n) throws XPathExpressionExcepti // Parse enum value String definitionTypeID = getValue(n, "definitionTypeID"); - if (StringUtils.isNotBlank(definitionTypeID)) { + if (definitionTypeID != null && !definitionTypeID.isBlank()) { String enumValueID = getValue(n, "enumValueID"); String enumName = getValue(n, "enumName"); return new WSEnumValue(id, Integer.valueOf(definitionTypeID), Integer.valueOf(enumValueID), enumName); @@ -150,7 +149,7 @@ private WSResourceValue parseResourceValue(Node n) throws XPathExpressionExcepti // Parse week day value value = getValue(n, "weekdayNumber"); - if (StringUtils.isNotBlank(value)) { + if (value != null && !value.isBlank()) { return new WSWeekdayValue(id, Integer.valueOf(value)); } diff --git a/bundles/org.openhab.binding.innogysmarthome/src/main/java/org/openhab/binding/innogysmarthome/internal/InnogyWebSocket.java b/bundles/org.openhab.binding.innogysmarthome/src/main/java/org/openhab/binding/innogysmarthome/internal/InnogyWebSocket.java index 5b0a5caa11a5c..61aa8d650d743 100644 --- a/bundles/org.openhab.binding.innogysmarthome/src/main/java/org/openhab/binding/innogysmarthome/internal/InnogyWebSocket.java +++ b/bundles/org.openhab.binding.innogysmarthome/src/main/java/org/openhab/binding/innogysmarthome/internal/InnogyWebSocket.java @@ -149,7 +149,7 @@ public void onMessage(String msg) { } WebSocketClient startWebSocketClient() throws Exception { - WebSocketClient client = new WebSocketClient(new SslContextFactory()); + WebSocketClient client = new WebSocketClient(new SslContextFactory.Client()); client.setMaxIdleTimeout(this.maxIdleTimeout); client.start(); return client; diff --git a/bundles/org.openhab.binding.innogysmarthome/src/main/java/org/openhab/binding/innogysmarthome/internal/client/InnogyClient.java b/bundles/org.openhab.binding.innogysmarthome/src/main/java/org/openhab/binding/innogysmarthome/internal/client/InnogyClient.java index 3a841962a3270..484d99add167f 100644 --- a/bundles/org.openhab.binding.innogysmarthome/src/main/java/org/openhab/binding/innogysmarthome/internal/client/InnogyClient.java +++ b/bundles/org.openhab.binding.innogysmarthome/src/main/java/org/openhab/binding/innogysmarthome/internal/client/InnogyClient.java @@ -16,13 +16,14 @@ import java.io.IOException; import java.net.URI; -import java.util.*; +import java.util.Arrays; +import java.util.Collection; +import java.util.List; import java.util.concurrent.ExecutionException; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; import java.util.stream.Collectors; -import org.apache.commons.lang.StringUtils; import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.Nullable; import org.eclipse.jetty.client.HttpClient; @@ -180,7 +181,8 @@ public AccessTokenResponse getAccessTokenResponse() throws AuthenticationExcepti } catch (OAuthException | OAuthResponseException e) { throw new AuthenticationException("Error fetching access token: " + e.getMessage()); } - if (accessTokenResponse == null || StringUtils.isBlank(accessTokenResponse.getAccessToken())) { + if (accessTokenResponse == null || accessTokenResponse.getAccessToken() == null + || accessTokenResponse.getAccessToken().isBlank()) { throw new AuthenticationException("No innogy accesstoken. Is this thing authorized?"); } return accessTokenResponse; diff --git a/bundles/org.openhab.binding.innogysmarthome/src/main/java/org/openhab/binding/innogysmarthome/internal/handler/InnogyBridgeHandler.java b/bundles/org.openhab.binding.innogysmarthome/src/main/java/org/openhab/binding/innogysmarthome/internal/handler/InnogyBridgeHandler.java index a9e74b1b7faf8..65943b322f4d5 100644 --- a/bundles/org.openhab.binding.innogysmarthome/src/main/java/org/openhab/binding/innogysmarthome/internal/handler/InnogyBridgeHandler.java +++ b/bundles/org.openhab.binding.innogysmarthome/src/main/java/org/openhab/binding/innogysmarthome/internal/handler/InnogyBridgeHandler.java @@ -20,11 +20,19 @@ import java.net.URI; import java.time.format.DateTimeFormatter; import java.time.format.FormatStyle; -import java.util.*; -import java.util.concurrent.*; - -import org.apache.commons.lang.StringUtils; -import org.apache.commons.lang.exception.ExceptionUtils; +import java.util.Collection; +import java.util.Collections; +import java.util.Map; +import java.util.Objects; +import java.util.Set; +import java.util.concurrent.CopyOnWriteArraySet; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.ScheduledFuture; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; + +import org.apache.commons.lang3.exception.ExceptionUtils; import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.Nullable; import org.eclipse.jetty.client.HttpClient; @@ -183,7 +191,7 @@ private void initializeClient() { * @return true if success */ private boolean checkOnAuthCode() { - if (StringUtils.isNotBlank(bridgeConfiguration.authcode)) { + if (!bridgeConfiguration.authcode.isBlank()) { logger.debug("Trying to get access and refresh tokens"); try { oAuthService.getAccessTokenResponseByAuthorizationCode(bridgeConfiguration.authcode, @@ -868,7 +876,7 @@ public void commandSetRollerShutterLevel(final String deviceId, final int roller /** * Sends the command to start or stop moving the rollershutter (ISR2) in a specified direction - * + * * @param deviceId * @param action */ @@ -970,7 +978,7 @@ private void refreshAccessToken() { /** * Checks if the job is already (re-)scheduled. - * + * * @param job job to check * @return true, when the job is already (re-)scheduled, otherwise false */ diff --git a/bundles/org.openhab.binding.intesis/README.md b/bundles/org.openhab.binding.intesis/README.md index 78ec5640adc65..300c6b4b4f5bb 100644 --- a/bundles/org.openhab.binding.intesis/README.md +++ b/bundles/org.openhab.binding.intesis/README.md @@ -36,7 +36,7 @@ The binding uses the following configuration parameters. | mode | String | The heating/cooling mode. | AUTO,HEAT,DRY,FAN,COOL | | fanSpeed | String | Fan speed (if applicable) | AUTO,1-10 | | vanesUpDown | String | Control of up/down vanes (if applicable) | AUTO,1-9,SWING,SWIRL,WIDE | -| vanesUpDown | String | Control of left/right vanes (if applicable) | AUTO,1-9,SWING,SWIRL,WIDE | +| vanesLeftRight | String | Control of left/right vanes (if applicable) | AUTO,1-9,SWING,SWIRL,WIDE | | targetTemperature | Number:Temperature | The currently set target temperature (if applicable) | range between 18°C and 30°C | | ambientTemperature | Number:Temperature | (Readonly) The ambient air temperature (if applicable) | | | outdoorTemperature | Number:Temperature | (Readonly) The outdoor air temperature (if applicable) | | diff --git a/bundles/org.openhab.binding.ipcamera/README.md b/bundles/org.openhab.binding.ipcamera/README.md index fd826ff21c0d0..44ad00c73830d 100644 --- a/bundles/org.openhab.binding.ipcamera/README.md +++ b/bundles/org.openhab.binding.ipcamera/README.md @@ -213,6 +213,7 @@ The channels are kept consistent as much as possible from brand to brand to make | `activateAlarmOutput2` | Switch | Toggles a cameras relay output 2. | | `audioAlarm` | Switch (read only) | When the camera detects noise above a threshold this switch will move to ON. | | `autoLED` | Switch | When ON this sets a cameras IR LED to automatically turn on or off. | +| `carAlarm` | Switch | When a car is detected the switch will turn ON. | | `cellMotionAlarm` | Switch (read only) | ONVIF cameras only will reflect the status of the ONVIF event of the same name. | | `doorBell` | Switch (read only) | Doorbird only, will reflect the status of the doorbell button. | | `enableAudioAlarm` | Switch | Allows the audio alarm to be turned ON or OFF. | @@ -234,6 +235,7 @@ The channels are kept consistent as much as possible from brand to brand to make | `gifHistoryLength` | Number | How many filenames are in the `gifHistory`. | | `gotoPreset` | String | ONVIF cameras that can move only. Will cause the camera to move to a preset location. | | `hlsUrl` | String | The URL for the ipcamera.m3u8 file. | +| `humanAlarm` | Switch | When a camera detects a human this switch will turn ON. | | `imageUrl` | String | The URL for the ipcamera.jpg file. | | `itemLeft` | Switch (read only) | Will turn ON if an API camera detects an item has been left behind. | | `itemTaken` | Switch (read only) | Will turn ON if an API camera detects an item has been stolen. | @@ -379,6 +381,9 @@ See this forum thread for examples of how to use snapshots and streams in a site ## Video Streams +To get video streams working, this forum thread has working widget examples that you can use. + + To get some of the video formats working, you need to install FFmpeg. Visit their site here to learn how diff --git a/bundles/org.openhab.binding.ipcamera/src/main/java/org/openhab/binding/ipcamera/internal/DahuaHandler.java b/bundles/org.openhab.binding.ipcamera/src/main/java/org/openhab/binding/ipcamera/internal/DahuaHandler.java index 19daffebc565b..65234b506364a 100644 --- a/bundles/org.openhab.binding.ipcamera/src/main/java/org/openhab/binding/ipcamera/internal/DahuaHandler.java +++ b/bundles/org.openhab.binding.ipcamera/src/main/java/org/openhab/binding/ipcamera/internal/DahuaHandler.java @@ -14,7 +14,7 @@ import static org.openhab.binding.ipcamera.internal.IpCameraBindingConstants.*; -import java.util.ArrayList; +import java.util.List; import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.Nullable; @@ -48,6 +48,150 @@ public DahuaHandler(IpCameraHandler handler, int nvrChannel) { this.nvrChannel = nvrChannel; } + private void processEvent(String content) { + int startIndex = content.indexOf("Code=", 12) + 5;// skip --myboundary + int endIndex = content.indexOf(";", startIndex + 1); + if (startIndex == -1 || endIndex == -1) { + ipCameraHandler.logger.debug("Code= not found in Dahua event. Content was:{}", content); + return; + } + String code = content.substring(startIndex, endIndex); + startIndex = endIndex + 8;// skip ;action= + endIndex = content.indexOf(";", startIndex); + if (startIndex == -1 || endIndex == -1) { + ipCameraHandler.logger.debug(";action= not found in Dahua event. Content was:{}", content); + return; + } + String action = content.substring(startIndex, endIndex); + switch (code) { + case "VideoMotion": + if (action.equals("Start")) { + ipCameraHandler.motionDetected(CHANNEL_MOTION_ALARM); + } else if (action.equals("Stop")) { + ipCameraHandler.noMotionDetected(CHANNEL_MOTION_ALARM); + } + break; + case "TakenAwayDetection": + if (action.equals("Start")) { + ipCameraHandler.motionDetected(CHANNEL_ITEM_TAKEN); + } else if (action.equals("Stop")) { + ipCameraHandler.noMotionDetected(CHANNEL_ITEM_TAKEN); + } + break; + case "LeftDetection": + if (action.equals("Start")) { + ipCameraHandler.motionDetected(CHANNEL_ITEM_LEFT); + } else if (action.equals("Stop")) { + ipCameraHandler.noMotionDetected(CHANNEL_ITEM_LEFT); + } + break; + case "SmartMotionVehicle": + if (action.equals("Start")) { + ipCameraHandler.motionDetected(CHANNEL_CAR_ALARM); + } else if (action.equals("Stop")) { + ipCameraHandler.noMotionDetected(CHANNEL_CAR_ALARM); + } + break; + case "SmartMotionHuman": + if (action.equals("Start")) { + ipCameraHandler.motionDetected(CHANNEL_HUMAN_ALARM); + } else if (action.equals("Stop")) { + ipCameraHandler.noMotionDetected(CHANNEL_HUMAN_ALARM); + } + break; + case "CrossLineDetection": + if (action.equals("Start")) { + ipCameraHandler.motionDetected(CHANNEL_LINE_CROSSING_ALARM); + } else if (action.equals("Stop")) { + ipCameraHandler.noMotionDetected(CHANNEL_LINE_CROSSING_ALARM); + } + break; + case "AudioMutation": + if (action.equals("Start")) { + ipCameraHandler.audioDetected(); + } else if (action.equals("Stop")) { + ipCameraHandler.noAudioDetected(); + } + break; + case "FaceDetection": + if (action.equals("Start")) { + ipCameraHandler.motionDetected(CHANNEL_FACE_DETECTED); + } else if (action.equals("Stop")) { + ipCameraHandler.noMotionDetected(CHANNEL_FACE_DETECTED); + } + break; + case "ParkingDetection": + if (action.equals("Start")) { + ipCameraHandler.setChannelState(CHANNEL_PARKING_ALARM, OnOffType.ON); + } else if (action.equals("Stop")) { + ipCameraHandler.setChannelState(CHANNEL_PARKING_ALARM, OnOffType.OFF); + } + break; + case "CrossRegionDetection": + if (action.equals("Start")) { + ipCameraHandler.motionDetected(CHANNEL_FIELD_DETECTION_ALARM); + } else if (action.equals("Stop")) { + ipCameraHandler.noMotionDetected(CHANNEL_FIELD_DETECTION_ALARM); + } + break; + case "VideoLoss": + case "VideoBlind": + if (action.equals("Start")) { + ipCameraHandler.setChannelState(CHANNEL_TOO_DARK_ALARM, OnOffType.ON); + } else if (action.equals("Stop")) { + ipCameraHandler.setChannelState(CHANNEL_TOO_DARK_ALARM, OnOffType.OFF); + } + break; + case "VideoAbnormalDetection": + if (action.equals("Start")) { + ipCameraHandler.setChannelState(CHANNEL_SCENE_CHANGE_ALARM, OnOffType.ON); + } else if (action.equals("Stop")) { + ipCameraHandler.setChannelState(CHANNEL_SCENE_CHANGE_ALARM, OnOffType.OFF); + } + break; + case "VideoUnFocus": + if (action.equals("Start")) { + ipCameraHandler.setChannelState(CHANNEL_TOO_BLURRY_ALARM, OnOffType.ON); + } else if (action.equals("Stop")) { + ipCameraHandler.setChannelState(CHANNEL_TOO_BLURRY_ALARM, OnOffType.OFF); + } + break; + case "AlarmLocal": + if (action.equals("Start")) { + if (content.contains("index=0")) { + ipCameraHandler.setChannelState(CHANNEL_EXTERNAL_ALARM_INPUT, OnOffType.ON); + } else { + ipCameraHandler.setChannelState(CHANNEL_EXTERNAL_ALARM_INPUT2, OnOffType.ON); + } + } else if (action.equals("Stop")) { + if (content.contains("index=0")) { + ipCameraHandler.setChannelState(CHANNEL_EXTERNAL_ALARM_INPUT, OnOffType.OFF); + } else { + ipCameraHandler.setChannelState(CHANNEL_EXTERNAL_ALARM_INPUT2, OnOffType.OFF); + } + } + break; + case "LensMaskOpen": + ipCameraHandler.setChannelState(CHANNEL_ENABLE_PRIVACY_MODE, OnOffType.ON); + break; + case "LensMaskClose": + ipCameraHandler.setChannelState(CHANNEL_ENABLE_PRIVACY_MODE, OnOffType.OFF); + break; + case "TimeChange": + case "NTPAdjustTime": + case "StorageChange": + case "Reboot": + case "NewFile": + case "VideoMotionInfo": + case "RtspSessionDisconnect": + case "LeFunctionStatusSync": + case "RecordDelete": + break; + default: + ipCameraHandler.logger.debug("Unrecognised Dahua event, Code={}, action={}", code, action); + } + } + // This handles the incoming http replies back from the camera. @Override public void channelRead(@Nullable ChannelHandlerContext ctx, @Nullable Object msg) throws Exception { @@ -56,6 +200,10 @@ public void channelRead(@Nullable ChannelHandlerContext ctx, @Nullable Object ms } try { String content = msg.toString(); + if (content.startsWith("--myboundary")) { + processEvent(content); + return; + } ipCameraHandler.logger.trace("HTTP Result back from camera is \t:{}:", content); // determine if the motion detection is turned on or off. if (content.contains("table.MotionDetect[0].Enable=true")) { @@ -63,77 +211,20 @@ public void channelRead(@Nullable ChannelHandlerContext ctx, @Nullable Object ms } else if (content.contains("table.MotionDetect[" + nvrChannel + "].Enable=false")) { ipCameraHandler.setChannelState(CHANNEL_ENABLE_MOTION_ALARM, OnOffType.OFF); } - // Handle motion alarm - if (content.contains("Code=VideoMotion;action=Start;index=0")) { - ipCameraHandler.motionDetected(CHANNEL_MOTION_ALARM); - } else if (content.contains("Code=VideoMotion;action=Stop;index=0")) { - ipCameraHandler.noMotionDetected(CHANNEL_MOTION_ALARM); - } - // Handle item taken alarm - if (content.contains("Code=TakenAwayDetection;action=Start;index=0")) { - ipCameraHandler.motionDetected(CHANNEL_ITEM_TAKEN); - } else if (content.contains("Code=TakenAwayDetection;action=Stop;index=0")) { - ipCameraHandler.noMotionDetected(CHANNEL_ITEM_TAKEN); - } - // Handle item left alarm - if (content.contains("Code=LeftDetection;action=Start;index=0")) { - ipCameraHandler.motionDetected(CHANNEL_ITEM_LEFT); - } else if (content.contains("Code=LeftDetection;action=Stop;index=0")) { - ipCameraHandler.noMotionDetected(CHANNEL_ITEM_LEFT); - } - // Handle CrossLineDetection alarm - if (content.contains("Code=CrossLineDetection;action=Start;index=0")) { - ipCameraHandler.motionDetected(CHANNEL_LINE_CROSSING_ALARM); - } else if (content.contains("Code=CrossLineDetection;action=Stop;index=0")) { - ipCameraHandler.noMotionDetected(CHANNEL_LINE_CROSSING_ALARM); - } + // determine if the audio alarm is turned on or off. if (content.contains("table.AudioDetect[0].MutationDetect=true")) { ipCameraHandler.setChannelState(CHANNEL_ENABLE_AUDIO_ALARM, OnOffType.ON); } else if (content.contains("table.AudioDetect[0].MutationDetect=false")) { ipCameraHandler.setChannelState(CHANNEL_ENABLE_AUDIO_ALARM, OnOffType.OFF); } - // Handle AudioMutation alarm - if (content.contains("Code=AudioMutation;action=Start;index=0")) { - ipCameraHandler.audioDetected(); - } else if (content.contains("Code=AudioMutation;action=Stop;index=0")) { - ipCameraHandler.noAudioDetected(); - } + // Handle AudioMutationThreshold alarm if (content.contains("table.AudioDetect[0].MutationThreold=")) { String value = ipCameraHandler.returnValueFromString(content, "table.AudioDetect[0].MutationThreold="); ipCameraHandler.setChannelState(CHANNEL_THRESHOLD_AUDIO_ALARM, PercentType.valueOf(value)); } - // Handle FaceDetection alarm - if (content.contains("Code=FaceDetection;action=Start;index=0")) { - ipCameraHandler.motionDetected(CHANNEL_FACE_DETECTED); - } else if (content.contains("Code=FaceDetection;action=Stop;index=0")) { - ipCameraHandler.noMotionDetected(CHANNEL_FACE_DETECTED); - } - // Handle ParkingDetection alarm - if (content.contains("Code=ParkingDetection;action=Start;index=0")) { - ipCameraHandler.motionDetected(CHANNEL_PARKING_ALARM); - } else if (content.contains("Code=ParkingDetection;action=Stop;index=0")) { - ipCameraHandler.noMotionDetected(CHANNEL_PARKING_ALARM); - } - // Handle CrossRegionDetection alarm - if (content.contains("Code=CrossRegionDetection;action=Start;index=0")) { - ipCameraHandler.motionDetected(CHANNEL_FIELD_DETECTION_ALARM); - } else if (content.contains("Code=CrossRegionDetection;action=Stop;index=0")) { - ipCameraHandler.noMotionDetected(CHANNEL_FIELD_DETECTION_ALARM); - } - // Handle External Input alarm - if (content.contains("Code=AlarmLocal;action=Start;index=0")) { - ipCameraHandler.setChannelState(CHANNEL_EXTERNAL_ALARM_INPUT, OnOffType.ON); - } else if (content.contains("Code=AlarmLocal;action=Stop;index=0")) { - ipCameraHandler.setChannelState(CHANNEL_EXTERNAL_ALARM_INPUT, OnOffType.OFF); - } - // Handle External Input alarm2 - if (content.contains("Code=AlarmLocal;action=Start;index=1")) { - ipCameraHandler.setChannelState(CHANNEL_EXTERNAL_ALARM_INPUT2, OnOffType.ON); - } else if (content.contains("Code=AlarmLocal;action=Stop;index=1")) { - ipCameraHandler.setChannelState(CHANNEL_EXTERNAL_ALARM_INPUT2, OnOffType.OFF); - } + // CrossLineDetection alarm on/off if (content.contains("table.VideoAnalyseRule[0][1].Enable=true")) { ipCameraHandler.setChannelState(CHANNEL_ENABLE_LINE_CROSSING_ALARM, OnOffType.ON); @@ -141,10 +232,9 @@ public void channelRead(@Nullable ChannelHandlerContext ctx, @Nullable Object ms ipCameraHandler.setChannelState(CHANNEL_ENABLE_LINE_CROSSING_ALARM, OnOffType.OFF); } // Privacy Mode on/off - if (content.contains("Code=LensMaskOpen;") || content.contains("table.LeLensMask[0].Enable=true")) { + if (content.contains("table.LeLensMask[0].Enable=true")) { ipCameraHandler.setChannelState(CHANNEL_ENABLE_PRIVACY_MODE, OnOffType.ON); - } else if (content.contains("Code=LensMaskClose;") - || content.contains("table.LeLensMask[0].Enable=false")) { + } else if (content.contains("table.LeLensMask[0].Enable=false")) { ipCameraHandler.setChannelState(CHANNEL_ENABLE_PRIVACY_MODE, OnOffType.OFF); } } finally { @@ -152,13 +242,10 @@ public void channelRead(@Nullable ChannelHandlerContext ctx, @Nullable Object ms } } - // This handles the commands that come from the Openhab event bus. + // This handles the commands that come from the openHAB event bus. public void handleCommand(ChannelUID channelUID, Command command) { if (command instanceof RefreshType) { switch (channelUID.getId()) { - case CHANNEL_THRESHOLD_AUDIO_ALARM: - // ipCameraHandler.sendHttpGET("/cgi-bin/configManager.cgi?action=getConfig&name=AudioDetect[0]"); - return; case CHANNEL_ENABLE_AUDIO_ALARM: ipCameraHandler.sendHttpGET("/cgi-bin/configManager.cgi?action=getConfig&name=AudioDetect[0]"); return; @@ -172,8 +259,7 @@ public void handleCommand(ChannelUID channelUID, Command command) { ipCameraHandler.sendHttpGET("/cgi-bin/configManager.cgi?action=getConfig&name=LeLensMask[0]"); return; } - return; // Return as we have handled the refresh command above and don't need to - // continue further. + return; } // end of "REFRESH" switch (channelUID.getId()) { case CHANNEL_TEXT_OVERLAY: @@ -272,8 +358,7 @@ public void handleCommand(ChannelUID channelUID, Command command) { // If a camera does not need to poll a request as often as snapshots, it can be // added here. Binding steps through the list. - public ArrayList getLowPriorityRequests() { - ArrayList lowPriorityRequests = new ArrayList(1); - return lowPriorityRequests; + public List getLowPriorityRequests() { + return List.of(); } } diff --git a/bundles/org.openhab.binding.ipcamera/src/main/java/org/openhab/binding/ipcamera/internal/DoorBirdHandler.java b/bundles/org.openhab.binding.ipcamera/src/main/java/org/openhab/binding/ipcamera/internal/DoorBirdHandler.java index c23295b76f92d..f6ab9bc8d19cb 100644 --- a/bundles/org.openhab.binding.ipcamera/src/main/java/org/openhab/binding/ipcamera/internal/DoorBirdHandler.java +++ b/bundles/org.openhab.binding.ipcamera/src/main/java/org/openhab/binding/ipcamera/internal/DoorBirdHandler.java @@ -53,16 +53,14 @@ public void channelRead(@Nullable ChannelHandlerContext ctx, @Nullable Object ms try { String content = msg.toString(); ipCameraHandler.logger.trace("HTTP Result back from camera is \t:{}:", content); - if (content.contains("doorbell:H")) { - ipCameraHandler.setChannelState(CHANNEL_DOORBELL, OnOffType.ON); - } if (content.contains("doorbell:L")) { ipCameraHandler.setChannelState(CHANNEL_DOORBELL, OnOffType.OFF); + } else if (content.contains("doorbell:H")) { + ipCameraHandler.setChannelState(CHANNEL_DOORBELL, OnOffType.ON); } if (content.contains("motionsensor:L")) { ipCameraHandler.noMotionDetected(CHANNEL_MOTION_ALARM); - } - if (content.contains("motionsensor:H")) { + } else if (content.contains("motionsensor:H")) { ipCameraHandler.motionDetected(CHANNEL_MOTION_ALARM); } } finally { diff --git a/bundles/org.openhab.binding.ipcamera/src/main/java/org/openhab/binding/ipcamera/internal/FoscamHandler.java b/bundles/org.openhab.binding.ipcamera/src/main/java/org/openhab/binding/ipcamera/internal/FoscamHandler.java index d85680b7ff5ba..ff314392e6804 100644 --- a/bundles/org.openhab.binding.ipcamera/src/main/java/org/openhab/binding/ipcamera/internal/FoscamHandler.java +++ b/bundles/org.openhab.binding.ipcamera/src/main/java/org/openhab/binding/ipcamera/internal/FoscamHandler.java @@ -14,7 +14,7 @@ import static org.openhab.binding.ipcamera.internal.IpCameraBindingConstants.*; -import java.util.ArrayList; +import java.util.List; import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.Nullable; @@ -212,9 +212,7 @@ public void handleCommand(ChannelUID channelUID, Command command) { // If a camera does not need to poll a request as often as snapshots, it can be // added here. Binding steps through the list. - public ArrayList getLowPriorityRequests() { - ArrayList lowPriorityRequests = new ArrayList(1); - lowPriorityRequests.add("/cgi-bin/CGIProxy.fcgi?cmd=getDevState&usr=" + username + "&pwd=" + password); - return lowPriorityRequests; + public List getLowPriorityRequests() { + return List.of(); } } diff --git a/bundles/org.openhab.binding.ipcamera/src/main/java/org/openhab/binding/ipcamera/internal/HikvisionHandler.java b/bundles/org.openhab.binding.ipcamera/src/main/java/org/openhab/binding/ipcamera/internal/HikvisionHandler.java index d99920f865c18..a2e86da0677c2 100644 --- a/bundles/org.openhab.binding.ipcamera/src/main/java/org/openhab/binding/ipcamera/internal/HikvisionHandler.java +++ b/bundles/org.openhab.binding.ipcamera/src/main/java/org/openhab/binding/ipcamera/internal/HikvisionHandler.java @@ -60,6 +60,56 @@ public HikvisionHandler(ThingHandler handler, int nvrChannel) { this.nvrChannel = nvrChannel; } + private void processEvent(String content) { + // some cameras use or and NVRs use channel 0 to say all channels + if (content.contains("hannelID>" + nvrChannel) || content.contains("0")) { + final int debounce = 3; + String eventType = Helper.fetchXML(content, "", ""); + switch (eventType) { + case "videoloss": + if (content.contains("inactive")) { + if (vmdCount > 1) { + vmdCount = 1; + } + countDown(); + countDown(); + } + break; + case "PIR": + ipCameraHandler.motionDetected(CHANNEL_PIR_ALARM); + pirCount = debounce; + break; + case "attendedBaggage": + ipCameraHandler.setChannelState(CHANNEL_ITEM_TAKEN, OnOffType.ON); + takenCount = debounce; + break; + case "unattendedBaggage": + ipCameraHandler.setChannelState(CHANNEL_ITEM_LEFT, OnOffType.ON); + leftCount = debounce; + break; + case "facedetection": + ipCameraHandler.setChannelState(CHANNEL_FACE_DETECTED, OnOffType.ON); + faceCount = debounce; + break; + case "VMD": + ipCameraHandler.motionDetected(CHANNEL_MOTION_ALARM); + vmdCount = debounce; + break; + case "fielddetection": + ipCameraHandler.motionDetected(CHANNEL_FIELD_DETECTION_ALARM); + fieldCount = debounce; + break; + case "linedetection": + ipCameraHandler.motionDetected(CHANNEL_LINE_CROSSING_ALARM); + lineCount = debounce; + break; + default: + logger.debug("Unrecognised Hikvision eventType={}", eventType); + } + } + countDown(); + } + // This handles the incoming http replies back from the camera. @Override public void channelRead(@Nullable ChannelHandlerContext ctx, @Nullable Object msg) throws Exception { @@ -67,59 +117,10 @@ public void channelRead(@Nullable ChannelHandlerContext ctx, @Nullable Object ms return; } try { - int debounce = 3; String content = msg.toString(); logger.trace("HTTP Result back from camera is \t:{}:", content); - if (content.contains("--boundary")) {// Alarm checking goes in here// - if (content.contains("" + nvrChannel + " - if (content.contains("linedetection")) { - ipCameraHandler.motionDetected(CHANNEL_LINE_CROSSING_ALARM); - lineCount = debounce; - } - if (content.contains("fielddetection")) { - ipCameraHandler.motionDetected(CHANNEL_FIELD_DETECTION_ALARM); - fieldCount = debounce; - } - if (content.contains("VMD")) { - ipCameraHandler.motionDetected(CHANNEL_MOTION_ALARM); - vmdCount = debounce; - } - if (content.contains("facedetection")) { - ipCameraHandler.setChannelState(CHANNEL_FACE_DETECTED, OnOffType.ON); - faceCount = debounce; - } - if (content.contains("unattendedBaggage")) { - ipCameraHandler.setChannelState(CHANNEL_ITEM_LEFT, OnOffType.ON); - leftCount = debounce; - } - if (content.contains("attendedBaggage")) { - ipCameraHandler.setChannelState(CHANNEL_ITEM_TAKEN, OnOffType.ON); - takenCount = debounce; - } - if (content.contains("PIR")) { - ipCameraHandler.motionDetected(CHANNEL_PIR_ALARM); - pirCount = debounce; - } - if (content.contains("videoloss\r\ninactive")) { - if (vmdCount > 1) { - vmdCount = 1; - } - countDown(); - countDown(); - } - } else if (content.contains("0")) {// NVR uses channel 0 to say all - // channels - if (content.contains("videoloss\r\ninactive")) { - if (vmdCount > 1) { - vmdCount = 1; - } - countDown(); - countDown(); - } - } - countDown(); - } + if (content.startsWith("--boundary")) {// Alarm checking goes in here// + processEvent(content); } else { String replyElement = Helper.fetchXML(content, "", "<"); switch (replyElement) { @@ -196,23 +197,7 @@ public void channelRead(@Nullable ChannelHandlerContext ctx, @Nullable Object ms } break; default: - if (content.contains("" + nvrChannel + "0")) {// some camera use c or - // - if (content.contains( - "videoloss\r\ninactive")) { - if (vmdCount > 1) { - vmdCount = 1; - } - countDown(); - countDown(); - } - countDown(); - } - } else { - logger.debug("Unhandled reply-{}.", content); - } + logger.debug("Unhandled reply-{}.", content); break; } } diff --git a/bundles/org.openhab.binding.ipcamera/src/main/java/org/openhab/binding/ipcamera/internal/IpCameraBindingConstants.java b/bundles/org.openhab.binding.ipcamera/src/main/java/org/openhab/binding/ipcamera/internal/IpCameraBindingConstants.java index e01c7118a356c..475f91a53d761 100644 --- a/bundles/org.openhab.binding.ipcamera/src/main/java/org/openhab/binding/ipcamera/internal/IpCameraBindingConstants.java +++ b/bundles/org.openhab.binding.ipcamera/src/main/java/org/openhab/binding/ipcamera/internal/IpCameraBindingConstants.java @@ -129,4 +129,6 @@ public static enum FFmpegFormat { public static final String CHANNEL_GOTO_PRESET = "gotoPreset"; public static final String CHANNEL_START_STREAM = "startStream"; public static final String CHANNEL_ENABLE_PRIVACY_MODE = "enablePrivacyMode"; + public static final String CHANNEL_CAR_ALARM = "carAlarm"; + public static final String CHANNEL_HUMAN_ALARM = "humanAlarm"; } diff --git a/bundles/org.openhab.binding.ipcamera/src/main/java/org/openhab/binding/ipcamera/internal/MyNettyAuthHandler.java b/bundles/org.openhab.binding.ipcamera/src/main/java/org/openhab/binding/ipcamera/internal/MyNettyAuthHandler.java index 58386308ed97f..e59b4055f5b6b 100644 --- a/bundles/org.openhab.binding.ipcamera/src/main/java/org/openhab/binding/ipcamera/internal/MyNettyAuthHandler.java +++ b/bundles/org.openhab.binding.ipcamera/src/main/java/org/openhab/binding/ipcamera/internal/MyNettyAuthHandler.java @@ -95,7 +95,6 @@ public void processAuth(String authenticate, String httpMethod, String requestUR nonce = Helper.searchString(authenticate, "nonce=\""); opaque = Helper.searchString(authenticate, "opaque=\""); qop = Helper.searchString(authenticate, "qop=\""); - if (!qop.isEmpty() && !realm.isEmpty()) { ipCameraHandler.useDigestAuth = true; } else { @@ -128,8 +127,10 @@ public void processAuth(String authenticate, String httpMethod, String requestUR String digestString = "username=\"" + username + "\", realm=\"" + realm + "\", nonce=\"" + nonce + "\", uri=\"" + requestURI + "\", cnonce=\"" + cnonce + "\", nc=" + nc + ", qop=\"" + qop + "\", response=\"" - + response + "\", opaque=\"" + opaque + "\""; - + + response + "\""; + if (!opaque.isEmpty()) { + digestString += ", opaque=\"" + opaque + "\""; + } if (reSend) { ipCameraHandler.sendHttpRequest(httpMethod, requestURI, digestString); return; diff --git a/bundles/org.openhab.binding.ipcamera/src/main/java/org/openhab/binding/ipcamera/internal/handler/IpCameraGroupHandler.java b/bundles/org.openhab.binding.ipcamera/src/main/java/org/openhab/binding/ipcamera/internal/handler/IpCameraGroupHandler.java index 6d2ffe0766bc3..c089e77681b95 100644 --- a/bundles/org.openhab.binding.ipcamera/src/main/java/org/openhab/binding/ipcamera/internal/handler/IpCameraGroupHandler.java +++ b/bundles/org.openhab.binding.ipcamera/src/main/java/org/openhab/binding/ipcamera/internal/handler/IpCameraGroupHandler.java @@ -353,7 +353,7 @@ public void initialize() { startStreamServer(true); } updateStatus(ThingStatus.ONLINE); - pollCameraGroupJob = pollCameraGroup.scheduleAtFixedRate(this::pollCameraGroup, 10000, + pollCameraGroupJob = pollCameraGroup.scheduleWithFixedDelay(this::pollCameraGroup, 10000, groupConfig.getPollTime(), TimeUnit.MILLISECONDS); } diff --git a/bundles/org.openhab.binding.ipcamera/src/main/java/org/openhab/binding/ipcamera/internal/handler/IpCameraHandler.java b/bundles/org.openhab.binding.ipcamera/src/main/java/org/openhab/binding/ipcamera/internal/handler/IpCameraHandler.java index ea43387e15c43..9e0c93215cc02 100644 --- a/bundles/org.openhab.binding.ipcamera/src/main/java/org/openhab/binding/ipcamera/internal/handler/IpCameraHandler.java +++ b/bundles/org.openhab.binding.ipcamera/src/main/java/org/openhab/binding/ipcamera/internal/handler/IpCameraHandler.java @@ -206,6 +206,7 @@ private class CommonCameraHandler extends ChannelDuplexHandler { private byte[] incomingJpeg = new byte[0]; private String incomingMessage = ""; private String contentType = "empty"; + private String boundary = ""; private Object reply = new Object(); private String requestUrl = ""; private boolean closeConnection = true; @@ -255,6 +256,8 @@ public void channelRead(@Nullable ChannelHandlerContext ctx, @Nullable Object ms firstStreamedMsg = msg; streamToGroup(firstStreamedMsg, mjpegChannelGroup, true); } + } else { + boundary = Helper.searchString(contentType, "boundary="); } } else if (contentType.contains("image/jp")) { if (bytesToRecieve == 0) { @@ -305,11 +308,32 @@ public void channelRead(@Nullable ChannelHandlerContext ctx, @Nullable Object ms } // Alarm Streams never have a LastHttpContent as they always stay open// else if (contentType.contains("multipart")) { - if (bytesAlreadyRecieved != 0) { - reply = incomingMessage; - incomingMessage = ""; - bytesToRecieve = 0; - bytesAlreadyRecieved = 0; + int beginIndex, endIndex; + if (bytesToRecieve == 0) { + beginIndex = incomingMessage.indexOf("Content-Length:"); + if (beginIndex != -1) { + endIndex = incomingMessage.indexOf("\r\n", beginIndex); + if (endIndex != -1) { + bytesToRecieve = Integer.parseInt( + incomingMessage.substring(beginIndex + 15, endIndex).strip()); + } + } + } + // --boundary and headers are not included in the Content-Length value + if (bytesAlreadyRecieved > bytesToRecieve) { + // Check if message has a second --boundary + endIndex = incomingMessage.indexOf("--" + boundary, bytesToRecieve); + if (endIndex == -1) { + reply = incomingMessage; + incomingMessage = ""; + bytesToRecieve = 0; + bytesAlreadyRecieved = 0; + } else { + reply = incomingMessage.substring(0, endIndex); + incomingMessage = incomingMessage.substring(endIndex, incomingMessage.length()); + bytesToRecieve = 0;// Triggers search next time for Content-Length: + bytesAlreadyRecieved = incomingMessage.length() - endIndex; + } super.channelRead(ctx, reply); } } @@ -591,8 +615,8 @@ public void operationComplete(@Nullable ChannelFuture future) { } logger.trace("Sending camera: {}: http://{}:{}{}", httpMethod, cameraConfig.getIp(), port, httpRequestURL); - channelTrackingMap.put(httpRequestURL, new ChannelTracking(ch, httpRequestURL)); + openChannel(ch, httpRequestURL); CommonCameraHandler commonHandler = (CommonCameraHandler) ch.pipeline().get(COMMON_HANDLER); commonHandler.setURL(httpRequestURLFull); MyNettyAuthHandler authHandler = (MyNettyAuthHandler) ch.pipeline().get(AUTH_HANDLER); @@ -782,6 +806,15 @@ public void setupMjpegStreaming(boolean start, ChannelHandlerContext ctx) { } } + void openChannel(Channel channel, String httpRequestURL) { + ChannelTracking tracker = channelTrackingMap.get(httpRequestURL); + if (tracker != null && !tracker.getReply().isEmpty()) {// We need to keep the stored reply + tracker.setChannel(channel); + return; + } + channelTrackingMap.put(httpRequestURL, new ChannelTracking(channel, httpRequestURL)); + } + void closeChannel(String url) { ChannelTracking channelTracking = channelTrackingMap.get(url); if (channelTracking != null) { @@ -1402,7 +1435,7 @@ public void setChannelState(String channelToUpdate, State valueOf) { updateState(channelToUpdate, valueOf); } - void bringCameraOnline() { + private void bringCameraOnline() { isOnline = true; updateStatus(ThingStatus.ONLINE); groupTracker.listOfOnlineCameraHandlers.add(this); @@ -1414,7 +1447,7 @@ void bringCameraOnline() { if (cameraConfig.getGifPreroll() > 0 || cameraConfig.getUpdateImageWhen().contains("1")) { snapshotPolling = true; - snapshotJob = threadPool.scheduleAtFixedRate(this::snapshotRunnable, 1000, cameraConfig.getPollTime(), + snapshotJob = threadPool.scheduleWithFixedDelay(this::snapshotRunnable, 1000, cameraConfig.getPollTime(), TimeUnit.MILLISECONDS); } @@ -1537,11 +1570,11 @@ public void startSnapshotPolling() { } if (streamingSnapshotMjpeg || streamingAutoFps) { snapshotPolling = true; - snapshotJob = threadPool.scheduleAtFixedRate(this::snapshotRunnable, 200, cameraConfig.getPollTime(), + snapshotJob = threadPool.scheduleWithFixedDelay(this::snapshotRunnable, 200, cameraConfig.getPollTime(), TimeUnit.MILLISECONDS); } else if (cameraConfig.getUpdateImageWhen().contains("4")) { // During Motion Alarms snapshotPolling = true; - snapshotJob = threadPool.scheduleAtFixedRate(this::snapshotRunnable, 200, cameraConfig.getPollTime(), + snapshotJob = threadPool.scheduleWithFixedDelay(this::snapshotRunnable, 200, cameraConfig.getPollTime(), TimeUnit.MILLISECONDS); } } @@ -1610,6 +1643,10 @@ void pollCameraRunnable() { sendHttpGET("/bha-api/monitor.cgi?ring=doorbell,motionsensor"); } break; + case FOSCAM_THING: + sendHttpGET("/cgi-bin/CGIProxy.fcgi?cmd=getDevState&usr=" + cameraConfig.getUser() + "&pwd=" + + cameraConfig.getPassword()); + break; } Ffmpeg localHLS = ffmpegHLS; if (localHLS != null) { @@ -1747,6 +1784,7 @@ public void dispose() { Ffmpeg localFfmpeg = ffmpegHLS; if (localFfmpeg != null) { localFfmpeg.stopConverting(); + localFfmpeg = null; } localFfmpeg = ffmpegRecord; if (localFfmpeg != null) { diff --git a/bundles/org.openhab.binding.ipcamera/src/main/java/org/openhab/binding/ipcamera/internal/onvif/OnvifDiscovery.java b/bundles/org.openhab.binding.ipcamera/src/main/java/org/openhab/binding/ipcamera/internal/onvif/OnvifDiscovery.java index 10c744c817b51..1705733152adb 100644 --- a/bundles/org.openhab.binding.ipcamera/src/main/java/org/openhab/binding/ipcamera/internal/onvif/OnvifDiscovery.java +++ b/bundles/org.openhab.binding.ipcamera/src/main/java/org/openhab/binding/ipcamera/internal/onvif/OnvifDiscovery.java @@ -124,9 +124,9 @@ void searchReply(String url, String xml) { void processCameraReplys() { for (DatagramPacket packet : listOfReplys) { - logger.trace("Device replied to discovery with:{}", packet.toString()); String xml = packet.content().toString(CharsetUtil.UTF_8); - String xAddr = Helper.fetchXML(xml, "", ""); + logger.trace("Device replied to discovery with:{}", xml); + String xAddr = Helper.fetchXML(xml, "", "d:XAddrs>");// Foscam and all other brands if (!xAddr.equals("")) { searchReply(xAddr, xml); } else if (xml.contains("onvif")) { diff --git a/bundles/org.openhab.binding.ipcamera/src/main/resources/OH-INF/thing/thing-types.xml b/bundles/org.openhab.binding.ipcamera/src/main/resources/OH-INF/thing/thing-types.xml index 1e354d4a30e9f..12624ebc1f1f1 100644 --- a/bundles/org.openhab.binding.ipcamera/src/main/resources/OH-INF/thing/thing-types.xml +++ b/bundles/org.openhab.binding.ipcamera/src/main/resources/OH-INF/thing/thing-types.xml @@ -936,6 +936,11 @@ + + + + + @@ -2542,6 +2547,22 @@ + + Switch + + A person has triggered the Human Detection. + Alarm + + + + + Switch + + A car has triggered the Vehicle Detection. + Alarm + + + Switch diff --git a/bundles/org.openhab.binding.irtrans/src/main/java/org/openhab/binding/irtrans/internal/handler/BlasterHandler.java b/bundles/org.openhab.binding.irtrans/src/main/java/org/openhab/binding/irtrans/internal/handler/BlasterHandler.java index a209af76cfc0f..d2ea1079a2f2b 100644 --- a/bundles/org.openhab.binding.irtrans/src/main/java/org/openhab/binding/irtrans/internal/handler/BlasterHandler.java +++ b/bundles/org.openhab.binding.irtrans/src/main/java/org/openhab/binding/irtrans/internal/handler/BlasterHandler.java @@ -14,7 +14,7 @@ import static org.openhab.binding.irtrans.internal.IRtransBindingConstants.CHANNEL_IO; -import org.apache.commons.lang.StringUtils; +import org.apache.commons.lang3.StringUtils; import org.openhab.binding.irtrans.internal.IRtransBindingConstants.Led; import org.openhab.binding.irtrans.internal.IrCommand; import org.openhab.core.library.types.StringType; diff --git a/bundles/org.openhab.binding.irtrans/src/main/java/org/openhab/binding/irtrans/internal/handler/EthernetBridgeHandler.java b/bundles/org.openhab.binding.irtrans/src/main/java/org/openhab/binding/irtrans/internal/handler/EthernetBridgeHandler.java index 4a70874e32691..4e0aae384e9c4 100644 --- a/bundles/org.openhab.binding.irtrans/src/main/java/org/openhab/binding/irtrans/internal/handler/EthernetBridgeHandler.java +++ b/bundles/org.openhab.binding.irtrans/src/main/java/org/openhab/binding/irtrans/internal/handler/EthernetBridgeHandler.java @@ -35,7 +35,7 @@ import java.util.regex.Matcher; import java.util.regex.Pattern; -import org.apache.commons.lang.StringUtils; +import org.apache.commons.lang3.StringUtils; import org.eclipse.jdt.annotation.NonNull; import org.openhab.binding.irtrans.internal.IRtransBindingConstants; import org.openhab.binding.irtrans.internal.IRtransBindingConstants.Led; diff --git a/bundles/org.openhab.binding.ism8/src/main/java/org/openhab/binding/ism8/server/Server.java b/bundles/org.openhab.binding.ism8/src/main/java/org/openhab/binding/ism8/server/Server.java index f0d6fcfd69eb0..8b2ac26cce94c 100644 --- a/bundles/org.openhab.binding.ism8/src/main/java/org/openhab/binding/ism8/server/Server.java +++ b/bundles/org.openhab.binding.ism8/src/main/java/org/openhab/binding/ism8/server/Server.java @@ -85,6 +85,7 @@ public boolean getConnected() { * Starts the server * */ + @Override public void run() { this.startRetries = 0; while (!this.isInterrupted()) { @@ -132,7 +133,7 @@ public void addDataPoint(int id, String knxType, String description) { IDataPoint dp = DataPointFactory.createDataPoint(id, knxType, description); if (dp != null) { - this.dataPoints.put(new Integer(id), dp); + this.dataPoints.put(Integer.valueOf(id), dp); } } diff --git a/bundles/org.openhab.binding.jeelink/src/main/java/org/openhab/binding/jeelink/internal/lacrosse/LaCrosseTemperatureSensorHandler.java b/bundles/org.openhab.binding.jeelink/src/main/java/org/openhab/binding/jeelink/internal/lacrosse/LaCrosseTemperatureSensorHandler.java index d7a39f554cb53..9befda8961dbb 100644 --- a/bundles/org.openhab.binding.jeelink/src/main/java/org/openhab/binding/jeelink/internal/lacrosse/LaCrosseTemperatureSensorHandler.java +++ b/bundles/org.openhab.binding.jeelink/src/main/java/org/openhab/binding/jeelink/internal/lacrosse/LaCrosseTemperatureSensorHandler.java @@ -21,7 +21,7 @@ import java.util.List; import java.util.Map; -import org.apache.commons.lang.StringUtils; +import org.apache.commons.lang3.StringUtils; import org.openhab.binding.jeelink.internal.JeeLinkSensorHandler; import org.openhab.binding.jeelink.internal.ReadingPublisher; import org.openhab.binding.jeelink.internal.RollingAveragePublisher; diff --git a/bundles/org.openhab.binding.jeelink/src/main/java/org/openhab/binding/jeelink/internal/lacrosse/LgwSensorHandler.java b/bundles/org.openhab.binding.jeelink/src/main/java/org/openhab/binding/jeelink/internal/lacrosse/LgwSensorHandler.java index 7074f1d57460a..fcba0f17a3c7a 100644 --- a/bundles/org.openhab.binding.jeelink/src/main/java/org/openhab/binding/jeelink/internal/lacrosse/LgwSensorHandler.java +++ b/bundles/org.openhab.binding.jeelink/src/main/java/org/openhab/binding/jeelink/internal/lacrosse/LgwSensorHandler.java @@ -18,7 +18,7 @@ import java.math.BigDecimal; import java.math.RoundingMode; -import org.apache.commons.lang.StringUtils; +import org.apache.commons.lang3.StringUtils; import org.eclipse.jdt.annotation.NonNullByDefault; import org.openhab.binding.jeelink.internal.JeeLinkSensorHandler; import org.openhab.binding.jeelink.internal.ReadingPublisher; diff --git a/bundles/org.openhab.binding.kaleidescape/src/main/java/org/openhab/binding/kaleidescape/internal/communication/KaleidescapeFormatter.java b/bundles/org.openhab.binding.kaleidescape/src/main/java/org/openhab/binding/kaleidescape/internal/communication/KaleidescapeFormatter.java index 1c6cd46a6611a..0dbe8727f53e4 100644 --- a/bundles/org.openhab.binding.kaleidescape/src/main/java/org/openhab/binding/kaleidescape/internal/communication/KaleidescapeFormatter.java +++ b/bundles/org.openhab.binding.kaleidescape/src/main/java/org/openhab/binding/kaleidescape/internal/communication/KaleidescapeFormatter.java @@ -12,7 +12,7 @@ */ package org.openhab.binding.kaleidescape.internal.communication; -import org.apache.commons.lang.StringEscapeUtils; +import org.apache.commons.lang3.StringEscapeUtils; import org.eclipse.jdt.annotation.NonNullByDefault; /** @@ -48,8 +48,8 @@ public static String formatString(String input) { // I.e. characters with accent, umlaut, etc., they need to be restored to the correct character // example: Noel (with umlaut 'o') comes in as N\d246el input = input.replaceAll("(?i)\\\\d([0-9]{3})", "\\&#$1;"); // first convert to html escaped codes - // then convert with unescapeHtml, not sure how to do this without the Apache libraries :( - return StringEscapeUtils.unescapeHtml(input); + // then convert with unescapeHtml4, not sure how to do this without the Apache libraries :( + return StringEscapeUtils.unescapeHtml4(input); } } return input; diff --git a/bundles/org.openhab.binding.keba/README.md b/bundles/org.openhab.binding.keba/README.md index 2c1c5bae994b5..e6037cfebc3f0 100644 --- a/bundles/org.openhab.binding.keba/README.md +++ b/bundles/org.openhab.binding.keba/README.md @@ -4,46 +4,48 @@ This binding integrates the [Keba KeContact EV Charging Stations](https://www.ke ## Supported Things -The Keba KeContact P20 and P30 stations are supported by this binding, the thing type id is `kecontact`. - +The Keba KeContact P20 and P30 stations which are providing the UDP interface (P20 LSA+ socket, P30 c-series and x-series) are supported by this binding, the thing type id is `kecontact`. ## Thing Configuration -The Keba KeContact P20/30 requires the ip address as the configuration parameter `ipAddress`. Optionally, a refresh interval (in seconds) can be defined as parameter `refreshInterval` that defines the polling of values from the charging station. - +The Keba KeContact P20/30 requires the IP address as the configuration parameter `ipAddress`. +Optionally, a refresh interval (in seconds) can be defined as parameter `refreshInterval` that defines the polling of values from the charging station. ## Channels All devices support the following channels: -| Channel ID | Item Type | Read-only | Description | -|--------------------|-----------|-----------|------------------------------------------------------------------------| -| state | Number | yes | current operational state of the wallbox | -| enabled | Switch | no | activation state of the wallbox | -| maxpresetcurrent | Number | no | maximum current the charging station should deliver to the EV | -| power | Number | yes | active power delivered by the charging station | -| wallbox | Switch | yes | plug state of wallbox | -| vehicle | Switch | yes | plug state of vehicle | -| locked | Switch | yes | lock state of plug at vehicle | -| I1/2/3 | Number | yes | current for the given phase | -| U1/2/3 | Number | yes | voltage for the given phase | -| output | Switch | no | state of the X1 relais | -| input | Switch | yes | state of the X2 contact | -| display | String | yes | display text on wallbox | -| error1 | String | yes | error code state 1, if in error (see the KeContact FAQ) | -| error2 | String | yes | error code state 2, if in error (see the KeContact FAQ) | -| maxsystemcurrent | Number | yes | maximum current the wallbox can deliver | -| failsafecurrent | Number | yes | maximum current the wallbox can deliver, if network is lost | -| uptime | DateTime | yes | system uptime since the last reset of the wallbox | -| sessionconsumption | Number | yes | energy delivered in current session | -| totalconsumption | Number | yes | total energy delivered since the last reset of the wallbox | -| authreq | Switch | yes | authentication required | -| authon | Switch | yes | authentication enabled | -| sessionrfidtag | String | yes | RFID tag used for the last charging session | -| sessionrfidclass | String | yes | RFID tag class used for the last charging session | -| sessionid | Number | yes | session ID of the last charging session | -| setenergylimit | Number | no | set an energy limit for an already running or the next charging session| -| authenticate | String | no | authenticate and start a session using RFID tag+RFID class | +| Channel ID | Item Type | Read-only | Description | +|---------------------------|---------------------------|-----------|---------------------------------------------------------------------------| +| state | Number | yes | current operational state of the wallbox | +| enabled | Switch | no | activation state of the wallbox | +| maxpresetcurrent | Number:ElectricCurrent | no | maximum current the charging station should deliver to the EV in A | +| maxpresetcurrentrange | Number:Dimensionless | no | maximum current the charging station should deliver to the EV in % | +| power | Number:Power | yes | active power delivered by the charging station | +| wallbox | Switch | yes | plug state of wallbox | +| vehicle | Switch | yes | plug state of vehicle | +| locked | Switch | yes | lock state of plug at vehicle | +| I1/2/3 | Number:ElectricCurrent | yes | current for the given phase | +| U1/2/3 | Number:ElectricPotential | yes | voltage for the given phase | +| output | Switch | no | state of the X1 relais | +| input | Switch | yes | state of the X2 contact | +| display | String | no | display text on wallbox | +| error1 | String | yes | error code state 1, if in error (see the KeContact FAQ) | +| error2 | String | yes | error code state 2, if in error (see the KeContact FAQ) | +| maxsystemcurrent | Number:ElectricCurrent | yes | maximum current the wallbox can deliver | +| failsafecurrent | Number:ElectricCurrent | yes | maximum current the wallbox can deliver, if network is lost | +| uptime | Number:Time | yes | system uptime since the last reset of the wallbox | +| sessionconsumption | Number:Energy | yes | energy delivered in current session | +| totalconsumption | Number:Energy | yes | total energy delivered since the last reset of the wallbox | +| authreq | Switch | yes | authentication required | +| authon | Switch | yes | authentication enabled | +| sessionrfidtag | String | yes | RFID tag used for the last charging session | +| sessionrfidclass | String | yes | RFID tag class used for the last charging session | +| sessionid | Number | yes | session ID of the last charging session | +| setenergylimit | Number:Energy | no | set an energy limit for an already running or the next charging session | +| authenticate | String | no | authenticate and start a session using RFID tag+RFID class | +| maxpilotcurrent | Number:ElectricCurrent | yes | current offered to the vehicle via control pilot signalization | +| maxpilotcurrentdutycyle | Number:Dimensionless | yes | duty cycle of the control pilot signal | ## Example @@ -57,28 +59,28 @@ Thing keba:kecontact:1 [ipAddress="192.168.0.64", refreshInterval=30] demo.items: ``` -Dimmer KebaCurrentRange {channel="keba:kecontact:1:maxpresetcurrentrange"} -Number KebaCurrent {channel="keba:kecontact:1:maxpresetcurrent"} -Number KebaSystemCurrent {channel="keba:kecontact:1:maxsystemcurrent"} -Number KebaFailSafeCurrent {channel="keba:kecontact:1:failsafecurrent"} -String KebaState {channel="keba:kecontact:1:state"} -Switch KebaSwitch {channel="keba:kecontact:1:enabled"} -Switch KebaWallboxPlugged {channel="keba:kecontact:1:wallbox"} -Switch KebaVehiclePlugged {channel="keba:kecontact:1:vehicle"} -Switch KebaPlugLocked {channel="keba:kecontact:1:locked"} -DateTime KebaUptime "Uptime [%1$tY Y, %1$tm M, %1$td D, %1$tT]" {channel="keba:kecontact:1:uptime"} -Number KebaI1 {channel="keba:kecontact:1:I1"} -Number KebaI2 {channel="keba:kecontact:1:I2"} -Number KebaI3 {channel="keba:kecontact:1:I3"} -Number KebaU1 {channel="keba:kecontact:1:U1"} -Number KebaU2 {channel="keba:kecontact:1:U2"} -Number KebaU3 {channel="keba:kecontact:1:U3"} -Number KebaPower {channel="keba:kecontact:1:power"} -Number KebaSessionEnergy {channel="keba:kecontact:1:sessionconsumption"} -Number KebaTotalEnergy {channel="keba:kecontact:1:totalconsumption"} -Switch KebaInputSwitch {channel="keba:kecontact:1:input"} -Switch KebaOutputSwitch {channel="keba:kecontact:1:output"} -Number KebaSetEnergyLimit {channel="keba:kecontact:1:setenergylimit"} +Number:Dimensionless KebaCurrentRange "Maximum supply current [%.1f %%]" {channel="keba:kecontact:1:maxpresetcurrentrange"} +Number:ElectricCurrent KebaCurrent "Maximum supply current [%.3f A]" {channel="keba:kecontact:1:maxpresetcurrent"} +Number:ElectricCurrent KebaSystemCurrent "Maximum system supply current [%.3f A]" {channel="keba:kecontact:1:maxsystemcurrent"} +Number:ElectricCurrent KebaFailSafeCurrent "Failsafe supply current [%.3f A]" {channel="keba:kecontact:1:failsafecurrent"} +String KebaState "Operating State [%s]" {channel="keba:kecontact:1:state"} +Switch KebaSwitch "Enabled" {channel="keba:kecontact:1:enabled"} +Switch KebaWallboxPlugged "Plugged into wallbox" {channel="keba:kecontact:1:wallbox"} +Switch KebaVehiclePlugged "Plugged into vehicle" {channel="keba:kecontact:1:vehicle"} +Switch KebaPlugLocked "Plug locked" {channel="keba:kecontact:1:locked"} +DateTime KebaUptime "Uptime [%s s]" {channel="keba:kecontact:1:uptime"} +Number:ElectricCurrent KebaI1 {channel="keba:kecontact:1:I1"} +Number:ElectricCurrent KebaI2 {channel="keba:kecontact:1:I2"} +Number:ElectricCurrent KebaI3 {channel="keba:kecontact:1:I3"} +Number:ElectricPotential KebaU1 {channel="keba:kecontact:1:U1"} +Number:ElectricPotential KebaU2 {channel="keba:kecontact:1:U2"} +Number:ElectricPotential KebaU3 {channel="keba:kecontact:1:U3"} +Number:Power KebaPower "Energy during current session [%.1f Wh]" {channel="keba:kecontact:1:power"} +Number:Energy KebaSessionEnergy {channel="keba:kecontact:1:sessionconsumption"} +Number:Energy KebaTotalEnergy "Energy during all sessions [%.1f Wh]" {channel="keba:kecontact:1:totalconsumption"} +Switch KebaInputSwitch {channel="keba:kecontact:1:input"} +Switch KebaOutputSwitch {channel="keba:kecontact:1:output"} +Number:Energy KebaSetEnergyLimit "Set charge energy limit [%.1f Wh]" {channel="keba:kecontact:1:setenergylimit"} ``` demo.sitemap: @@ -86,20 +88,63 @@ demo.sitemap: ``` sitemap demo label="Main Menu" { - Text label="Charging Station" { - Text item=KebaState label="Operating State [%s]" - Text item=KebaUptime - Switch item=KebaSwitch label="Enabled" mappings=[ON=ON, OFF=OFF ] - Switch item=KebaWallboxPlugged label="Plugged into wallbox" mappings=[ON=ON, OFF=OFF ] - Switch item=KebaVehiclePlugged label="Plugged into vehicle" mappings=[ON=ON, OFF=OFF ] - Switch item=KebaPlugLocked label="Plug locked" mappings=[ON=ON, OFF=OFF ] - Slider item=KebaCurrentRange switchSupport label="Maximum supply current [%.1f %%]" - Text item=KebaCurrent label="Maximum supply current [%.0f mA]" - Text item=KebaSystemCurrent label="Maximum system supply current [%.0f mA]" - Text item=KebaFailSafeCurrent label="Failsafe supply current [%.0f mA]" - Text item=KebaSessionEnergy label="Energy during current session [%.0f Wh]" - Text item=KebaTotalEnergy label="Energy during all sessions [%.0f Wh]" - Switch item=KebaSetEnergyLimit label="Set charge energy limit" mappings=[0="off", 20000="20kWh"] - } + Text label="Charging Station" { + Text item=KebaState + Text item=KebaUptime + Switch item=KebaSwitch + Switch item=KebaWallboxPlugged + Switch item=KebaVehiclePlugged + Switch item=KebaPlugLocked + Slider item=KebaCurrentRange + Text item=KebaCurrent + Text item=KebaSystemCurrent + Text item=KebaFailSafeCurrent + Text item=KebaSessionEnergy + Text item=KebaTotalEnergy + Switch item=KebaSetEnergyLimit + } } ``` + +## Troubleshooting + +### Enable Verbose Logging + +Enable `DEBUG` or `TRACE` (even more verbose) logging for the logger named: + + org.openhab.binding.keba + +If everything is working fine, you see the cyclic reception of `report 1`, `2` & `3` from the station. The frequency is according to the `refreshInterval` configuration. + +### UDP Ports used + + Send port = UDP 7090 + +The Keba station is the server + + Receive port = UDP 7090 + +This binding is providing the server + +UDP port 7090 needs to be available/free on the openHAB server. + + +In order to enable the UDP port 7090 on the Keba station with full functionality, `DIP switch 1.3` must be `ON`. +With `DIP switch 1.3 OFF` only ident-data can be read (`i` and `report 1`) but not the other reports as well as the commands needed for the write access. +After setting the DIP switch, you need to `power OFF` and `ON` the station. SW-reset via WebGUI seems not to be sufficient in order to apply the new configuration. + + +The right configuration can be validated as follows: + +- WebGUI DSW Settings: + - `DIP 1.3 | ON | UDP interface (SmartHome)` +- UDP response of `report 1`: + - `DIP-Sw1` `0x20` Bit is set (enable at least `DEBUG` log-level for the binding) + +### Supported stations + +- KeContact P20 charging station with network connection (LSA+ socket) + - Product code: `KC-P20-xxxxxx2x-xxx` or `KC-P20-xxxxxx3x-xxx` + - Firmware version: 2.5 or higher +- KeContact P30 charging station (c- or x-series) or BMW wallbox + - Firmware version 3.05 or higher diff --git a/bundles/org.openhab.binding.keba/src/main/java/org/openhab/binding/keba/internal/KebaBindingConstants.java b/bundles/org.openhab.binding.keba/src/main/java/org/openhab/binding/keba/internal/KebaBindingConstants.java index fa4b0d227ccfc..8280827d8a974 100644 --- a/bundles/org.openhab.binding.keba/src/main/java/org/openhab/binding/keba/internal/KebaBindingConstants.java +++ b/bundles/org.openhab.binding.keba/src/main/java/org/openhab/binding/keba/internal/KebaBindingConstants.java @@ -15,7 +15,7 @@ import java.util.ArrayList; import java.util.List; -import org.apache.commons.lang.ArrayUtils; +import org.apache.commons.lang3.ArrayUtils; import org.eclipse.jdt.annotation.NonNullByDefault; import org.openhab.core.thing.ThingTypeUID; @@ -44,7 +44,7 @@ public class KebaBindingConstants { public static final String CHANNEL_PLUG_LOCKED = "locked"; public static final String CHANNEL_ENABLED = "enabled"; public static final String CHANNEL_PILOT_CURRENT = "maxpilotcurrent"; - public static final String CHANNEL_PILOT_PWM = "pwmpilotcurrent"; + public static final String CHANNEL_PILOT_PWM = "maxpilotcurrentdutycyle"; public static final String CHANNEL_MAX_SYSTEM_CURRENT = "maxsystemcurrent"; public static final String CHANNEL_MAX_PRESET_CURRENT_RANGE = "maxpresetcurrentrange"; public static final String CHANNEL_MAX_PRESET_CURRENT = "maxpresetcurrent"; @@ -82,7 +82,7 @@ public enum KebaSeries { E('0'), B('1'), C('2', '3'), - X('A', 'B', 'C', 'D'); + X('A', 'B', 'C', 'D', 'E', 'G', 'H'); private final List things = new ArrayList<>(); @@ -104,7 +104,7 @@ public static KebaSeries getSeries(char text) throws IllegalArgumentException { } } - throw new IllegalArgumentException("Not a valid series"); + throw new IllegalArgumentException("Not a valid series: '" + text + "'"); } } } diff --git a/bundles/org.openhab.binding.keba/src/main/java/org/openhab/binding/keba/internal/handler/KeContactHandler.java b/bundles/org.openhab.binding.keba/src/main/java/org/openhab/binding/keba/internal/handler/KeContactHandler.java index 478c49bf9c24b..af2b999a91516 100644 --- a/bundles/org.openhab.binding.keba/src/main/java/org/openhab/binding/keba/internal/handler/KeContactHandler.java +++ b/bundles/org.openhab.binding.keba/src/main/java/org/openhab/binding/keba/internal/handler/KeContactHandler.java @@ -16,27 +16,33 @@ import java.io.IOException; import java.math.BigDecimal; -import java.net.InetAddress; +import java.net.InetSocketAddress; +import java.net.Socket; +import java.net.SocketAddress; import java.nio.ByteBuffer; -import java.text.SimpleDateFormat; -import java.util.Calendar; import java.util.Map; import java.util.Map.Entry; -import java.util.TimeZone; import java.util.concurrent.ScheduledFuture; import java.util.concurrent.TimeUnit; -import org.apache.commons.lang.StringUtils; +import javax.measure.quantity.Dimensionless; +import javax.measure.quantity.ElectricCurrent; +import javax.measure.quantity.ElectricPotential; +import javax.measure.quantity.Energy; +import javax.measure.quantity.Power; +import javax.measure.quantity.Time; + +import org.apache.commons.lang3.StringUtils; import org.openhab.binding.keba.internal.KebaBindingConstants.KebaSeries; import org.openhab.binding.keba.internal.KebaBindingConstants.KebaType; import org.openhab.core.cache.ExpiringCacheMap; import org.openhab.core.config.core.Configuration; -import org.openhab.core.library.types.DateTimeType; import org.openhab.core.library.types.DecimalType; import org.openhab.core.library.types.IncreaseDecreaseType; import org.openhab.core.library.types.OnOffType; -import org.openhab.core.library.types.PercentType; +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; @@ -63,18 +69,19 @@ public class KeContactHandler extends BaseThingHandler { public static final String IP_ADDRESS = "ipAddress"; public static final String POLLING_REFRESH_INTERVAL = "refreshInterval"; + public static final int POLLING_REFRESH_INTERVAL_DEFAULT = 15; public static final int REPORT_INTERVAL = 3000; - public static final int PING_TIME_OUT = 3000; public static final int BUFFER_SIZE = 1024; public static final int REMOTE_PORT_NUMBER = 7090; private static final String CACHE_REPORT_1 = "REPORT_1"; private static final String CACHE_REPORT_2 = "REPORT_2"; private static final String CACHE_REPORT_3 = "REPORT_3"; private static final String CACHE_REPORT_100 = "REPORT_100"; + public static final int SOCKET_TIME_OUT_MS = 3000; + public static final int SOCKET_CHECK_PORT_NUMBER = 80; private final Logger logger = LoggerFactory.getLogger(KeContactHandler.class); - protected final JsonParser parser = new JsonParser(); private final KeContactTransceiver transceiver; private ScheduledFuture pollingJob; @@ -94,32 +101,46 @@ public KeContactHandler(Thing thing, KeContactTransceiver transceiver) { @Override public void initialize() { - if (getConfig().get(IP_ADDRESS) != null && !getConfig().get(IP_ADDRESS).equals("")) { - transceiver.registerHandler(this); - - cache = new ExpiringCacheMap<>( - Math.max((((BigDecimal) getConfig().get(POLLING_REFRESH_INTERVAL)).intValue()) - 5, 0) * 1000); - - cache.put(CACHE_REPORT_1, () -> transceiver.send("report 1", getHandler())); - cache.put(CACHE_REPORT_2, () -> transceiver.send("report 2", getHandler())); - cache.put(CACHE_REPORT_3, () -> transceiver.send("report 3", getHandler())); - cache.put(CACHE_REPORT_100, () -> transceiver.send("report 100", getHandler())); - - if (pollingJob == null || pollingJob.isCancelled()) { - try { - pollingJob = scheduler.scheduleWithFixedDelay(this::pollingRunnable, 0, - ((BigDecimal) getConfig().get(POLLING_REFRESH_INTERVAL)).intValue(), TimeUnit.SECONDS); - } catch (Exception e) { - updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.NONE, - "An exception occurred while scheduling the polling job"); + try { + if (isKebaReachable()) { + transceiver.registerHandler(this); + + int refreshInterval = getRefreshInterval(); + cache = new ExpiringCacheMap<>(Math.max(refreshInterval - 5, 0) * 1000); + + cache.put(CACHE_REPORT_1, () -> transceiver.send("report 1", getHandler())); + cache.put(CACHE_REPORT_2, () -> transceiver.send("report 2", getHandler())); + cache.put(CACHE_REPORT_3, () -> transceiver.send("report 3", getHandler())); + cache.put(CACHE_REPORT_100, () -> transceiver.send("report 100", getHandler())); + + if (pollingJob == null || pollingJob.isCancelled()) { + pollingJob = scheduler.scheduleWithFixedDelay(this::pollingRunnable, 0, refreshInterval, + TimeUnit.SECONDS); } + } else { + updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, + "IP address or port number not set"); } - } else { - updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, - "IP address or port number not set"); + } catch (IOException e) { + updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, + "Exception during initialization of binding: " + e.toString()); } } + private boolean isKebaReachable() throws IOException { + boolean isReachable = false; + SocketAddress sockAddr = new InetSocketAddress(getIPAddress(), SOCKET_CHECK_PORT_NUMBER); + Socket socket = new Socket(); + try { + socket.connect(sockAddr, SOCKET_TIME_OUT_MS); + isReachable = true; + } finally { + socket.close(); + } + logger.debug("isKebaReachable() returns {}", isReachable); + return isReachable; + } + @Override public void dispose() { if (pollingJob != null && !pollingJob.isCancelled()) { @@ -134,6 +155,12 @@ public String getIPAddress() { return getConfig().get(IP_ADDRESS) != null ? (String) getConfig().get(IP_ADDRESS) : ""; } + public int getRefreshInterval() { + return getConfig().get(POLLING_REFRESH_INTERVAL) != null + ? ((BigDecimal) getConfig().get(POLLING_REFRESH_INTERVAL)).intValue() + : POLLING_REFRESH_INTERVAL_DEFAULT; + } + private KeContactHandler getHandler() { return this; } @@ -150,9 +177,10 @@ protected Configuration getConfig() { private void pollingRunnable() { try { + logger.debug("Running pollingRunnable to connect Keba wallbox"); long stamp = System.currentTimeMillis(); - if (!InetAddress.getByName(((String) getConfig().get(IP_ADDRESS))).isReachable(PING_TIME_OUT)) { - logger.debug("Ping timed out after '{}' milliseconds", System.currentTimeMillis() - stamp); + if (!isKebaReachable()) { + logger.debug("isKebaReachable() timed out after '{}' milliseconds", System.currentTimeMillis() - stamp); transceiver.unRegisterHandler(getHandler()); } else { if (getThing().getStatus() == ThingStatus.ONLINE) { @@ -207,7 +235,7 @@ protected void onData(ByteBuffer byteBuffer) { } try { - JsonObject readObject = parser.parse(response).getAsJsonObject(); + JsonObject readObject = JsonParser.parseString(response).getAsJsonObject(); for (Entry entry : readObject.entrySet()) { switch (entry.getKey()) { @@ -300,14 +328,15 @@ protected void onData(ByteBuffer byteBuffer) { case "Curr HW": { int state = entry.getValue().getAsInt(); maxSystemCurrent = state; - State newState = new DecimalType(state); + State newState = new QuantityType(state / 1000.0, Units.AMPERE); updateState(CHANNEL_MAX_SYSTEM_CURRENT, newState); if (maxSystemCurrent != 0) { if (maxSystemCurrent < maxPresetCurrent) { transceiver.send("curr " + String.valueOf(maxSystemCurrent), this); - updateState(CHANNEL_MAX_PRESET_CURRENT, new DecimalType(maxSystemCurrent)); - updateState(CHANNEL_MAX_PRESET_CURRENT_RANGE, - new PercentType((maxSystemCurrent - 6000) * 100 / (maxSystemCurrent - 6000))); + updateState(CHANNEL_MAX_PRESET_CURRENT, + new QuantityType(maxSystemCurrent / 1000.0, Units.AMPERE)); + updateState(CHANNEL_MAX_PRESET_CURRENT_RANGE, new QuantityType( + (maxSystemCurrent - 6000) * 100 / (maxSystemCurrent - 6000), Units.PERCENT)); } } else { logger.debug("maxSystemCurrent is 0. Ignoring."); @@ -317,24 +346,31 @@ protected void onData(ByteBuffer byteBuffer) { case "Curr user": { int state = entry.getValue().getAsInt(); maxPresetCurrent = state; - updateState(CHANNEL_MAX_PRESET_CURRENT, new DecimalType(state)); + State newState = new QuantityType(state / 1000.0, Units.AMPERE); + updateState(CHANNEL_MAX_PRESET_CURRENT, newState); if (maxSystemCurrent != 0) { - updateState(CHANNEL_MAX_PRESET_CURRENT_RANGE, - new PercentType(Math.min(100, (state - 6000) * 100 / (maxSystemCurrent - 6000)))); + updateState(CHANNEL_MAX_PRESET_CURRENT_RANGE, new QuantityType( + Math.min(100, (state - 6000) * 100 / (maxSystemCurrent - 6000)), Units.PERCENT)); } break; } case "Curr FS": { int state = entry.getValue().getAsInt(); - State newState = new DecimalType(state); + State newState = new QuantityType(state / 1000.0, Units.AMPERE); updateState(CHANNEL_FAILSAFE_CURRENT, newState); break; } case "Max curr": { int state = entry.getValue().getAsInt(); maxPresetCurrent = state; - updateState(CHANNEL_PILOT_CURRENT, new DecimalType(state)); - updateState(CHANNEL_PILOT_PWM, new DecimalType(state)); + State newState = new QuantityType(state / 1000.0, Units.AMPERE); + updateState(CHANNEL_PILOT_CURRENT, newState); + break; + } + case "Max curr %": { + int state = entry.getValue().getAsInt(); + State newState = new QuantityType(state / 10.0, Units.PERCENT); + updateState(CHANNEL_PILOT_PWM, newState); break; } case "Output": { @@ -367,73 +403,67 @@ protected void onData(ByteBuffer byteBuffer) { } case "Sec": { long state = entry.getValue().getAsLong(); - - Calendar uptime = Calendar.getInstance(); - uptime.setTimeZone(TimeZone.getTimeZone("GMT")); - uptime.setTimeInMillis(state * 1000); - SimpleDateFormat pFormatter = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss"); - pFormatter.setTimeZone(TimeZone.getTimeZone("GMT")); - - updateState(CHANNEL_UPTIME, new DateTimeType(pFormatter.format(uptime.getTime()))); + State newState = new QuantityType - Number + Number:ElectricCurrent - Preset Current in mA - + Preset Current + - Number + Number:ElectricCurrent - Current in mA - + Current + - Number + Number:ElectricCurrent - Maximal System Current in mA - + Maximal System Current + - Number + Number:ElectricCurrent - Failsafe Current in mA (if network is lost) - + Failsafe Current (if network is lost) + - Dimmer + Number:Dimensionless - Current in % of the 6000-63000 mA range accepted by the wallbox - + Current in % of the 6-63 A range accepted by the wallbox + - Number + Number:ElectricCurrent - Current preset value via Control pilot in mA - + Current value offered to the vehicle via control pilot signalization (PWM) + - - Number - - Current preset value via Control pilot in 0,1% of the PWM value - + + Number:Dimensionless + + Duty cycle of the control pilot signal + - DateTime + Number:Time System uptime since the last reset of the wallbox - + - Number + Number:ElectricPotential - Voltage in V - + Voltage + - Number + Number:Power - Active Power in W - + Active Power + - Number + Number:Dimensionless Power factor (cosphi) - + - Number - - Power consumption in Wh. - + Number:Energy + + Power consumption + - Number - + Number:Energy + Total energy consumption is added up after each completed charging session - + String @@ -248,10 +248,10 @@ - Number + Number:Energy - An energy limit for an already running or the next charging session. - + An energy limit for an already running or the next charging session + String diff --git a/bundles/org.openhab.binding.km200/src/main/java/org/openhab/binding/km200/internal/KM200Cryption.java b/bundles/org.openhab.binding.km200/src/main/java/org/openhab/binding/km200/internal/KM200Cryption.java index dfb9af331f58f..35261b4ffb190 100644 --- a/bundles/org.openhab.binding.km200/src/main/java/org/openhab/binding/km200/internal/KM200Cryption.java +++ b/bundles/org.openhab.binding.km200/src/main/java/org/openhab/binding/km200/internal/KM200Cryption.java @@ -23,7 +23,6 @@ import javax.crypto.Cipher; import javax.crypto.spec.SecretKeySpec; -import org.apache.commons.lang.StringUtils; import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.Nullable; import org.slf4j.Logger; @@ -131,8 +130,8 @@ private byte[] addZeroPadding(byte[] bdata, int bSize, String cSet) throws Unsup * @author Markus Eckhardt */ public void recreateKeys() { - if (StringUtils.isNotBlank(remoteDevice.getGatewayPassword()) - && StringUtils.isNotBlank(remoteDevice.getPrivatePassword()) && remoteDevice.getMD5Salt().length > 0) { + if (!remoteDevice.getGatewayPassword().isBlank() && !remoteDevice.getPrivatePassword().isBlank() + && remoteDevice.getMD5Salt().length > 0) { byte[] md5K1 = null; byte[] md5K2Init = null; byte[] md5K2Private = null; diff --git a/bundles/org.openhab.binding.km200/src/main/java/org/openhab/binding/km200/internal/KM200Device.java b/bundles/org.openhab.binding.km200/src/main/java/org/openhab/binding/km200/internal/KM200Device.java index aefc94246a8a2..7a6e73609d472 100644 --- a/bundles/org.openhab.binding.km200/src/main/java/org/openhab/binding/km200/internal/KM200Device.java +++ b/bundles/org.openhab.binding.km200/src/main/java/org/openhab/binding/km200/internal/KM200Device.java @@ -19,8 +19,7 @@ import java.util.List; import java.util.Map; -import org.apache.commons.lang.ArrayUtils; -import org.apache.commons.lang.StringUtils; +import org.apache.commons.lang3.ArrayUtils; import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.Nullable; import org.eclipse.jetty.client.HttpClient; @@ -41,7 +40,6 @@ public class KM200Device { private final Logger logger = LoggerFactory.getLogger(KM200Device.class); - private final JsonParser jsonParser = new JsonParser(); private final KM200Cryption comCryption; private final KM200Comm deviceCommunicator; @@ -84,7 +82,7 @@ public KM200Device(HttpClient httpClient) { } public Boolean isConfigured() { - return StringUtils.isNotBlank(ip4Address) && cryptKeyPriv.length > 0; + return !ip4Address.isBlank() && cryptKeyPriv.length > 0; } public String getIP4Address() { @@ -361,7 +359,7 @@ public Boolean containsService(String service) { logger.debug("{}: SERVICE NOT AVAILABLE", service); return null; } else { - nodeRoot = (JsonObject) jsonParser.parse(decodedData); + nodeRoot = (JsonObject) JsonParser.parseString(decodedData); } } else { logger.debug("Get empty reply"); diff --git a/bundles/org.openhab.binding.km200/src/main/java/org/openhab/binding/km200/internal/handler/KM200DataHandler.java b/bundles/org.openhab.binding.km200/src/main/java/org/openhab/binding/km200/internal/handler/KM200DataHandler.java index ea3c9bc0273b9..e490b59011adc 100644 --- a/bundles/org.openhab.binding.km200/src/main/java/org/openhab/binding/km200/internal/handler/KM200DataHandler.java +++ b/bundles/org.openhab.binding.km200/src/main/java/org/openhab/binding/km200/internal/handler/KM200DataHandler.java @@ -48,7 +48,6 @@ @NonNullByDefault public class KM200DataHandler { private final Logger logger = LoggerFactory.getLogger(KM200DataHandler.class); - private final JsonParser jsonParser = new JsonParser(); private final KM200Device remoteDevice; @@ -539,7 +538,7 @@ public KM200DataHandler(KM200Device remoteDevice) { /* The JSONArray of switch items can be send directly */ try { /* Check whether this input string is a valid JSONArray */ - JsonArray userArray = (JsonArray) jsonParser.parse(val); + JsonArray userArray = (JsonArray) JsonParser.parseString(val); newObject = userArray.getAsJsonObject(); } catch (JsonParseException e) { logger.warn("The input for the switchProgram is not a valid JSONArray : {}", diff --git a/bundles/org.openhab.binding.km200/src/main/java/org/openhab/binding/km200/internal/handler/KM200GatewayHandler.java b/bundles/org.openhab.binding.km200/src/main/java/org/openhab/binding/km200/internal/handler/KM200GatewayHandler.java index 8b11ac6e5e92e..3a125dd152467 100644 --- a/bundles/org.openhab.binding.km200/src/main/java/org/openhab/binding/km200/internal/handler/KM200GatewayHandler.java +++ b/bundles/org.openhab.binding.km200/src/main/java/org/openhab/binding/km200/internal/handler/KM200GatewayHandler.java @@ -30,7 +30,6 @@ import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; -import org.apache.commons.lang.StringUtils; import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.Nullable; import org.eclipse.jetty.client.HttpClient; @@ -177,7 +176,7 @@ private void getConfiguration() { switch (key) { case "ip4Address": String ip = (String) configuration.get("ip4Address"); - if (StringUtils.isNotBlank(ip)) { + if (ip != null && !ip.isBlank()) { try { InetAddress.getByName(ip); } catch (UnknownHostException e) { @@ -190,25 +189,25 @@ private void getConfiguration() { break; case "privateKey": String privateKey = (String) configuration.get("privateKey"); - if (StringUtils.isNotBlank(privateKey)) { + if (privateKey != null && !privateKey.isBlank()) { getDevice().setCryptKeyPriv(privateKey); } break; case "md5Salt": String md5Salt = (String) configuration.get("md5Salt"); - if (StringUtils.isNotBlank(md5Salt)) { + if (md5Salt != null && !md5Salt.isBlank()) { getDevice().setMD5Salt(md5Salt); } break; case "gatewayPassword": String gatewayPassword = (String) configuration.get("gatewayPassword"); - if (StringUtils.isNotBlank(gatewayPassword)) { + if (gatewayPassword != null && !gatewayPassword.isBlank()) { getDevice().setGatewayPassword(gatewayPassword); } break; case "privatePassword": String privatePassword = (String) configuration.get("privatePassword"); - if (StringUtils.isNotBlank(privatePassword)) { + if (privatePassword != null && !privatePassword.isBlank()) { getDevice().setPrivatePassword(privatePassword); } break; diff --git a/bundles/org.openhab.binding.knx/src/main/java/org/openhab/binding/knx/internal/dpt/KNXCoreTypeMapper.java b/bundles/org.openhab.binding.knx/src/main/java/org/openhab/binding/knx/internal/dpt/KNXCoreTypeMapper.java index 605f18a7b271c..ef2f93085dca4 100644 --- a/bundles/org.openhab.binding.knx/src/main/java/org/openhab/binding/knx/internal/dpt/KNXCoreTypeMapper.java +++ b/bundles/org.openhab.binding.knx/src/main/java/org/openhab/binding/knx/internal/dpt/KNXCoreTypeMapper.java @@ -13,6 +13,7 @@ package org.openhab.binding.knx.internal.dpt; import java.math.BigDecimal; +import java.math.RoundingMode; import java.text.DecimalFormat; import java.text.NumberFormat; import java.text.ParseException; @@ -1017,6 +1018,6 @@ private int getMainNumber(String dptID) { */ private int convertPercentToByte(PercentType percent) { return percent.toBigDecimal().multiply(BigDecimal.valueOf(255)) - .divide(BigDecimal.valueOf(100), 2, BigDecimal.ROUND_HALF_UP).intValue(); + .divide(BigDecimal.valueOf(100), 2, RoundingMode.HALF_UP).intValue(); } } diff --git a/bundles/org.openhab.binding.knx/src/main/resources/OH-INF/config/channelConfig.xml b/bundles/org.openhab.binding.knx/src/main/resources/OH-INF/config/channelConfig.xml index 4f8f11338100c..f4256cfce783f 100644 --- a/bundles/org.openhab.binding.knx/src/main/resources/OH-INF/config/channelConfig.xml +++ b/bundles/org.openhab.binding.knx/src/main/resources/OH-INF/config/channelConfig.xml @@ -7,37 +7,31 @@ - + The group address(es) in Group Address Notation to toggle the dimmer on or off - false - + The group address(es) in Group Address Notation to set the absolute position of the dimmer - false - + The group address(es) in Group Address Notation to increase or decrease the dimmer - false - + The group address(es) in Group Address Notation to toggle the dimmer on or off - false - + The group address(es) in Group Address Notation to set the absolute position of the dimmer - false - + The group address(es) in Group Address Notation to increase or decrease the dimmer - false @@ -49,47 +43,39 @@ - + The group address(es) in Group Address Notation for the color value - false - + The group address(es) in Group Address Notation to toggle the color on or off - false - + The group address(es) in Group Address Notation to set the absolute position of the color - false - + The group address(es) in Group Address Notation to increase or decrease the color - false - + The group address(es) in Group Address Notation for the color value - false - + The group address(es) in Group Address Notation to toggle the color on or off - false - + The group address(es) in Group Address Notation to set the absolute position of the color - false - + The group address(es) in Group Address Notation to increase or decrease the color - false @@ -101,29 +87,25 @@ - + The group address(es) in Group Address Notation to move the shutter in the DOWN or UP direction - false - + The group address(es) in Group Address Notation to start (MOVE) or STOP shutter movement - false - + The group address(es) in Group Address Notation to set the absolute position of the shutter, in % - false - + The group address(es) in Group Address Notation - true diff --git a/bundles/org.openhab.binding.knx/src/main/resources/OH-INF/thing/ip.xml b/bundles/org.openhab.binding.knx/src/main/resources/OH-INF/thing/ip.xml index 36fb253ed5669..89e2c6472f22f 100644 --- a/bundles/org.openhab.binding.knx/src/main/resources/OH-INF/thing/ip.xml +++ b/bundles/org.openhab.binding.knx/src/main/resources/OH-INF/thing/ip.xml @@ -9,10 +9,9 @@ This is a KNX IP interface or router - + The ip connection type for connecting to the KNX bus. Could be either TUNNEL or ROUTER - true @@ -23,9 +22,8 @@ Network address of the KNX/IP gateway network-address - + Port number of the KNX/IP gateway - false 3671 diff --git a/bundles/org.openhab.binding.knx/src/main/resources/OH-INF/thing/serial.xml b/bundles/org.openhab.binding.knx/src/main/resources/OH-INF/thing/serial.xml index 1a5f39510c82e..bb7705928889d 100644 --- a/bundles/org.openhab.binding.knx/src/main/resources/OH-INF/thing/serial.xml +++ b/bundles/org.openhab.binding.knx/src/main/resources/OH-INF/thing/serial.xml @@ -8,35 +8,30 @@ This is a serial interface for accessing the KNX bus - + serial-port The serial port to use for connecting to the KNX bus - true - + Time in milliseconds of how long should be paused between two read requests to the bus during initialization - true 50 - + Seconds to wait for a response from the KNX bus - true 10 - + Limits the read retries while initialization from the KNX bus - true 3 - + Seconds between connect retries when KNX link has been lost, 0 means never retry - true 0 diff --git a/bundles/org.openhab.binding.kodi/src/main/java/org/openhab/binding/kodi/internal/protocol/KodiClientSocket.java b/bundles/org.openhab.binding.kodi/src/main/java/org/openhab/binding/kodi/internal/protocol/KodiClientSocket.java index 8f77edfce2f28..5226074f33fe0 100644 --- a/bundles/org.openhab.binding.kodi/src/main/java/org/openhab/binding/kodi/internal/protocol/KodiClientSocket.java +++ b/bundles/org.openhab.binding.kodi/src/main/java/org/openhab/binding/kodi/internal/protocol/KodiClientSocket.java @@ -54,7 +54,6 @@ public class KodiClientSocket { private boolean connected = false; - private final JsonParser parser = new JsonParser(); private final Gson mapper = new Gson(); private final URI uri; private final WebSocketClient client; @@ -130,7 +129,7 @@ public void onConnect(Session wssession) { @OnWebSocketMessage public void onMessage(String message) { logger.trace("Message received from server: {}", message); - final JsonObject json = parser.parse(message).getAsJsonObject(); + final JsonObject json = JsonParser.parseString(message).getAsJsonObject(); if (json.has("id")) { int messageId = json.get("id").getAsInt(); if (messageId == nextMessageId - 1) { diff --git a/bundles/org.openhab.binding.kostalinverter/src/main/java/org/openhab/binding/kostalinverter/internal/secondgeneration/SecondGenerationConfigurationHandler.java b/bundles/org.openhab.binding.kostalinverter/src/main/java/org/openhab/binding/kostalinverter/internal/secondgeneration/SecondGenerationConfigurationHandler.java index e5d064e80a948..d045056555bef 100644 --- a/bundles/org.openhab.binding.kostalinverter/src/main/java/org/openhab/binding/kostalinverter/internal/secondgeneration/SecondGenerationConfigurationHandler.java +++ b/bundles/org.openhab.binding.kostalinverter/src/main/java/org/openhab/binding/kostalinverter/internal/secondgeneration/SecondGenerationConfigurationHandler.java @@ -54,12 +54,12 @@ public static void executeConfigurationChanges(HttpClient httpClient, String url String getAuthenticateResponse = httpClient.GET(urlLogin).getContentAsString(); try { - JsonObject getAuthenticateResponseJsonObject = (JsonObject) new JsonParser() - .parse(transformJsonResponse(getAuthenticateResponse)); + JsonObject getAuthenticateResponseJsonObject = (JsonObject) JsonParser + .parseString(transformJsonResponse(getAuthenticateResponse)); sessionId = extractSessionId(getAuthenticateResponseJsonObject); - JsonObject authenticateJsonObject = new JsonParser().parse(getAuthenticateResponse.toString()) + JsonObject authenticateJsonObject = JsonParser.parseString(getAuthenticateResponse.toString()) .getAsJsonObject(); salt = authenticateJsonObject.get("salt").getAsString(); @@ -84,8 +84,8 @@ public static void executeConfigurationChanges(HttpClient httpClient, String url String loginPostResponse = new String(loginPostJsonDataContentResponse.getContent(), StandardCharsets.UTF_8); - JsonObject loginPostJsonObject = (JsonObject) new JsonParser() - .parse(transformJsonResponse(loginPostResponse)); + JsonObject loginPostJsonObject = (JsonObject) JsonParser + .parseString(transformJsonResponse(loginPostResponse)); sessionId = extractSessionId(loginPostJsonObject); diff --git a/bundles/org.openhab.binding.lametrictime/src/main/java/org/openhab/binding/lametrictime/internal/handler/LaMetricTimeHandler.java b/bundles/org.openhab.binding.lametrictime/src/main/java/org/openhab/binding/lametrictime/internal/handler/LaMetricTimeHandler.java index 887a7dbbf799c..026076918ac32 100644 --- a/bundles/org.openhab.binding.lametrictime/src/main/java/org/openhab/binding/lametrictime/internal/handler/LaMetricTimeHandler.java +++ b/bundles/org.openhab.binding.lametrictime/src/main/java/org/openhab/binding/lametrictime/internal/handler/LaMetricTimeHandler.java @@ -25,7 +25,6 @@ import javax.ws.rs.client.ClientBuilder; -import org.apache.commons.lang.StringUtils; import org.openhab.binding.lametrictime.api.Configuration; import org.openhab.binding.lametrictime.api.LaMetricTime; import org.openhab.binding.lametrictime.api.local.ApplicationActivationException; @@ -360,12 +359,12 @@ public Collection getConfigStatus() { String host = config.host; String apiKey = config.apiKey; - if (StringUtils.isEmpty(host)) { + if (host == null || host.isEmpty()) { configStatusMessages.add(ConfigStatusMessage.Builder.error(HOST) .withMessageKeySuffix(LaMetricTimeConfigStatusMessage.HOST_MISSING).withArguments(HOST).build()); } - if (StringUtils.isEmpty(apiKey)) { + if (apiKey == null || apiKey.isEmpty()) { configStatusMessages.add(ConfigStatusMessage.Builder.error(API_KEY) .withMessageKeySuffix(LaMetricTimeConfigStatusMessage.API_KEY_MISSING).withArguments(API_KEY) .build()); diff --git a/bundles/org.openhab.binding.leapmotion/src/main/feature/feature.xml b/bundles/org.openhab.binding.leapmotion/src/main/feature/feature.xml index 4292bff68d6d0..75e76a87d0b36 100644 --- a/bundles/org.openhab.binding.leapmotion/src/main/feature/feature.xml +++ b/bundles/org.openhab.binding.leapmotion/src/main/feature/feature.xml @@ -5,7 +5,7 @@ openhab-runtime-base mvn:org.openhab.addons.bundles/org.openhab.binding.leapmotion/${project.version} - mvn:${project.groupId}/org.openhab.addons.features.karaf.openhab-addons-external/${project.version}/lib/libLeap - mvn:${project.groupId}/org.openhab.addons.features.karaf.openhab-addons-external/${project.version}/lib/libLeapJava + mvn:org.openhab.addons.features.karaf/org.openhab.addons.features.karaf.openhab-addons-external/${project.version}/lib/libLeap + mvn:org.openhab.addons.features.karaf/org.openhab.addons.features.karaf.openhab-addons-external/${project.version}/lib/libLeapJava diff --git a/bundles/org.openhab.binding.lgwebos/src/main/java/org/openhab/binding/lgwebos/internal/action/LGWebOSActions.java b/bundles/org.openhab.binding.lgwebos/src/main/java/org/openhab/binding/lgwebos/internal/action/LGWebOSActions.java index bc34e2e381d03..663318bd3e181 100644 --- a/bundles/org.openhab.binding.lgwebos/src/main/java/org/openhab/binding/lgwebos/internal/action/LGWebOSActions.java +++ b/bundles/org.openhab.binding.lgwebos/src/main/java/org/openhab/binding/lgwebos/internal/action/LGWebOSActions.java @@ -154,8 +154,7 @@ public void launchApplication( @ActionInput(name = "appId", label = "@text/actionLaunchApplicationInputAppIDLabel", description = "@text/actionLaunchApplicationInputAppIDDesc") String appId, @ActionInput(name = "params", label = "@text/actionLaunchApplicationInputParamsLabel", description = "@text/actionLaunchApplicationInputParamsDesc") String params) { try { - JsonParser parser = new JsonParser(); - JsonObject payload = (JsonObject) parser.parse(params); + JsonObject payload = (JsonObject) JsonParser.parseString(params); Optional appInfo = getAppInfos().stream().filter(a -> a.getId().equals(appId)).findFirst(); if (appInfo.isPresent()) { diff --git a/bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/LifxChannelFactory.java b/bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/LifxChannelFactory.java index dca40a8ef1fb5..b507648a71f7d 100644 --- a/bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/LifxChannelFactory.java +++ b/bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/LifxChannelFactory.java @@ -19,7 +19,7 @@ /** * The {@link LifxChannelFactory} creates dynamic LIFX channels. * - * @author Wouter Born - Add i18n support + * @author Wouter Born - Initial contribution */ @NonNullByDefault public interface LifxChannelFactory { diff --git a/bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/LifxChannelFactoryImpl.java b/bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/LifxChannelFactoryImpl.java index 7c8c3b12ecca4..333c610a7486f 100644 --- a/bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/LifxChannelFactoryImpl.java +++ b/bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/LifxChannelFactoryImpl.java @@ -34,7 +34,7 @@ /** * The {@link LifxChannelFactoryImpl} creates dynamic LIFX channels. * - * @author Wouter Born - Add i18n support + * @author Wouter Born - Initial contribution */ @NonNullByDefault @Component(service = LifxChannelFactory.class) diff --git a/bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/LifxLightCommunicationHandler.java b/bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/LifxLightCommunicationHandler.java index 375ff33aa8bed..bd27a3a7a83fc 100644 --- a/bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/LifxLightCommunicationHandler.java +++ b/bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/LifxLightCommunicationHandler.java @@ -32,12 +32,12 @@ import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.Nullable; +import org.openhab.binding.lifx.internal.dto.GetServiceRequest; +import org.openhab.binding.lifx.internal.dto.Packet; +import org.openhab.binding.lifx.internal.dto.StateServiceResponse; import org.openhab.binding.lifx.internal.fields.MACAddress; import org.openhab.binding.lifx.internal.handler.LifxLightHandler.CurrentLightState; import org.openhab.binding.lifx.internal.listener.LifxResponsePacketListener; -import org.openhab.binding.lifx.internal.protocol.GetServiceRequest; -import org.openhab.binding.lifx.internal.protocol.Packet; -import org.openhab.binding.lifx.internal.protocol.StateServiceResponse; import org.openhab.binding.lifx.internal.util.LifxNetworkUtil; import org.openhab.binding.lifx.internal.util.LifxSelectorUtil; import org.slf4j.Logger; @@ -46,7 +46,7 @@ /** * The {@link LifxLightCommunicationHandler} is responsible for the communications with a light. * - * @author Wouter Born - Extracted class from LifxLightHandler + * @author Wouter Born - Initial contribution */ @NonNullByDefault public class LifxLightCommunicationHandler { diff --git a/bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/LifxLightConfig.java b/bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/LifxLightConfig.java index f39c6fbf5162d..4b193398c824a 100644 --- a/bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/LifxLightConfig.java +++ b/bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/LifxLightConfig.java @@ -33,7 +33,7 @@ public class LifxLightConfig { public @Nullable MACAddress getMACAddress() { String localDeviceId = deviceId; - return localDeviceId == null ? null : new MACAddress(localDeviceId, true); + return localDeviceId == null ? null : new MACAddress(localDeviceId); } public @Nullable InetSocketAddress getHost() { diff --git a/bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/LifxLightContext.java b/bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/LifxLightContext.java index 254d87921c8b1..5a65ff3fa412c 100644 --- a/bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/LifxLightContext.java +++ b/bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/LifxLightContext.java @@ -15,14 +15,14 @@ import java.util.concurrent.ScheduledExecutorService; import org.eclipse.jdt.annotation.NonNullByDefault; +import org.openhab.binding.lifx.internal.LifxProduct.Features; import org.openhab.binding.lifx.internal.handler.LifxLightHandler; import org.openhab.binding.lifx.internal.handler.LifxLightHandler.CurrentLightState; -import org.openhab.binding.lifx.internal.protocol.Product; /** * The {@link LifxLightContext} shares the context of a light with {@link LifxLightHandler} helper objects. * - * @author Wouter Born - Add optional host configuration parameter + * @author Wouter Born - Initial contribution */ @NonNullByDefault public class LifxLightContext { @@ -31,14 +31,14 @@ public class LifxLightContext { private final LifxLightConfig configuration; private final CurrentLightState currentLightState; private final LifxLightState pendingLightState; - private final Product product; + private final Features features; private final ScheduledExecutorService scheduler; - public LifxLightContext(String logId, Product product, LifxLightConfig configuration, + public LifxLightContext(String logId, Features features, LifxLightConfig configuration, CurrentLightState currentLightState, LifxLightState pendingLightState, ScheduledExecutorService scheduler) { this.logId = logId; this.configuration = configuration; - this.product = product; + this.features = features; this.currentLightState = currentLightState; this.pendingLightState = pendingLightState; this.scheduler = scheduler; @@ -52,8 +52,8 @@ public LifxLightConfig getConfiguration() { return configuration; } - public Product getProduct() { - return product; + public Features getFeatures() { + return features; } public CurrentLightState getCurrentLightState() { diff --git a/bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/LifxLightCurrentStateUpdater.java b/bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/LifxLightCurrentStateUpdater.java index ad94aa26514f5..ef6ad12b69152 100644 --- a/bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/LifxLightCurrentStateUpdater.java +++ b/bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/LifxLightCurrentStateUpdater.java @@ -13,7 +13,7 @@ package org.openhab.binding.lifx.internal; import static org.openhab.binding.lifx.internal.LifxBindingConstants.MIN_ZONE_INDEX; -import static org.openhab.binding.lifx.internal.protocol.Product.Feature.*; +import static org.openhab.binding.lifx.internal.LifxProduct.Feature.*; import static org.openhab.binding.lifx.internal.util.LifxMessageUtil.infraredToPercentType; import java.util.concurrent.ScheduledExecutorService; @@ -23,22 +23,22 @@ import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.Nullable; +import org.openhab.binding.lifx.internal.LifxProduct.Features; +import org.openhab.binding.lifx.internal.dto.GetColorZonesRequest; +import org.openhab.binding.lifx.internal.dto.GetLightInfraredRequest; +import org.openhab.binding.lifx.internal.dto.GetRequest; +import org.openhab.binding.lifx.internal.dto.GetTileEffectRequest; +import org.openhab.binding.lifx.internal.dto.GetWifiInfoRequest; +import org.openhab.binding.lifx.internal.dto.Packet; +import org.openhab.binding.lifx.internal.dto.StateLightInfraredResponse; +import org.openhab.binding.lifx.internal.dto.StateLightPowerResponse; +import org.openhab.binding.lifx.internal.dto.StateMultiZoneResponse; +import org.openhab.binding.lifx.internal.dto.StatePowerResponse; +import org.openhab.binding.lifx.internal.dto.StateResponse; +import org.openhab.binding.lifx.internal.dto.StateTileEffectResponse; +import org.openhab.binding.lifx.internal.dto.StateWifiInfoResponse; import org.openhab.binding.lifx.internal.fields.HSBK; import org.openhab.binding.lifx.internal.handler.LifxLightHandler.CurrentLightState; -import org.openhab.binding.lifx.internal.protocol.GetColorZonesRequest; -import org.openhab.binding.lifx.internal.protocol.GetLightInfraredRequest; -import org.openhab.binding.lifx.internal.protocol.GetRequest; -import org.openhab.binding.lifx.internal.protocol.GetTileEffectRequest; -import org.openhab.binding.lifx.internal.protocol.GetWifiInfoRequest; -import org.openhab.binding.lifx.internal.protocol.Packet; -import org.openhab.binding.lifx.internal.protocol.Product; -import org.openhab.binding.lifx.internal.protocol.StateLightInfraredResponse; -import org.openhab.binding.lifx.internal.protocol.StateLightPowerResponse; -import org.openhab.binding.lifx.internal.protocol.StateMultiZoneResponse; -import org.openhab.binding.lifx.internal.protocol.StatePowerResponse; -import org.openhab.binding.lifx.internal.protocol.StateResponse; -import org.openhab.binding.lifx.internal.protocol.StateTileEffectResponse; -import org.openhab.binding.lifx.internal.protocol.StateWifiInfoResponse; import org.openhab.core.library.types.PercentType; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -47,7 +47,7 @@ * The {@link LifxLightCurrentStateUpdater} sends packets to a light in order to update the {@code currentLightState} to * the actual light state. * - * @author Wouter Born - Extracted class from LifxLightHandler + * @author Wouter Born - Initial contribution */ @NonNullByDefault public class LifxLightCurrentStateUpdater { @@ -57,7 +57,7 @@ public class LifxLightCurrentStateUpdater { private final Logger logger = LoggerFactory.getLogger(LifxLightCurrentStateUpdater.class); private final String logId; - private final Product product; + private final Features features; private final CurrentLightState currentLightState; private final ScheduledExecutorService scheduler; private final LifxLightCommunicationHandler communicationHandler; @@ -71,7 +71,7 @@ public class LifxLightCurrentStateUpdater { public LifxLightCurrentStateUpdater(LifxLightContext context, LifxLightCommunicationHandler communicationHandler) { this.logId = context.getLogId(); - this.product = context.getProduct(); + this.features = context.getFeatures(); this.currentLightState = context.getCurrentLightState(); this.scheduler = context.getScheduler(); this.communicationHandler = communicationHandler; @@ -133,13 +133,13 @@ public void stop() { private void sendLightStateRequests() { communicationHandler.sendPacket(new GetRequest()); - if (product.hasFeature(INFRARED)) { + if (features.hasFeature(INFRARED)) { communicationHandler.sendPacket(new GetLightInfraredRequest()); } - if (product.hasFeature(MULTIZONE)) { + if (features.hasFeature(MULTIZONE)) { communicationHandler.sendPacket(new GetColorZonesRequest()); } - if (product.hasFeature(TILE_EFFECT)) { + if (features.hasFeature(TILE_EFFECT)) { communicationHandler.sendPacket(new GetTileEffectRequest()); } if (updateSignalStrength) { diff --git a/bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/LifxLightDiscovery.java b/bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/LifxLightDiscovery.java index 21011010dbe48..5799af1994af7 100644 --- a/bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/LifxLightDiscovery.java +++ b/bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/LifxLightDiscovery.java @@ -26,18 +26,16 @@ import java.util.concurrent.TimeUnit; import java.util.function.Supplier; -import org.apache.commons.lang.StringUtils; import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.Nullable; +import org.openhab.binding.lifx.internal.dto.GetLabelRequest; +import org.openhab.binding.lifx.internal.dto.GetServiceRequest; +import org.openhab.binding.lifx.internal.dto.GetVersionRequest; +import org.openhab.binding.lifx.internal.dto.Packet; +import org.openhab.binding.lifx.internal.dto.StateLabelResponse; +import org.openhab.binding.lifx.internal.dto.StateServiceResponse; +import org.openhab.binding.lifx.internal.dto.StateVersionResponse; import org.openhab.binding.lifx.internal.fields.MACAddress; -import org.openhab.binding.lifx.internal.protocol.GetLabelRequest; -import org.openhab.binding.lifx.internal.protocol.GetServiceRequest; -import org.openhab.binding.lifx.internal.protocol.GetVersionRequest; -import org.openhab.binding.lifx.internal.protocol.Packet; -import org.openhab.binding.lifx.internal.protocol.Product; -import org.openhab.binding.lifx.internal.protocol.StateLabelResponse; -import org.openhab.binding.lifx.internal.protocol.StateServiceResponse; -import org.openhab.binding.lifx.internal.protocol.StateVersionResponse; import org.openhab.binding.lifx.internal.util.LifxSelectorUtil; import org.openhab.core.config.discovery.AbstractDiscoveryService; import org.openhab.core.config.discovery.DiscoveryResult; @@ -87,7 +85,7 @@ private class DiscoveredLight { private InetSocketAddress socketAddress; private String logId; private @Nullable String label; - private @Nullable Product product; + private @Nullable LifxProduct product; private long productVersion; private boolean supportedProduct = true; private LifxSelectorContext selectorContext; @@ -300,8 +298,11 @@ private void handlePacket(Packet packet, InetSocketAddress address) { light.label = ((StateLabelResponse) packet).getLabel().trim(); } else if (packet instanceof StateVersionResponse) { try { - light.product = Product.getProductFromProductID(((StateVersionResponse) packet).getProduct()); + LifxProduct product = LifxProduct + .getProductFromProductID(((StateVersionResponse) packet).getProduct()); + light.product = product; light.productVersion = ((StateVersionResponse) packet).getVersion(); + light.supportedProduct = product.isLight(); } catch (IllegalArgumentException e) { logger.debug("Discovered an unsupported light ({}): {}", light.macAddress.getAsLabel(), e.getMessage()); @@ -310,7 +311,7 @@ private void handlePacket(Packet packet, InetSocketAddress address) { } } - if (light != null && light.isDataComplete()) { + if (light != null && light.supportedProduct && light.isDataComplete()) { try { thingDiscovered(createDiscoveryResult(light)); } catch (IllegalArgumentException e) { @@ -322,7 +323,7 @@ private void handlePacket(Packet packet, InetSocketAddress address) { } private DiscoveryResult createDiscoveryResult(DiscoveredLight light) throws IllegalArgumentException { - Product product = light.product; + LifxProduct product = light.product; if (product == null) { throw new IllegalArgumentException("Product of discovered light is null"); } @@ -331,7 +332,7 @@ private DiscoveryResult createDiscoveryResult(DiscoveredLight light) throws Ille ThingUID thingUID = new ThingUID(product.getThingTypeUID(), macAsLabel); String label = light.label; - if (StringUtils.isBlank(label)) { + if (label == null || label.isBlank()) { label = product.getName(); } diff --git a/bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/LifxLightOnlineStateUpdater.java b/bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/LifxLightOnlineStateUpdater.java index c40400e3678ba..62ef83091cdf9 100644 --- a/bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/LifxLightOnlineStateUpdater.java +++ b/bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/LifxLightOnlineStateUpdater.java @@ -21,17 +21,17 @@ import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.Nullable; +import org.openhab.binding.lifx.internal.dto.GetEchoRequest; +import org.openhab.binding.lifx.internal.dto.GetServiceRequest; +import org.openhab.binding.lifx.internal.dto.Packet; import org.openhab.binding.lifx.internal.handler.LifxLightHandler.CurrentLightState; -import org.openhab.binding.lifx.internal.protocol.GetEchoRequest; -import org.openhab.binding.lifx.internal.protocol.GetServiceRequest; -import org.openhab.binding.lifx.internal.protocol.Packet; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * The {@link LifxLightOnlineStateUpdater} sets the state of a light offline when it no longer responds to echo packets. * - * @author Wouter Born - Extracted class from LifxLightHandler + * @author Wouter Born - Initial contribution */ @NonNullByDefault public class LifxLightOnlineStateUpdater { diff --git a/bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/LifxLightPropertiesUpdater.java b/bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/LifxLightPropertiesUpdater.java index 7be78cffbd52c..9de0a75ff6f6a 100644 --- a/bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/LifxLightPropertiesUpdater.java +++ b/bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/LifxLightPropertiesUpdater.java @@ -27,17 +27,16 @@ import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.Nullable; +import org.openhab.binding.lifx.internal.dto.GetHostFirmwareRequest; +import org.openhab.binding.lifx.internal.dto.GetVersionRequest; +import org.openhab.binding.lifx.internal.dto.GetWifiFirmwareRequest; +import org.openhab.binding.lifx.internal.dto.Packet; +import org.openhab.binding.lifx.internal.dto.StateHostFirmwareResponse; +import org.openhab.binding.lifx.internal.dto.StateVersionResponse; +import org.openhab.binding.lifx.internal.dto.StateWifiFirmwareResponse; import org.openhab.binding.lifx.internal.fields.MACAddress; import org.openhab.binding.lifx.internal.handler.LifxLightHandler.CurrentLightState; import org.openhab.binding.lifx.internal.listener.LifxPropertiesUpdateListener; -import org.openhab.binding.lifx.internal.protocol.GetHostFirmwareRequest; -import org.openhab.binding.lifx.internal.protocol.GetVersionRequest; -import org.openhab.binding.lifx.internal.protocol.GetWifiFirmwareRequest; -import org.openhab.binding.lifx.internal.protocol.Packet; -import org.openhab.binding.lifx.internal.protocol.Product; -import org.openhab.binding.lifx.internal.protocol.StateHostFirmwareResponse; -import org.openhab.binding.lifx.internal.protocol.StateVersionResponse; -import org.openhab.binding.lifx.internal.protocol.StateWifiFirmwareResponse; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -45,7 +44,7 @@ * The {@link LifxLightPropertiesUpdater} updates the light properties when a light goes online. When packets get lost * the requests are resent when the {@code UPDATE_INTERVAL} elapses. * - * @author Wouter Born - Update light properties when online + * @author Wouter Born - Initial contribution */ @NonNullByDefault public class LifxLightPropertiesUpdater { @@ -157,7 +156,7 @@ public void handleResponsePacket(Packet packet) { properties.put(LifxBindingConstants.PROPERTY_PRODUCT_VERSION, Long.toString(productVersion)); try { - Product product = Product.getProductFromProductID(productId); + LifxProduct product = LifxProduct.getProductFromProductID(productId); properties.put(LifxBindingConstants.PROPERTY_PRODUCT_NAME, product.getName()); properties.put(LifxBindingConstants.PROPERTY_VENDOR_ID, Long.toString(product.getVendor().getID())); properties.put(LifxBindingConstants.PROPERTY_VENDOR_NAME, product.getVendor().getName()); diff --git a/bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/LifxLightState.java b/bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/LifxLightState.java index a4db52c45cfe6..b3bbefaf201a7 100644 --- a/bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/LifxLightState.java +++ b/bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/LifxLightState.java @@ -22,11 +22,11 @@ import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.Nullable; +import org.openhab.binding.lifx.internal.dto.Effect; +import org.openhab.binding.lifx.internal.dto.PowerState; +import org.openhab.binding.lifx.internal.dto.SignalStrength; import org.openhab.binding.lifx.internal.fields.HSBK; import org.openhab.binding.lifx.internal.listener.LifxLightStateListener; -import org.openhab.binding.lifx.internal.protocol.Effect; -import org.openhab.binding.lifx.internal.protocol.PowerState; -import org.openhab.binding.lifx.internal.protocol.SignalStrength; import org.openhab.core.library.types.HSBType; import org.openhab.core.library.types.OnOffType; import org.openhab.core.library.types.PercentType; @@ -34,7 +34,7 @@ /** * The {@link LifxLightState} stores the properties that represent the state of a light. * - * @author Wouter Born - Extracted class from LifxLightHandler, added listener logic + * @author Wouter Born - Initial contribution */ @NonNullByDefault public class LifxLightState { diff --git a/bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/LifxLightStateChanger.java b/bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/LifxLightStateChanger.java index 4e2ccd16ab8ec..dbccb2113b5d7 100644 --- a/bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/LifxLightStateChanger.java +++ b/bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/LifxLightStateChanger.java @@ -13,7 +13,7 @@ package org.openhab.binding.lifx.internal; import static org.openhab.binding.lifx.internal.LifxBindingConstants.PACKET_INTERVAL; -import static org.openhab.binding.lifx.internal.protocol.Product.Feature.MULTIZONE; +import static org.openhab.binding.lifx.internal.LifxProduct.Feature.MULTIZONE; import static org.openhab.binding.lifx.internal.util.LifxMessageUtil.*; import java.time.Duration; @@ -29,25 +29,25 @@ import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.Nullable; +import org.openhab.binding.lifx.internal.LifxProduct.Features; +import org.openhab.binding.lifx.internal.dto.AcknowledgementResponse; +import org.openhab.binding.lifx.internal.dto.ApplicationRequest; +import org.openhab.binding.lifx.internal.dto.Effect; +import org.openhab.binding.lifx.internal.dto.GetColorZonesRequest; +import org.openhab.binding.lifx.internal.dto.GetLightInfraredRequest; +import org.openhab.binding.lifx.internal.dto.GetLightPowerRequest; +import org.openhab.binding.lifx.internal.dto.GetRequest; +import org.openhab.binding.lifx.internal.dto.Packet; +import org.openhab.binding.lifx.internal.dto.PowerState; +import org.openhab.binding.lifx.internal.dto.SetColorRequest; +import org.openhab.binding.lifx.internal.dto.SetColorZonesRequest; +import org.openhab.binding.lifx.internal.dto.SetLightInfraredRequest; +import org.openhab.binding.lifx.internal.dto.SetLightPowerRequest; +import org.openhab.binding.lifx.internal.dto.SetPowerRequest; +import org.openhab.binding.lifx.internal.dto.SetTileEffectRequest; +import org.openhab.binding.lifx.internal.dto.SignalStrength; import org.openhab.binding.lifx.internal.fields.HSBK; import org.openhab.binding.lifx.internal.listener.LifxLightStateListener; -import org.openhab.binding.lifx.internal.protocol.AcknowledgementResponse; -import org.openhab.binding.lifx.internal.protocol.ApplicationRequest; -import org.openhab.binding.lifx.internal.protocol.Effect; -import org.openhab.binding.lifx.internal.protocol.GetColorZonesRequest; -import org.openhab.binding.lifx.internal.protocol.GetLightInfraredRequest; -import org.openhab.binding.lifx.internal.protocol.GetLightPowerRequest; -import org.openhab.binding.lifx.internal.protocol.GetRequest; -import org.openhab.binding.lifx.internal.protocol.Packet; -import org.openhab.binding.lifx.internal.protocol.PowerState; -import org.openhab.binding.lifx.internal.protocol.Product; -import org.openhab.binding.lifx.internal.protocol.SetColorRequest; -import org.openhab.binding.lifx.internal.protocol.SetColorZonesRequest; -import org.openhab.binding.lifx.internal.protocol.SetLightInfraredRequest; -import org.openhab.binding.lifx.internal.protocol.SetLightPowerRequest; -import org.openhab.binding.lifx.internal.protocol.SetPowerRequest; -import org.openhab.binding.lifx.internal.protocol.SetTileEffectRequest; -import org.openhab.binding.lifx.internal.protocol.SignalStrength; import org.openhab.core.library.types.PercentType; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -57,7 +57,7 @@ * light so the change the actual light state to that of the {@code pendingLightState}. When the light does not * acknowledge a packet, it resends it (max 3 times). * - * @author Wouter Born - Extracted class from LifxLightHandler, added logic for handling packet loss + * @author Wouter Born - Initial contribution */ @NonNullByDefault public class LifxLightStateChanger implements LifxLightStateListener { @@ -75,7 +75,7 @@ public class LifxLightStateChanger implements LifxLightStateListener { private final Logger logger = LoggerFactory.getLogger(LifxLightStateChanger.class); private final String logId; - private final Product product; + private final Features features; private final Duration fadeTime; private final LifxLightState pendingLightState; private final ScheduledExecutorService scheduler; @@ -105,7 +105,7 @@ private boolean hasAcknowledgeIntervalElapsed() { public LifxLightStateChanger(LifxLightContext context, LifxLightCommunicationHandler communicationHandler) { this.logId = context.getLogId(); - this.product = context.getProduct(); + this.features = context.getFeatures(); this.fadeTime = context.getConfiguration().getFadeTime(); this.pendingLightState = context.getPendingLightState(); this.scheduler = context.getScheduler(); @@ -372,7 +372,7 @@ public void handleResponsePacket(Packet packet) { } private void getZonesIfZonesAreSet() { - if (product.hasFeature(MULTIZONE)) { + if (features.hasFeature(MULTIZONE)) { List pending = pendingPacketsMap.get(SetColorZonesRequest.TYPE); if (pending == null || pending.isEmpty()) { GetColorZonesRequest zoneColorPacket = new GetColorZonesRequest(); diff --git a/bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/LifxProduct.java b/bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/LifxProduct.java new file mode 100644 index 0000000000000..3f561393514e1 --- /dev/null +++ b/bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/LifxProduct.java @@ -0,0 +1,433 @@ +/** + * Copyright (c) 2010-2021 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.lifx.internal; + +import static org.openhab.binding.lifx.internal.LifxProduct.Feature.*; +import static org.openhab.binding.lifx.internal.LifxProduct.TemperatureRange.*; +import static org.openhab.binding.lifx.internal.LifxProduct.Vendor.LIFX; + +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.openhab.core.thing.ThingTypeUID; + +/** + * Enumerates the LIFX products, their IDs and feature set. + * + * @see https://lan.developer.lifx.com/docs/lifx-products + * + * @author Wouter Born - Initial contribution + * @author Wouter Born - Add temperature ranges and simplify feature definitions + */ +@NonNullByDefault +public enum LifxProduct { + + PRODUCT_1(1, "LIFX Original 1000", new Features(TR_2500_9000, COLOR)), + PRODUCT_3(3, "LIFX Color 650", new Features(TR_2500_9000, COLOR)), + PRODUCT_10(10, "LIFX White 800 (Low Voltage)", new Features(TR_2700_6500)), + PRODUCT_11(11, "LIFX White 800 (High Voltage)", new Features(TR_2700_6500)), + PRODUCT_15(15, "LIFX Color 1000", new Features(TR_2500_9000, COLOR)), + PRODUCT_18(18, "LIFX White 900 BR30 (Low Voltage)", new Features(TR_2700_6500)), + PRODUCT_19(19, "LIFX White 900 BR30 (High Voltage)", new Features(TR_2700_6500)), + PRODUCT_20(20, "LIFX Color 1000 BR30", new Features(TR_2500_9000, COLOR)), + PRODUCT_22(22, "LIFX Color 1000", new Features(TR_2500_9000, COLOR)), + PRODUCT_27(27, "LIFX A19", new Features(TR_2500_9000, COLOR), // + new Upgrade(2, 80, new Features(TR_1500_9000))), + PRODUCT_28(28, "LIFX BR30", new Features(TR_2500_9000, COLOR), // + new Upgrade(2, 80, new Features(TR_1500_9000))), + PRODUCT_29(29, "LIFX A19 Night Vision", new Features(TR_2500_9000, COLOR, INFRARED), // + new Upgrade(2, 80, new Features(TR_1500_9000))), + PRODUCT_30(30, "LIFX BR30 Night Vision", new Features(TR_2500_9000, COLOR, INFRARED), // + new Upgrade(2, 80, new Features(TR_1500_9000))), + PRODUCT_31(31, "LIFX Z", new Features(TR_2500_9000, COLOR, MULTIZONE)), + PRODUCT_32(32, "LIFX Z", new Features(TR_2500_9000, COLOR, MULTIZONE), // + new Upgrade(2, 77, new Features(EXTENDED_MULTIZONE)), // + new Upgrade(2, 80, new Features(TR_1500_9000))), + PRODUCT_36(36, "LIFX Downlight", new Features(TR_2500_9000, COLOR), // + new Upgrade(2, 80, new Features(TR_1500_9000))), + PRODUCT_37(37, "LIFX Downlight", new Features(TR_2500_9000, COLOR), // + new Upgrade(2, 80, new Features(TR_1500_9000))), + PRODUCT_38(38, "LIFX Beam", new Features(TR_2500_9000, COLOR, MULTIZONE), // + new Upgrade(2, 77, new Features(EXTENDED_MULTIZONE)), // + new Upgrade(2, 80, new Features(TR_1500_9000))), + PRODUCT_39(39, "LIFX Downlight White to Warm", new Features(TR_2500_9000), // + new Upgrade(2, 80, new Features(TR_1500_9000))), + PRODUCT_40(40, "LIFX Downlight", new Features(TR_2500_9000, COLOR), // + new Upgrade(2, 80, new Features(TR_1500_9000))), + PRODUCT_43(43, "LIFX A19", new Features(TR_2500_9000, COLOR), // + new Upgrade(2, 80, new Features(TR_1500_9000))), + PRODUCT_44(44, "LIFX BR30", new Features(TR_2500_9000, COLOR), // + new Upgrade(2, 80, new Features(TR_1500_9000))), + PRODUCT_45(45, "LIFX A19 Night Vision", new Features(TR_2500_9000, COLOR, INFRARED), // + new Upgrade(2, 80, new Features(TR_1500_9000))), + PRODUCT_46(46, "LIFX BR30 Night Vision", new Features(TR_2500_9000, COLOR, INFRARED), // + new Upgrade(2, 80, new Features(TR_1500_9000))), + PRODUCT_49(49, "LIFX Mini Color", new Features(TR_1500_9000, COLOR)), + PRODUCT_50(50, "LIFX Mini White to Warm", new Features(TR_1500_6500), // + new Upgrade(3, 70, new Features(TR_1500_9000))), + PRODUCT_51(51, "LIFX Mini White", new Features(TR_2700_2700)), + PRODUCT_52(52, "LIFX GU10", new Features(TR_1500_9000, COLOR)), + PRODUCT_53(53, "LIFX GU10", new Features(TR_1500_9000, COLOR)), + PRODUCT_55(55, "LIFX Tile", new Features(TR_2500_9000, CHAIN, COLOR, MATRIX, TILE_EFFECT)), + PRODUCT_57(57, "LIFX Candle", new Features(TR_1500_9000, COLOR, MATRIX)), + PRODUCT_59(59, "LIFX Mini Color", new Features(TR_1500_9000, COLOR)), + PRODUCT_60(60, "LIFX Mini White to Warm", new Features(TR_1500_6500), // + new Upgrade(3, 70, new Features(TR_1500_9000))), + PRODUCT_61(61, "LIFX Mini White", new Features(TR_2700_2700)), + PRODUCT_62(62, "LIFX A19", new Features(TR_1500_9000, COLOR)), + PRODUCT_63(63, "LIFX BR30", new Features(TR_1500_9000, COLOR)), + PRODUCT_64(64, "LIFX A19 Night Vision", new Features(TR_1500_9000, COLOR, INFRARED)), + PRODUCT_65(65, "LIFX BR30 Night Vision", new Features(TR_1500_9000, COLOR, INFRARED)), + PRODUCT_66(66, "LIFX Mini White", new Features(TR_2700_2700)), + PRODUCT_68(68, "LIFX Candle", new Features(TR_1500_9000, MATRIX)), + PRODUCT_70(70, "LIFX Switch", new Features(BUTTONS, RELAYS)), + PRODUCT_71(71, "LIFX Switch", new Features(BUTTONS, RELAYS)), + PRODUCT_81(81, "LIFX Candle White to Warm", new Features(TR_2200_6500)), + PRODUCT_82(82, "LIFX Filament Clear", new Features(TR_2100_2100)), + PRODUCT_85(85, "LIFX Filament Amber", new Features(TR_2000_2000)), + PRODUCT_87(87, "LIFX Mini White", new Features(TR_2700_2700)), + PRODUCT_88(88, "LIFX Mini White", new Features(TR_2700_2700)), + PRODUCT_89(89, "LIFX Switch", new Features(BUTTONS, RELAYS)), + PRODUCT_90(90, "LIFX Clean", new Features(TR_1500_9000, HEV)), + PRODUCT_91(91, "LIFX Color", new Features(TR_1500_9000, COLOR)), + PRODUCT_92(92, "LIFX Color", new Features(TR_1500_9000, COLOR)), + PRODUCT_94(94, "LIFX BR30", new Features(TR_1500_9000, COLOR)), + PRODUCT_96(96, "LIFX Candle White to Warm", new Features(TR_2200_6500)), + PRODUCT_97(97, "LIFX A19", new Features(TR_1500_9000, COLOR)), + PRODUCT_98(98, "LIFX BR30", new Features(TR_1500_9000, COLOR)), + PRODUCT_99(99, "LIFX Clean", new Features(TR_1500_9000, HEV)), + PRODUCT_100(100, "LIFX Filament Clear", new Features(TR_2100_2100)), + PRODUCT_101(101, "LIFX Filament Amber", new Features(TR_2000_2000)), + PRODUCT_109(109, "LIFX A19 Night Vision", new Features(TR_1500_9000, COLOR, INFRARED)), + PRODUCT_110(110, "LIFX BR30 Night Vision", new Features(TR_1500_9000, COLOR, INFRARED)), + PRODUCT_111(111, "LIFX A19 Night Vision", new Features(TR_1500_9000, COLOR, INFRARED)); + + /** + * Enumerates the product features. + */ + public enum Feature { + BUTTONS, + CHAIN, + COLOR, + EXTENDED_MULTIZONE, + HEV, + INFRARED, + MATRIX, + MULTIZONE, + RELAYS, + TILE_EFFECT + } + + /** + * Enumerates the product vendors. + */ + public enum Vendor { + LIFX(1, "LIFX"); + + private final int id; + private final String name; + + Vendor(int id, String name) { + this.id = id; + this.name = name; + } + + public int getID() { + return id; + } + + public String getName() { + return name; + } + } + + /** + * Enumerates the color temperature ranges of lights. + */ + public enum TemperatureRange { + /** + * When the temperature range is not defined for {@link Upgrade}s it is inherited from the most + * recent firmware version. + */ + NONE(0, 0), + + /** + * 1500-4000K + */ + TR_1500_4000(1500, 4000), + + /** + * 1500-6500K + */ + TR_1500_6500(1500, 6500), + + /** + * 1500-9000K + */ + TR_1500_9000(1500, 9000), + + /** + * 2000-2000K + */ + TR_2000_2000(2000, 2000), + + /** + * 2100-2100K + */ + TR_2100_2100(2100, 2100), + + /** + * 2200-6500K + */ + TR_2200_6500(2200, 6500), + + /** + * 2500-9000K + */ + TR_2500_9000(2500, 9000), + + /** + * 2700-2700K + */ + TR_2700_2700(2700, 2700), + + /** + * 2700-6500K + */ + TR_2700_6500(2700, 6500); + + private final int minimum; + private final int maximum; + + TemperatureRange(int minimum, int maximum) { + this.minimum = minimum; + this.maximum = maximum; + } + + /** + * The minimum color temperature in degrees Kelvin. + * + * @return minimum color temperature (K) + */ + public int getMinimum() { + return minimum; + } + + /** + * The maxiumum color temperature in degrees Kelvin. + * + * @return maximum color temperature (K) + */ + public int getMaximum() { + return maximum; + } + + /** + * The color temperature range in degrees Kelvin. + * + * @return difference between maximum and minimum color temperature values + */ + public int getRange() { + return maximum - minimum; + } + } + + public static class Features { + private TemperatureRange temperatureRange; + private Set features; + + private Features(Feature... features) { + this(NONE, features); + } + + private Features(Features other) { + this(other.temperatureRange, other.features); + } + + private Features(TemperatureRange temperatureRange, Feature... features) { + this.temperatureRange = temperatureRange; + this.features = Set.of(features); + } + + private Features(TemperatureRange temperatureRange, Set features) { + this.temperatureRange = temperatureRange; + this.features = Set.copyOf(features); + } + + public TemperatureRange getTemperatureRange() { + return temperatureRange; + } + + public boolean hasFeature(Feature feature) { + return features.contains(feature); + } + + public void update(Features other) { + temperatureRange = other.temperatureRange; + features = other.features; + } + } + + static class Upgrade { + final long major; + final long minor; + final Features features; + + private Upgrade(long major, long minor, Features features) { + this.major = major; + this.minor = minor; + this.features = features; + } + } + + private final Vendor vendor; + private final long id; + private final String name; + private final Features features; + private final List upgrades; + + private LifxProduct(long id, String name, Features features, Upgrade... upgrades) { + this(LIFX, id, name, features, upgrades); + } + + private LifxProduct(Vendor vendor, long id, String name, Features features, Upgrade... upgrades) { + this.vendor = vendor; + this.id = id; + this.name = name; + this.features = features; + this.upgrades = List.of(upgrades); + } + + @Override + public String toString() { + return name; + } + + public Vendor getVendor() { + return vendor; + } + + public long getID() { + return id; + } + + public String getName() { + return name; + } + + /** + * Returns the features of the initial product firmware version. + * + * @return the initial features + */ + public Features getFeatures() { + return new Features(features); + } + + /** + * Returns the features for a specific product firmware version. + * + * @param version the firmware version + * @return the composite features of all firmware upgrades for the given major and minor firmware version + */ + public Features getFeatures(String version) { + if (upgrades.isEmpty() || !version.contains(".")) { + return new Features(features); + } + + String[] majorMinorVersion = version.split("\\."); + long major = Long.valueOf(majorMinorVersion[0]); + long minor = Long.valueOf(majorMinorVersion[1]); + + TemperatureRange temperatureRange = features.temperatureRange; + Set features = new HashSet<>(this.features.features); + + for (Upgrade upgrade : upgrades) { + if (upgrade.major < major || (upgrade.major == major && upgrade.minor <= minor)) { + Features upgradeFeatures = upgrade.features; + if (upgradeFeatures.temperatureRange != NONE) { + temperatureRange = upgradeFeatures.temperatureRange; + } + features.addAll(upgradeFeatures.features); + } else { + break; + } + } + + return new Features(temperatureRange, features); + } + + public ThingTypeUID getThingTypeUID() { + if (hasFeature(COLOR)) { + if (hasFeature(TILE_EFFECT)) { + return LifxBindingConstants.THING_TYPE_TILELIGHT; + } else if (hasFeature(INFRARED)) { + return LifxBindingConstants.THING_TYPE_COLORIRLIGHT; + } else if (hasFeature(MULTIZONE)) { + return LifxBindingConstants.THING_TYPE_COLORMZLIGHT; + } else { + return LifxBindingConstants.THING_TYPE_COLORLIGHT; + } + } else { + return LifxBindingConstants.THING_TYPE_WHITELIGHT; + } + } + + private boolean hasFeature(Feature feature) { + return features.hasFeature(feature); + } + + /** + * Returns a product that has the given thing type UID. + * + * @param uid a thing type UID + * @return a product that has the given thing type UID + * @throws IllegalArgumentException when uid is not a valid LIFX thing type UID + */ + public static LifxProduct getLikelyProduct(ThingTypeUID uid) throws IllegalArgumentException { + for (LifxProduct product : LifxProduct.values()) { + if (product.getThingTypeUID().equals(uid)) { + return product; + } + } + + throw new IllegalArgumentException(uid + " is not a valid product thing type UID"); + } + + /** + * Returns the product that has the given product ID. + * + * @param id the product ID + * @return the product that has the given product ID + * @throws IllegalArgumentException when id is not a valid LIFX product ID + */ + public static LifxProduct getProductFromProductID(long id) throws IllegalArgumentException { + for (LifxProduct product : LifxProduct.values()) { + if (product.id == id) { + return product; + } + } + + throw new IllegalArgumentException(id + " is not a valid product ID"); + } + + List getUpgrades() { + return upgrades; + } + + public boolean isLight() { + return !features.hasFeature(Feature.BUTTONS) && !features.hasFeature(Feature.RELAYS); + } +} diff --git a/bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/LifxSelectorContext.java b/bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/LifxSelectorContext.java index 0cd6f671959a4..bc7f3ffd3a59c 100644 --- a/bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/LifxSelectorContext.java +++ b/bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/LifxSelectorContext.java @@ -25,7 +25,7 @@ * The {@link LifxSelectorContext} stores the context that is used for broadcast and unicast communications with a * light using a {@link Selector}. * - * @author Wouter Born - Make selector logic reusable between discovery and handlers + * @author Wouter Born - Initial contribution */ @NonNullByDefault public class LifxSelectorContext { diff --git a/bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/LifxSequenceNumberSupplier.java b/bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/LifxSequenceNumberSupplier.java index 5f9e10899153a..1d94e146389b5 100644 --- a/bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/LifxSequenceNumberSupplier.java +++ b/bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/LifxSequenceNumberSupplier.java @@ -20,7 +20,7 @@ /** * Supplies sequence numbers for packets in the range [0, 255]. * - * @author Wouter Born - Make selector logic reusable between discovery and handlers + * @author Wouter Born - Initial contribution */ @NonNullByDefault public class LifxSequenceNumberSupplier implements Supplier { diff --git a/bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/protocol/AcknowledgementResponse.java b/bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/dto/AcknowledgementResponse.java similarity index 91% rename from bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/protocol/AcknowledgementResponse.java rename to bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/dto/AcknowledgementResponse.java index 79eb57808d134..74ace861ab219 100644 --- a/bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/protocol/AcknowledgementResponse.java +++ b/bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/dto/AcknowledgementResponse.java @@ -10,12 +10,12 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.binding.lifx.internal.protocol; +package org.openhab.binding.lifx.internal.dto; import java.nio.ByteBuffer; /** - * @author Tim Buckley - Initial Contribution + * @author Tim Buckley - Initial contribution * @author Karel Goderis - Enhancement for the V2 LIFX Firmware and LAN Protocol Specification */ public class AcknowledgementResponse extends Packet { diff --git a/bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/protocol/ApplicationRequest.java b/bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/dto/ApplicationRequest.java similarity index 93% rename from bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/protocol/ApplicationRequest.java rename to bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/dto/ApplicationRequest.java index 233cfde2bda09..9e53718a873b8 100644 --- a/bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/protocol/ApplicationRequest.java +++ b/bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/dto/ApplicationRequest.java @@ -10,10 +10,10 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.binding.lifx.internal.protocol; +package org.openhab.binding.lifx.internal.dto; /** - * @author Wouter Born - Add support for MultiZone light control + * @author Wouter Born - Initial contribution */ public enum ApplicationRequest { diff --git a/bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/protocol/EchoRequestResponse.java b/bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/dto/EchoRequestResponse.java similarity index 94% rename from bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/protocol/EchoRequestResponse.java rename to bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/dto/EchoRequestResponse.java index 8ea752276bbea..058a76031a22b 100644 --- a/bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/protocol/EchoRequestResponse.java +++ b/bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/dto/EchoRequestResponse.java @@ -10,7 +10,7 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.binding.lifx.internal.protocol; +package org.openhab.binding.lifx.internal.dto; import java.nio.ByteBuffer; @@ -18,7 +18,7 @@ import org.openhab.binding.lifx.internal.fields.Field; /** - * @author Tim Buckley - Initial Contribution + * @author Tim Buckley - Initial contribution * @author Karel Goderis - Enhancement for the V2 LIFX Firmware and LAN Protocol Specification */ public class EchoRequestResponse extends Packet { diff --git a/bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/protocol/Effect.java b/bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/dto/Effect.java similarity index 98% rename from bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/protocol/Effect.java rename to bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/dto/Effect.java index 73f9d6437df6c..2490e22633585 100644 --- a/bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/protocol/Effect.java +++ b/bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/dto/Effect.java @@ -10,7 +10,7 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.binding.lifx.internal.protocol; +package org.openhab.binding.lifx.internal.dto; import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.Nullable; @@ -20,7 +20,7 @@ /** * This class represents LIFX Tile effect * - * @author Pawel Pieczul - initial contribution + * @author Pawel Pieczul - Initial contribution */ @NonNullByDefault public class Effect { diff --git a/bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/protocol/GenericHandler.java b/bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/dto/GenericHandler.java similarity index 95% rename from bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/protocol/GenericHandler.java rename to bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/dto/GenericHandler.java index 13127f9fc3168..8447c2d27b2b4 100644 --- a/bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/protocol/GenericHandler.java +++ b/bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/dto/GenericHandler.java @@ -10,7 +10,7 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.binding.lifx.internal.protocol; +package org.openhab.binding.lifx.internal.dto; import java.lang.reflect.Constructor; import java.lang.reflect.Field; @@ -27,7 +27,7 @@ * * @param the packet subtype this handler constructs * - * @author Tim Buckley - Initial Contribution + * @author Tim Buckley - Initial contribution * @author Karel Goderis - Enhancement for the V2 LIFX Firmware and LAN Protocol Specification */ @NonNullByDefault diff --git a/bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/protocol/GenericPacket.java b/bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/dto/GenericPacket.java similarity index 90% rename from bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/protocol/GenericPacket.java rename to bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/dto/GenericPacket.java index 49667965ad1b2..b0a738054b470 100644 --- a/bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/protocol/GenericPacket.java +++ b/bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/dto/GenericPacket.java @@ -10,12 +10,12 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.binding.lifx.internal.protocol; +package org.openhab.binding.lifx.internal.dto; import java.nio.ByteBuffer; /** - * @author Tim Buckley - Initial Contribution + * @author Tim Buckley - Initial contribution * @author Karel Goderis - Enhancement for the V2 LIFX Firmware and LAN Protocol Specification */ public class GenericPacket extends Packet { diff --git a/bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/protocol/GetColorZonesRequest.java b/bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/dto/GetColorZonesRequest.java similarity index 94% rename from bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/protocol/GetColorZonesRequest.java rename to bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/dto/GetColorZonesRequest.java index c53515412db75..fbdc0ddec7c86 100644 --- a/bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/protocol/GetColorZonesRequest.java +++ b/bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/dto/GetColorZonesRequest.java @@ -10,7 +10,7 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.binding.lifx.internal.protocol; +package org.openhab.binding.lifx.internal.dto; import static org.openhab.binding.lifx.internal.LifxBindingConstants.*; @@ -20,7 +20,7 @@ import org.openhab.binding.lifx.internal.fields.UInt8Field; /** - * @author Wouter Born - Add support for MultiZone light control + * @author Wouter Born - Initial contribution */ public class GetColorZonesRequest extends Packet { diff --git a/bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/protocol/GetEchoRequest.java b/bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/dto/GetEchoRequest.java similarity index 95% rename from bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/protocol/GetEchoRequest.java rename to bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/dto/GetEchoRequest.java index 43b0fc262bff5..be09a903d5ab9 100644 --- a/bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/protocol/GetEchoRequest.java +++ b/bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/dto/GetEchoRequest.java @@ -10,7 +10,7 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.binding.lifx.internal.protocol; +package org.openhab.binding.lifx.internal.dto; import java.nio.ByteBuffer; @@ -18,7 +18,7 @@ import org.openhab.binding.lifx.internal.fields.Field; /** - * @author Tim Buckley - Initial Contribution + * @author Tim Buckley - Initial contribution * @author Karel Goderis - Enhancement for the V2 LIFX Firmware and LAN Protocol Specification */ public class GetEchoRequest extends Packet { diff --git a/bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/protocol/GetGroupRequest.java b/bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/dto/GetGroupRequest.java similarity index 92% rename from bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/protocol/GetGroupRequest.java rename to bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/dto/GetGroupRequest.java index 8bb6cf23e4c24..a6f8e134b994b 100644 --- a/bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/protocol/GetGroupRequest.java +++ b/bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/dto/GetGroupRequest.java @@ -10,12 +10,12 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.binding.lifx.internal.protocol; +package org.openhab.binding.lifx.internal.dto; import java.nio.ByteBuffer; /** - * @author Tim Buckley - Initial Contribution + * @author Tim Buckley - Initial contribution * @author Karel Goderis - Enhancement for the V2 LIFX Firmware and LAN Protocol Specification */ public class GetGroupRequest extends Packet { diff --git a/bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/protocol/GetHostFirmwareRequest.java b/bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/dto/GetHostFirmwareRequest.java similarity index 92% rename from bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/protocol/GetHostFirmwareRequest.java rename to bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/dto/GetHostFirmwareRequest.java index 9905cecec2553..814f8738e6437 100644 --- a/bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/protocol/GetHostFirmwareRequest.java +++ b/bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/dto/GetHostFirmwareRequest.java @@ -10,12 +10,12 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.binding.lifx.internal.protocol; +package org.openhab.binding.lifx.internal.dto; import java.nio.ByteBuffer; /** - * @author Tim Buckley - Initial Contribution + * @author Tim Buckley - Initial contribution * @author Karel Goderis - Enhancement for the V2 LIFX Firmware and LAN Protocol Specification */ public class GetHostFirmwareRequest extends Packet { diff --git a/bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/protocol/GetHostInfoRequest.java b/bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/dto/GetHostInfoRequest.java similarity index 92% rename from bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/protocol/GetHostInfoRequest.java rename to bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/dto/GetHostInfoRequest.java index 290c934c42987..a2f1b5131e17b 100644 --- a/bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/protocol/GetHostInfoRequest.java +++ b/bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/dto/GetHostInfoRequest.java @@ -10,12 +10,12 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.binding.lifx.internal.protocol; +package org.openhab.binding.lifx.internal.dto; import java.nio.ByteBuffer; /** - * @author Tim Buckley - Initial Contribution + * @author Tim Buckley - Initial contribution * @author Karel Goderis - Enhancement for the V2 LIFX Firmware and LAN Protocol Specification */ public class GetHostInfoRequest extends Packet { diff --git a/bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/protocol/GetInfoRequest.java b/bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/dto/GetInfoRequest.java similarity index 92% rename from bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/protocol/GetInfoRequest.java rename to bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/dto/GetInfoRequest.java index 33e830f987083..47212e8648ec0 100644 --- a/bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/protocol/GetInfoRequest.java +++ b/bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/dto/GetInfoRequest.java @@ -10,12 +10,12 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.binding.lifx.internal.protocol; +package org.openhab.binding.lifx.internal.dto; import java.nio.ByteBuffer; /** - * @author Tim Buckley - Initial Contribution + * @author Tim Buckley - Initial contribution * @author Karel Goderis - Enhancement for the V2 LIFX Firmware and LAN Protocol Specification */ public class GetInfoRequest extends Packet { diff --git a/bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/protocol/GetLabelRequest.java b/bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/dto/GetLabelRequest.java similarity index 92% rename from bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/protocol/GetLabelRequest.java rename to bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/dto/GetLabelRequest.java index 20d6b5a03b3d1..681ac98ad90f3 100644 --- a/bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/protocol/GetLabelRequest.java +++ b/bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/dto/GetLabelRequest.java @@ -10,12 +10,12 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.binding.lifx.internal.protocol; +package org.openhab.binding.lifx.internal.dto; import java.nio.ByteBuffer; /** - * @author Tim Buckley - Initial Contribution + * @author Tim Buckley - Initial contribution * @author Karel Goderis - Enhancement for the V2 LIFX Firmware and LAN Protocol Specification */ public class GetLabelRequest extends Packet { diff --git a/bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/protocol/GetLightInfraredRequest.java b/bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/dto/GetLightInfraredRequest.java similarity index 89% rename from bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/protocol/GetLightInfraredRequest.java rename to bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/dto/GetLightInfraredRequest.java index 1297026e87b4c..f4b2ce1d88b9e 100644 --- a/bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/protocol/GetLightInfraredRequest.java +++ b/bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/dto/GetLightInfraredRequest.java @@ -10,12 +10,12 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.binding.lifx.internal.protocol; +package org.openhab.binding.lifx.internal.dto; import java.nio.ByteBuffer; /** - * @author Wouter Born - Support LIFX 2016 product line-up and infrared functionality + * @author Wouter Born - Initial contribution */ public class GetLightInfraredRequest extends Packet { diff --git a/bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/protocol/GetLightPowerRequest.java b/bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/dto/GetLightPowerRequest.java similarity index 92% rename from bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/protocol/GetLightPowerRequest.java rename to bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/dto/GetLightPowerRequest.java index d8853b7a44a21..2fe69366d0a94 100644 --- a/bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/protocol/GetLightPowerRequest.java +++ b/bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/dto/GetLightPowerRequest.java @@ -10,12 +10,12 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.binding.lifx.internal.protocol; +package org.openhab.binding.lifx.internal.dto; import java.nio.ByteBuffer; /** - * @author Tim Buckley - Initial Contribution + * @author Tim Buckley - Initial contribution * @author Karel Goderis - Enhancement for the V2 LIFX Firmware and LAN Protocol Specification */ public class GetLightPowerRequest extends Packet { diff --git a/bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/protocol/GetLocationRequest.java b/bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/dto/GetLocationRequest.java similarity index 92% rename from bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/protocol/GetLocationRequest.java rename to bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/dto/GetLocationRequest.java index abf7b8337fcae..af75939bee782 100644 --- a/bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/protocol/GetLocationRequest.java +++ b/bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/dto/GetLocationRequest.java @@ -10,12 +10,12 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.binding.lifx.internal.protocol; +package org.openhab.binding.lifx.internal.dto; import java.nio.ByteBuffer; /** - * @author Tim Buckley - Initial Contribution + * @author Tim Buckley - Initial contribution * @author Karel Goderis - Enhancement for the V2 LIFX Firmware and LAN Protocol Specification */ public class GetLocationRequest extends Packet { diff --git a/bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/protocol/GetPowerRequest.java b/bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/dto/GetPowerRequest.java similarity index 92% rename from bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/protocol/GetPowerRequest.java rename to bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/dto/GetPowerRequest.java index 6e09a2d79dfd6..2b9bfdddbcc01 100644 --- a/bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/protocol/GetPowerRequest.java +++ b/bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/dto/GetPowerRequest.java @@ -10,12 +10,12 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.binding.lifx.internal.protocol; +package org.openhab.binding.lifx.internal.dto; import java.nio.ByteBuffer; /** - * @author Tim Buckley - Initial Contribution + * @author Tim Buckley - Initial contribution * @author Karel Goderis - Enhancement for the V2 LIFX Firmware and LAN Protocol Specification */ public class GetPowerRequest extends Packet { diff --git a/bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/protocol/GetRequest.java b/bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/dto/GetRequest.java similarity index 92% rename from bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/protocol/GetRequest.java rename to bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/dto/GetRequest.java index 3f11ae49ecea7..ab64dc6d50f15 100644 --- a/bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/protocol/GetRequest.java +++ b/bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/dto/GetRequest.java @@ -10,12 +10,12 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.binding.lifx.internal.protocol; +package org.openhab.binding.lifx.internal.dto; import java.nio.ByteBuffer; /** - * @author Tim Buckley - Initial Contribution + * @author Tim Buckley - Initial contribution * @author Karel Goderis - Enhancement for the V2 LIFX Firmware and LAN Protocol Specification */ public class GetRequest extends Packet { diff --git a/bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/protocol/GetServiceRequest.java b/bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/dto/GetServiceRequest.java similarity index 92% rename from bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/protocol/GetServiceRequest.java rename to bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/dto/GetServiceRequest.java index 400a19bb58dec..b0b8d745df9b9 100644 --- a/bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/protocol/GetServiceRequest.java +++ b/bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/dto/GetServiceRequest.java @@ -10,12 +10,12 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.binding.lifx.internal.protocol; +package org.openhab.binding.lifx.internal.dto; import java.nio.ByteBuffer; /** - * @author Tim Buckley - Initial Contribution + * @author Tim Buckley - Initial contribution * @author Karel Goderis - Enhancement for the V2 LIFX Firmware and LAN Protocol Specification */ public class GetServiceRequest extends Packet { diff --git a/bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/protocol/GetTagLabelsRequest.java b/bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/dto/GetTagLabelsRequest.java similarity index 93% rename from bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/protocol/GetTagLabelsRequest.java rename to bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/dto/GetTagLabelsRequest.java index 39aeb592e01d0..c3c313e95667a 100644 --- a/bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/protocol/GetTagLabelsRequest.java +++ b/bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/dto/GetTagLabelsRequest.java @@ -10,7 +10,7 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.binding.lifx.internal.protocol; +package org.openhab.binding.lifx.internal.dto; import java.nio.ByteBuffer; @@ -18,7 +18,7 @@ import org.openhab.binding.lifx.internal.fields.UInt64Field; /** - * @author Tim Buckley - Initial Contribution + * @author Tim Buckley - Initial contribution * @author Karel Goderis - Enhancement for the V2 LIFX Firmware and LAN Protocol Specification */ public class GetTagLabelsRequest extends Packet { diff --git a/bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/protocol/GetTagsRequest.java b/bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/dto/GetTagsRequest.java similarity index 91% rename from bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/protocol/GetTagsRequest.java rename to bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/dto/GetTagsRequest.java index 1b76a2df2f5e7..c0570cca52912 100644 --- a/bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/protocol/GetTagsRequest.java +++ b/bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/dto/GetTagsRequest.java @@ -10,12 +10,12 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.binding.lifx.internal.protocol; +package org.openhab.binding.lifx.internal.dto; import java.nio.ByteBuffer; /** - * @author Tim Buckley - Initial Contribution + * @author Tim Buckley - Initial contribution * @author Karel Goderis - Enhancement for the V2 LIFX Firmware and LAN Protocol Specification */ public class GetTagsRequest extends Packet { diff --git a/bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/protocol/GetTileEffectRequest.java b/bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/dto/GetTileEffectRequest.java similarity index 91% rename from bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/protocol/GetTileEffectRequest.java rename to bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/dto/GetTileEffectRequest.java index afb4fa641f21e..fde9dcc647d1e 100644 --- a/bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/protocol/GetTileEffectRequest.java +++ b/bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/dto/GetTileEffectRequest.java @@ -10,14 +10,14 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.binding.lifx.internal.protocol; +package org.openhab.binding.lifx.internal.dto; import java.nio.ByteBuffer; /** * Implementation of GetTileEffect packet - * - * @author Pawel Pieczul - Add support for Tile Effects + * + * @author Pawel Pieczul - Initial contribution */ public class GetTileEffectRequest extends Packet { diff --git a/bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/protocol/GetVersionRequest.java b/bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/dto/GetVersionRequest.java similarity index 92% rename from bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/protocol/GetVersionRequest.java rename to bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/dto/GetVersionRequest.java index 9e789f8793844..a278480581579 100644 --- a/bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/protocol/GetVersionRequest.java +++ b/bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/dto/GetVersionRequest.java @@ -10,12 +10,12 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.binding.lifx.internal.protocol; +package org.openhab.binding.lifx.internal.dto; import java.nio.ByteBuffer; /** - * @author Tim Buckley - Initial Contribution + * @author Tim Buckley - Initial contribution * @author Karel Goderis - Enhancement for the V2 LIFX Firmware and LAN Protocol Specification */ public class GetVersionRequest extends Packet { diff --git a/bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/protocol/GetWifiFirmwareRequest.java b/bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/dto/GetWifiFirmwareRequest.java similarity index 92% rename from bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/protocol/GetWifiFirmwareRequest.java rename to bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/dto/GetWifiFirmwareRequest.java index ca18a4a15c23f..e08771de08f7c 100644 --- a/bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/protocol/GetWifiFirmwareRequest.java +++ b/bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/dto/GetWifiFirmwareRequest.java @@ -10,12 +10,12 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.binding.lifx.internal.protocol; +package org.openhab.binding.lifx.internal.dto; import java.nio.ByteBuffer; /** - * @author Tim Buckley - Initial Contribution + * @author Tim Buckley - Initial contribution * @author Karel Goderis - Enhancement for the V2 LIFX Firmware and LAN Protocol Specification */ public class GetWifiFirmwareRequest extends Packet { diff --git a/bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/protocol/GetWifiInfoRequest.java b/bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/dto/GetWifiInfoRequest.java similarity index 92% rename from bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/protocol/GetWifiInfoRequest.java rename to bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/dto/GetWifiInfoRequest.java index 5740ccea6faab..3b97981a1cce7 100644 --- a/bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/protocol/GetWifiInfoRequest.java +++ b/bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/dto/GetWifiInfoRequest.java @@ -10,12 +10,12 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.binding.lifx.internal.protocol; +package org.openhab.binding.lifx.internal.dto; import java.nio.ByteBuffer; /** - * @author Tim Buckley - Initial Contribution + * @author Tim Buckley - Initial contribution * @author Karel Goderis - Enhancement for the V2 LIFX Firmware and LAN Protocol Specification */ public class GetWifiInfoRequest extends Packet { diff --git a/bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/protocol/Packet.java b/bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/dto/Packet.java similarity index 99% rename from bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/protocol/Packet.java rename to bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/dto/Packet.java index d9084ab866078..3b65afc370956 100644 --- a/bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/protocol/Packet.java +++ b/bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/dto/Packet.java @@ -10,7 +10,7 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.binding.lifx.internal.protocol; +package org.openhab.binding.lifx.internal.dto; import java.nio.ByteBuffer; @@ -36,7 +36,7 @@ * Field definitions should remain accessible to outside classes in the event * they need to worked with directly elsewhere. * - * @author Tim Buckley - Initial Contribution + * @author Tim Buckley - Initial contribution * @author Karel Goderis - Enhancement for the V2 LIFX Firmware and LAN Protocol Specification */ public abstract class Packet { diff --git a/bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/protocol/PacketFactory.java b/bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/dto/PacketFactory.java similarity index 98% rename from bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/protocol/PacketFactory.java rename to bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/dto/PacketFactory.java index 6718d31f3c040..ba9ea39f0f4c3 100644 --- a/bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/protocol/PacketFactory.java +++ b/bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/dto/PacketFactory.java @@ -10,7 +10,7 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.binding.lifx.internal.protocol; +package org.openhab.binding.lifx.internal.dto; import java.util.HashMap; import java.util.Map; @@ -24,7 +24,7 @@ * e. Packet handlers (used to construct actual packet * instances) may be retrieved via their packet type. * - * @author Tim Buckley - Initial Contribution + * @author Tim Buckley - Initial contribution * @author Karel Goderis - Enhancement for the V2 LIFX Firmware and LAN Protocol Specification * @author Wouter Born - Support LIFX 2016 product line-up and infrared functionality */ diff --git a/bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/protocol/PacketHandler.java b/bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/dto/PacketHandler.java similarity index 92% rename from bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/protocol/PacketHandler.java rename to bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/dto/PacketHandler.java index fe8944eb2c8e5..a93b4759be9cb 100644 --- a/bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/protocol/PacketHandler.java +++ b/bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/dto/PacketHandler.java @@ -10,7 +10,7 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.binding.lifx.internal.protocol; +package org.openhab.binding.lifx.internal.dto; import java.nio.ByteBuffer; @@ -22,7 +22,7 @@ * * @param the generic packet type * - * @author Tim Buckley - Initial Contribution + * @author Tim Buckley - Initial contribution * @author Karel Goderis - Enhancement for the V2 LIFX Firmware and LAN Protocol Specification */ @NonNullByDefault diff --git a/bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/protocol/PowerState.java b/bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/dto/PowerState.java similarity index 93% rename from bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/protocol/PowerState.java rename to bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/dto/PowerState.java index 69d6ad11fd5b5..f12ba58b7d9b3 100644 --- a/bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/protocol/PowerState.java +++ b/bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/dto/PowerState.java @@ -10,14 +10,14 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.binding.lifx.internal.protocol; +package org.openhab.binding.lifx.internal.dto; import org.openhab.core.library.types.OnOffType; /** * Represents light power states (on or off). * - * @author Tim Buckley - Initial Contribution + * @author Tim Buckley - Initial contribution * @author Karel Goderis - Enhancement for the V2 LIFX Firmware and LAN Protocol Specification * @author Wouter Born - Added OnOffType conversion methods */ diff --git a/bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/protocol/SetColorRequest.java b/bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/dto/SetColorRequest.java similarity index 96% rename from bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/protocol/SetColorRequest.java rename to bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/dto/SetColorRequest.java index 3a24126e6b83f..2ebe1760fe7a2 100644 --- a/bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/protocol/SetColorRequest.java +++ b/bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/dto/SetColorRequest.java @@ -10,7 +10,7 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.binding.lifx.internal.protocol; +package org.openhab.binding.lifx.internal.dto; import java.nio.ByteBuffer; @@ -21,7 +21,7 @@ import org.openhab.binding.lifx.internal.fields.UInt32Field; /** - * @author Tim Buckley - Initial Contribution + * @author Tim Buckley - Initial contribution * @author Karel Goderis - Enhancement for the V2 LIFX Firmware and LAN Protocol Specification */ public class SetColorRequest extends Packet { diff --git a/bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/protocol/SetColorZonesRequest.java b/bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/dto/SetColorZonesRequest.java similarity index 96% rename from bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/protocol/SetColorZonesRequest.java rename to bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/dto/SetColorZonesRequest.java index 5553877994221..5e40d1b06a8e2 100644 --- a/bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/protocol/SetColorZonesRequest.java +++ b/bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/dto/SetColorZonesRequest.java @@ -10,7 +10,7 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.binding.lifx.internal.protocol; +package org.openhab.binding.lifx.internal.dto; import static org.openhab.binding.lifx.internal.LifxBindingConstants.*; @@ -23,7 +23,7 @@ import org.openhab.binding.lifx.internal.fields.UInt8Field; /** - * @author Wouter Born - Add support for MultiZone light control + * @author Wouter Born - Initial contribution */ public class SetColorZonesRequest extends Packet { diff --git a/bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/protocol/SetDimAbsoluteRequest.java b/bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/dto/SetDimAbsoluteRequest.java similarity index 94% rename from bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/protocol/SetDimAbsoluteRequest.java rename to bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/dto/SetDimAbsoluteRequest.java index 648db5845c7d2..ca92dedca713b 100644 --- a/bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/protocol/SetDimAbsoluteRequest.java +++ b/bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/dto/SetDimAbsoluteRequest.java @@ -10,7 +10,7 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.binding.lifx.internal.protocol; +package org.openhab.binding.lifx.internal.dto; import java.nio.ByteBuffer; @@ -19,7 +19,7 @@ import org.openhab.binding.lifx.internal.fields.UInt32Field; /** - * @author Tim Buckley - Initial Contribution + * @author Tim Buckley - Initial contribution * @author Karel Goderis - Enhancement for the V2 LIFX Firmware and LAN Protocol Specification */ public class SetDimAbsoluteRequest extends Packet { diff --git a/bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/protocol/SetLabelRequest.java b/bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/dto/SetLabelRequest.java similarity index 94% rename from bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/protocol/SetLabelRequest.java rename to bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/dto/SetLabelRequest.java index e55dbe5ab60b7..ee11b747cb658 100644 --- a/bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/protocol/SetLabelRequest.java +++ b/bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/dto/SetLabelRequest.java @@ -10,7 +10,7 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.binding.lifx.internal.protocol; +package org.openhab.binding.lifx.internal.dto; import java.nio.ByteBuffer; @@ -18,7 +18,7 @@ import org.openhab.binding.lifx.internal.fields.StringField; /** - * @author Tim Buckley - Initial Contribution + * @author Tim Buckley - Initial contribution * @author Karel Goderis - Enhancement for the V2 LIFX Firmware and LAN Protocol Specification */ public class SetLabelRequest extends Packet { diff --git a/bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/protocol/SetLightInfraredRequest.java b/bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/dto/SetLightInfraredRequest.java similarity index 91% rename from bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/protocol/SetLightInfraredRequest.java rename to bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/dto/SetLightInfraredRequest.java index e9d601cf321f1..1a0abda764716 100644 --- a/bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/protocol/SetLightInfraredRequest.java +++ b/bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/dto/SetLightInfraredRequest.java @@ -10,7 +10,7 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.binding.lifx.internal.protocol; +package org.openhab.binding.lifx.internal.dto; import java.nio.ByteBuffer; @@ -18,7 +18,7 @@ import org.openhab.binding.lifx.internal.fields.UInt16Field; /** - * @author Wouter Born - Support LIFX 2016 product line-up and infrared functionality + * @author Wouter Born - Initial contribution */ public class SetLightInfraredRequest extends Packet { diff --git a/bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/protocol/SetLightPowerRequest.java b/bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/dto/SetLightPowerRequest.java similarity index 95% rename from bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/protocol/SetLightPowerRequest.java rename to bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/dto/SetLightPowerRequest.java index ed186fd097e43..ecbfb7a1a7f26 100644 --- a/bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/protocol/SetLightPowerRequest.java +++ b/bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/dto/SetLightPowerRequest.java @@ -10,7 +10,7 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.binding.lifx.internal.protocol; +package org.openhab.binding.lifx.internal.dto; import java.nio.ByteBuffer; @@ -19,7 +19,7 @@ import org.openhab.binding.lifx.internal.fields.UInt32Field; /** - * @author Tim Buckley - Initial Contribution + * @author Tim Buckley - Initial contribution * @author Karel Goderis - Enhancement for the V2 LIFX Firmware and LAN Protocol Specification */ public class SetLightPowerRequest extends Packet { diff --git a/bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/protocol/SetPowerRequest.java b/bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/dto/SetPowerRequest.java similarity index 94% rename from bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/protocol/SetPowerRequest.java rename to bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/dto/SetPowerRequest.java index 8b0d61a30b14d..ff4988b08b645 100644 --- a/bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/protocol/SetPowerRequest.java +++ b/bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/dto/SetPowerRequest.java @@ -10,7 +10,7 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.binding.lifx.internal.protocol; +package org.openhab.binding.lifx.internal.dto; import java.nio.ByteBuffer; @@ -18,7 +18,7 @@ import org.openhab.binding.lifx.internal.fields.UInt16Field; /** - * @author Tim Buckley - Initial Contribution + * @author Tim Buckley - Initial contribution * @author Karel Goderis - Enhancement for the V2 LIFX Firmware and LAN Protocol Specification */ public class SetPowerRequest extends Packet { diff --git a/bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/protocol/SetTagsRequest.java b/bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/dto/SetTagsRequest.java similarity index 94% rename from bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/protocol/SetTagsRequest.java rename to bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/dto/SetTagsRequest.java index 384ae11df8811..abbf5b58ddfe2 100644 --- a/bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/protocol/SetTagsRequest.java +++ b/bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/dto/SetTagsRequest.java @@ -10,7 +10,7 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.binding.lifx.internal.protocol; +package org.openhab.binding.lifx.internal.dto; import java.nio.ByteBuffer; @@ -18,7 +18,7 @@ import org.openhab.binding.lifx.internal.fields.UInt64Field; /** - * @author Tim Buckley - Initial Contribution + * @author Tim Buckley - Initial contribution * @author Karel Goderis - Enhancement for the V2 LIFX Firmware and LAN Protocol Specification */ public class SetTagsRequest extends Packet { diff --git a/bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/protocol/SetTileEffectRequest.java b/bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/dto/SetTileEffectRequest.java similarity index 99% rename from bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/protocol/SetTileEffectRequest.java rename to bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/dto/SetTileEffectRequest.java index f4cef593ae922..21d398e495514 100644 --- a/bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/protocol/SetTileEffectRequest.java +++ b/bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/dto/SetTileEffectRequest.java @@ -10,7 +10,7 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.binding.lifx.internal.protocol; +package org.openhab.binding.lifx.internal.dto; import java.nio.ByteBuffer; diff --git a/bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/protocol/SignalStrength.java b/bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/dto/SignalStrength.java similarity index 94% rename from bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/protocol/SignalStrength.java rename to bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/dto/SignalStrength.java index 5a10b8a2da399..716a93f94014e 100644 --- a/bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/protocol/SignalStrength.java +++ b/bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/dto/SignalStrength.java @@ -10,12 +10,12 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.binding.lifx.internal.protocol; +package org.openhab.binding.lifx.internal.dto; /** * The signal strength of a light. * - * @author Wouter Born - Add signal strength channel + * @author Wouter Born - Initial contribution */ public class SignalStrength { diff --git a/bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/protocol/StateGroupResponse.java b/bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/dto/StateGroupResponse.java similarity index 95% rename from bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/protocol/StateGroupResponse.java rename to bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/dto/StateGroupResponse.java index 274a9c510a082..2463edacf9bcb 100644 --- a/bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/protocol/StateGroupResponse.java +++ b/bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/dto/StateGroupResponse.java @@ -10,7 +10,7 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.binding.lifx.internal.protocol; +package org.openhab.binding.lifx.internal.dto; import java.nio.ByteBuffer; @@ -20,7 +20,7 @@ import org.openhab.binding.lifx.internal.fields.UInt64Field; /** - * @author Tim Buckley - Initial Contribution + * @author Tim Buckley - Initial contribution * @author Karel Goderis - Enhancement for the V2 LIFX Firmware and LAN Protocol Specification */ public class StateGroupResponse extends Packet { diff --git a/bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/protocol/StateHostFirmwareResponse.java b/bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/dto/StateHostFirmwareResponse.java similarity index 95% rename from bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/protocol/StateHostFirmwareResponse.java rename to bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/dto/StateHostFirmwareResponse.java index 4eb17305a73a5..065c06595f766 100644 --- a/bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/protocol/StateHostFirmwareResponse.java +++ b/bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/dto/StateHostFirmwareResponse.java @@ -10,7 +10,7 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.binding.lifx.internal.protocol; +package org.openhab.binding.lifx.internal.dto; import java.nio.ByteBuffer; @@ -20,7 +20,7 @@ import org.openhab.binding.lifx.internal.fields.VersionField; /** - * @author Tim Buckley - Initial Contribution + * @author Tim Buckley - Initial contribution * @author Karel Goderis - Enhancement for the V2 LIFX Firmware and LAN Protocol Specification */ public class StateHostFirmwareResponse extends Packet { diff --git a/bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/protocol/StateHostInfoResponse.java b/bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/dto/StateHostInfoResponse.java similarity index 95% rename from bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/protocol/StateHostInfoResponse.java rename to bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/dto/StateHostInfoResponse.java index 5b4b712806292..1690bd235183b 100644 --- a/bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/protocol/StateHostInfoResponse.java +++ b/bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/dto/StateHostInfoResponse.java @@ -10,7 +10,7 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.binding.lifx.internal.protocol; +package org.openhab.binding.lifx.internal.dto; import java.nio.ByteBuffer; @@ -20,7 +20,7 @@ import org.openhab.binding.lifx.internal.fields.UInt32Field; /** - * @author Tim Buckley - Initial Contribution + * @author Tim Buckley - Initial contribution * @author Karel Goderis - Enhancement for the V2 LIFX Firmware and LAN Protocol Specification */ public class StateHostInfoResponse extends Packet { diff --git a/bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/protocol/StateInfoResponse.java b/bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/dto/StateInfoResponse.java similarity index 95% rename from bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/protocol/StateInfoResponse.java rename to bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/dto/StateInfoResponse.java index 5b7e9143303c1..ca3ccf276d657 100644 --- a/bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/protocol/StateInfoResponse.java +++ b/bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/dto/StateInfoResponse.java @@ -10,7 +10,7 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.binding.lifx.internal.protocol; +package org.openhab.binding.lifx.internal.dto; import java.nio.ByteBuffer; @@ -18,7 +18,7 @@ import org.openhab.binding.lifx.internal.fields.UInt64Field; /** - * @author Tim Buckley - Initial Contribution + * @author Tim Buckley - Initial contribution * @author Karel Goderis - Enhancement for the V2 LIFX Firmware and LAN Protocol Specification */ public class StateInfoResponse extends Packet { diff --git a/bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/protocol/StateLabelResponse.java b/bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/dto/StateLabelResponse.java similarity index 93% rename from bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/protocol/StateLabelResponse.java rename to bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/dto/StateLabelResponse.java index c41afa3184ea4..409d61f40024a 100644 --- a/bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/protocol/StateLabelResponse.java +++ b/bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/dto/StateLabelResponse.java @@ -10,7 +10,7 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.binding.lifx.internal.protocol; +package org.openhab.binding.lifx.internal.dto; import java.nio.ByteBuffer; @@ -18,7 +18,7 @@ import org.openhab.binding.lifx.internal.fields.StringField; /** - * @author Tim Buckley - Initial Contribution + * @author Tim Buckley - Initial contribution * @author Karel Goderis - Enhancement for the V2 LIFX Firmware and LAN Protocol Specification */ public class StateLabelResponse extends Packet { diff --git a/bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/protocol/StateLightInfraredResponse.java b/bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/dto/StateLightInfraredResponse.java similarity index 90% rename from bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/protocol/StateLightInfraredResponse.java rename to bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/dto/StateLightInfraredResponse.java index 9012583da0674..53d561be493ff 100644 --- a/bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/protocol/StateLightInfraredResponse.java +++ b/bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/dto/StateLightInfraredResponse.java @@ -10,7 +10,7 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.binding.lifx.internal.protocol; +package org.openhab.binding.lifx.internal.dto; import java.nio.ByteBuffer; @@ -18,7 +18,7 @@ import org.openhab.binding.lifx.internal.fields.UInt16Field; /** - * @author Wouter Born - Support LIFX 2016 product line-up and infrared functionality + * @author Wouter Born - Initial contribution */ public class StateLightInfraredResponse extends Packet { diff --git a/bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/protocol/StateLightPowerResponse.java b/bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/dto/StateLightPowerResponse.java similarity index 93% rename from bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/protocol/StateLightPowerResponse.java rename to bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/dto/StateLightPowerResponse.java index 6b848afb6caa8..9d248a16ee816 100644 --- a/bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/protocol/StateLightPowerResponse.java +++ b/bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/dto/StateLightPowerResponse.java @@ -10,7 +10,7 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.binding.lifx.internal.protocol; +package org.openhab.binding.lifx.internal.dto; import java.nio.ByteBuffer; @@ -18,7 +18,7 @@ import org.openhab.binding.lifx.internal.fields.UInt16Field; /** - * @author Tim Buckley - Initial Contribution + * @author Tim Buckley - Initial contribution * @author Karel Goderis - Enhancement for the V2 LIFX Firmware and LAN Protocol Specification */ public class StateLightPowerResponse extends Packet { diff --git a/bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/protocol/StateLocationResponse.java b/bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/dto/StateLocationResponse.java similarity index 96% rename from bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/protocol/StateLocationResponse.java rename to bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/dto/StateLocationResponse.java index 35d726aad2bc1..77c014688996b 100644 --- a/bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/protocol/StateLocationResponse.java +++ b/bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/dto/StateLocationResponse.java @@ -10,7 +10,7 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.binding.lifx.internal.protocol; +package org.openhab.binding.lifx.internal.dto; import java.nio.ByteBuffer; @@ -20,7 +20,7 @@ import org.openhab.binding.lifx.internal.fields.UInt64Field; /** - * @author Tim Buckley - Initial Contribution + * @author Tim Buckley - Initial contribution * @author Karel Goderis - Enhancement for the V2 LIFX Firmware and LAN Protocol Specification */ public class StateLocationResponse extends Packet { diff --git a/bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/protocol/StateMultiZoneResponse.java b/bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/dto/StateMultiZoneResponse.java similarity index 95% rename from bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/protocol/StateMultiZoneResponse.java rename to bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/dto/StateMultiZoneResponse.java index 47f167deb0f11..4e2443bdd2dca 100644 --- a/bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/protocol/StateMultiZoneResponse.java +++ b/bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/dto/StateMultiZoneResponse.java @@ -10,7 +10,7 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.binding.lifx.internal.protocol; +package org.openhab.binding.lifx.internal.dto; import java.nio.ByteBuffer; @@ -20,7 +20,7 @@ import org.openhab.binding.lifx.internal.fields.UInt8Field; /** - * @author Wouter Born - Add support for MultiZone light control + * @author Wouter Born - Initial contribution */ public class StateMultiZoneResponse extends Packet { diff --git a/bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/protocol/StatePowerResponse.java b/bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/dto/StatePowerResponse.java similarity index 93% rename from bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/protocol/StatePowerResponse.java rename to bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/dto/StatePowerResponse.java index b6689a6429901..7d86030cfcde5 100644 --- a/bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/protocol/StatePowerResponse.java +++ b/bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/dto/StatePowerResponse.java @@ -10,7 +10,7 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.binding.lifx.internal.protocol; +package org.openhab.binding.lifx.internal.dto; import java.nio.ByteBuffer; @@ -18,7 +18,7 @@ import org.openhab.binding.lifx.internal.fields.UInt16Field; /** - * @author Tim Buckley - Initial Contribution + * @author Tim Buckley - Initial contribution * @author Karel Goderis - Enhancement for the V2 LIFX Firmware and LAN Protocol Specification */ public class StatePowerResponse extends Packet { diff --git a/bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/protocol/StateResponse.java b/bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/dto/StateResponse.java similarity index 96% rename from bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/protocol/StateResponse.java rename to bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/dto/StateResponse.java index ff3bf40bf2bd8..808577b376f3c 100644 --- a/bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/protocol/StateResponse.java +++ b/bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/dto/StateResponse.java @@ -10,7 +10,7 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.binding.lifx.internal.protocol; +package org.openhab.binding.lifx.internal.dto; import java.nio.ByteBuffer; @@ -22,7 +22,7 @@ import org.openhab.binding.lifx.internal.fields.UInt64Field; /** - * @author Tim Buckley - Initial Contribution + * @author Tim Buckley - Initial contribution * @author Karel Goderis - Enhancement for the V2 LIFX Firmware and LAN Protocol Specification */ public class StateResponse extends Packet { diff --git a/bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/protocol/StateServiceResponse.java b/bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/dto/StateServiceResponse.java similarity index 94% rename from bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/protocol/StateServiceResponse.java rename to bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/dto/StateServiceResponse.java index 84c6a23826e0a..599da30b7098d 100644 --- a/bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/protocol/StateServiceResponse.java +++ b/bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/dto/StateServiceResponse.java @@ -10,7 +10,7 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.binding.lifx.internal.protocol; +package org.openhab.binding.lifx.internal.dto; import java.nio.ByteBuffer; @@ -20,7 +20,7 @@ import org.openhab.binding.lifx.internal.fields.UInt8Field; /** - * @author Tim Buckley - Initial Contribution + * @author Tim Buckley - Initial contribution * @author Karel Goderis - Enhancement for the V2 LIFX Firmware and LAN Protocol Specification */ public class StateServiceResponse extends Packet { diff --git a/bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/protocol/StateTileEffectResponse.java b/bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/dto/StateTileEffectResponse.java similarity index 98% rename from bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/protocol/StateTileEffectResponse.java rename to bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/dto/StateTileEffectResponse.java index 6bfb630f595f6..c9b6f40e9d6d3 100644 --- a/bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/protocol/StateTileEffectResponse.java +++ b/bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/dto/StateTileEffectResponse.java @@ -10,7 +10,7 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.binding.lifx.internal.protocol; +package org.openhab.binding.lifx.internal.dto; import java.nio.ByteBuffer; @@ -26,7 +26,7 @@ /** * Implementation of StateTileEffect packet * - * @author Pawel Pieczul - Initial Contribution + * @author Pawel Pieczul - Initial contribution */ public class StateTileEffectResponse extends Packet { diff --git a/bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/protocol/StateVersionResponse.java b/bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/dto/StateVersionResponse.java similarity index 95% rename from bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/protocol/StateVersionResponse.java rename to bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/dto/StateVersionResponse.java index 410dc3720b1b7..db0b14e433493 100644 --- a/bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/protocol/StateVersionResponse.java +++ b/bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/dto/StateVersionResponse.java @@ -10,7 +10,7 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.binding.lifx.internal.protocol; +package org.openhab.binding.lifx.internal.dto; import java.nio.ByteBuffer; @@ -18,7 +18,7 @@ import org.openhab.binding.lifx.internal.fields.UInt32Field; /** - * @author Tim Buckley - Initial Contribution + * @author Tim Buckley - Initial contribution * @author Karel Goderis - Enhancement for the V2 LIFX Firmware and LAN Protocol Specification */ public class StateVersionResponse extends Packet { diff --git a/bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/protocol/StateWifiFirmwareResponse.java b/bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/dto/StateWifiFirmwareResponse.java similarity index 95% rename from bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/protocol/StateWifiFirmwareResponse.java rename to bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/dto/StateWifiFirmwareResponse.java index b0cbc950106b0..6cf177f0e961f 100644 --- a/bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/protocol/StateWifiFirmwareResponse.java +++ b/bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/dto/StateWifiFirmwareResponse.java @@ -10,7 +10,7 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.binding.lifx.internal.protocol; +package org.openhab.binding.lifx.internal.dto; import java.nio.ByteBuffer; @@ -20,7 +20,7 @@ import org.openhab.binding.lifx.internal.fields.VersionField; /** - * @author Tim Buckley - Initial Contribution + * @author Tim Buckley - Initial contribution * @author Karel Goderis - Enhancement for the V2 LIFX Firmware and LAN Protocol Specification */ public class StateWifiFirmwareResponse extends Packet { diff --git a/bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/protocol/StateWifiInfoResponse.java b/bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/dto/StateWifiInfoResponse.java similarity index 95% rename from bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/protocol/StateWifiInfoResponse.java rename to bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/dto/StateWifiInfoResponse.java index 3cb832b1538c3..516562d2dae22 100644 --- a/bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/protocol/StateWifiInfoResponse.java +++ b/bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/dto/StateWifiInfoResponse.java @@ -10,7 +10,7 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.binding.lifx.internal.protocol; +package org.openhab.binding.lifx.internal.dto; import java.nio.ByteBuffer; @@ -20,7 +20,7 @@ import org.openhab.binding.lifx.internal.fields.UInt32Field; /** - * @author Tim Buckley - Initial Contribution + * @author Tim Buckley - Initial contribution * @author Karel Goderis - Enhancement for the V2 LIFX Firmware and LAN Protocol Specification */ public class StateWifiInfoResponse extends Packet { diff --git a/bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/protocol/StateZoneResponse.java b/bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/dto/StateZoneResponse.java similarity index 94% rename from bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/protocol/StateZoneResponse.java rename to bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/dto/StateZoneResponse.java index e95135fc8e54d..26eaf2824635a 100644 --- a/bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/protocol/StateZoneResponse.java +++ b/bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/dto/StateZoneResponse.java @@ -10,7 +10,7 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.binding.lifx.internal.protocol; +package org.openhab.binding.lifx.internal.dto; import java.nio.ByteBuffer; @@ -20,7 +20,7 @@ import org.openhab.binding.lifx.internal.fields.UInt8Field; /** - * @author Wouter Born - Add support for MultiZone light control + * @author Wouter Born - Initial contribution */ public class StateZoneResponse extends Packet { diff --git a/bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/protocol/TagLabelsResponse.java b/bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/dto/TagLabelsResponse.java similarity index 94% rename from bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/protocol/TagLabelsResponse.java rename to bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/dto/TagLabelsResponse.java index ef3aa17676f72..5ecd16b0c7d45 100644 --- a/bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/protocol/TagLabelsResponse.java +++ b/bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/dto/TagLabelsResponse.java @@ -10,7 +10,7 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.binding.lifx.internal.protocol; +package org.openhab.binding.lifx.internal.dto; import java.nio.ByteBuffer; @@ -19,7 +19,7 @@ import org.openhab.binding.lifx.internal.fields.UInt64Field; /** - * @author Tim Buckley - Initial Contribution + * @author Tim Buckley - Initial contribution * @author Karel Goderis - Enhancement for the V2 LIFX Firmware and LAN Protocol Specification */ public class TagLabelsResponse extends Packet { diff --git a/bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/protocol/TagsResponse.java b/bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/dto/TagsResponse.java similarity index 93% rename from bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/protocol/TagsResponse.java rename to bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/dto/TagsResponse.java index 257f28eab9444..104ad08b26cae 100644 --- a/bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/protocol/TagsResponse.java +++ b/bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/dto/TagsResponse.java @@ -10,7 +10,7 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.binding.lifx.internal.protocol; +package org.openhab.binding.lifx.internal.dto; import java.nio.ByteBuffer; @@ -18,7 +18,7 @@ import org.openhab.binding.lifx.internal.fields.UInt64Field; /** - * @author Tim Buckley - Initial Contribution + * @author Tim Buckley - Initial contribution * @author Karel Goderis - Enhancement for the V2 LIFX Firmware and LAN Protocol Specification */ public class TagsResponse extends Packet { diff --git a/bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/fields/ByteField.java b/bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/fields/ByteField.java index 29418f3263a4e..5649a32d81e6f 100644 --- a/bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/fields/ByteField.java +++ b/bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/fields/ByteField.java @@ -17,7 +17,7 @@ import org.eclipse.jdt.annotation.NonNullByDefault; /** - * @author Tim Buckley + * @author Tim Buckley - Initial contribution */ @NonNullByDefault public class ByteField extends Field { diff --git a/bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/fields/Field.java b/bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/fields/Field.java index e2bcc7d9e6407..1a1ae2c92c49a 100644 --- a/bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/fields/Field.java +++ b/bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/fields/Field.java @@ -22,7 +22,7 @@ * * @param the field datatype * - * @author Tim Buckley + * @author Tim Buckley - Initial contribution */ @NonNullByDefault public abstract class Field { diff --git a/bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/fields/FloatField.java b/bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/fields/FloatField.java index 7e29fd5819025..ffb7bd40f09d8 100644 --- a/bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/fields/FloatField.java +++ b/bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/fields/FloatField.java @@ -17,7 +17,7 @@ import org.eclipse.jdt.annotation.NonNullByDefault; /** - * @author Tim Buckley + * @author Tim Buckley - Initial contribution */ @NonNullByDefault public class FloatField extends Field { diff --git a/bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/fields/HSBK.java b/bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/fields/HSBK.java index e31629cbfa158..23885500c6d43 100644 --- a/bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/fields/HSBK.java +++ b/bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/fields/HSBK.java @@ -21,7 +21,7 @@ import org.openhab.core.library.types.PercentType; /** - * @author Wouter Born - Add support for MultiZone light control + * @author Wouter Born - Initial contribution */ @NonNullByDefault public class HSBK { diff --git a/bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/fields/HSBKField.java b/bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/fields/HSBKField.java index c254f4bdb7919..75d1e167019ef 100644 --- a/bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/fields/HSBKField.java +++ b/bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/fields/HSBKField.java @@ -17,7 +17,7 @@ import org.eclipse.jdt.annotation.NonNullByDefault; /** - * @author Wouter Born - Add support for MultiZone light control + * @author Wouter Born - Initial contribution */ @NonNullByDefault public class HSBKField extends Field { diff --git a/bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/fields/LittleField.java b/bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/fields/LittleField.java index 8dfcb257f42ec..5352491dfec37 100644 --- a/bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/fields/LittleField.java +++ b/bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/fields/LittleField.java @@ -19,7 +19,7 @@ /** * Reads a wrapped field in reversed byte order. * - * @author Tim Buckley + * @author Tim Buckley - Initial contribution */ @NonNullByDefault public class LittleField extends Field { diff --git a/bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/fields/MACAddress.java b/bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/fields/MACAddress.java index bfce15f801418..91190f4242b42 100644 --- a/bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/fields/MACAddress.java +++ b/bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/fields/MACAddress.java @@ -12,30 +12,23 @@ */ package org.openhab.binding.lifx.internal.fields; -import java.io.ByteArrayInputStream; -import java.io.IOException; import java.nio.ByteBuffer; -import java.util.Arrays; import java.util.LinkedList; import java.util.List; import java.util.Objects; -import org.apache.commons.lang.StringUtils; import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.Nullable; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; +import org.openhab.core.util.HexUtils; /** - * @author Tim Buckley - * @author Karel Goderis + * @author Tim Buckley - Initial contribution + * @author Karel Goderis - Initial contribution */ @NonNullByDefault public class MACAddress { - public static final MACAddress BROADCAST_ADDRESS = new MACAddress("000000000000", true); - - private final Logger logger = LoggerFactory.getLogger(MACAddress.class); + public static final MACAddress BROADCAST_ADDRESS = new MACAddress("000000000000"); private ByteBuffer bytes; private String hex = ""; @@ -54,28 +47,10 @@ public MACAddress(ByteBuffer bytes) { createHex(); } - public MACAddress(String string, boolean isHex) { - if (!isHex) { - this.bytes = ByteBuffer.wrap(string.getBytes()); - createHex(); - } else { - this.bytes = ByteBuffer.wrap(parseHexBinary(string)); - - try { - formatHex(string, 2, ":"); - } catch (IOException e) { - logger.error("An exception occurred while formatting an HEX string : '{}'", e.getMessage()); - } - } - } - - private byte[] parseHexBinary(String s) { - int len = s.length(); - byte[] data = new byte[len / 2]; - for (int i = 0; i < len; i += 2) { - data[i / 2] = (byte) ((Character.digit(s.charAt(i), 16) << 4) + Character.digit(s.charAt(i + 1), 16)); - } - return data; + public MACAddress(String string) { + byte[] byteArray = HexUtils.hexToBytes(string); + this.bytes = ByteBuffer.wrap(byteArray); + this.hex = HexUtils.bytesToHex(byteArray, ":"); } public MACAddress() { @@ -90,7 +65,7 @@ private void createHex() { byteStrings.add(String.format("%02X", bytes.get())); } - hex = StringUtils.join(byteStrings, ':'); + hex = String.join(":", byteStrings); bytes.rewind(); } @@ -108,21 +83,6 @@ public String getAsLabel() { return hex.toString(); } - private void formatHex(String original, int length, String separator) throws IOException { - ByteArrayInputStream bis = new ByteArrayInputStream(original.getBytes()); - byte[] buffer = new byte[length]; - String result = ""; - while (bis.read(buffer) > 0) { - for (byte b : buffer) { - result += (char) b; - } - Arrays.fill(buffer, (byte) 0); - result += separator; - } - - hex = StringUtils.left(result, result.length() - 1); - } - @Override public int hashCode() { int hash = 7; diff --git a/bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/fields/MACAddressField.java b/bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/fields/MACAddressField.java index 417348cb71e05..57471120c0279 100644 --- a/bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/fields/MACAddressField.java +++ b/bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/fields/MACAddressField.java @@ -17,8 +17,8 @@ import org.eclipse.jdt.annotation.NonNullByDefault; /** - * @author Tim Buckley - * @author Karel Goderis + * @author Tim Buckley - Initial contribution + * @author Karel Goderis - Initial contribution */ @NonNullByDefault public class MACAddressField extends Field { diff --git a/bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/fields/StringField.java b/bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/fields/StringField.java index 360ceb2816126..0184d65c09ee0 100644 --- a/bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/fields/StringField.java +++ b/bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/fields/StringField.java @@ -19,7 +19,7 @@ import org.eclipse.jdt.annotation.NonNullByDefault; /** - * @author Tim Buckley + * @author Tim Buckley - Initial contribution */ @NonNullByDefault public class StringField extends Field { diff --git a/bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/fields/UInt16Field.java b/bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/fields/UInt16Field.java index 3cdcb44c28bbe..a9c65d2e53008 100644 --- a/bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/fields/UInt16Field.java +++ b/bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/fields/UInt16Field.java @@ -17,7 +17,7 @@ import org.eclipse.jdt.annotation.NonNullByDefault; /** - * @author Tim Buckley + * @author Tim Buckley - Initial contribution */ @NonNullByDefault public class UInt16Field extends Field { diff --git a/bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/fields/UInt32Field.java b/bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/fields/UInt32Field.java index 04b9d7acbee7b..89ce6fb8828e7 100644 --- a/bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/fields/UInt32Field.java +++ b/bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/fields/UInt32Field.java @@ -17,7 +17,7 @@ import org.eclipse.jdt.annotation.NonNullByDefault; /** - * @author Tim Buckley + * @author Tim Buckley - Initial contribution */ @NonNullByDefault public class UInt32Field extends Field { diff --git a/bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/fields/UInt64Field.java b/bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/fields/UInt64Field.java index bfc229a4dd354..b72ef6b2db748 100644 --- a/bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/fields/UInt64Field.java +++ b/bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/fields/UInt64Field.java @@ -21,7 +21,7 @@ * unexpected values will likely be shown if exposed to users. Most bit-level * operations should still work (addition, multiplication, shifting, etc). * - * @author Tim Buckley + * @author Tim Buckley - Initial contribution */ @NonNullByDefault public class UInt64Field extends Field { diff --git a/bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/fields/UInt8Field.java b/bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/fields/UInt8Field.java index 23ba2353bde72..969c468061fe6 100644 --- a/bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/fields/UInt8Field.java +++ b/bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/fields/UInt8Field.java @@ -17,7 +17,7 @@ import org.eclipse.jdt.annotation.NonNullByDefault; /** - * @author Tim Buckley + * @author Tim Buckley - Initial contribution */ @NonNullByDefault public class UInt8Field extends Field { diff --git a/bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/fields/Version.java b/bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/fields/Version.java index 559bae30ecac8..3d53df7703c92 100644 --- a/bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/fields/Version.java +++ b/bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/fields/Version.java @@ -15,7 +15,7 @@ import org.eclipse.jdt.annotation.NonNullByDefault; /** - * @author Wouter Born - Add Thing properties + * @author Wouter Born - Initial contribution */ @NonNullByDefault public class Version { diff --git a/bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/fields/VersionField.java b/bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/fields/VersionField.java index 7cec7a8e96add..26af5382c6a61 100644 --- a/bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/fields/VersionField.java +++ b/bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/fields/VersionField.java @@ -17,7 +17,7 @@ import org.eclipse.jdt.annotation.NonNullByDefault; /** - * @author Wouter Born - Add Thing properties + * @author Wouter Born - Initial contribution */ @NonNullByDefault public class VersionField extends Field { diff --git a/bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/handler/LifxLightHandler.java b/bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/handler/LifxLightHandler.java index af941d2f96692..fd0684298003c 100644 --- a/bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/handler/LifxLightHandler.java +++ b/bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/handler/LifxLightHandler.java @@ -13,7 +13,7 @@ package org.openhab.binding.lifx.internal.handler; import static org.openhab.binding.lifx.internal.LifxBindingConstants.*; -import static org.openhab.binding.lifx.internal.protocol.Product.Feature.*; +import static org.openhab.binding.lifx.internal.LifxProduct.Feature.*; import static org.openhab.binding.lifx.internal.util.LifxMessageUtil.*; import java.net.InetSocketAddress; @@ -24,6 +24,7 @@ import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.Objects; import java.util.concurrent.locks.ReentrantLock; import org.eclipse.jdt.annotation.NonNullByDefault; @@ -38,18 +39,19 @@ import org.openhab.binding.lifx.internal.LifxLightPropertiesUpdater; import org.openhab.binding.lifx.internal.LifxLightState; import org.openhab.binding.lifx.internal.LifxLightStateChanger; +import org.openhab.binding.lifx.internal.LifxProduct; +import org.openhab.binding.lifx.internal.LifxProduct.Features; +import org.openhab.binding.lifx.internal.dto.Effect; +import org.openhab.binding.lifx.internal.dto.GetLightInfraredRequest; +import org.openhab.binding.lifx.internal.dto.GetLightPowerRequest; +import org.openhab.binding.lifx.internal.dto.GetRequest; +import org.openhab.binding.lifx.internal.dto.GetTileEffectRequest; +import org.openhab.binding.lifx.internal.dto.GetWifiInfoRequest; +import org.openhab.binding.lifx.internal.dto.Packet; +import org.openhab.binding.lifx.internal.dto.PowerState; +import org.openhab.binding.lifx.internal.dto.SignalStrength; import org.openhab.binding.lifx.internal.fields.HSBK; import org.openhab.binding.lifx.internal.fields.MACAddress; -import org.openhab.binding.lifx.internal.protocol.Effect; -import org.openhab.binding.lifx.internal.protocol.GetLightInfraredRequest; -import org.openhab.binding.lifx.internal.protocol.GetLightPowerRequest; -import org.openhab.binding.lifx.internal.protocol.GetRequest; -import org.openhab.binding.lifx.internal.protocol.GetTileEffectRequest; -import org.openhab.binding.lifx.internal.protocol.GetWifiInfoRequest; -import org.openhab.binding.lifx.internal.protocol.Packet; -import org.openhab.binding.lifx.internal.protocol.PowerState; -import org.openhab.binding.lifx.internal.protocol.Product; -import org.openhab.binding.lifx.internal.protocol.SignalStrength; import org.openhab.core.config.core.Configuration; import org.openhab.core.library.types.DecimalType; import org.openhab.core.library.types.HSBType; @@ -90,7 +92,7 @@ public class LifxLightHandler extends BaseThingHandler { private static final Duration MAX_STATE_CHANGE_DURATION = Duration.ofSeconds(4); private final LifxChannelFactory channelFactory; - private @NonNullByDefault({}) Product product; + private @NonNullByDefault({}) Features features; private @Nullable PercentType powerOnBrightness; private @Nullable HSBType powerOnColor; @@ -175,7 +177,7 @@ private void updateColorChannels(@Nullable PowerState powerState, HSBK[] colors) updateStateIfChanged(CHANNEL_COLOR, hsb); updateStateIfChanged(CHANNEL_BRIGHTNESS, hsb.getBrightness()); updateStateIfChanged(CHANNEL_TEMPERATURE, - kelvinToPercentType(updateColor.getKelvin(), product.getTemperatureRange())); + kelvinToPercentType(updateColor.getKelvin(), features.getTemperatureRange())); updateZoneChannels(powerState, colors); } @@ -210,7 +212,7 @@ public void setTileEffect(Effect effect) { } private void updateZoneChannels(@Nullable PowerState powerState, HSBK[] colors) { - if (!product.hasFeature(MULTIZONE) || colors.length == 0) { + if (!features.hasFeature(MULTIZONE) || colors.length == 0) { return; } @@ -225,7 +227,7 @@ private void updateZoneChannels(@Nullable PowerState powerState, HSBK[] colors) HSBK updateColor = nullSafeUpdateColor(powerState, color); updateStateIfChanged(CHANNEL_COLOR_ZONE + i, updateColor.getHSB()); updateStateIfChanged(CHANNEL_TEMPERATURE_ZONE + i, - kelvinToPercentType(updateColor.getKelvin(), product.getTemperatureRange())); + kelvinToPercentType(updateColor.getKelvin(), features.getTemperatureRange())); } } } @@ -243,9 +245,12 @@ public void initialize() { LifxLightConfig configuration = getConfigAs(LifxLightConfig.class); logId = getLogId(configuration.getMACAddress(), configuration.getHost()); - product = getProduct(); - logger.debug("{} : Initializing handler for product {}", logId, product.getName()); + if (logger.isDebugEnabled()) { + logger.debug("{} : Initializing handler for product {}", logId, getProduct().getName()); + } + + features = getFeatures(); powerOnBrightness = getPowerOnBrightness(); powerOnColor = getPowerOnColor(); @@ -262,7 +267,7 @@ public void initialize() { currentLightState = new CurrentLightState(); pendingLightState = new LifxLightState(); - LifxLightContext context = new LifxLightContext(logId, product, configuration, currentLightState, + LifxLightContext context = new LifxLightContext(logId, features, configuration, currentLightState, pendingLightState, scheduler); communicationHandler = new LifxLightCommunicationHandler(context); @@ -338,7 +343,7 @@ public String getLogId(@Nullable MACAddress macAddress, @Nullable InetSocketAddr private @Nullable PercentType getPowerOnBrightness() { Channel channel = null; - if (product.hasFeature(COLOR)) { + if (features.hasFeature(COLOR)) { ChannelUID channelUID = new ChannelUID(getThing().getUID(), LifxBindingConstants.CHANNEL_COLOR); channel = getThing().getChannel(channelUID.getId()); } else { @@ -358,7 +363,7 @@ public String getLogId(@Nullable MACAddress macAddress, @Nullable InetSocketAddr private @Nullable HSBType getPowerOnColor() { Channel channel = null; - if (product.hasFeature(COLOR)) { + if (features.hasFeature(COLOR)) { ChannelUID channelUID = new ChannelUID(getThing().getUID(), LifxBindingConstants.CHANNEL_COLOR); channel = getThing().getChannel(channelUID.getId()); } @@ -391,7 +396,7 @@ public String getLogId(@Nullable MACAddress macAddress, @Nullable InetSocketAddr private @Nullable Double getEffectSpeed(String parameter) { Channel channel = null; - if (product.hasFeature(TILE_EFFECT)) { + if (features.hasFeature(TILE_EFFECT)) { ChannelUID channelUID = new ChannelUID(getThing().getUID(), LifxBindingConstants.CHANNEL_EFFECT); channel = getThing().getChannel(channelUID.getId()); } @@ -402,23 +407,36 @@ public String getLogId(@Nullable MACAddress macAddress, @Nullable InetSocketAddr Configuration configuration = channel.getConfiguration(); Object speed = configuration.get(parameter); - return speed == null ? null : new Double(speed.toString()); + return speed == null ? null : Double.valueOf(speed.toString()); } - private Product getProduct() { - Object propertyValue = getThing().getProperties().get(LifxBindingConstants.PROPERTY_PRODUCT_ID); + private Features getFeatures() { + LifxProduct product = getProduct(); + + String propertyValue = getThing().getProperties().get(LifxBindingConstants.PROPERTY_HOST_VERSION); if (propertyValue == null) { - return Product.getLikelyProduct(getThing().getThingTypeUID()); + logger.debug("{} : Using features of initial firmware version", logId); + return product.getFeatures(); + } + + logger.debug("{} : Using features of firmware version {}", logId, propertyValue); + return product.getFeatures(propertyValue); + } + + private LifxProduct getProduct() { + String propertyValue = getThing().getProperties().get(LifxBindingConstants.PROPERTY_PRODUCT_ID); + if (propertyValue == null) { + return LifxProduct.getLikelyProduct(getThing().getThingTypeUID()); } try { // Without first conversion to double, on a very first thing creation from discovery inbox, // the product type is incorrectly parsed, as framework passed it as a floating point number // (e.g. 50.0 instead of 50) - Double d = Double.parseDouble((String) propertyValue); + Double d = Double.valueOf(propertyValue); long productID = d.longValue(); - return Product.getProductFromProductID(productID); + return LifxProduct.getProductFromProductID(productID); } catch (IllegalArgumentException e) { - return Product.getLikelyProduct(getThing().getThingTypeUID()); + return LifxProduct.getLikelyProduct(getThing().getThingTypeUID()); } } @@ -485,7 +503,7 @@ public void handleCommand(ChannelUID channelUID, Command command) { sendPacket(new GetWifiInfoRequest()); break; case CHANNEL_EFFECT: - if (product.hasFeature(TILE_EFFECT)) { + if (features.hasFeature(TILE_EFFECT)) { sendPacket(new GetTileEffectRequest()); } break; @@ -538,7 +556,7 @@ public void handleCommand(ChannelUID channelUID, Command command) { } break; case CHANNEL_EFFECT: - if (command instanceof StringType && product.hasFeature(TILE_EFFECT)) { + if (command instanceof StringType && features.hasFeature(TILE_EFFECT)) { handleTileEffectCommand((StringType) command); } else { supportedCommand = false; @@ -597,14 +615,14 @@ private boolean isStateChangePending() { private void handleTemperatureCommand(PercentType temperature) { HSBK newColor = getLightStateForCommand().getColor(); newColor.setSaturation(PercentType.ZERO); - newColor.setKelvin(percentTypeToKelvin(temperature, product.getTemperatureRange())); + newColor.setKelvin(percentTypeToKelvin(temperature, features.getTemperatureRange())); getLightStateForCommand().setColor(newColor); } private void handleTemperatureCommand(PercentType temperature, int zoneIndex) { HSBK newColor = getLightStateForCommand().getColor(zoneIndex); newColor.setSaturation(PercentType.ZERO); - newColor.setKelvin(percentTypeToKelvin(temperature, product.getTemperatureRange())); + newColor.setKelvin(percentTypeToKelvin(temperature, features.getTemperatureRange())); getLightStateForCommand().setColor(newColor, zoneIndex); } @@ -633,7 +651,7 @@ private void handleOnOffCommand(OnOffType onOff) { PercentType localPowerOnTemperature = powerOnTemperature; if (localPowerOnTemperature != null && onOff == OnOffType.ON) { getLightStateForCommand() - .setTemperature(percentTypeToKelvin(localPowerOnTemperature, product.getTemperatureRange())); + .setTemperature(percentTypeToKelvin(localPowerOnTemperature, features.getTemperatureRange())); } PercentType powerOnBrightness = this.powerOnBrightness; @@ -658,14 +676,14 @@ private void handleIncreaseDecreaseCommand(IncreaseDecreaseType increaseDecrease private void handleIncreaseDecreaseTemperatureCommand(IncreaseDecreaseType increaseDecrease) { PercentType baseTemperature = kelvinToPercentType(getLightStateForCommand().getColor().getKelvin(), - product.getTemperatureRange()); + features.getTemperatureRange()); PercentType newTemperature = increaseDecreasePercentType(increaseDecrease, baseTemperature); handleTemperatureCommand(newTemperature); } private void handleIncreaseDecreaseTemperatureCommand(IncreaseDecreaseType increaseDecrease, int zoneIndex) { PercentType baseTemperature = kelvinToPercentType(getLightStateForCommand().getColor(zoneIndex).getKelvin(), - product.getTemperatureRange()); + features.getTemperatureRange()); PercentType newTemperature = increaseDecreasePercentType(increaseDecrease, baseTemperature); handleTemperatureCommand(newTemperature, zoneIndex); } @@ -691,7 +709,18 @@ private void handleTileEffectCommand(StringType type) { flameSpeedInMSecs.longValue()); getLightStateForCommand().setTileEffect(effect); } catch (IllegalArgumentException e) { - logger.debug("Wrong effect type received as command: {}", type); + logger.debug("{} : Wrong effect type received as command: {}", logId, type); + } + } + + @Override + protected void updateProperties(Map properties) { + String oldHostVersion = getThing().getProperties().get(LifxBindingConstants.PROPERTY_HOST_VERSION); + super.updateProperties(properties); + String newHostVersion = getThing().getProperties().get(LifxBindingConstants.PROPERTY_HOST_VERSION); + + if (!Objects.equals(oldHostVersion, newHostVersion)) { + features.update(getFeatures()); } } diff --git a/bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/listener/LifxLightStateListener.java b/bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/listener/LifxLightStateListener.java index 68b7c460aaf7c..fab89fa7f1984 100644 --- a/bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/listener/LifxLightStateListener.java +++ b/bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/listener/LifxLightStateListener.java @@ -15,10 +15,10 @@ import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.Nullable; import org.openhab.binding.lifx.internal.LifxLightState; +import org.openhab.binding.lifx.internal.dto.Effect; +import org.openhab.binding.lifx.internal.dto.PowerState; +import org.openhab.binding.lifx.internal.dto.SignalStrength; import org.openhab.binding.lifx.internal.fields.HSBK; -import org.openhab.binding.lifx.internal.protocol.Effect; -import org.openhab.binding.lifx.internal.protocol.PowerState; -import org.openhab.binding.lifx.internal.protocol.SignalStrength; import org.openhab.core.library.types.PercentType; /** diff --git a/bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/listener/LifxPropertiesUpdateListener.java b/bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/listener/LifxPropertiesUpdateListener.java index 8909ee73bf356..c4dedcdea3e0a 100644 --- a/bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/listener/LifxPropertiesUpdateListener.java +++ b/bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/listener/LifxPropertiesUpdateListener.java @@ -21,7 +21,7 @@ * The {@link LifxPropertiesUpdateListener} is notified when the {@link LifxLightPropertiesUpdater} has * updated light properties. * - * @author Wouter Born - Update light properties when online + * @author Wouter Born - Initial contribution */ @NonNullByDefault public interface LifxPropertiesUpdateListener { diff --git a/bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/listener/LifxResponsePacketListener.java b/bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/listener/LifxResponsePacketListener.java index fa622f6e64b22..10396ac5e8964 100644 --- a/bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/listener/LifxResponsePacketListener.java +++ b/bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/listener/LifxResponsePacketListener.java @@ -14,7 +14,7 @@ import org.eclipse.jdt.annotation.NonNullByDefault; import org.openhab.binding.lifx.internal.LifxLightCommunicationHandler; -import org.openhab.binding.lifx.internal.protocol.Packet; +import org.openhab.binding.lifx.internal.dto.Packet; /** * The {@link LifxResponsePacketListener} is notified when the {@link LifxLightCommunicationHandler} receives a response diff --git a/bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/protocol/Product.java b/bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/protocol/Product.java deleted file mode 100644 index cee8e8d04dd79..0000000000000 --- a/bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/protocol/Product.java +++ /dev/null @@ -1,312 +0,0 @@ -/** - * Copyright (c) 2010-2021 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.lifx.internal.protocol; - -import static org.openhab.binding.lifx.internal.protocol.Product.Feature.*; -import static org.openhab.binding.lifx.internal.protocol.Product.TemperatureRange.*; -import static org.openhab.binding.lifx.internal.protocol.Product.Vendor.LIFX; - -import java.util.Arrays; -import java.util.EnumSet; - -import org.eclipse.jdt.annotation.NonNullByDefault; -import org.openhab.binding.lifx.internal.LifxBindingConstants; -import org.openhab.core.thing.ThingTypeUID; - -/** - * Enumerates the LIFX products, their IDs and feature set. - * - * @see https://lan.developer.lifx.com/docs/lifx-products - * - * @author Wouter Born - Support LIFX 2016 product line-up and infrared functionality - * @author Wouter Born - Add temperature ranges and simplify feature definitions - */ -@NonNullByDefault -public enum Product { - - PRODUCT_1(1, "LIFX Original 1000", TR_2500_9000, COLOR), - PRODUCT_3(3, "LIFX Color 650", TR_2500_9000, COLOR), - PRODUCT_10(10, "LIFX White 800 (Low Voltage)", TR_2700_6500), - PRODUCT_11(11, "LIFX White 800 (High Voltage)", TR_2700_6500), - PRODUCT_15(15, "LIFX Color 1000", TR_2500_9000, COLOR), - PRODUCT_18(18, "LIFX White 900 BR30 (Low Voltage)", TR_2700_6500), - PRODUCT_19(19, "LIFX White 900 BR30 (High Voltage)", TR_2700_6500), - PRODUCT_20(20, "LIFX Color 1000 BR30", TR_2500_9000, COLOR), - PRODUCT_22(22, "LIFX Color 1000", TR_2500_9000, COLOR), - PRODUCT_27(27, "LIFX A19", TR_2500_9000, COLOR), - PRODUCT_28(28, "LIFX BR30", TR_2500_9000, COLOR), - PRODUCT_29(29, "LIFX A19 Night Vision", TR_2500_9000, COLOR, INFRARED), - PRODUCT_30(30, "LIFX BR30 Night Vision", TR_2500_9000, COLOR, INFRARED), - PRODUCT_31(31, "LIFX Z", TR_2500_9000, COLOR, MULTIZONE), - PRODUCT_32(32, "LIFX Z", TR_2500_9000, COLOR, MULTIZONE), - PRODUCT_36(36, "LIFX Downlight", TR_2500_9000, COLOR), - PRODUCT_37(37, "LIFX Downlight", TR_2500_9000, COLOR), - PRODUCT_38(38, "LIFX Beam", TR_2500_9000, COLOR, MULTIZONE), - PRODUCT_39(39, "LIFX Downlight White to Warm", TR_1500_9000), - PRODUCT_40(40, "LIFX Downlight", TR_2500_9000, COLOR), - PRODUCT_43(43, "LIFX A19", TR_2500_9000, COLOR), - PRODUCT_44(44, "LIFX BR30", TR_2500_9000, COLOR), - PRODUCT_45(45, "LIFX A19 Night Vision", TR_2500_9000, COLOR, INFRARED), - PRODUCT_46(46, "LIFX BR30 Night Vision", TR_2500_9000, COLOR, INFRARED), - PRODUCT_49(49, "LIFX Mini Color", TR_2500_9000, COLOR), - PRODUCT_50(50, "LIFX Mini White to Warm", TR_1500_4000), - PRODUCT_51(51, "LIFX Mini White", TR_2700_2700), - PRODUCT_52(52, "LIFX GU10", TR_2500_9000, COLOR), - PRODUCT_53(53, "LIFX GU10", TR_2500_9000, COLOR), - PRODUCT_55(55, "LIFX Tile", TR_2500_9000, CHAIN, COLOR, MATRIX, TILE_EFFECT), - PRODUCT_57(57, "LIFX Candle", TR_1500_9000, COLOR, MATRIX), - PRODUCT_59(59, "LIFX Mini Color", TR_2500_9000, COLOR), - PRODUCT_60(60, "LIFX Mini White to Warm", TR_1500_4000), - PRODUCT_61(61, "LIFX Mini White", TR_2700_2700), - PRODUCT_62(62, "LIFX A19", TR_2500_9000, COLOR), - PRODUCT_63(63, "LIFX BR30", TR_2500_9000, COLOR), - PRODUCT_64(64, "LIFX A19 Night Vision", TR_2500_9000, COLOR, INFRARED), - PRODUCT_65(65, "LIFX BR30 Night Vision", TR_2500_9000, COLOR, INFRARED), - PRODUCT_66(66, "LIFX Mini White", TR_2700_2700), - PRODUCT_68(68, "LIFX Candle", TR_1500_9000, MATRIX), - PRODUCT_81(81, "LIFX Candle White to Warm", TR_2200_6500), - PRODUCT_82(82, "LIFX Filament Clear", TR_2100_2100), - PRODUCT_85(85, "LIFX Filament Amber", TR_2000_2000), - PRODUCT_87(87, "LIFX Mini White", TR_2700_2700), - PRODUCT_88(88, "LIFX Mini White", TR_2700_2700), - PRODUCT_90(90, "LIFX Clean", TR_2500_9000, HEV), - PRODUCT_91(91, "LIFX Color", TR_2500_9000, COLOR), - PRODUCT_92(92, "LIFX Color", TR_2500_9000, COLOR), - PRODUCT_94(94, "LIFX BR30", TR_2500_9000, COLOR), - PRODUCT_96(96, "LIFX Candle White to Warm", TR_2200_6500), - PRODUCT_97(97, "LIFX A19", TR_2500_9000, COLOR), - PRODUCT_98(98, "LIFX BR30", TR_2500_9000, COLOR), - PRODUCT_99(99, "LIFX Clean", TR_2500_9000, HEV), - PRODUCT_100(100, "LIFX Filament Clear", TR_2100_2100), - PRODUCT_101(101, "LIFX Filament Amber", TR_2000_2000), - PRODUCT_109(109, "LIFX A19 Night Vision", TR_2500_9000, COLOR, INFRARED), - PRODUCT_110(110, "LIFX BR30 Night Vision", TR_2500_9000, COLOR, INFRARED), - PRODUCT_111(111, "LIFX A19 Night Vision", TR_2500_9000, COLOR, INFRARED); - - /** - * Enumerates the product features. - */ - public enum Feature { - CHAIN, - COLOR, - HEV, - INFRARED, - MATRIX, - MULTIZONE, - TILE_EFFECT - } - - /** - * Enumerates the product vendors. - */ - public enum Vendor { - LIFX(1, "LIFX"); - - private final int id; - private final String name; - - Vendor(int id, String name) { - this.id = id; - this.name = name; - } - - public int getID() { - return id; - } - - public String getName() { - return name; - } - } - - /** - * Enumerates the color temperature ranges of lights. - */ - public enum TemperatureRange { - /** - * 1500-4000K - */ - TR_1500_4000(1500, 4000), - - /** - * 1500-9000K - */ - TR_1500_9000(1500, 9000), - - /** - * 2000-2000K - */ - TR_2000_2000(2000, 2000), - - /** - * 2100-2100K - */ - TR_2100_2100(2100, 2100), - - /** - * 2200-6500K - */ - TR_2200_6500(2200, 6500), - - /** - * 2500-9000K - */ - TR_2500_9000(2500, 9000), - - /** - * 2700-2700K - */ - TR_2700_2700(2700, 2700), - - /** - * 2700-6500K - */ - TR_2700_6500(2700, 6500); - - private final int minimum; - private final int maximum; - - TemperatureRange(int minimum, int maximum) { - this.minimum = minimum; - this.maximum = maximum; - } - - /** - * The minimum color temperature in degrees Kelvin. - * - * @return minimum color temperature (K) - */ - public int getMinimum() { - return minimum; - } - - /** - * The maxiumum color temperature in degrees Kelvin. - * - * @return maximum color temperature (K) - */ - public int getMaximum() { - return maximum; - } - - /** - * The color temperature range in degrees Kelvin. - * - * @return difference between maximum and minimum color temperature values - */ - public int getRange() { - return maximum - minimum; - } - } - - private final Vendor vendor; - private final long id; - private final String name; - private final TemperatureRange temperatureRange; - private final EnumSet features = EnumSet.noneOf(Feature.class); - - private Product(long id, String name, TemperatureRange temperatureRange) { - this(LIFX, id, name, temperatureRange); - } - - private Product(long id, String name, TemperatureRange temperatureRange, Feature... features) { - this(LIFX, id, name, temperatureRange, features); - } - - private Product(Vendor vendor, long id, String name, TemperatureRange temperatureRange) { - this(vendor, id, name, temperatureRange, new Feature[0]); - } - - private Product(Vendor vendor, long id, String name, TemperatureRange temperatureRange, Feature... features) { - this.vendor = vendor; - this.id = id; - this.name = name; - this.temperatureRange = temperatureRange; - this.features.addAll(Arrays.asList(features)); - } - - @Override - public String toString() { - return name; - } - - public Vendor getVendor() { - return vendor; - } - - public long getID() { - return id; - } - - public String getName() { - return name; - } - - public TemperatureRange getTemperatureRange() { - return temperatureRange; - } - - public ThingTypeUID getThingTypeUID() { - if (hasFeature(COLOR)) { - if (hasFeature(TILE_EFFECT)) { - return LifxBindingConstants.THING_TYPE_TILELIGHT; - } else if (hasFeature(INFRARED)) { - return LifxBindingConstants.THING_TYPE_COLORIRLIGHT; - } else if (hasFeature(MULTIZONE)) { - return LifxBindingConstants.THING_TYPE_COLORMZLIGHT; - } else { - return LifxBindingConstants.THING_TYPE_COLORLIGHT; - } - } else { - return LifxBindingConstants.THING_TYPE_WHITELIGHT; - } - } - - public boolean hasFeature(Feature feature) { - return features.contains(feature); - } - - /** - * Returns a product that has the given thing type UID. - * - * @param uid a thing type UID - * @return a product that has the given thing type UID - * @throws IllegalArgumentException when uid is not a valid LIFX thing type UID - */ - public static Product getLikelyProduct(ThingTypeUID uid) throws IllegalArgumentException { - for (Product product : Product.values()) { - if (product.getThingTypeUID().equals(uid)) { - return product; - } - } - - throw new IllegalArgumentException(uid + " is not a valid product thing type UID"); - } - - /** - * Returns the product that has the given product ID. - * - * @param id the product ID - * @return the product that has the given product ID - * @throws IllegalArgumentException when id is not a valid LIFX product ID - */ - public static Product getProductFromProductID(long id) throws IllegalArgumentException { - for (Product product : Product.values()) { - if (product.id == id) { - return product; - } - } - - throw new IllegalArgumentException(id + " is not a valid product ID"); - } -} diff --git a/bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/util/LifxMessageUtil.java b/bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/util/LifxMessageUtil.java index 7218a29f3dcc5..03c8135e2d7f3 100644 --- a/bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/util/LifxMessageUtil.java +++ b/bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/util/LifxMessageUtil.java @@ -17,8 +17,8 @@ import java.util.UUID; import org.eclipse.jdt.annotation.NonNullByDefault; +import org.openhab.binding.lifx.internal.LifxProduct.TemperatureRange; import org.openhab.binding.lifx.internal.fields.HSBK; -import org.openhab.binding.lifx.internal.protocol.Product.TemperatureRange; import org.openhab.core.library.types.DecimalType; import org.openhab.core.library.types.IncreaseDecreaseType; import org.openhab.core.library.types.PercentType; @@ -26,7 +26,7 @@ /** * Utility class for sharing message utility methods between objects. * - * @author Wouter Born - Extracted methods from LifxLightHandler + * @author Wouter Born - Initial contribution */ @NonNullByDefault public final class LifxMessageUtil { diff --git a/bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/util/LifxNetworkUtil.java b/bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/util/LifxNetworkUtil.java index 4d4774801447a..eca9a34a17efa 100644 --- a/bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/util/LifxNetworkUtil.java +++ b/bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/util/LifxNetworkUtil.java @@ -34,7 +34,7 @@ * The {@link LifxNetworkUtil} provides network interface information to the LIFX binding objects. The information is * updated when it is older than {@link #UPDATE_INTERVAL_MILLIS}. * - * @author Wouter Born - Periodically update available interface information + * @author Wouter Born - Initial contribution */ @NonNullByDefault public final class LifxNetworkUtil { diff --git a/bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/util/LifxSelectorUtil.java b/bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/util/LifxSelectorUtil.java index 10dec21b90a22..1630ec296acd5 100644 --- a/bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/util/LifxSelectorUtil.java +++ b/bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/util/LifxSelectorUtil.java @@ -32,17 +32,17 @@ import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.Nullable; import org.openhab.binding.lifx.internal.LifxSelectorContext; +import org.openhab.binding.lifx.internal.dto.Packet; +import org.openhab.binding.lifx.internal.dto.PacketFactory; +import org.openhab.binding.lifx.internal.dto.PacketHandler; import org.openhab.binding.lifx.internal.fields.MACAddress; -import org.openhab.binding.lifx.internal.protocol.Packet; -import org.openhab.binding.lifx.internal.protocol.PacketFactory; -import org.openhab.binding.lifx.internal.protocol.PacketHandler; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * Utility class for sharing {@link Selector} logic between objects. * - * @author Wouter Born - Make selector logic reusable between discovery and handlers + * @author Wouter Born - Initial contribution */ @NonNullByDefault public class LifxSelectorUtil { diff --git a/bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/util/LifxThrottlingUtil.java b/bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/util/LifxThrottlingUtil.java index 397df9e07ea02..8001d1e74b5e7 100644 --- a/bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/util/LifxThrottlingUtil.java +++ b/bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/util/LifxThrottlingUtil.java @@ -31,7 +31,7 @@ * sent to LIFX lights. The LIFX LAN Protocol Specification states that lights can process up to 20 messages per second, * not more. * - * @author Karel Goderis - Initial Contribution + * @author Karel Goderis - Initial contribution * @author Wouter Born - Deadlock fix */ @NonNullByDefault diff --git a/bundles/org.openhab.binding.lifx/src/test/java/org/openhab/binding/lifx/internal/LifxProductTest.java b/bundles/org.openhab.binding.lifx/src/test/java/org/openhab/binding/lifx/internal/LifxProductTest.java new file mode 100644 index 0000000000000..d0547ed6709fb --- /dev/null +++ b/bundles/org.openhab.binding.lifx/src/test/java/org/openhab/binding/lifx/internal/LifxProductTest.java @@ -0,0 +1,140 @@ +/** + * Copyright (c) 2010-2021 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.lifx.internal; + +import static org.hamcrest.CoreMatchers.*; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.hasSize; +import static org.openhab.binding.lifx.internal.LifxProduct.Feature.*; +import static org.openhab.binding.lifx.internal.LifxProduct.TemperatureRange.*; + +import java.util.HashSet; +import java.util.Set; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.junit.jupiter.api.Test; +import org.openhab.binding.lifx.internal.LifxProduct.Features; +import org.openhab.binding.lifx.internal.LifxProduct.Upgrade; + +/** + * Tests {@link LifxProduct}. + * + * @author Wouter Born - Initial contribution + */ +@NonNullByDefault +public class LifxProductTest { + + @Test + public void productIDsAreUnique() { + Set productIDs = new HashSet<>(); + for (LifxProduct product : LifxProduct.values()) { + assertThat(productIDs, not(hasItem(product.getID()))); + productIDs.add(product.getID()); + } + } + + @Test + public void productNamesMatchProductIDs() { + for (LifxProduct product : LifxProduct.values()) { + assertThat(product.name(), is("PRODUCT_" + product.getID())); + } + } + + @Test + public void lightsHaveDefinedTemperatureRange() { + for (LifxProduct product : LifxProduct.values()) { + if (product.isLight()) { + String reason = String.format("The %s light does not define a temperature range", product.name()); + assertThat(reason, product.getFeatures().getTemperatureRange(), is(not(NONE))); + } + } + } + + @Test + public void upgradesSortedByMajorMinor() { + for (LifxProduct product : LifxProduct.values()) { + long major = 0; + long minor = 0; + for (Upgrade upgrade : product.getUpgrades()) { + String reason = String.format("Upgrades for %s are not sorted by major minor (%s.%s >= %s.%s)", + product.name(), major, minor, upgrade.major, upgrade.minor); + assertThat(reason, major < upgrade.major || (major == upgrade.major && minor < upgrade.minor), + is(true)); + major = upgrade.major; + minor = upgrade.minor; + } + } + } + + @Test + public void getFeaturesForProductWithoutUpgrades() { + LifxProduct product = LifxProduct.PRODUCT_1; + assertThat(product.getUpgrades(), hasSize(0)); + + Features features = product.getFeatures(); + assertThat(features.getTemperatureRange(), is(TR_2500_9000)); + assertThat(features.hasFeature(COLOR), is(true)); + + features = product.getFeatures("1.23"); + assertThat(features.getTemperatureRange(), is(TR_2500_9000)); + assertThat(features.hasFeature(COLOR), is(true)); + } + + @Test + public void getFeaturesForProductWithUpgrades() { + LifxProduct product = LifxProduct.PRODUCT_32; + assertThat(product.getUpgrades(), hasSize(2)); + + Features features = product.getFeatures(); + assertThat(features.getTemperatureRange(), is(TR_2500_9000)); + assertThat(features.hasFeature(COLOR), is(true)); + assertThat(features.hasFeature(EXTENDED_MULTIZONE), is(false)); + assertThat(features.hasFeature(INFRARED), is(false)); + assertThat(features.hasFeature(MULTIZONE), is(true)); + + features = product.getFeatures("2.70"); + assertThat(features.getTemperatureRange(), is(TR_2500_9000)); + assertThat(features.hasFeature(COLOR), is(true)); + assertThat(features.hasFeature(EXTENDED_MULTIZONE), is(false)); + assertThat(features.hasFeature(INFRARED), is(false)); + assertThat(features.hasFeature(MULTIZONE), is(true)); + + features = product.getFeatures("2.77"); + assertThat(features.getTemperatureRange(), is(TR_2500_9000)); + assertThat(features.hasFeature(COLOR), is(true)); + assertThat(features.hasFeature(EXTENDED_MULTIZONE), is(true)); + assertThat(features.hasFeature(INFRARED), is(false)); + assertThat(features.hasFeature(MULTIZONE), is(true)); + + features = product.getFeatures("2.79"); + assertThat(features.getTemperatureRange(), is(TR_2500_9000)); + assertThat(features.hasFeature(COLOR), is(true)); + assertThat(features.hasFeature(EXTENDED_MULTIZONE), is(true)); + assertThat(features.hasFeature(INFRARED), is(false)); + assertThat(features.hasFeature(MULTIZONE), is(true)); + + features = product.getFeatures("2.80"); + assertThat(features.getTemperatureRange(), is(TR_1500_9000)); + assertThat(features.hasFeature(COLOR), is(true)); + assertThat(features.hasFeature(EXTENDED_MULTIZONE), is(true)); + assertThat(features.hasFeature(INFRARED), is(false)); + assertThat(features.hasFeature(MULTIZONE), is(true)); + + features = product.getFeatures("2.81"); + assertThat(features.getTemperatureRange(), is(TR_1500_9000)); + assertThat(features.hasFeature(COLOR), is(true)); + assertThat(features.hasFeature(EXTENDED_MULTIZONE), is(true)); + assertThat(features.hasFeature(INFRARED), is(false)); + assertThat(features.hasFeature(MULTIZONE), is(true)); + } +} diff --git a/bundles/org.openhab.binding.lifx/src/test/java/org/openhab/binding/lifx/internal/fields/MACAddressTest.java b/bundles/org.openhab.binding.lifx/src/test/java/org/openhab/binding/lifx/internal/fields/MACAddressTest.java new file mode 100644 index 0000000000000..78b620b7af4f4 --- /dev/null +++ b/bundles/org.openhab.binding.lifx/src/test/java/org/openhab/binding/lifx/internal/fields/MACAddressTest.java @@ -0,0 +1,88 @@ +/** + * Copyright (c) 2010-2021 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.lifx.internal.fields; + +import static org.hamcrest.CoreMatchers.not; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.core.Is.is; +import static org.openhab.binding.lifx.internal.fields.MACAddress.BROADCAST_ADDRESS; +import static org.openhab.core.util.HexUtils.bytesToHex; + +import java.nio.ByteBuffer; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.junit.jupiter.api.Test; +import org.openhab.core.util.HexUtils; + +/** + * Tests {@link MACAddress}. + * + * @author Wouter Born - Initial contribution + */ +@NonNullByDefault +public class MACAddressTest { + + @Test + public void broadcastAddress() { + assertThat(BROADCAST_ADDRESS.getAsLabel(), is("000000000000")); + assertThat(BROADCAST_ADDRESS.getHex(), is("00:00:00:00:00:00")); + assertThat(bytesToHex(BROADCAST_ADDRESS.getBytes().array()), is("000000000000")); + } + + @Test + public void defaultConstructor() { + MACAddress macAddress = new MACAddress(); + assertThat(macAddress.getAsLabel(), is("000000000000")); + assertThat(macAddress.getHex(), is("00:00:00:00:00:00")); + } + + @Test + public void constructFromByteBuffer() { + MACAddress macAddress = new MACAddress(ByteBuffer.wrap(HexUtils.hexToBytes("D073D5123456"))); + assertThat(macAddress.getAsLabel(), is("D073D5123456")); + assertThat(macAddress.getHex(), is("D0:73:D5:12:34:56")); + assertThat(bytesToHex(macAddress.getBytes().array()), is("D073D5123456")); + } + + @Test + public void constructFromString() { + MACAddress macAddress = new MACAddress("D073D5ABCDEF"); + assertThat(macAddress.getAsLabel(), is("D073D5ABCDEF")); + assertThat(macAddress.getHex(), is("D0:73:D5:AB:CD:EF")); + assertThat(bytesToHex(macAddress.getBytes().array()), is("D073D5ABCDEF")); + } + + @Test + public void broadcastAddressComparison() { + assertThat(BROADCAST_ADDRESS, is(BROADCAST_ADDRESS)); + assertThat(BROADCAST_ADDRESS.hashCode(), is(BROADCAST_ADDRESS.hashCode())); + + assertThat(BROADCAST_ADDRESS, is(new MACAddress())); + assertThat(BROADCAST_ADDRESS.hashCode(), is(new MACAddress().hashCode())); + + assertThat(BROADCAST_ADDRESS, is(not(new MACAddress("D073D5ABCDEF")))); + assertThat(BROADCAST_ADDRESS, is(not(new MACAddress("D073D5ABCDEF").hashCode()))); + } + + @Test + public void macAddressComparison() { + assertThat(new MACAddress("D073D5ABCDEF"), is(new MACAddress("D073D5ABCDEF"))); + assertThat(new MACAddress("D073D5ABCDEF").hashCode(), is(new MACAddress("D073D5ABCDEF").hashCode())); + + assertThat(new MACAddress("D073D5ABCDEF"), is(not(BROADCAST_ADDRESS))); + assertThat(new MACAddress("D073D5ABCDEF").hashCode(), is(not(BROADCAST_ADDRESS.hashCode()))); + + assertThat(new MACAddress("D073D5ABCDEF"), is(not(new MACAddress("D073D5123456")))); + assertThat(new MACAddress("D073D5ABCDEF").hashCode(), is(not(new MACAddress("D073D5123456").hashCode()))); + } +} diff --git a/bundles/org.openhab.binding.loxone/README.md b/bundles/org.openhab.binding.loxone/README.md index 37911c24bfa62..37ab59094e673 100644 --- a/bundles/org.openhab.binding.loxone/README.md +++ b/bundles/org.openhab.binding.loxone/README.md @@ -96,6 +96,9 @@ The acquired token will remain active for several weeks following the last succe In case a websocket connection to the Miniserver remains active for the whole duration of the token's life span, the binding will refresh the token one day before token expiration, without the need of providing the password. +In case of connecting to Generation 2 Miniservers, it is possible to establish a secure WebSocket connection over HTTPS protocol. Binding will automatically detect if HTTPS connection is available and will use it. In that case, commands sent to the Miniserver will not be additionally encrypted. When HTTPS is not available, binding will use unsecure HTTP connection and will encrypt each command. + +It is possible to override the communication protocol by setting `webSocketType` configuration parameter. Setting it to 1 will force to always establish HTTPS connection. Setting it to 2 will force to always establish HTTP connection. Default value of 0 means the binding will determine the right protocol in the runtime. A method to enable unrestricted security policy depends on the JRE version and vendor, some examples can be found [here](https://www.petefreitag.com/item/844.cfm) and [here](https://stackoverflow.com/questions/41580489/how-to-install-unlimited-strength-jurisdiction-policy-files). @@ -195,9 +198,10 @@ To define a parameter value in a .things file, please refer to it by parameter's ### Security -| ID | Name | Values | Default | Description | -|--------------|-----------------------|-------------------------------------------------|--------------|-------------------------------------------------------| -| `authMethod` | Authentication method | 0: Automatic
1: Hash-based
2: Token-based | 0: Automatic | A method used to authenticate user in the Miniserver. | +| ID | Name | Values | Default | Description | +|-----------------|-----------------------|-------------------------------------------------|--------------|-------------------------------------------------------| +| `authMethod` | Authentication method | 0: Automatic
1: Hash-based
2: Token-based | 0: Automatic | A method used to authenticate user in the Miniserver. | +| `webSocketType` | WebSocket protocol | 0: Automatic
1: Force HTTPS
2: Force HTTP | 0: Automatic | Communication protocol used for WebSocket connection. | ### Timeouts diff --git a/bundles/org.openhab.binding.loxone/src/main/java/org/openhab/binding/loxone/internal/LxBindingConfiguration.java b/bundles/org.openhab.binding.loxone/src/main/java/org/openhab/binding/loxone/internal/LxBindingConfiguration.java index 1cebb8770567a..5d817bd2f4021 100644 --- a/bundles/org.openhab.binding.loxone/src/main/java/org/openhab/binding/loxone/internal/LxBindingConfiguration.java +++ b/bundles/org.openhab.binding.loxone/src/main/java/org/openhab/binding/loxone/internal/LxBindingConfiguration.java @@ -24,9 +24,13 @@ public class LxBindingConfiguration { */ public String host; /** - * Port of web service of the Miniserver + * Port of HTTP web service of the Miniserver */ public int port; + /** + * Port of HTTPS web service of the Miniserver + */ + public int httpsPort; /** * User name used to log into the Miniserver */ @@ -76,4 +80,8 @@ public class LxBindingConfiguration { * Authentication method (0-auto, 1-hash, 2-token) */ public int authMethod; + /** + * WebSocket connection type (0-auto, 1-HTTPS, 2-HTTP) + */ + public int webSocketType; } diff --git a/bundles/org.openhab.binding.loxone/src/main/java/org/openhab/binding/loxone/internal/LxServerHandler.java b/bundles/org.openhab.binding.loxone/src/main/java/org/openhab/binding/loxone/internal/LxServerHandler.java index aa6b9d58dedc0..17517e2b3faa5 100644 --- a/bundles/org.openhab.binding.loxone/src/main/java/org/openhab/binding/loxone/internal/LxServerHandler.java +++ b/bundles/org.openhab.binding.loxone/src/main/java/org/openhab/binding/loxone/internal/LxServerHandler.java @@ -32,6 +32,7 @@ import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; +import org.eclipse.jetty.util.ssl.SslContextFactory; import org.eclipse.jetty.util.thread.QueuedThreadPool; import org.eclipse.jetty.websocket.client.ClientUpgradeRequest; import org.eclipse.jetty.websocket.client.WebSocketClient; @@ -72,7 +73,7 @@ public class LxServerHandler extends BaseThingHandler implements LxServerHandlerApi { private static final String SOCKET_URL = "/ws/rfc6455"; - private static final String CMD_CFG_API = "jdev/cfg/api"; + private static final String CMD_CFG_API = "jdev/cfg/apiKey"; private static final Gson GSON; @@ -139,6 +140,7 @@ public LxServerHandler(Thing thing, LxDynamicStateDescriptionProvider provider) @Override public void handleCommand(ChannelUID channelUID, Command command) { + logger.debug("[{}] Handle command: channelUID={}, command={}", debugId, channelUID, command); if (command instanceof RefreshType) { updateChannelState(channelUID); return; @@ -146,6 +148,8 @@ public void handleCommand(ChannelUID channelUID, Command command) { try { LxControl control = channels.get(channelUID); if (control != null) { + logger.debug("[{}] Dispatching command to control UUID={}, name={}", debugId, control.getUuid(), + control.getName()); control.handleCommand(channelUID, command); } else { logger.error("[{}] Received command {} for unknown control.", debugId, command); @@ -182,7 +186,7 @@ public void initialize() { jettyThreadPool.setDaemon(true); socket = new LxWebSocket(debugId, this, bindingConfig, host); - wsClient = new WebSocketClient(); + wsClient = new WebSocketClient(new SslContextFactory.Client(true)); wsClient.setExecutor(jettyThreadPool); if (debugId > 1) { reconnectDelay.set(0); @@ -478,8 +482,16 @@ private void updateStateValue(LxStateUpdate update) { Map perStateUuid = states.get(update.getUuid()); if (perStateUuid != null) { perStateUuid.forEach((controlUuid, state) -> { + logger.debug("[{}] State update (UUID={}, value={}) dispatched to control UUID={}, state name={}", + debugId, update.getUuid(), update.getValue(), controlUuid, state.getName()); + state.setStateValue(update.getValue()); }); + if (perStateUuid.size() == 0) { + logger.debug("[{}] State update UUID={} has empty controls table", debugId, update.getUuid()); + } + } else { + logger.debug("[{}] State update UUID={} has no controls table", debugId, update.getUuid()); } } @@ -553,16 +565,36 @@ private boolean connect() { * Try to read CfgApi structure from the miniserver. It contains serial number and firmware version. If it can't * be read this is not a fatal issue, we will assume most recent version running. */ + boolean httpsCapable = false; String message = socket.httpGet(CMD_CFG_API); if (message != null) { LxResponse resp = socket.getResponse(message); if (resp != null) { - socket.setFwVersion(GSON.fromJson(resp.getValueAsString(), LxResponse.LxResponseCfgApi.class).version); + LxResponse.LxResponseCfgApi apiResp = GSON.fromJson(resp.getValueAsString(), + LxResponse.LxResponseCfgApi.class); + if (apiResp != null) { + socket.setFwVersion(apiResp.version); + httpsCapable = apiResp.httpsStatus != null && apiResp.httpsStatus == 1; + } } } else { logger.debug("[{}] Http get failed for API config request.", debugId); } + switch (bindingConfig.webSocketType) { + case 0: + // keep automatically determined option + break; + case 1: + logger.debug("[{}] Forcing HTTPS websocket connection.", debugId); + httpsCapable = true; + break; + case 2: + logger.debug("[{}] Forcing HTTP websocket connection.", debugId); + httpsCapable = false; + break; + } + try { wsClient.start(); @@ -570,7 +602,14 @@ private boolean connect() { // without this zero timeout, jetty will wait 30 seconds for stopping the client to eventually fail // with the timeout it is immediate and all threads end correctly jettyThreadPool.setStopTimeout(0); - URI target = new URI("ws://" + host.getHostAddress() + ":" + bindingConfig.port + SOCKET_URL); + URI target; + if (httpsCapable) { + target = new URI("wss://" + host.getHostAddress() + ":" + bindingConfig.httpsPort + SOCKET_URL); + socket.setHttps(true); + } else { + target = new URI("ws://" + host.getHostAddress() + ":" + bindingConfig.port + SOCKET_URL); + socket.setHttps(false); + } ClientUpgradeRequest request = new ClientUpgradeRequest(); request.setSubProtocols("remotecontrol"); diff --git a/bundles/org.openhab.binding.loxone/src/main/java/org/openhab/binding/loxone/internal/LxWebSocket.java b/bundles/org.openhab.binding.loxone/src/main/java/org/openhab/binding/loxone/internal/LxWebSocket.java index 79867bd910adc..298e53f154579 100644 --- a/bundles/org.openhab.binding.loxone/src/main/java/org/openhab/binding/loxone/internal/LxWebSocket.java +++ b/bundles/org.openhab.binding.loxone/src/main/java/org/openhab/binding/loxone/internal/LxWebSocket.java @@ -78,6 +78,7 @@ public class LxWebSocket { private Session session; private String fwVersion; + private boolean httpsSession = false; private ScheduledFuture timeout; private LxWsBinaryHeader header; private LxWsSecurity security; @@ -455,9 +456,20 @@ void sendKeepAlive() { * @param fwVersion Miniserver firmware version */ void setFwVersion(String fwVersion) { + logger.debug("[{}] Firmware version: {}", debugId, fwVersion); this.fwVersion = fwVersion; } + /** + * Sets information if session is over HTTPS or HTTP protocol + * + * @param httpsSession true when HTTPS session + */ + void setHttps(boolean httpsSession) { + logger.debug("[{}] HTTPS session: {}", debugId, httpsSession); + this.httpsSession = httpsSession; + } + /** * Start a timer to wait for a Miniserver response to an action sent from the binding. * When timer expires, connection is removed and server error is reported. Further connection attempt can be made @@ -536,7 +548,7 @@ private boolean sendCmdNoResp(String command, boolean encrypt) { try { if (session != null) { String encrypted; - if (encrypt) { + if (encrypt && !httpsSession) { encrypted = security.encrypt(command); logger.debug("[{}] Sending encrypted string: {}", debugId, command); logger.debug("[{}] Encrypted: {}", debugId, encrypted); @@ -580,7 +592,9 @@ private void processResponse(String message) { } logger.debug("[{}] Response: {}", debugId, message.trim()); String control = resp.getCommand().trim(); - control = security.decryptControl(control); + if (!httpsSession) { + control = security.decryptControl(control); + } // for some reason the responses to some commands starting with jdev begin with dev, not jdev // this seems to be a bug in the Miniserver if (control.startsWith("dev/")) { diff --git a/bundles/org.openhab.binding.loxone/src/main/java/org/openhab/binding/loxone/internal/controls/LxControl.java b/bundles/org.openhab.binding.loxone/src/main/java/org/openhab/binding/loxone/internal/controls/LxControl.java index 2533b90f8ce38..f16c04596fa3d 100644 --- a/bundles/org.openhab.binding.loxone/src/main/java/org/openhab/binding/loxone/internal/controls/LxControl.java +++ b/bundles/org.openhab.binding.loxone/src/main/java/org/openhab/binding/loxone/internal/controls/LxControl.java @@ -264,6 +264,8 @@ public final void handleCommand(ChannelUID channelId, Command command) throws IO Callbacks c = callbacks.get(channelId); if (c != null && c.commandCallback != null) { c.commandCallback.handleCommand(command); + } else { + logger.debug("Control UUID={} has no command handler", getUuid()); } } diff --git a/bundles/org.openhab.binding.loxone/src/main/java/org/openhab/binding/loxone/internal/types/LxResponse.java b/bundles/org.openhab.binding.loxone/src/main/java/org/openhab/binding/loxone/internal/types/LxResponse.java index 45af9c7da117a..c9e4a37c6223d 100644 --- a/bundles/org.openhab.binding.loxone/src/main/java/org/openhab/binding/loxone/internal/types/LxResponse.java +++ b/bundles/org.openhab.binding.loxone/src/main/java/org/openhab/binding/loxone/internal/types/LxResponse.java @@ -39,6 +39,8 @@ public class LxResponse { public class LxResponseCfgApi { public String snr; public String version; + public String key; + public Integer httpsStatus; } /** diff --git a/bundles/org.openhab.binding.loxone/src/main/resources/OH-INF/thing/thing-types.xml b/bundles/org.openhab.binding.loxone/src/main/resources/OH-INF/thing/thing-types.xml index 52ef9ccf6c210..72330526c8f9f 100644 --- a/bundles/org.openhab.binding.loxone/src/main/resources/OH-INF/thing/thing-types.xml +++ b/bundles/org.openhab.binding.loxone/src/main/resources/OH-INF/thing/thing-types.xml @@ -39,10 +39,15 @@ Host address or IP of the Loxone Miniserver
- - Web interface port of the Loxone Miniserver + + HTTP Web interface port of the Loxone Miniserver 80 + + + HTTPS Web interface port of the Loxone Miniserver + 443 + @@ -71,6 +76,18 @@ true + + + Protocol used to communicate over WebSocket to the Miniserver + 0 + + + + + + true + true + Time between binding initialization and first connection attempt (seconds, 0-120) diff --git a/bundles/org.openhab.binding.lutron/src/main/java/org/openhab/binding/lutron/internal/config/IPBridgeConfig.java b/bundles/org.openhab.binding.lutron/src/main/java/org/openhab/binding/lutron/internal/config/IPBridgeConfig.java index 4a1a3ce455724..5bde94ae0741f 100644 --- a/bundles/org.openhab.binding.lutron/src/main/java/org/openhab/binding/lutron/internal/config/IPBridgeConfig.java +++ b/bundles/org.openhab.binding.lutron/src/main/java/org/openhab/binding/lutron/internal/config/IPBridgeConfig.java @@ -12,7 +12,7 @@ */ package org.openhab.binding.lutron.internal.config; -import org.openhab.binding.lutron.internal.StringUtils; +import java.util.Objects; /** * Configuration settings for an {@link org.openhab.binding.lutron.internal.handler.IPBridgeHandler}. @@ -30,8 +30,8 @@ public class IPBridgeConfig { public int delay = 0; public boolean sameConnectionParameters(IPBridgeConfig config) { - return StringUtils.equals(ipAddress, config.ipAddress) && StringUtils.equals(user, config.user) - && StringUtils.equals(password, config.password) && (reconnect == config.reconnect) + return Objects.equals(ipAddress, config.ipAddress) && Objects.equals(user, config.user) + && Objects.equals(password, config.password) && (reconnect == config.reconnect) && (heartbeat == config.heartbeat) && (delay == config.delay); } } diff --git a/bundles/org.openhab.binding.lutron/src/main/java/org/openhab/binding/lutron/internal/handler/IPBridgeHandler.java b/bundles/org.openhab.binding.lutron/src/main/java/org/openhab/binding/lutron/internal/handler/IPBridgeHandler.java index e564ee5225c37..61c6cf4e9bae7 100644 --- a/bundles/org.openhab.binding.lutron/src/main/java/org/openhab/binding/lutron/internal/handler/IPBridgeHandler.java +++ b/bundles/org.openhab.binding.lutron/src/main/java/org/openhab/binding/lutron/internal/handler/IPBridgeHandler.java @@ -25,7 +25,6 @@ import java.util.regex.Matcher; import java.util.regex.Pattern; -import org.openhab.binding.lutron.internal.StringUtils; import org.openhab.binding.lutron.internal.config.IPBridgeConfig; import org.openhab.binding.lutron.internal.discovery.LutronDeviceDiscoveryService; import org.openhab.binding.lutron.internal.net.TelnetSession; @@ -156,7 +155,8 @@ private boolean validConfiguration(IPBridgeConfig config) { return false; } - if (StringUtils.isEmpty(config.ipAddress)) { + String ipAddress = config.ipAddress; + if (ipAddress == null || ipAddress.isEmpty()) { updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, "bridge address not specified"); return false; diff --git a/bundles/org.openhab.binding.lutron/src/main/java/org/openhab/binding/lutron/internal/protocol/leap/LeapMessageParser.java b/bundles/org.openhab.binding.lutron/src/main/java/org/openhab/binding/lutron/internal/protocol/leap/LeapMessageParser.java index 31f1cd975276a..a66f0bf9e700d 100644 --- a/bundles/org.openhab.binding.lutron/src/main/java/org/openhab/binding/lutron/internal/protocol/leap/LeapMessageParser.java +++ b/bundles/org.openhab.binding.lutron/src/main/java/org/openhab/binding/lutron/internal/protocol/leap/LeapMessageParser.java @@ -76,7 +76,7 @@ public void handleMessage(String msg) { logger.trace("Received message: {}", msg); try { - JsonObject message = (JsonObject) new JsonParser().parse(msg); + JsonObject message = (JsonObject) JsonParser.parseString(msg); if (!message.has("CommuniqueType")) { logger.debug("No CommuniqueType found in message: {}", msg); diff --git a/bundles/org.openhab.binding.magentatv/README.md b/bundles/org.openhab.binding.magentatv/README.md index 02acd4a1b12f8..ddfcf61040014 100644 --- a/bundles/org.openhab.binding.magentatv/README.md +++ b/bundles/org.openhab.binding.magentatv/README.md @@ -117,7 +117,7 @@ For security reasons the credentials are automatically deleted from the thing co | |key |String |Send key code to the receiver (see code table below) | | |mute |Switch |Mute volume (mute the speaker) | |status |playMode |String |Current play mode - this info is not reliable | -| |channelCode |Number  |The channel code from the EPG. | +| |channelCode |Number |The channel code from the EPG. | |program |title |String |Title of the running program or video being played | | |text |String |Some description (as reported by the receiver, could be empty) | | |start |DateTime |Time when the program started | @@ -327,4 +327,4 @@ to switch it ON and to switch it off. -After an openHAB restart you need to make sure that OH and receiver are in sync, because the binding can't read the power status at startup. +After an openHAB restart you need to make sure that OH and receiver are in sync, because the binding can't read the power status at startup. \ No newline at end of file diff --git a/bundles/org.openhab.binding.magentatv/src/main/java/org/openhab/binding/magentatv/internal/MagentaTVBindingConstants.java b/bundles/org.openhab.binding.magentatv/src/main/java/org/openhab/binding/magentatv/internal/MagentaTVBindingConstants.java old mode 100644 new mode 100755 diff --git a/bundles/org.openhab.binding.magentatv/src/main/java/org/openhab/binding/magentatv/internal/MagentaTVConsoleHandler.java b/bundles/org.openhab.binding.magentatv/src/main/java/org/openhab/binding/magentatv/internal/MagentaTVConsoleHandler.java index 6f041ddb7c85d..7c62e31b15683 100644 --- a/bundles/org.openhab.binding.magentatv/src/main/java/org/openhab/binding/magentatv/internal/MagentaTVConsoleHandler.java +++ b/bundles/org.openhab.binding.magentatv/src/main/java/org/openhab/binding/magentatv/internal/MagentaTVConsoleHandler.java @@ -20,18 +20,15 @@ import java.util.Arrays; import java.util.List; -import javax.ws.rs.client.ClientBuilder; - import org.eclipse.jdt.annotation.NonNullByDefault; -import org.eclipse.jdt.annotation.Nullable; import org.openhab.binding.magentatv.internal.network.MagentaTVOAuth; import org.openhab.core.io.console.Console; import org.openhab.core.io.console.extensions.AbstractConsoleCommandExtension; import org.openhab.core.io.console.extensions.ConsoleCommandExtension; +import org.openhab.core.io.net.http.HttpClientFactory; import org.osgi.service.component.annotations.Activate; import org.osgi.service.component.annotations.Component; import org.osgi.service.component.annotations.Reference; -import org.osgi.service.component.annotations.ReferenceCardinality; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -47,14 +44,12 @@ public class MagentaTVConsoleHandler extends AbstractConsoleCommandExtension { private static final String CMD_LOGIN = "login"; private final Logger logger = LoggerFactory.getLogger(MagentaTVConsoleHandler.class); - private final MagentaTVOAuth oauth = new MagentaTVOAuth(); - - @Reference(cardinality = ReferenceCardinality.OPTIONAL) - private @Nullable ClientBuilder injectedClientBuilder; + private final MagentaTVOAuth oauth; @Activate - public MagentaTVConsoleHandler() { + public MagentaTVConsoleHandler(@Reference HttpClientFactory httpClientFactory) { super(BINDING_ID, "Interact with the " + BINDING_ID + " integration."); + oauth = new MagentaTVOAuth(httpClientFactory.getCommonHttpClient()); } @Override diff --git a/bundles/org.openhab.binding.magentatv/src/main/java/org/openhab/binding/magentatv/internal/MagentaTVException.java b/bundles/org.openhab.binding.magentatv/src/main/java/org/openhab/binding/magentatv/internal/MagentaTVException.java old mode 100644 new mode 100755 diff --git a/bundles/org.openhab.binding.magentatv/src/main/java/org/openhab/binding/magentatv/internal/MagentaTVGsonDTO.java b/bundles/org.openhab.binding.magentatv/src/main/java/org/openhab/binding/magentatv/internal/MagentaTVGsonDTO.java old mode 100644 new mode 100755 diff --git a/bundles/org.openhab.binding.magentatv/src/main/java/org/openhab/binding/magentatv/internal/MagentaTVHandlerFactory.java b/bundles/org.openhab.binding.magentatv/src/main/java/org/openhab/binding/magentatv/internal/MagentaTVHandlerFactory.java old mode 100644 new mode 100755 index 87484939660ab..7508121483e1b --- a/bundles/org.openhab.binding.magentatv/src/main/java/org/openhab/binding/magentatv/internal/MagentaTVHandlerFactory.java +++ b/bundles/org.openhab.binding.magentatv/src/main/java/org/openhab/binding/magentatv/internal/MagentaTVHandlerFactory.java @@ -19,10 +19,12 @@ import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.Nullable; +import org.eclipse.jetty.client.HttpClient; import org.openhab.binding.magentatv.internal.MagentaTVDeviceManager.MagentaTVDevice; import org.openhab.binding.magentatv.internal.handler.MagentaTVHandler; import org.openhab.binding.magentatv.internal.network.MagentaTVNetwork; import org.openhab.binding.magentatv.internal.network.MagentaTVPoweroffListener; +import org.openhab.core.io.net.http.HttpClientFactory; import org.openhab.core.net.HttpServiceUtil; import org.openhab.core.net.NetworkAddressService; import org.openhab.core.thing.Thing; @@ -51,6 +53,7 @@ public class MagentaTVHandlerFactory extends BaseThingHandlerFactory { private final MagentaTVNetwork network = new MagentaTVNetwork(); private final MagentaTVDeviceManager manager; + private final HttpClient httpClient; private @Nullable MagentaTVPoweroffListener upnpListener; private boolean servletInitialized = false; @@ -64,11 +67,11 @@ public class MagentaTVHandlerFactory extends BaseThingHandlerFactory { @Activate public MagentaTVHandlerFactory(@Reference NetworkAddressService networkAddressService, - @Reference MagentaTVDeviceManager manager, ComponentContext componentContext, - Map configProperties) throws IOException { + @Reference HttpClientFactory httpClientFactory, @Reference MagentaTVDeviceManager manager, + ComponentContext componentContext, Map configProperties) throws IOException { super.activate(componentContext); this.manager = manager; - + this.httpClient = httpClientFactory.getCommonHttpClient(); try { logger.debug("Initialize network access"); System.setProperty("java.net.preferIPv4Stack", "true"); @@ -99,7 +102,7 @@ public boolean supportsThingType(ThingTypeUID thingTypeUID) { logger.debug("Create thing type {}", thing.getThingTypeUID().getAsString()); if (THING_TYPE_RECEIVER.equals(thingTypeUID)) { - return new MagentaTVHandler(manager, thing, network); + return new MagentaTVHandler(manager, thing, network, httpClient); } return null; diff --git a/bundles/org.openhab.binding.magentatv/src/main/java/org/openhab/binding/magentatv/internal/config/MagentaTVThingConfiguration.java b/bundles/org.openhab.binding.magentatv/src/main/java/org/openhab/binding/magentatv/internal/config/MagentaTVThingConfiguration.java old mode 100644 new mode 100755 diff --git a/bundles/org.openhab.binding.magentatv/src/main/java/org/openhab/binding/magentatv/internal/discovery/MagentaTVDiscoveryParticipant.java b/bundles/org.openhab.binding.magentatv/src/main/java/org/openhab/binding/magentatv/internal/discovery/MagentaTVDiscoveryParticipant.java old mode 100644 new mode 100755 diff --git a/bundles/org.openhab.binding.magentatv/src/main/java/org/openhab/binding/magentatv/internal/handler/MagentaTVControl.java b/bundles/org.openhab.binding.magentatv/src/main/java/org/openhab/binding/magentatv/internal/handler/MagentaTVControl.java old mode 100644 new mode 100755 index f67ff2b4fa8a3..68bed02f44245 --- a/bundles/org.openhab.binding.magentatv/src/main/java/org/openhab/binding/magentatv/internal/handler/MagentaTVControl.java +++ b/bundles/org.openhab.binding.magentatv/src/main/java/org/openhab/binding/magentatv/internal/handler/MagentaTVControl.java @@ -23,6 +23,7 @@ import java.util.StringTokenizer; import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jetty.client.HttpClient; import org.openhab.binding.magentatv.internal.MagentaTVException; import org.openhab.binding.magentatv.internal.config.MagentaTVDynamicConfig; import org.openhab.binding.magentatv.internal.network.MagentaTVHttp; @@ -44,7 +45,7 @@ public class MagentaTVControl { private final MagentaTVNetwork network; private final MagentaTVHttp http = new MagentaTVHttp(); - private final MagentaTVOAuth oauth = new MagentaTVOAuth(); + private final MagentaTVOAuth oauth; private final MagentaTVDynamicConfig config; private boolean initialized = false; private String thingId = ""; @@ -52,11 +53,13 @@ public class MagentaTVControl { public MagentaTVControl() { config = new MagentaTVDynamicConfig(); network = new MagentaTVNetwork(); + oauth = new MagentaTVOAuth(new HttpClient()); } - public MagentaTVControl(MagentaTVDynamicConfig config, MagentaTVNetwork network) { + public MagentaTVControl(MagentaTVDynamicConfig config, MagentaTVNetwork network, HttpClient httpClient) { thingId = config.getFriendlyName(); this.network = network; + this.oauth = new MagentaTVOAuth(httpClient); this.config = config; this.config.setTerminalID(computeMD5(network.getLocalMAC().toUpperCase() + config.getUDN())); this.config.setLocalIP(network.getLocalIP()); @@ -391,7 +394,8 @@ private String getKeyCode(String key) { // direct key code return key; } - return KEY_MAP.getOrDefault(key, ""); + String code = KEY_MAP.get(key); + return code != null ? code : ""; } /** diff --git a/bundles/org.openhab.binding.magentatv/src/main/java/org/openhab/binding/magentatv/internal/handler/MagentaTVHandler.java b/bundles/org.openhab.binding.magentatv/src/main/java/org/openhab/binding/magentatv/internal/handler/MagentaTVHandler.java old mode 100644 new mode 100755 index 66efbe059c746..3d9a9f6716908 --- a/bundles/org.openhab.binding.magentatv/src/main/java/org/openhab/binding/magentatv/internal/handler/MagentaTVHandler.java +++ b/bundles/org.openhab.binding.magentatv/src/main/java/org/openhab/binding/magentatv/internal/handler/MagentaTVHandler.java @@ -33,6 +33,7 @@ import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.Nullable; +import org.eclipse.jetty.client.HttpClient; import org.openhab.binding.magentatv.internal.MagentaTVDeviceManager; import org.openhab.binding.magentatv.internal.MagentaTVException; import org.openhab.binding.magentatv.internal.MagentaTVGsonDTO.MRPayEvent; @@ -88,6 +89,7 @@ public class MagentaTVHandler extends BaseThingHandler implements MagentaTVListe private final Gson gson; protected final MagentaTVNetwork network; protected final MagentaTVDeviceManager manager; + private final HttpClient httpClient; protected MagentaTVControl control = new MagentaTVControl(); private String thingId = ""; @@ -102,10 +104,12 @@ public class MagentaTVHandler extends BaseThingHandler implements MagentaTVListe * @param thing * @param bindingConfig */ - public MagentaTVHandler(MagentaTVDeviceManager manager, Thing thing, MagentaTVNetwork network) { + public MagentaTVHandler(MagentaTVDeviceManager manager, Thing thing, MagentaTVNetwork network, + HttpClient httpClient) { super(thing); this.manager = manager; this.network = network; + this.httpClient = httpClient; gson = new GsonBuilder().registerTypeAdapter(OauthCredentials.class, new MRProgramInfoEventInstanceCreator()) .registerTypeAdapter(OAuthTokenResponse.class, new MRProgramStatusInstanceCreator()) .registerTypeAdapter(OAuthAuthenticateResponse.class, new MRShortProgramInfoInstanceCreator()) @@ -150,7 +154,7 @@ private void initializeThing() { } config.setMacAddress(macAddress); } - control = new MagentaTVControl(config, network); + control = new MagentaTVControl(config, network, httpClient); config.updateNetwork(control.getConfig()); // get network parameters from control // Check for emoty credentials (e.g. missing in .things file) @@ -185,7 +189,7 @@ private void initializeThing() { } /** - * This routine is called every time the Thing configuration has been changed. + * This routine is called every time the Thing configuration has been changed */ @Override public void handleConfigurationUpdate(Map configurationParameters) { diff --git a/bundles/org.openhab.binding.magentatv/src/main/java/org/openhab/binding/magentatv/internal/handler/MagentaTVListener.java b/bundles/org.openhab.binding.magentatv/src/main/java/org/openhab/binding/magentatv/internal/handler/MagentaTVListener.java old mode 100644 new mode 100755 diff --git a/bundles/org.openhab.binding.magentatv/src/main/java/org/openhab/binding/magentatv/internal/network/MagentaTVHttp.java b/bundles/org.openhab.binding.magentatv/src/main/java/org/openhab/binding/magentatv/internal/network/MagentaTVHttp.java old mode 100644 new mode 100755 diff --git a/bundles/org.openhab.binding.magentatv/src/main/java/org/openhab/binding/magentatv/internal/network/MagentaTVNetwork.java b/bundles/org.openhab.binding.magentatv/src/main/java/org/openhab/binding/magentatv/internal/network/MagentaTVNetwork.java old mode 100644 new mode 100755 index 0a2ab367fb886..c2554689312e2 --- a/bundles/org.openhab.binding.magentatv/src/main/java/org/openhab/binding/magentatv/internal/network/MagentaTVNetwork.java +++ b/bundles/org.openhab.binding.magentatv/src/main/java/org/openhab/binding/magentatv/internal/network/MagentaTVNetwork.java @@ -20,7 +20,6 @@ import java.net.SocketException; import java.net.UnknownHostException; -import org.apache.commons.net.util.SubnetUtils; import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.Nullable; import org.openhab.binding.magentatv.internal.MagentaTVException; @@ -76,34 +75,6 @@ public void initLocalNet(String localIP, String localPort) throws MagentaTVExcep "Unable to get local IP / MAC address, check network settings in openHAB system configuration!"); } - /** - * Checks if client ip equals or is in range of ip networks provided by - * semicolon separated list - * - * @param clientIp in numeric form like "192.168.0.10" - * @param ipList like "127.0.0.1;192.168.0.0/24;10.0.0.0/8" - * @return true if client ip from the list os ips and networks - */ - public static boolean isIpInSubnet(String clientIp, String ipList) { - if (ipList.isEmpty()) { - // No ip address provided - return true; - } - String[] subnetMasks = ipList.split(";"); - for (String subnetMask : subnetMasks) { - subnetMask = subnetMask.trim(); - if (clientIp.equals(subnetMask)) { - return true; - } - if (subnetMask.contains("/")) { - if (new SubnetUtils(subnetMask).getInfo().isInRange(clientIp)) { - return true; - } - } - } - return false; - } - @Nullable public NetworkInterface getLocalInterface() { return localInterface; diff --git a/bundles/org.openhab.binding.magentatv/src/main/java/org/openhab/binding/magentatv/internal/network/MagentaTVNotifyServlet.java b/bundles/org.openhab.binding.magentatv/src/main/java/org/openhab/binding/magentatv/internal/network/MagentaTVNotifyServlet.java old mode 100644 new mode 100755 diff --git a/bundles/org.openhab.binding.magentatv/src/main/java/org/openhab/binding/magentatv/internal/network/MagentaTVOAuth.java b/bundles/org.openhab.binding.magentatv/src/main/java/org/openhab/binding/magentatv/internal/network/MagentaTVOAuth.java old mode 100644 new mode 100755 index 36acdb4ebc287..a6b6539ba6248 --- a/bundles/org.openhab.binding.magentatv/src/main/java/org/openhab/binding/magentatv/internal/network/MagentaTVOAuth.java +++ b/bundles/org.openhab.binding.magentatv/src/main/java/org/openhab/binding/magentatv/internal/network/MagentaTVOAuth.java @@ -13,21 +13,34 @@ package org.openhab.binding.magentatv.internal.network; import static org.openhab.binding.magentatv.internal.MagentaTVBindingConstants.*; -import static org.openhab.binding.magentatv.internal.MagentaTVUtil.substringAfterLast; +import static org.openhab.binding.magentatv.internal.MagentaTVUtil.*; -import java.io.ByteArrayInputStream; -import java.io.IOException; -import java.io.InputStream; +import java.io.UnsupportedEncodingException; +import java.net.HttpCookie; import java.net.URLEncoder; -import java.nio.charset.Charset; +import java.nio.charset.StandardCharsets; import java.text.MessageFormat; +import java.util.ArrayList; +import java.util.Enumeration; +import java.util.List; import java.util.Properties; import java.util.UUID; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; import javax.ws.rs.HttpMethod; import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.Nullable; +import org.eclipse.jetty.client.HttpClient; +import org.eclipse.jetty.client.api.ContentResponse; +import org.eclipse.jetty.client.api.Request; +import org.eclipse.jetty.client.util.StringContentProvider; +import org.eclipse.jetty.http.HttpField; +import org.eclipse.jetty.http.HttpFields; +import org.eclipse.jetty.http.HttpHeader; +import org.eclipse.jetty.http.HttpStatus; import org.openhab.binding.magentatv.internal.MagentaTVException; import org.openhab.binding.magentatv.internal.MagentaTVGsonDTO.OAuthAuthenticateResponse; import org.openhab.binding.magentatv.internal.MagentaTVGsonDTO.OAuthAuthenticateResponseInstanceCreator; @@ -37,7 +50,6 @@ import org.openhab.binding.magentatv.internal.MagentaTVGsonDTO.OauthCredentialsInstanceCreator; import org.openhab.binding.magentatv.internal.MagentaTVGsonDTO.OauthKeyValue; import org.openhab.binding.magentatv.internal.handler.MagentaTVControl; -import org.openhab.core.io.net.http.HttpUtil; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -50,7 +62,7 @@ * * @author Markus Michels - Initial contribution * - * Deutsche Telekom uses a OAuth-based authentication to access the EPG portal. The + * Deutsche Telekom uses an OAuth-based authentication to access the EPG portal. The * communication between the MR and the remote app requires a pairing before the receiver could be * controlled by sending keys etc. The so called userID is not directly derived from any local parameters * (like terminalID as a has from the mac address), but will be returned as a result from the OAuth @@ -63,9 +75,12 @@ @NonNullByDefault public class MagentaTVOAuth { private final Logger logger = LoggerFactory.getLogger(MagentaTVOAuth.class); - final Gson gson; + private HttpClient httpClient; + private final Gson gson; + private List cookies = new ArrayList<>(); - public MagentaTVOAuth() { + public MagentaTVOAuth(HttpClient httpClient) { + this.httpClient = httpClient; gson = new GsonBuilder().registerTypeAdapter(OauthCredentials.class, new OauthCredentialsInstanceCreator()) .registerTypeAdapter(OAuthTokenResponse.class, new OAuthTokenResponseInstanceCreator()) .registerTypeAdapter(OAuthAuthenticateResponse.class, new OAuthAuthenticateResponseInstanceCreator()) @@ -78,124 +93,209 @@ public String getUserId(String accountName, String accountPassword) throws Magen throw new MagentaTVException("Credentials for OAuth missing, check thing config!"); } - String step = "initialize"; String url = ""; - Properties httpHeader; + Properties httpHeader = initHttpHeader(); String postData = ""; String httpResponse = ""; - InputStream dataStream = null; // OAuth autentication results String oAuthScope = ""; String oAuthService = ""; String epghttpsurl = ""; - String retcode = ""; - String retmsg = ""; + // Get credentials + url = OAUTH_GET_CRED_URL + ":" + OAUTH_GET_CRED_PORT + OAUTH_GET_CRED_URI; + httpHeader.setProperty(HttpHeader.HOST.toString(), substringAfterLast(OAUTH_GET_CRED_URL, "/")); + httpResponse = httpRequest(HttpMethod.GET, url, httpHeader, ""); + OauthCredentials cred = gson.fromJson(httpResponse, OauthCredentials.class); + epghttpsurl = getString(cred.epghttpsurl); + if (epghttpsurl.isEmpty()) { + throw new MagentaTVException("Unable to determine EPG url"); + } + if (!epghttpsurl.contains("/EPG")) { + epghttpsurl = epghttpsurl + "/EPG"; + } + logger.debug("OAuth: epghttpsurl = {}", epghttpsurl); + + // get OAuth data from response + if (cred.sam3Para != null) { + for (OauthKeyValue si : cred.sam3Para) { + logger.trace("sam3Para.{} = {}", si.key, si.value); + if (si.key.equalsIgnoreCase("oAuthScope")) { + oAuthScope = si.value; + } else if (si.key.equalsIgnoreCase("SAM3ServiceURL")) { + oAuthService = si.value; + } + } + } + if (oAuthScope.isEmpty() || oAuthService.isEmpty()) { + throw new MagentaTVException("OAuth failed: Can't get Scope and Service: " + httpResponse); + } + + // Get OAuth token (New flow based on WebTV) + String userId = ""; + String terminalId = UUID.randomUUID().toString(); + String cnonce = MagentaTVControl.computeMD5(terminalId); + + url = oAuthService + "/oauth2/tokens"; + postData = MessageFormat.format( + "password={0}&scope={1}+offline_access&grant_type=password&username={2}&x_telekom.access_token.format=CompactToken&x_telekom.access_token.encoding=text%2Fbase64&client_id=10LIVESAM30000004901NGTVWEB0000000000000", + urlEncode(accountPassword), oAuthScope, urlEncode(accountName)); + url = oAuthService + "/oauth2/tokens"; + httpResponse = httpRequest(HttpMethod.POST, url, httpHeader, postData); + OAuthTokenResponse resp = gson.fromJson(httpResponse, OAuthTokenResponse.class); + if (resp.accessToken.isEmpty()) { + String errorMessage = MessageFormat.format("Unable to authenticate: accountName={0}, rc={1} ({2})", + accountName, getString(resp.errorDescription), getString(resp.error)); + logger.warn("{}", errorMessage); + throw new MagentaTVException(errorMessage); + } + logger.debug("OAuth: Access Token retrieved"); + + // General authentication + logger.debug("OAuth: Generating CSRF token"); + url = "https://api.prod.sngtv.magentatv.de/EPG/JSON/Authenticate"; + httpHeader = initHttpHeader(); + httpHeader.setProperty(HttpHeader.HOST.toString(), "api.prod.sngtv.magentatv.de"); + httpHeader.setProperty("Origin", "https://web.magentatv.de"); + httpHeader.setProperty(HttpHeader.REFERER.toString(), "https://web.magentatv.de/"); + postData = "{\"areaid\":\"1\",\"cnonce\":\"" + cnonce + "\",\"mac\":\"" + terminalId + + "\",\"preSharedKeyID\":\"NGTV000001\",\"subnetId\":\"4901\",\"templatename\":\"NGTV\",\"terminalid\":\"" + + terminalId + + "\",\"terminaltype\":\"WEB-MTV\",\"terminalvendor\":\"WebTV\",\"timezone\":\"Europe/Berlin\",\"usergroup\":\"-1\",\"userType\":3,\"utcEnable\":1}"; + httpResponse = httpRequest(HttpMethod.POST, url, httpHeader, postData); + String csrf = ""; + for (HttpCookie c : cookies) { // get CRSF Token + String value = c.getValue(); + if (value.contains("CSRFSESSION")) { + csrf = substringBetween(value, "CSRFSESSION" + "=", ";"); + } + } + if (csrf.isEmpty()) { + throw new MagentaTVException("OAuth: Unable to get CSRF token!"); + } + + // Final step: Retrieve userId + url = "https://api.prod.sngtv.magentatv.de/EPG/JSON/DTAuthenticate"; + httpHeader = initHttpHeader(); + httpHeader.setProperty(HttpHeader.HOST.toString(), "api.prod.sngtv.magentatv.de"); + httpHeader.setProperty("Origin", "https://web.magentatv.de"); + httpHeader.setProperty(HttpHeader.REFERER.toString(), "https://web.magentatv.de/"); + httpHeader.setProperty("X_CSRFToken", csrf); + postData = "{\"areaid\":\"1\",\"cnonce\":\"" + cnonce + "\",\"mac\":\"" + terminalId + "\"," + + "\"preSharedKeyID\":\"NGTV000001\",\"subnetId\":\"4901\",\"templatename\":\"NGTV\"," + + "\"terminalid\":\"" + terminalId + "\",\"terminaltype\":\"WEB-MTV\",\"terminalvendor\":\"WebTV\"," + + "\"timezone\":\"Europe/Berlin\",\"usergroup\":\"\",\"userType\":\"1\",\"utcEnable\":1," + + "\"accessToken\":\"" + resp.accessToken + + "\",\"caDeviceInfo\":[{\"caDeviceId\":\"4ef4d933-9a43-41d3-9e3a-84979f22c9eb\"," + + "\"caDeviceType\":8}],\"connectType\":1,\"osversion\":\"Mac OS 10.15.7\",\"softwareVersion\":\"1.33.4.3\"," + + "\"terminalDetail\":[{\"key\":\"GUID\",\"value\":\"" + terminalId + "\"}," + + "{\"key\":\"HardwareSupplier\",\"value\":\"WEB-MTV\"},{\"key\":\"DeviceClass\",\"value\":\"TV\"}," + + "{\"key\":\"DeviceStorage\",\"value\":0},{\"key\":\"DeviceStorageSize\",\"value\":0}]}"; + httpResponse = httpRequest(HttpMethod.POST, url, httpHeader, postData); + OAuthAuthenticateResponse authResp = gson.fromJson(httpResponse, OAuthAuthenticateResponse.class); + if (authResp.userID.isEmpty()) { + String errorMessage = MessageFormat.format("Unable to authenticate: accountName={0}, rc={1} {2}", + accountName, getString(authResp.retcode), getString(authResp.desc)); + logger.warn("{}", errorMessage); + throw new MagentaTVException(errorMessage); + } + userId = getString(authResp.userID); + if (userId.isEmpty()) { + throw new MagentaTVException("No userID received!"); + } + String hashedUserID = MagentaTVControl.computeMD5(userId).toUpperCase(); + logger.trace("done, userID = {}", hashedUserID); + return hashedUserID; + } + + private String httpRequest(String method, String url, Properties headers, String data) throws MagentaTVException { + String result = ""; try { - step = "get credentials"; - httpHeader = initHttpHeader(); - url = OAUTH_GET_CRED_URL + ":" + OAUTH_GET_CRED_PORT + OAUTH_GET_CRED_URI; - httpHeader.setProperty(HEADER_HOST, substringAfterLast(OAUTH_GET_CRED_URL, "/")); - logger.trace("{} from {}", step, url); - httpResponse = HttpUtil.executeUrl(HttpMethod.GET, url, httpHeader, null, null, NETWORK_TIMEOUT_MS); - logger.trace("http response = {}", httpResponse); - OauthCredentials cred = gson.fromJson(httpResponse, OauthCredentials.class); - epghttpsurl = getString(cred.epghttpsurl); - if (epghttpsurl.isEmpty()) { - throw new MagentaTVException("Unable to determine EPG url"); + Request request = httpClient.newRequest(url).method(method).timeout(NETWORK_TIMEOUT_MS, + TimeUnit.MILLISECONDS); + for (Enumeration e = headers.keys(); e.hasMoreElements();) { + String key = (String) e.nextElement(); + String val = (String) headers.get(key); + request.header(key, val); } - if (!epghttpsurl.contains("/EPG")) { - epghttpsurl = epghttpsurl + "/EPG"; + if (method.equals(HttpMethod.POST)) { + fillPostData(request, data); } - logger.debug("epghttpsurl = {}", epghttpsurl); - - // get OAuth data from response - if (cred.sam3Para != null) { - for (OauthKeyValue si : cred.sam3Para) { - logger.trace("sam3Para.{} = {}", si.key, si.value); - if (si.key.equalsIgnoreCase("oAuthScope")) { - oAuthScope = si.value; - } else if (si.key.equalsIgnoreCase("SAM3ServiceURL")) { - oAuthService = si.value; - } + if (cookies.size() > 0) { + // Add cookies + String cookieValue = ""; + for (HttpCookie c : cookies) { + cookieValue = cookieValue + substringBefore(c.getValue(), ";") + "; "; } + request.header("Cookie", substringBeforeLast(cookieValue, ";")); } + logger.debug("OAuth: HTTP Request\n\tHTTP {} {}\n\tData={}", method, url, data.isEmpty() ? "" : data); + logger.trace("\n\tHeaders={}\tCookies={}", request.getHeaders(), request.getCookies()); - if (oAuthScope.isEmpty() || oAuthService.isEmpty()) { - throw new MagentaTVException("OAuth failed: Can't get Scope and Service: " + httpResponse); - } + ContentResponse contentResponse = request.send(); + result = contentResponse.getContentAsString().replace("\t", "").replace("\r\n", "").trim(); + int status = contentResponse.getStatus(); + logger.debug("OAuth: HTTP Response\n\tStatus={} {}\n\tData={}", status, contentResponse.getReason(), + result.isEmpty() ? "" : result); + logger.trace("\n\tHeaders={}", contentResponse.getHeaders()); - // Get OAuth token - step = "get token"; - url = oAuthService + "/oauth2/tokens"; - logger.debug("{} from {}", step, url); - - String userId = ""; - String uuid = UUID.randomUUID().toString(); - String cnonce = MagentaTVControl.computeMD5(uuid); - // New flow based on WebTV - postData = MessageFormat.format( - "password={0}&scope={1}+offline_access&grant_type=password&username={2}&x_telekom.access_token.format=CompactToken&x_telekom.access_token.encoding=text%2Fbase64&client_id=10LIVESAM30000004901NGTVWEB0000000000000", - URLEncoder.encode(accountPassword, UTF_8), oAuthScope, URLEncoder.encode(accountName, UTF_8)); - url = oAuthService + "/oauth2/tokens"; - dataStream = new ByteArrayInputStream(postData.getBytes(Charset.forName("UTF-8"))); - httpResponse = HttpUtil.executeUrl(HttpMethod.POST, url, httpHeader, dataStream, null, NETWORK_TIMEOUT_MS); - logger.trace("http response={}", httpResponse); - OAuthTokenResponse resp = gson.fromJson(httpResponse, OAuthTokenResponse.class); - if (resp.accessToken.isEmpty()) { - String errorMessage = MessageFormat.format("Unable to authenticate: accountName={0}, rc={1} ({2})", - accountName, getString(resp.errorDescription), getString(resp.error)); - logger.warn("{}", errorMessage); - throw new MagentaTVException(errorMessage); + // validate response, API errors are reported as Json + HttpFields responseHeaders = contentResponse.getHeaders(); + for (HttpField f : responseHeaders) { + if (f.getName().equals("Set-Cookie")) { + HttpCookie c = new HttpCookie(f.getName(), f.getValue()); + cookies.add(c); + } } - uuid = "t_" + MagentaTVControl.computeMD5(accountName); - url = "https://web.magentatv.de/EPG/JSON/DTAuthenticate?SID=user&T=Mac_chrome_81"; - postData = "{\"userType\":1,\"terminalid\":\"" + uuid + "\",\"mac\":\"" + uuid + "\"" - + ",\"terminaltype\":\"MACWEBTV\",\"utcEnable\":1,\"timezone\":\"Europe/Berlin\"," - + "\"terminalDetail\":[{\"key\":\"GUID\",\"value\":\"" + uuid + "\"}," - + "{\"key\":\"HardwareSupplier\",\"value\":\"\"},{\"key\":\"DeviceClass\",\"value\":\"PC\"}," - + "{\"key\":\"DeviceStorage\",\"value\":\"1\"},{\"key\":\"DeviceStorageSize\",\"value\":\"\"}]," - + "\"softwareVersion\":\"\",\"osversion\":\"\",\"terminalvendor\":\"Unknown\"," - + "\"caDeviceInfo\":[{\"caDeviceType\":6,\"caDeviceId\":\"" + uuid + "\"}]," + "\"accessToken\":\"" - + resp.accessToken + "\",\"preSharedKeyID\":\"PC01P00002\",\"cnonce\":\"" + cnonce + "\"}"; - dataStream = new ByteArrayInputStream(postData.getBytes(Charset.forName("UTF-8"))); - logger.debug("HTTP POST {}, postData={}", url, postData); - httpResponse = HttpUtil.executeUrl(HttpMethod.POST, url, httpHeader, dataStream, null, NETWORK_TIMEOUT_MS); - - logger.trace("http response={}", httpResponse); - OAuthAuthenticateResponse authResp = gson.fromJson(httpResponse, OAuthAuthenticateResponse.class); - if (authResp.userID.isEmpty()) { - String errorMessage = MessageFormat.format("Unable to authenticate: accountName={0}, rc={1} {2}", - accountName, getString(authResp.retcode), getString(authResp.desc)); - logger.warn("{}", errorMessage); - throw new MagentaTVException(errorMessage); + if (status != HttpStatus.OK_200) { + String error = "HTTP reqaest failed for URL " + url + ", Code=" + contentResponse.getReason() + "(" + + status + ")"; + throw new MagentaTVException(error); } - userId = getString(authResp.userID); - if (userId.isEmpty()) { - throw new MagentaTVException("No userID received!"); - } - String hashedUserID = MagentaTVControl.computeMD5(userId).toUpperCase(); - logger.trace("done, userID = {}", hashedUserID); - return hashedUserID; - } catch (IOException e) { - throw new MagentaTVException(e, - "Unable to authenticate {0}: {1} failed; serviceURL={2}, rc={3}/{4}, response={5}", accountName, - step, oAuthService, retcode, retmsg, httpResponse); + } catch (ExecutionException | InterruptedException | TimeoutException e) { + String error = "HTTP reqaest failed for URL " + url; + logger.info("{}", error, e); + throw new MagentaTVException(e, error); } + return result; } private Properties initHttpHeader() { Properties httpHeader = new Properties(); - httpHeader.setProperty(HEADER_USER_AGENT, OAUTH_USER_AGENT); - httpHeader.setProperty(HEADER_ACCEPT, "*/*"); - httpHeader.setProperty(HEADER_LANGUAGE, "de-de"); - httpHeader.setProperty(HEADER_CACHE_CONTROL, "no-cache"); + httpHeader.setProperty(HttpHeader.ACCEPT.toString(), "*/*"); + httpHeader.setProperty(HttpHeader.ACCEPT_LANGUAGE.toString(), "en-US,en;q=0.9,de;q=0.8"); + httpHeader.setProperty(HttpHeader.CACHE_CONTROL.toString(), "no-cache"); return httpHeader; } + private void fillPostData(Request request, String data) { + if (!data.isEmpty()) { + StringContentProvider postData; + if (request.getHeaders().contains(HttpHeader.CONTENT_TYPE)) { + String contentType = request.getHeaders().get(HttpHeader.CONTENT_TYPE); + postData = new StringContentProvider(contentType, data, StandardCharsets.UTF_8); + } else { + boolean json = data.startsWith("{"); + postData = new StringContentProvider(json ? "application/json" : "application/x-www-form-urlencoded", + data, StandardCharsets.UTF_8); + } + request.content(postData); + request.header(HttpHeader.CONTENT_LENGTH, Long.toString(postData.getLength())); + } + } + private String getString(@Nullable String value) { return value != null ? value : ""; } + + private String urlEncode(String url) { + try { + return URLEncoder.encode(url, UTF_8); + } catch (UnsupportedEncodingException e) { + logger.warn("OAuth: Unable to URL encode string {}", url, e); + return ""; + } + } } diff --git a/bundles/org.openhab.binding.magentatv/src/main/java/org/openhab/binding/magentatv/internal/network/MagentaTVPoweroffListener.java b/bundles/org.openhab.binding.magentatv/src/main/java/org/openhab/binding/magentatv/internal/network/MagentaTVPoweroffListener.java old mode 100644 new mode 100755 diff --git a/bundles/org.openhab.binding.magentatv/src/main/resources/OH-INF/binding/binding.xml b/bundles/org.openhab.binding.magentatv/src/main/resources/OH-INF/binding/binding.xml old mode 100644 new mode 100755 diff --git a/bundles/org.openhab.binding.magentatv/src/main/resources/OH-INF/i18n/magentatv_de.properties b/bundles/org.openhab.binding.magentatv/src/main/resources/OH-INF/i18n/magentatv_de.properties index 6374fc9c8c23d..14734cd785591 100644 --- a/bundles/org.openhab.binding.magentatv/src/main/resources/OH-INF/i18n/magentatv_de.properties +++ b/bundles/org.openhab.binding.magentatv/src/main/resources/OH-INF/i18n/magentatv_de.properties @@ -9,8 +9,8 @@ thing-type.magentatv.receiver.description = Media Receiver zum Epmfang von Magen # Thing configuration thing-type.config.magentatv.receiver.ipAddress.label = IP-Adresse thing-type.config.magentatv.ipAddress.description = IP Adresse des Media Receivers -thing-type.config.magentatv.receiver.userId.label = UID -thing-type.config.magentatv.receiver.userId.description = Technische Benutzerkennung (User ID), siehe Dokumentation; wird automatisch gefüllt, wenn Login-Name und Passwort angegeben sind. +thing-type.config.magentatv.receiver.userId.label = User ID +thing-type.config.magentatv.receiver.userId.description = Technische Benutzerkennung, siehe Dokumentation thing-type.config.magentatv.receiver.accountName.label = Login-Name thing-type.config.magentatv.receiver.accountName.description = Login-Name (E-Mail) zur Anmeldung im Telekom Kundencenter thing-type.config.magentatv.receiver.accountPassword.label = Passwort diff --git a/bundles/org.openhab.binding.magentatv/src/main/resources/OH-INF/thing/thing-types.xml b/bundles/org.openhab.binding.magentatv/src/main/resources/OH-INF/thing/thing-types.xml old mode 100644 new mode 100755 index 36b0ced037670..79d4f29e64429 --- a/bundles/org.openhab.binding.magentatv/src/main/resources/OH-INF/thing/thing-types.xml +++ b/bundles/org.openhab.binding.magentatv/src/main/resources/OH-INF/thing/thing-types.xml @@ -20,8 +20,8 @@ - macAddress + @@ -40,7 +40,7 @@ - Technical User ID required for pairing process, auto-filled when account credentials are given + Technical User ID required for pairing process diff --git a/bundles/org.openhab.binding.max/src/main/java/org/openhab/binding/max/internal/command/MCommand.java b/bundles/org.openhab.binding.max/src/main/java/org/openhab/binding/max/internal/command/MCommand.java index 9bcf43a95ca9e..96a71d69fff65 100644 --- a/bundles/org.openhab.binding.max/src/main/java/org/openhab/binding/max/internal/command/MCommand.java +++ b/bundles/org.openhab.binding.max/src/main/java/org/openhab/binding/max/internal/command/MCommand.java @@ -21,7 +21,7 @@ import java.util.Set; import java.util.TreeSet; -import org.apache.commons.lang.StringUtils; +import org.apache.commons.lang3.StringUtils; import org.eclipse.jdt.annotation.NonNullByDefault; import org.openhab.binding.max.internal.Utils; import org.openhab.binding.max.internal.device.Device; diff --git a/bundles/org.openhab.binding.max/src/main/java/org/openhab/binding/max/internal/command/TCommand.java b/bundles/org.openhab.binding.max/src/main/java/org/openhab/binding/max/internal/command/TCommand.java index 8927ac272f8a6..c5ac9eed0fe6d 100644 --- a/bundles/org.openhab.binding.max/src/main/java/org/openhab/binding/max/internal/command/TCommand.java +++ b/bundles/org.openhab.binding.max/src/main/java/org/openhab/binding/max/internal/command/TCommand.java @@ -16,7 +16,7 @@ import java.util.Base64; import java.util.List; -import org.apache.commons.lang.ArrayUtils; +import org.apache.commons.lang3.ArrayUtils; import org.eclipse.jdt.annotation.NonNullByDefault; import org.openhab.binding.max.internal.Utils; diff --git a/bundles/org.openhab.binding.max/src/main/java/org/openhab/binding/max/internal/message/CMessage.java b/bundles/org.openhab.binding.max/src/main/java/org/openhab/binding/max/internal/message/CMessage.java index a90f9aaaae3c9..a4b326e3874bb 100644 --- a/bundles/org.openhab.binding.max/src/main/java/org/openhab/binding/max/internal/message/CMessage.java +++ b/bundles/org.openhab.binding.max/src/main/java/org/openhab/binding/max/internal/message/CMessage.java @@ -25,7 +25,7 @@ import java.util.HashMap; import java.util.Map; -import org.apache.commons.lang.StringUtils; +import org.apache.commons.lang3.StringUtils; import org.eclipse.jdt.annotation.NonNullByDefault; import org.openhab.binding.max.internal.Utils; import org.openhab.binding.max.internal.device.DeviceType; @@ -278,7 +278,7 @@ public void debug(Logger logger) { logger.debug("RoomID: {}", roomId); for (String key : properties.keySet()) { if (!key.startsWith("Unknown")) { - String propertyName = StringUtils.join(StringUtils.splitByCharacterTypeCamelCase(key), ' '); + String propertyName = String.join(" ", StringUtils.splitByCharacterTypeCamelCase(key)); logger.debug("{}: {}", propertyName, properties.get(key)); } else { logger.debug("{}: {}", key, properties.get(key)); diff --git a/bundles/org.openhab.binding.mcp23017/pom.xml b/bundles/org.openhab.binding.mcp23017/pom.xml index 839f2a02e36d0..1c8a534d99ac2 100644 --- a/bundles/org.openhab.binding.mcp23017/pom.xml +++ b/bundles/org.openhab.binding.mcp23017/pom.xml @@ -18,13 +18,13 @@ com.pi4j pi4j-core - 1.2 + 1.4 compile com.pi4j pi4j-gpio-extension - 1.2 + 1.3 compile
diff --git a/bundles/org.openhab.binding.meteoblue/src/main/java/org/openhab/binding/meteoblue/internal/MeteoBlueConfiguration.java b/bundles/org.openhab.binding.meteoblue/src/main/java/org/openhab/binding/meteoblue/internal/MeteoBlueConfiguration.java index dbfe3d76433ee..212525809b806 100644 --- a/bundles/org.openhab.binding.meteoblue/src/main/java/org/openhab/binding/meteoblue/internal/MeteoBlueConfiguration.java +++ b/bundles/org.openhab.binding.meteoblue/src/main/java/org/openhab/binding/meteoblue/internal/MeteoBlueConfiguration.java @@ -12,8 +12,6 @@ */ package org.openhab.binding.meteoblue.internal; -import org.apache.commons.lang.StringUtils; - /** * Model for the meteoblue binding configuration. * @@ -64,15 +62,15 @@ public void parseLocation() { String a2 = split.length > 1 ? split[1] : null; String a3 = split.length > 2 ? split[2] : null; - if (!StringUtils.isBlank(a1)) { + if (a1 != null && !a1.isBlank()) { latitude = tryGetDouble(a1); } - if (!StringUtils.isBlank(a2)) { + if (a2 != null && !a2.isBlank()) { longitude = tryGetDouble(a2); } - if (!StringUtils.isBlank(a3)) { + if (a3 != null && !a3.isBlank()) { altitude = tryGetDouble(a3); } } diff --git a/bundles/org.openhab.binding.meteoblue/src/main/java/org/openhab/binding/meteoblue/internal/handler/MeteoBlueBridgeHandler.java b/bundles/org.openhab.binding.meteoblue/src/main/java/org/openhab/binding/meteoblue/internal/handler/MeteoBlueBridgeHandler.java index beae447647649..c74cbbb19b5b8 100644 --- a/bundles/org.openhab.binding.meteoblue/src/main/java/org/openhab/binding/meteoblue/internal/handler/MeteoBlueBridgeHandler.java +++ b/bundles/org.openhab.binding.meteoblue/src/main/java/org/openhab/binding/meteoblue/internal/handler/MeteoBlueBridgeHandler.java @@ -17,7 +17,6 @@ import java.util.Collections; import java.util.Set; -import org.apache.commons.lang.StringUtils; import org.openhab.binding.meteoblue.internal.MeteoBlueBridgeConfig; import org.openhab.core.io.net.http.HttpUtil; import org.openhab.core.thing.Bridge; @@ -55,7 +54,7 @@ public void initialize() { MeteoBlueBridgeConfig config = getConfigAs(MeteoBlueBridgeConfig.class); String apiKeyTemp = config.getApiKey(); - if (StringUtils.isBlank(apiKeyTemp)) { + if (apiKeyTemp == null || apiKeyTemp.isBlank()) { updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, "Cannot initialize meteoblue bridge. No apiKey provided."); return; diff --git a/bundles/org.openhab.binding.meteoblue/src/main/java/org/openhab/binding/meteoblue/internal/handler/MeteoBlueHandler.java b/bundles/org.openhab.binding.meteoblue/src/main/java/org/openhab/binding/meteoblue/internal/handler/MeteoBlueHandler.java index c35212c19cb11..eb62fa856b436 100644 --- a/bundles/org.openhab.binding.meteoblue/src/main/java/org/openhab/binding/meteoblue/internal/handler/MeteoBlueHandler.java +++ b/bundles/org.openhab.binding.meteoblue/src/main/java/org/openhab/binding/meteoblue/internal/handler/MeteoBlueHandler.java @@ -27,7 +27,6 @@ import javax.imageio.ImageIO; -import org.apache.commons.lang.StringUtils; import org.openhab.binding.meteoblue.internal.Forecast; import org.openhab.binding.meteoblue.internal.MeteoBlueConfiguration; import org.openhab.binding.meteoblue.internal.json.JsonData; @@ -97,13 +96,13 @@ public void initialize() { MeteoBlueConfiguration config = getConfigAs(MeteoBlueConfiguration.class); - if (StringUtils.isBlank(config.serviceType)) { + if (config.serviceType == null || config.serviceType.isBlank()) { config.serviceType = MeteoBlueConfiguration.SERVICETYPE_NONCOMM; logger.debug("Using default service type ({}).", config.serviceType); return; } - if (StringUtils.isBlank(config.location)) { + if (config.location == null || config.location.isBlank()) { flagBadConfig("The location was not configured."); return; } @@ -315,7 +314,7 @@ private boolean updateWeatherData() { if (config.altitude != null) { builder.append("&asl=" + config.altitude); } - if (StringUtils.isNotBlank(config.timeZone)) { + if (config.timeZone != null && !config.timeZone.isBlank()) { builder.append("&tz=" + config.timeZone); } url = url.replace("#FORMAT_PARAMS#", builder.toString()); diff --git a/bundles/org.openhab.binding.miele/src/main/java/org/openhab/binding/miele/internal/discovery/MieleApplianceDiscoveryService.java b/bundles/org.openhab.binding.miele/src/main/java/org/openhab/binding/miele/internal/discovery/MieleApplianceDiscoveryService.java index a82acc26b2ed2..b85ce8e11763d 100644 --- a/bundles/org.openhab.binding.miele/src/main/java/org/openhab/binding/miele/internal/discovery/MieleApplianceDiscoveryService.java +++ b/bundles/org.openhab.binding.miele/src/main/java/org/openhab/binding/miele/internal/discovery/MieleApplianceDiscoveryService.java @@ -20,7 +20,6 @@ import java.util.Map; import java.util.Set; -import org.apache.commons.lang.StringUtils; import org.openhab.binding.miele.internal.handler.ApplianceStatusListener; import org.openhab.binding.miele.internal.handler.MieleApplianceHandler; import org.openhab.binding.miele.internal.handler.MieleBridgeHandler; @@ -46,6 +45,9 @@ */ public class MieleApplianceDiscoveryService extends AbstractDiscoveryService implements ApplianceStatusListener { + private static final String MIELE_APPLIANCE_CLASS = "com.miele.xgw3000.gateway.hdm.deviceclasses.MieleAppliance"; + private static final String MIELE_CLASS = "com.miele.xgw3000.gateway.hdm.deviceclasses.Miele"; + private final Logger logger = LoggerFactory.getLogger(MieleApplianceDiscoveryService.class); private static final int SEARCH_TIME = 60; @@ -103,10 +105,9 @@ private void onApplianceAddedInternal(HomeDevice appliance) { properties.put(APPLIANCE_ID, appliance.getApplianceId()); for (JsonElement dc : appliance.DeviceClasses) { - if (dc.getAsString().contains("com.miele.xgw3000.gateway.hdm.deviceclasses.Miele") - && !dc.getAsString().equals("com.miele.xgw3000.gateway.hdm.deviceclasses.MieleAppliance")) { - properties.put(DEVICE_CLASS, StringUtils.right(dc.getAsString(), dc.getAsString().length() - - new String("com.miele.xgw3000.gateway.hdm.deviceclasses.Miele").length())); + String dcStr = dc.getAsString(); + if (dcStr.contains(MIELE_CLASS) && !dcStr.equals(MIELE_APPLIANCE_CLASS)) { + properties.put(DEVICE_CLASS, dcStr.substring(MIELE_CLASS.length())); break; } } @@ -145,17 +146,16 @@ private ThingUID getThingUID(HomeDevice appliance) { String modelID = null; for (JsonElement dc : appliance.DeviceClasses) { - if (dc.getAsString().contains("com.miele.xgw3000.gateway.hdm.deviceclasses.Miele") - && !dc.getAsString().equals("com.miele.xgw3000.gateway.hdm.deviceclasses.MieleAppliance")) { - modelID = StringUtils.right(dc.getAsString(), dc.getAsString().length() - - new String("com.miele.xgw3000.gateway.hdm.deviceclasses.Miele").length()); + String dcStr = dc.getAsString(); + if (dcStr.contains(MIELE_CLASS) && !dcStr.equals(MIELE_APPLIANCE_CLASS)) { + modelID = dcStr.substring(MIELE_CLASS.length()); break; } } if (modelID != null) { ThingTypeUID thingTypeUID = new ThingTypeUID(BINDING_ID, - StringUtils.lowerCase(modelID.replaceAll("[^a-zA-Z0-9_]", "_"))); + modelID.replaceAll("[^a-zA-Z0-9_]", "_").toLowerCase()); if (getSupportedThingTypes().contains(thingTypeUID)) { ThingUID thingUID = new ThingUID(thingTypeUID, bridgeUID, appliance.getId()); diff --git a/bundles/org.openhab.binding.miele/src/main/java/org/openhab/binding/miele/internal/handler/MieleApplianceHandler.java b/bundles/org.openhab.binding.miele/src/main/java/org/openhab/binding/miele/internal/handler/MieleApplianceHandler.java index 8f0db9dd3c706..b3ba33c1969cf 100644 --- a/bundles/org.openhab.binding.miele/src/main/java/org/openhab/binding/miele/internal/handler/MieleApplianceHandler.java +++ b/bundles/org.openhab.binding.miele/src/main/java/org/openhab/binding/miele/internal/handler/MieleApplianceHandler.java @@ -20,7 +20,7 @@ import java.util.stream.Collectors; import java.util.stream.Stream; -import org.apache.commons.lang.StringUtils; +import org.apache.commons.lang3.StringUtils; import org.openhab.binding.miele.internal.handler.MieleBridgeHandler.DeviceClassObject; import org.openhab.binding.miele.internal.handler.MieleBridgeHandler.DeviceMetaData; import org.openhab.binding.miele.internal.handler.MieleBridgeHandler.DeviceOperation; @@ -144,7 +144,7 @@ public void handleCommand(ChannelUID channelUID, Command command) { @Override public void onApplianceStateChanged(String UID, DeviceClassObject dco) { - String myUID = ((String) getThing().getProperties().get(PROTOCOL_PROPERTY_NAME)) + String myUID = (getThing().getProperties().get(PROTOCOL_PROPERTY_NAME)) + (String) getThing().getConfiguration().getProperties().get(APPLIANCE_ID); String modelID = StringUtils.right(dco.DeviceClass, dco.DeviceClass.length() - new String("com.miele.xgw3000.gateway.hdm.deviceclasses.Miele").length()); @@ -177,7 +177,7 @@ public void onApplianceStateChanged(String UID, DeviceClassObject dco) { @Override public void onAppliancePropertyChanged(String UID, DeviceProperty dp) { - String myUID = ((String) getThing().getProperties().get(PROTOCOL_PROPERTY_NAME)) + String myUID = (getThing().getProperties().get(PROTOCOL_PROPERTY_NAME)) + (String) getThing().getConfiguration().getProperties().get(APPLIANCE_ID); if (myUID.equals(UID)) { @@ -186,8 +186,7 @@ public void onAppliancePropertyChanged(String UID, DeviceProperty dp) { if (dp.Metadata == null) { String metadata = metaDataCache.get(new StringBuilder().append(dp.Name).toString().trim()); if (metadata != null) { - JsonParser parser = new JsonParser(); - JsonObject jsonMetaData = (JsonObject) parser.parse(metadata); + JsonObject jsonMetaData = (JsonObject) JsonParser.parseString(metadata); dmd = gson.fromJson(jsonMetaData, DeviceMetaData.class); // only keep the enum, if any - that's all we care for events we receive via multicast // all other fields are nulled @@ -199,8 +198,7 @@ public void onAppliancePropertyChanged(String UID, DeviceProperty dp) { } if (dp.Metadata != null) { String metadata = StringUtils.replace(dp.Metadata.toString(), "enum", "MieleEnum"); - JsonParser parser = new JsonParser(); - JsonObject jsonMetaData = (JsonObject) parser.parse(metadata); + JsonObject jsonMetaData = (JsonObject) JsonParser.parseString(metadata); dmd = gson.fromJson(jsonMetaData, DeviceMetaData.class); metaDataCache.put(new StringBuilder().append(dp.Name).toString().trim(), metadata); } diff --git a/bundles/org.openhab.binding.miele/src/main/java/org/openhab/binding/miele/internal/handler/MieleBridgeHandler.java b/bundles/org.openhab.binding.miele/src/main/java/org/openhab/binding/miele/internal/handler/MieleBridgeHandler.java index 7306393008dbe..b02df0947f593 100644 --- a/bundles/org.openhab.binding.miele/src/main/java/org/openhab/binding/miele/internal/handler/MieleBridgeHandler.java +++ b/bundles/org.openhab.binding.miele/src/main/java/org/openhab/binding/miele/internal/handler/MieleBridgeHandler.java @@ -44,7 +44,7 @@ import java.util.regex.Pattern; import java.util.zip.GZIPInputStream; -import org.apache.commons.lang.StringUtils; +import org.apache.commons.lang3.StringUtils; import org.openhab.core.common.NamedThreadFactory; import org.openhab.core.thing.Bridge; import org.openhab.core.thing.ChannelUID; @@ -480,8 +480,7 @@ protected JsonElement invokeRPC(String methodName, Object[] args) { if (responseData != null) { logger.debug("The request '{}' yields '{}'", requestData, responseData); - JsonParser parser = new JsonParser(); - JsonObject resp = (JsonObject) parser.parse(new StringReader(responseData)); + JsonObject resp = (JsonObject) JsonParser.parseReader(new StringReader(responseData)); result = resp.get("result"); JsonElement error = resp.get("error"); diff --git a/bundles/org.openhab.binding.miele/src/main/java/org/openhab/binding/miele/internal/handler/WashingMachineChannelSelector.java b/bundles/org.openhab.binding.miele/src/main/java/org/openhab/binding/miele/internal/handler/WashingMachineChannelSelector.java index 810a317cf1c97..7f1e43fee6478 100644 --- a/bundles/org.openhab.binding.miele/src/main/java/org/openhab/binding/miele/internal/handler/WashingMachineChannelSelector.java +++ b/bundles/org.openhab.binding.miele/src/main/java/org/openhab/binding/miele/internal/handler/WashingMachineChannelSelector.java @@ -18,7 +18,7 @@ import java.util.Map.Entry; import java.util.TimeZone; -import org.apache.commons.lang.StringUtils; +import org.apache.commons.lang3.StringUtils; import org.openhab.binding.miele.internal.handler.MieleBridgeHandler.DeviceMetaData; import org.openhab.core.library.types.DateTimeType; import org.openhab.core.library.types.DecimalType; diff --git a/bundles/org.openhab.binding.miele/src/main/resources/OH-INF/thing/coffeemachine.xml b/bundles/org.openhab.binding.miele/src/main/resources/OH-INF/thing/coffeemachine.xml index d4d6f8588f028..8a34bfc992d19 100644 --- a/bundles/org.openhab.binding.miele/src/main/resources/OH-INF/thing/coffeemachine.xml +++ b/bundles/org.openhab.binding.miele/src/main/resources/OH-INF/thing/coffeemachine.xml @@ -23,10 +23,9 @@ - + The identifier identifies one certain appliance on the ZigBee network. - true diff --git a/bundles/org.openhab.binding.miele/src/main/resources/OH-INF/thing/dishwasher.xml b/bundles/org.openhab.binding.miele/src/main/resources/OH-INF/thing/dishwasher.xml index 5aae629917f2c..902fbaced0548 100644 --- a/bundles/org.openhab.binding.miele/src/main/resources/OH-INF/thing/dishwasher.xml +++ b/bundles/org.openhab.binding.miele/src/main/resources/OH-INF/thing/dishwasher.xml @@ -26,10 +26,9 @@ - + The identifier identifies one certain appliance on the ZigBee network. - true diff --git a/bundles/org.openhab.binding.miele/src/main/resources/OH-INF/thing/fridge.xml b/bundles/org.openhab.binding.miele/src/main/resources/OH-INF/thing/fridge.xml index 0e76fa038fc95..c68c643376971 100644 --- a/bundles/org.openhab.binding.miele/src/main/resources/OH-INF/thing/fridge.xml +++ b/bundles/org.openhab.binding.miele/src/main/resources/OH-INF/thing/fridge.xml @@ -23,10 +23,9 @@ - + The identifier identifies one certain appliance on the ZigBee network. - true diff --git a/bundles/org.openhab.binding.miele/src/main/resources/OH-INF/thing/fridgefreezer.xml b/bundles/org.openhab.binding.miele/src/main/resources/OH-INF/thing/fridgefreezer.xml index 0982b2581a42c..585ad21d9c4e2 100644 --- a/bundles/org.openhab.binding.miele/src/main/resources/OH-INF/thing/fridgefreezer.xml +++ b/bundles/org.openhab.binding.miele/src/main/resources/OH-INF/thing/fridgefreezer.xml @@ -28,10 +28,9 @@ - + The identifier identifies one certain appliance on the ZigBee network. - true diff --git a/bundles/org.openhab.binding.miele/src/main/resources/OH-INF/thing/hob.xml b/bundles/org.openhab.binding.miele/src/main/resources/OH-INF/thing/hob.xml index c091f9ad6c0b3..71e6ab990a580 100644 --- a/bundles/org.openhab.binding.miele/src/main/resources/OH-INF/thing/hob.xml +++ b/bundles/org.openhab.binding.miele/src/main/resources/OH-INF/thing/hob.xml @@ -36,10 +36,9 @@ - + The identifier identifies one certain appliance on the ZigBee network. - true diff --git a/bundles/org.openhab.binding.miele/src/main/resources/OH-INF/thing/hood.xml b/bundles/org.openhab.binding.miele/src/main/resources/OH-INF/thing/hood.xml index 668c81ef11a2a..aedd3eade74ae 100644 --- a/bundles/org.openhab.binding.miele/src/main/resources/OH-INF/thing/hood.xml +++ b/bundles/org.openhab.binding.miele/src/main/resources/OH-INF/thing/hood.xml @@ -21,10 +21,9 @@ - + The identifier identifies the appliance on the ZigBee network. - true diff --git a/bundles/org.openhab.binding.miele/src/main/resources/OH-INF/thing/oven.xml b/bundles/org.openhab.binding.miele/src/main/resources/OH-INF/thing/oven.xml index 0e42274429a32..4090178d656ac 100644 --- a/bundles/org.openhab.binding.miele/src/main/resources/OH-INF/thing/oven.xml +++ b/bundles/org.openhab.binding.miele/src/main/resources/OH-INF/thing/oven.xml @@ -32,10 +32,9 @@ - + The identifier identifies one certain appliance on the ZigBee network. - true diff --git a/bundles/org.openhab.binding.miele/src/main/resources/OH-INF/thing/tumbledryer.xml b/bundles/org.openhab.binding.miele/src/main/resources/OH-INF/thing/tumbledryer.xml index 22c81484ff98c..24035a4211b48 100644 --- a/bundles/org.openhab.binding.miele/src/main/resources/OH-INF/thing/tumbledryer.xml +++ b/bundles/org.openhab.binding.miele/src/main/resources/OH-INF/thing/tumbledryer.xml @@ -28,10 +28,9 @@ - + The identifier identifies one certain appliance on the ZigBee network. - true diff --git a/bundles/org.openhab.binding.miele/src/main/resources/OH-INF/thing/washingmachine.xml b/bundles/org.openhab.binding.miele/src/main/resources/OH-INF/thing/washingmachine.xml index 3330fd36ef4b5..2a41f546dc636 100644 --- a/bundles/org.openhab.binding.miele/src/main/resources/OH-INF/thing/washingmachine.xml +++ b/bundles/org.openhab.binding.miele/src/main/resources/OH-INF/thing/washingmachine.xml @@ -29,10 +29,9 @@ - + The identifier identifies one certain appliance on the ZigBee network. - true diff --git a/bundles/org.openhab.binding.miele/src/main/resources/OH-INF/thing/xgw3000.xml b/bundles/org.openhab.binding.miele/src/main/resources/OH-INF/thing/xgw3000.xml index 36cb38e137c65..184c3daaccec2 100644 --- a/bundles/org.openhab.binding.miele/src/main/resources/OH-INF/thing/xgw3000.xml +++ b/bundles/org.openhab.binding.miele/src/main/resources/OH-INF/thing/xgw3000.xml @@ -14,31 +14,27 @@ - + network-address Network address of the Miele@home gateway. - true - + network-address Network address of openHAB host interface where the binding will listen for multicast events coming from the Miele@home gateway - true - + Name of a registered Miele@home user. - false - + password Password for the registered Miele@home - false diff --git a/bundles/org.openhab.binding.mihome/README.md b/bundles/org.openhab.binding.mihome/README.md index 4ae3fd2e4ddc4..4e0a574d8fec9 100644 --- a/bundles/org.openhab.binding.mihome/README.md +++ b/bundles/org.openhab.binding.mihome/README.md @@ -8,7 +8,7 @@ The sensors run on a coin cell battery for over a year. After setup, you can disconnect the gateway from the internet to keep your sensor information private. -Please note that using the Xiaomi gateway with OpenHAB requires enabling the developer mode and that multiple user reports suggest that it is no longer posible. +Please note that using the Xiaomi gateway with openHAB requires enabling the developer mode and that multiple user reports suggest that it is no longer possible. Zigbee2Mqtt provides an alternative method to integrate Xiaomi devices. ## Supported devices @@ -102,7 +102,7 @@ __Hints:__ ## Removing devices from the gateway -If you remove a Thing in PapaerUI it will also trigger the gateway to unpair the device. +If you remove a Thing in PaperUI it will also trigger the gateway to unpair the device. It will only reappear in your Inbox, if you connect it to the gateway again. Just follow the instructions in ["Connecting devices to the gateway"](#connecting-devices-to-the-gateway). @@ -110,13 +110,17 @@ Just follow the instructions in ["Connecting devices to the gateway"](#connectin - The binding requires port `9898` to not be used by any other service on the system. - Make sure multicast traffic is correctly routed between the gateway and your openHAB instance +- To correctly receive multicast traffic, when your openHAB machine is using multiple network interfaces, you might need to configure the optional `interface` property on the `Bridge` Thing, like so: +``` +Bridge mihome:bridge:f0b429XXXXXX "Xiaomi Gateway" [ ..., interface="eth0", ... ] { +``` ## Configuration examples ### xiaomi.things: ``` -Bridge mihome:bridge:f0b429XXXXXX "Xiaomi Gateway" [ serialNumber="f0b429XXXXXX", ipAddress="192.168.0.3", port=9898, key="XXXXXXXXXXXXXXXX", pollingInterval=6000 ] { +Bridge mihome:bridge:f0b429XXXXXX "Xiaomi Gateway" [ serialNumber="f0b429XXXXXX", ipAddress="192.168.0.3", port=9898, key="XXXXXXXXXXXXXXXX" ] { Things: gateway f0b429XXXXXX "Xiaomi Mi Smart Home Gateway" [itemId="f0b429XXXXXX"] sensor_ht 158d0001XXXXXX "Xiaomi Temperature Sensor" [itemId="158d0001XXXXXX"] @@ -452,10 +456,10 @@ Make sure you have connected your gateway to openHAB and the communication is wo The devices send different types of messages to the gateway. You have to capture as many of them as possible, so that the device is fully supported in the end. -1. Heartbeat (transmitted usually every 60 minutes) +1. Heartbeat (usually transmitted every 60 minutes) 2. Report (device reports new sensor or status values) 3. Read ACK (binding refreshes all sensor values after a restart of openHAB) -4. Write ACK (device has received a command) __not avaiable for sensor-only devices__ +4. Write ACK (device has received a command) __not available for sensor-only devices__ ### Open a new issue or get your hands dirty @@ -465,7 +469,7 @@ Post an issue in the GitHub repository with as much information as possible abou - model name - content of all the different message types -Or implement the support by youself and submit a pull request. +Or implement the support by yourself and submit a pull request. ### Handle the message contents of a basic device thing with items @@ -484,7 +488,7 @@ _Example for the same message from the heartbeat channel - only the data is retu These messages are in JSON format, which also gives you the ability to parse single values. -_Example for the retrieved IP from the heartbeat message and transformed with JSONPATH transfomration: ```String Gateway_IP {channel="mihome:basic:xxx:heartbeatMessage"[profile="transform:JSONPATH", function="$.ip"]}```_ +_Example for the retrieved IP from the heartbeat message and transformed with JSONPATH transformation: ```String Gateway_IP {channel="mihome:basic:xxx:heartbeatMessage"[profile="transform:JSONPATH", function="$.ip"]}```_ The item will get the value `192.168.0.124`. @@ -545,7 +549,18 @@ In case you want to check if the communication between the machine and the gatew - make sure you have __netcat__ installed - Enter ```netcat -ukl 9898``` - At least every 10 seconds you should see a message coming in from the gateway which looks like - ```{"cmd":"heartbeat","model":"gateway","sid":"`xxx","short_id":"0","token":"xxx","data":"{\"ip\":\"`xxx\"}"}``` +```{"cmd":"heartbeat","model":"gateway","sid":"`xxx","short_id":"0","token":"xxx","data":"{\"ip\":\"`xxx\"}"}``` + +#### Multiple network interfaces + +When the computer running openHAB has more than one network interface configured (typically, a VLAN for your segregated IoT devices, and the other for your regular traffic like internet, openHAB panel access, etc), it could be that openHAB will attempt to listen for Multicast traffic of the Gateway on the wrong network interface. That will prevent openHAB and `netcat` from receiving the messages from the Xiaomi Gateway. Within openHAB this manifests by seeing the Gateway and its devices online for a brief period after openHAB startup, after which they timeout and are shown Offline. No channel triggers from the Gateway work in this case. + +In order to verify that traffic is actually received by the machine use `tcpdump` on each interface: +- List your network interfaces `ifconfig | grep MULTICAST` or `ip link | grep MULTICAST` +- Use `tcpdump -i port 9898` for each interface to verify if you receive traffic + +If you already know the correct interface, or you found the correct one through tcpdump: +- Configure the `interface` property of the `Bridge` Thing with the correct name (for example `eth0`, etc) ### Check if your Windows/Mac machine receives multicast traffic @@ -562,6 +577,7 @@ __My gateway shows up in openHAB and I have added all devices, but I don't get a - Make sure the gateway and the machine are in the same subnet - Try to connect your machine via Ethernet instead of Wifi - Make sure you don't have any firewall rules blocking multicast + - If you have multiple network interfaces, try to configure the `interface` property of the `Bridge` Thing __I have connected my gateway to the network but it doesn't show up in openHAB:__ - Make sure to have the developer mode enabled in the MiHome app diff --git a/bundles/org.openhab.binding.mihome/src/main/java/org/openhab/binding/mihome/internal/XiaomiGatewayBindingConstants.java b/bundles/org.openhab.binding.mihome/src/main/java/org/openhab/binding/mihome/internal/XiaomiGatewayBindingConstants.java index bed8f71e45143..bf944e3b406fe 100644 --- a/bundles/org.openhab.binding.mihome/src/main/java/org/openhab/binding/mihome/internal/XiaomiGatewayBindingConstants.java +++ b/bundles/org.openhab.binding.mihome/src/main/java/org/openhab/binding/mihome/internal/XiaomiGatewayBindingConstants.java @@ -123,6 +123,7 @@ public class XiaomiGatewayBindingConstants { public static final String SERIAL_NUMBER = "serialNumber"; public static final String HOST = "ipAddress"; public static final String PORT = "port"; + public static final String INTERFACE = "interface"; public static final String TOKEN = "token"; // Item config properties diff --git a/bundles/org.openhab.binding.mihome/src/main/java/org/openhab/binding/mihome/internal/handler/XiaomiBridgeHandler.java b/bundles/org.openhab.binding.mihome/src/main/java/org/openhab/binding/mihome/internal/handler/XiaomiBridgeHandler.java index 2977214685a10..d84956104cc67 100644 --- a/bundles/org.openhab.binding.mihome/src/main/java/org/openhab/binding/mihome/internal/handler/XiaomiBridgeHandler.java +++ b/bundles/org.openhab.binding.mihome/src/main/java/org/openhab/binding/mihome/internal/handler/XiaomiBridgeHandler.java @@ -67,7 +67,6 @@ public class XiaomiBridgeHandler extends ConfigStatusBridgeHandler implements Xi private static final String NO = "no"; public static final Set SUPPORTED_THING_TYPES = Collections.singleton(THING_TYPE_BRIDGE); - private static final JsonParser PARSER = new JsonParser(); private static final EncryptionHelper CRYPTER = new EncryptionHelper(); private static Map retentionInbox = new ConcurrentHashMap<>(); @@ -131,7 +130,7 @@ public void initialize() { return; } logger.debug("Init socket on Port: {}", port); - socket = new XiaomiBridgeSocket(port, getThing().getUID().getId()); + socket = new XiaomiBridgeSocket(port, (String) config.get(INTERFACE), getThing().getUID().getId()); socket.initialize(); socket.registerListener(this); @@ -175,7 +174,7 @@ public void onDataReceived(JsonObject message) { } break; case "get_id_list_ack": - JsonArray devices = PARSER.parse(message.get("data").getAsString()).getAsJsonArray(); + JsonArray devices = JsonParser.parseString(message.get("data").getAsString()).getAsJsonArray(); for (JsonElement deviceId : devices) { String device = deviceId.getAsString(); sendCommandToBridge("read", device); diff --git a/bundles/org.openhab.binding.mihome/src/main/java/org/openhab/binding/mihome/internal/handler/XiaomiDeviceBaseHandler.java b/bundles/org.openhab.binding.mihome/src/main/java/org/openhab/binding/mihome/internal/handler/XiaomiDeviceBaseHandler.java index a71935fb9117d..43ad72b4720fa 100644 --- a/bundles/org.openhab.binding.mihome/src/main/java/org/openhab/binding/mihome/internal/handler/XiaomiDeviceBaseHandler.java +++ b/bundles/org.openhab.binding.mihome/src/main/java/org/openhab/binding/mihome/internal/handler/XiaomiDeviceBaseHandler.java @@ -81,8 +81,6 @@ public class XiaomiDeviceBaseHandler extends BaseThingHandler implements XiaomiI private static final long ONLINE_TIMEOUT_MILLIS = TimeUnit.HOURS.toMillis(2); private ScheduledFuture onlineCheckTask; - private JsonParser parser = new JsonParser(); - private XiaomiBridgeHandler bridgeHandler; private String itemId; @@ -148,7 +146,7 @@ public void onItemUpdate(String sid, String command, JsonObject message) { } logger.debug("Item got update: {}", message); try { - JsonObject data = parser.parse(message.get("data").getAsString()).getAsJsonObject(); + JsonObject data = JsonParser.parseString(message.get("data").getAsString()).getAsJsonObject(); parseCommand(command, data); if (THING_TYPE_BASIC.equals(getThing().getThingTypeUID())) { parseDefault(message); diff --git a/bundles/org.openhab.binding.mihome/src/main/java/org/openhab/binding/mihome/internal/socket/XiaomiBridgeSocket.java b/bundles/org.openhab.binding.mihome/src/main/java/org/openhab/binding/mihome/internal/socket/XiaomiBridgeSocket.java index ba0ae12ed0823..e5d4f9f60cd42 100644 --- a/bundles/org.openhab.binding.mihome/src/main/java/org/openhab/binding/mihome/internal/socket/XiaomiBridgeSocket.java +++ b/bundles/org.openhab.binding.mihome/src/main/java/org/openhab/binding/mihome/internal/socket/XiaomiBridgeSocket.java @@ -15,8 +15,10 @@ import java.io.IOException; import java.net.InetAddress; import java.net.MulticastSocket; +import java.net.NetworkInterface; import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -30,9 +32,11 @@ public class XiaomiBridgeSocket extends XiaomiSocket { private final Logger logger = LoggerFactory.getLogger(XiaomiBridgeSocket.class); + private @Nullable final String netIf; - public XiaomiBridgeSocket(int port, String owner) { + public XiaomiBridgeSocket(int port, String netIf, String owner) { super(port, owner); + this.netIf = netIf; } /** @@ -51,10 +55,16 @@ protected synchronized void setupSocket() { try { logger.debug("Setup socket"); socket = new MulticastSocket(getPort()); + + if (netIf != null) { + socket.setNetworkInterface(NetworkInterface.getByName(netIf)); + } + setSocket(socket); // must bind receive side socket.joinGroup(InetAddress.getByName(MCAST_ADDR)); - logger.debug("Initialized socket to {}:{} on {}:{}", socket.getRemoteSocketAddress(), socket.getPort(), - socket.getLocalAddress(), socket.getLocalPort()); + logger.debug("Initialized socket to {}:{} on {}:{} bound to {} network interface", + socket.getRemoteSocketAddress(), socket.getPort(), socket.getLocalAddress(), socket.getLocalPort(), + socket.getNetworkInterface()); } catch (IOException e) { logger.error("Setup socket error", e); } diff --git a/bundles/org.openhab.binding.mihome/src/main/java/org/openhab/binding/mihome/internal/socket/XiaomiSocket.java b/bundles/org.openhab.binding.mihome/src/main/java/org/openhab/binding/mihome/internal/socket/XiaomiSocket.java index fc80a269b1582..4e1ce434c77c7 100644 --- a/bundles/org.openhab.binding.mihome/src/main/java/org/openhab/binding/mihome/internal/socket/XiaomiSocket.java +++ b/bundles/org.openhab.binding.mihome/src/main/java/org/openhab/binding/mihome/internal/socket/XiaomiSocket.java @@ -44,7 +44,6 @@ public abstract class XiaomiSocket { static final String MCAST_ADDR = "224.0.0.50"; private static final int BUFFER_LENGTH = 1024; - private static final JsonParser PARSER = new JsonParser(); private final Logger logger = LoggerFactory.getLogger(XiaomiSocket.class); @@ -197,7 +196,7 @@ private void receiveData() { logger.debug("Received Datagram from {}:{} on port {}", address.getHostAddress(), datagramPacket.getPort(), localPort); String sentence = new String(datagramPacket.getData(), 0, datagramPacket.getLength()); - JsonObject message = PARSER.parse(sentence).getAsJsonObject(); + JsonObject message = JsonParser.parseString(sentence).getAsJsonObject(); notifyListeners(message, address); logger.trace("Data received and notified {} listeners", listeners.size()); } diff --git a/bundles/org.openhab.binding.mihome/src/main/resources/OH-INF/config/config.xml b/bundles/org.openhab.binding.mihome/src/main/resources/OH-INF/config/config.xml index e20556b87b3de..936f081ef7b54 100644 --- a/bundles/org.openhab.binding.mihome/src/main/resources/OH-INF/config/config.xml +++ b/bundles/org.openhab.binding.mihome/src/main/resources/OH-INF/config/config.xml @@ -36,6 +36,13 @@ true + + + Interface to bind to for the MiHome communication channel + false + true + + Developer key extracted from Xiaomi's app diff --git a/bundles/org.openhab.binding.miio/README.base.md b/bundles/org.openhab.binding.miio/README.base.md index 88306fd08807c..8223a88c09f2a 100644 --- a/bundles/org.openhab.binding.miio/README.base.md +++ b/bundles/org.openhab.binding.miio/README.base.md @@ -245,8 +245,20 @@ Image map "Cleaning Map" (gVacLast) {channel="miio:vacuum:034F0E45:cleaning#map" Note: cleaning map is only available with cloud access. -Additionally depending on the capabilities of your robot vacuum other channels may be enabled at runtime +There are several advanced channels, which may be useful in rules (e.g. for individual room cleaning etc) +In case your vacuum does not support one of these commands, it will show "unsupported_method" for string channels or no value for numeric channels. +| Type | Channel | Description | +|---------|-----------------------------------|----------------------------| +| Number | status#segment_status | Segment Status | +| Number | status#map_status | Map Box Status | +| Number | status#led_status | Led Box Status | +| String | info#carpet_mode | Carpet Mode details | +| String | info#fw_features | Firmware Features | +| String | info#room_mapping | Room Mapping details | +| String | info#multi_maps_list | Maps Listing details | + +Additionally depending on the capabilities of your robot vacuum other channels may be enabled at runtime | Type | Channel | Description | |---------|-----------------------------------|----------------------------| diff --git a/bundles/org.openhab.binding.miio/README.md b/bundles/org.openhab.binding.miio/README.md index 44218acc5ebb2..f2677ea1d5e32 100644 --- a/bundles/org.openhab.binding.miio/README.md +++ b/bundles/org.openhab.binding.miio/README.md @@ -173,7 +173,7 @@ This will change the communication method and the Mi IO binding can communicate # Mi IO Devices -Currently the miio binding supports more than 260 different models. +Currently the miio binding supports more than 280 different models. | Device | ThingType | Device Model | Supported | Remark | |------------------------------|------------------|------------------------|-----------|------------| @@ -197,6 +197,8 @@ Currently the miio binding supports more than 260 different models. | Mi IH Pressure Rice Cooker | miio:unsupported | chunmi.cooker.press1 | No | | | Mi IH Pressure Rice Cooker | miio:unsupported | chunmi.cooker.press2 | No | | | Gosund Smart Plug | miio:basic | [cuco.plug.cp1](#cuco-plug-cp1) | Yes | | +| Mi Smart Antibacterial Humidifier | miio:basic | [deerma.humidifier.jsq](#deerma-humidifier-jsq) | Yes | | +| Mi S Smart humidifer | miio:basic | [deerma.humidifier.jsq1](#deerma-humidifier-jsq1) | Yes | | | Mi Smart Humidifier | miio:basic | [deerma.humidifier.mjjsq](#deerma-humidifier-mjjsq) | Yes | | | Mi Fresh Air Ventilator A1-150 | miio:basic | [dmaker.airfresh.a1](#dmaker-airfresh-a1) | Yes | | | Mi Fresh Air Ventilator | miio:basic | [dmaker.airfresh.t2017](#dmaker-airfresh-t2017) | Yes | | @@ -208,6 +210,10 @@ Currently the miio binding supports more than 260 different models. | Mi Robot Vacuum Mop 1C STYTJ01ZHM | miio:basic | [dreame.vacuum.mc1808](#dreame-vacuum-mc1808) | Yes | Identified manual actions for execution
`action{"did":"battery-start-charge","siid":2,"aiid":1,"in":[]}`
`action{"did":"vacuum-start-sweep","siid":3,"aiid":1,"in":[]}`
`action{"did":"vacuum-stop-sweeping","siid":3,"aiid":2,"in":[]}`
`action{"did":"brush-cleaner-reset-brush-life","siid":26,"aiid":1,"in":[]}`
`action{"did":"filter-reset-filter-life","siid":27,"aiid":1,"in":[]}`
`action{"did":"brush-cleaner-reset-brush-life","siid":28,"aiid":1,"in":[]}`
`action{"did":"clean-start-clean","siid":18,"aiid":1,"in":[]}`
`action{"did":"clean-stop-clean","siid":18,"aiid":2,"in":[]}`
`action{"did":"remote-start-remote","siid":21,"aiid":1,"in":[1.0, 2.0]}`
`action{"did":"remote-stop-remote","siid":21,"aiid":2,"in":[]}`
`action{"did":"remote-exit-remote","siid":21,"aiid":3,"in":[]}`
`action{"did":"map-map-req","siid":23,"aiid":1,"in":[2.0]}`
`action{"did":"audio-position","siid":24,"aiid":1,"in":[]}`
`action{"did":"audio-set-voice","siid":24,"aiid":2,"in":[]}`
`action{"did":"audio-play-sound","siid":24,"aiid":3,"in":[]}`
Please test and feedback if they are working to they can be linked to a channel.
Experimental support. Please report back if all channels are functional. Preferably share the debug log of property refresh and command responses | | Dreame Robot Vacuum-Mop F9 | miio:basic | [dreame.vacuum.p2008](#dreame-vacuum-p2008) | Yes | Identified manual actions for execution
`action{"did":"vacuum-start-sweep","siid":2,"aiid":1,"in":[]}`
`action{"did":"vacuum-stop-sweeping","siid":2,"aiid":2,"in":[]}`
`action{"did":"battery-start-charge","siid":3,"aiid":1,"in":[]}`
`action{"did":"brush-cleaner-reset-brush-life","siid":9,"aiid":1,"in":[]}`
`action{"did":"brush-cleaner-reset-brush-life","siid":10,"aiid":1,"in":[]}`
`action{"did":"filter-reset-filter-life","siid":11,"aiid":1,"in":[]}`
`action{"did":"vacuum-extend-start-clean","siid":4,"aiid":1,"in":[10.0]}`
`action{"did":"vacuum-extend-stop-clean","siid":4,"aiid":2,"in":[]}`
`action{"did":"map-map-req","siid":6,"aiid":1,"in":[2.0]}`
`action{"did":"map-update-map","siid":6,"aiid":2,"in":[4.0]}`
`action{"did":"audio-position","siid":7,"aiid":1,"in":[]}`
`action{"did":"audio-play-sound","siid":7,"aiid":2,"in":[]}`
`action{"did":"time-delete-timer","siid":8,"aiid":1,"in":[3.0]}`
Please test and feedback if they are working to they can be linked to a channel.
Experimental support. Please report back if all channels are functional. Preferably share the debug log of property refresh and command responses | | Dreame Robot Vacuum D9 | miio:basic | [dreame.vacuum.p2009](#dreame-vacuum-p2009) | Yes | Identified manual actions for execution not linked in the database >`action{"did":"vacuum-extend-start-clean","siid":4,"aiid":1,"in":[10.0]}`
`action{"did":"vacuum-extend-stop-clean","siid":4,"aiid":2,"in":[]}`
`action{"did":"map-map-req","siid":6,"aiid":1,"in":[2.0]}`
`action{"did":"map-update-map","siid":6,"aiid":2,"in":[4.0]}`
`action{"did":"audio-position","siid":7,"aiid":1,"in":[]}`
`action{"did":"audio-play-sound","siid":7,"aiid":2,"in":[]}`
`action{"did":"time-delete-timer","siid":8,"aiid":1,"in":[3.0]}`
| +| Trouver Robot LDS Vacuum-Mop Finder | miio:basic | [dreame.vacuum.p2036](#dreame-vacuum-p2036) | Yes | Identified manual actions for execution not linked in the database >`action{"did":"vacuum-extend-start-clean","siid":4,"aiid":1,"in":[10.0]}`
`action{"did":"vacuum-extend-stop-clean","siid":4,"aiid":2,"in":[]}`
`action{"did":"map-map-req","siid":6,"aiid":1,"in":[2.0]}`
`action{"did":"map-update-map","siid":6,"aiid":2,"in":[4.0]}`
`action{"did":"audio-position","siid":7,"aiid":1,"in":[]}`
`action{"did":"audio-play-sound","siid":7,"aiid":2,"in":[]}`
`action{"did":"time-delete-timer","siid":8,"aiid":1,"in":[3.0]}`
| +| Mi Robot Vacuum-Mop 2 Pro+ | miio:basic | [dreame.vacuum.p2041o](#dreame-vacuum-p2041o) | Yes | Identified manual actions for execution
`action{"did":"vacuum-start-sweep","siid":2,"aiid":1,"in":[]}`
`action{"did":"vacuum-stop-sweeping","siid":2,"aiid":2,"in":[]}`
`action{"did":"battery-start-charge","siid":3,"aiid":1,"in":[]}`
`action{"did":"brush-cleaner-reset-brush-life","siid":9,"aiid":1,"in":[]}`
`action{"did":"brush-cleaner-reset-brush-life","siid":10,"aiid":1,"in":[]}`
`action{"did":"filter-reset-filter-life","siid":11,"aiid":1,"in":[]}`
`action{"did":"vacuum-extend-start-clean","siid":4,"aiid":1,"in":[10.0]}`
`action{"did":"vacuum-extend-stop-clean","siid":4,"aiid":2,"in":[]}`
`action{"did":"map-map-req","siid":6,"aiid":1,"in":[2.0]}`
`action{"did":"map-update-map","siid":6,"aiid":2,"in":[4.0]}`
`action{"did":"audio-position","siid":7,"aiid":1,"in":[]}`
`action{"did":"audio-play-sound","siid":7,"aiid":2,"in":[]}`
`action{"did":"time-delete-timer","siid":8,"aiid":1,"in":[3.0]}`
Please test and feedback if they are working to they can be linked to a channel.
Experimental support. Please report back if all channels are functional. Preferably share the debug log of property refresh and command responses | +| MOVA Z500 Robot Vacuum and Mop Cleaner | miio:basic | [dreame.vacuum.p2156o](#dreame-vacuum-p2156o) | Yes | Identified manual actions for execution
`action{"did":"vacuum-start-sweep","siid":2,"aiid":1,"in":[]}`
`action{"did":"vacuum-stop-sweeping","siid":2,"aiid":2,"in":[]}`
`action{"did":"battery-start-charge","siid":3,"aiid":1,"in":[]}`
`action{"did":"brush-cleaner-reset-brush-life","siid":9,"aiid":1,"in":[]}`
`action{"did":"brush-cleaner-reset-brush-life","siid":10,"aiid":1,"in":[]}`
`action{"did":"filter-reset-filter-life","siid":11,"aiid":1,"in":[]}`
`action{"did":"vacuum-extend-start-clean","siid":4,"aiid":1,"in":[10.0]}`
`action{"did":"vacuum-extend-stop-clean","siid":4,"aiid":2,"in":[]}`
`action{"did":"map-map-req","siid":6,"aiid":1,"in":[2.0]}`
`action{"did":"map-update-map","siid":6,"aiid":2,"in":[4.0]}`
`action{"did":"audio-position","siid":7,"aiid":1,"in":[]}`
`action{"did":"audio-play-sound","siid":7,"aiid":2,"in":[]}`
`action{"did":"time-delete-timer","siid":8,"aiid":1,"in":[3.0]}`
Please test and feedback if they are working to they can be linked to a channel.
Experimental support. Please report back if all channels are functional. Preferably share the debug log of property refresh and command responses | +| MOVA L600 Robot Vacuum and Mop Cleaner | miio:basic | [dreame.vacuum.p2157](#dreame-vacuum-p2157) | Yes | Identified manual actions for execution not linked in the database >`action{"did":"vacuum-extend-start-clean","siid":4,"aiid":1,"in":[10.0]}`
`action{"did":"vacuum-extend-stop-clean","siid":4,"aiid":2,"in":[]}`
`action{"did":"map-map-req","siid":6,"aiid":1,"in":[2.0]}`
`action{"did":"map-update-map","siid":6,"aiid":2,"in":[4.0]}`
`action{"did":"audio-position","siid":7,"aiid":1,"in":[]}`
`action{"did":"audio-play-sound","siid":7,"aiid":2,"in":[]}`
`action{"did":"time-delete-timer","siid":8,"aiid":1,"in":[3.0]}`
| | HUIZUO ARIES For Bedroom | miio:basic | [huayi.light.ari013](#huayi-light-ari013) | Yes | Experimental support. Please report back if all channels are functional. Preferably share the debug log of property refresh and command responses | | HUIZUO ARIES For Living Room | miio:basic | [huayi.light.aries](#huayi-light-aries) | Yes | Experimental support. Please report back if all channels are functional. Preferably share the debug log of property refresh and command responses | | HUIZUO Fan Light | miio:basic | [huayi.light.fanwy](#huayi-light-fanwy) | Yes | Experimental support. Please report back if all channels are functional. Preferably share the debug log of property refresh and command responses | @@ -454,6 +460,7 @@ Currently the miio binding supports more than 260 different models. | Mi Air Purifier v5 | miio:basic | [zhimi.airpurifier.v5](#zhimi-airpurifier-v5) | Yes | | | Mi Air Purifier Pro v6 | miio:basic | [zhimi.airpurifier.v6](#zhimi-airpurifier-v6) | Yes | | | Mi Air Purifier Pro v7 | miio:basic | [zhimi.airpurifier.v7](#zhimi-airpurifier-v7) | Yes | | +| Mi Air Purifier Pro H | miio:basic | [zhimi.airpurifier.vb2](#zhimi-airpurifier-vb2) | Yes | | | Mi Air Purifier virtual | miio:unsupported | zhimi.airpurifier.virtual | No | | | Mi Air Purifier 2(Virtual) | miio:unsupported | zhimi.airpurifier.vtl_m1 | No | | | Mi Standing Fan | miio:basic | [zhimi.fan.sa1](#zhimi-fan-sa1) | Yes | | @@ -463,6 +470,12 @@ Currently the miio binding supports more than 260 different models. | Smartmi Inverter Pedestal Fan | miio:basic | [zhimi.fan.za1](#zhimi-fan-za1) | Yes | | | Smartmi Standing Fan 2 | miio:basic | [zhimi.fan.za3](#zhimi-fan-za3) | Yes | | | Smartmi Standing Fan 2S | miio:basic | [zhimi.fan.za4](#zhimi-fan-za4) | Yes | | +| Smartmi Standing Fan 3 | miio:basic | [zhimi.fan.za5](#zhimi-fan-za5) | Yes | Experimental support. Please report back if all channels are functional. Preferably share the debug log of property refresh and command responses | +| Mi Smart Space Heater S | miio:basic | [zhimi.heater.ma2](#zhimi-heater-ma2) | Yes | Experimental support. Please report back if all channels are functional. Preferably share the debug log of property refresh and command responses | +| Mi Smart Baseboard Heater E | miio:basic | [zhimi.heater.ma3](#zhimi-heater-ma3) | Yes | Identified manual actions for execution
`action{"did":"private-service-toggle-switch","siid":8,"aiid":1,"in":[]}`
Please test and feedback if they are working to they can be linked to a channel.
Experimental support. Please report back if all channels are functional. Preferably share the debug log of property refresh and command responses | +| Mi Smart Space Heater S | miio:basic | [zhimi.heater.mc2](#zhimi-heater-mc2) | Yes | Experimental support. Please report back if all channels are functional. Preferably share the debug log of property refresh and command responses | +| Smartmi Smart Fan | miio:basic | [zhimi.heater.na1](#zhimi-heater-na1) | Yes | Experimental support. Please report back if all channels are functional. Preferably share the debug log of property refresh and command responses | +| Smartmi Smart Fan Heater | miio:basic | [zhimi.heater.nb1](#zhimi-heater-nb1) | Yes | Experimental support. Please report back if all channels are functional. Preferably share the debug log of property refresh and command responses | | Smartmi Radiant Heater Smart Version | miio:basic | [zhimi.heater.za1](#zhimi-heater-za1) | Yes | Experimental support. Please report back if all channels are functional. Preferably share the debug log of property refresh and command responses | | Smartmi Smart Convector Heater 1S | miio:basic | [zhimi.heater.za2](#zhimi-heater-za2) | Yes | Experimental support. Please report back if all channels are functional. Preferably share the debug log of property refresh and command responses | | Smartmi Smart Convector Heater 1S | miio:basic | [zhimi.heater.zb1](#zhimi-heater-zb1) | Yes | Experimental support. Please report back if all channels are functional. Preferably share the debug log of property refresh and command responses | @@ -522,14 +535,14 @@ e.g. `openhab:send actionCommand 'upd_timer["1498595904821", "on"]'` would enabl |----------------------|----------------------|------------------------------------------|------------| | on | Switch | Power | | | temperature | Number:Temperature | Temperature | | -| working-time | Number:Duration | Working Time | | +| working-time | Number:Time | Working Time | | | on1 | Switch | Indicator Light - Switch Status | | | power-consumption | Number:Energy | Daily Power Consumption | | | electric-current | Number:Current | Power Consumption - Electric Current | | | voltage | Number:ElectricPotential | Power Consumption - Voltage | | | electric-power | Number:Power | Current Power Consumption - Electric Power | | -| on-duration | Number:Duration | Imilab Timer - On Duration | | -| off-duration | Number:Duration | Imilab Timer - Off Duration | | +| on-duration | Number:Time | Imilab Timer - On Duration | | +| off-duration | Number:Time | Imilab Timer - Off Duration | | | countdown | Number:Time | Imilab Timer - Countdown | | | task-switch | Switch | Imilab Timer - Task Switch | | | countdown-info | Switch | Imilab Timer - Countdown Info | | @@ -546,7 +559,7 @@ e.g. `openhab:send actionCommand 'upd_timer["1498595904821", "on"]'` would enabl | Channel | Type | Description | Comment | |----------------------|----------------------|------------------------------------------|------------| -| power | Switch | Power | If this channel does not respond to on/off replace the model with chuangmi.plug.v3old in the config or upgrade firmware | +| power | Switch | Power | If this channel does not respond to on/off upgrade firmware | | usb | Switch | USB | | | temperature | Number:Temperature | Temperature | | | led | Switch | Wifi LED | | @@ -555,7 +568,7 @@ e.g. `openhab:send actionCommand 'upd_timer["1498595904821", "on"]'` would enabl | Channel | Type | Description | Comment | |----------------------|----------------------|------------------------------------------|------------| -| power | Switch | Power | If this channel does not respond to on/off replace the model with chuangmi.plug.v3old in the config or upgrade firmware | +| power | Switch | Power | If this channel does not respond to on/off upgrade firmware | | usb | Switch | USB | | | temperature | Number:Temperature | Temperature | | | led | Switch | Wifi LED | | @@ -595,7 +608,7 @@ e.g. `openhab:send actionCommand 'upd_timer["1498595904821", "on"]'` would enabl | Channel | Type | Description | Comment | |----------------------|----------------------|------------------------------------------|------------| -| power | Switch | Power | If this channel does not respond to on/off replace the model with chuangmi.plug.v3old in the config or upgrade firmware | +| power | Switch | Power | If this channel does not respond to on/off upgrade firmware | | usb | Switch | USB | | | temperature | Number:Temperature | Temperature | | | led | Switch | Wifi LED | | @@ -610,6 +623,43 @@ e.g. `openhab:send actionCommand 'upd_timer["1498595904821", "on"]'` would enabl | SerialNumber | String | Device Information-Device Serial Number | | | On | Switch | Switch-Switch Status | | +### Mi Smart Antibacterial Humidifier (deerma.humidifier.jsq) Channels + +| Channel | Type | Description | Comment | +|----------------------|----------------------|------------------------------------------|------------| +| power | Switch | Power | | +| mode | Number | Mode | | +| humidity | Number:Dimensionless | Humidity | | +| humidity_set | Number:Dimensionless | Humidity Setting | | +| led | Switch | LED indicator Light | | +| sound | Switch | Notification Sounds | | +| watertankstatus | Number | Watertank Status | | + +### Mi S Smart humidifer (deerma.humidifier.jsq1) Channels + +| Channel | Type | Description | Comment | +|----------------------|----------------------|------------------------------------------|------------| +| power | Switch | Power | | +| mode | Number | Mode | | +| humidity | Number:Dimensionless | Humidity | | +| humidity_set | Number:Dimensionless | Humidity Setting | | +| led | Switch | LED indicator Light | | +| sound | Switch | Notification Sounds | | +| watertankstatus | Number | Watertank Status | | +| wet_and_protect | Switch | Wet and Protect | | + +### Mi Smart Humidifier (deerma.humidifier.mjjsq) Channels + +| Channel | Type | Description | Comment | +|----------------------|----------------------|------------------------------------------|------------| +| power | Switch | Power | | +| mode | Number | Mode | | +| humidity | Number:Dimensionless | Humidity | | +| humidity_set | Number:Dimensionless | Humidity Setting | | +| led | Switch | LED indicator Light | | +| sound | Switch | Notification Sounds | | +| watertankstatus | Number | Watertank Status | | + ### Mi Fresh Air Ventilator A1-150 (dmaker.airfresh.a1) Channels | Channel | Type | Description | Comment | @@ -837,7 +887,175 @@ e.g. `openhab:send actionCommand 'upd_timer["1498595904821", "on"]'` would enabl | cleaning-mode | Number | Vacuum Extend - Cleaning Mode | Value mapping ["0"="mode 0","1"="mode 1","2"="mode 2","3"="mode 3"] | | mop-mode | Number | Vacuum Extend - Mop Mode | Value mapping ["1"="low water","2"="medium water","3"="high water"] | | waterbox-status | Number | Vacuum Extend - Waterbox Status | Value mapping ["0"="Status 0","1"="Status 1"] | -| task-status | Number | Vacuum Extend - Task Status | | +| task-status | Number | Vacuum Extend - Task Status | Value mapping ["0"="Notask","1"="AutoClean","2"="CustomClean","3"="SelectAreanClean","4"="SpotArea"] | +| break-point-restart | Number | Vacuum Extend - Break Point Restart | Value mapping ["0"="Off","1"="On"] | +| carpet-press | Number | Vacuum Extend - Carpet Press | Value mapping ["0"="Off","1"="On"] | +| serial-number1 | String | Vacuum Extend - Serial Number | | +| clean-rags-tip | Number:Time | Vacuum Extend - Clean Rags Tip | | +| keep-sweeper-time | Number:Time | Vacuum Extend - Keep Sweeper Time | | +| faults | String | Vacuum Extend - Faults | | +| enable | Switch | Do Not Disturb - Enable | | +| start-time | String | Do Not Disturb - Start Time | | +| end-time | String | Do Not Disturb - End Time | | +| volume | Number:Dimensionless | Audio - Volume | | +| voice-packet-id | String | Audio - Voice Packet Id | | +| voice-change-state | String | Audio - Voice Change State | | +| time-zone | String | Time - Time Zone | | +| timer-clean | String | Time - Timer Clean | | +| first-clean-time | Number | Clean Logs - First Clean Time | | +| total-clean-time | Number:Time | Clean Logs - Total Clean Time | | +| total-clean-times | Number | Clean Logs - Total Clean Times | | +| total-clean-area | Number | Clean Logs - Total Clean Area | | + +### Trouver Robot LDS Vacuum-Mop Finder (dreame.vacuum.p2036) Channels + +| Channel | Type | Description | Comment | +|----------------------|----------------------|------------------------------------------|------------| +| vacuumaction | String | Vacuum Action | Value mapping ["sweep"="Sweep","stopsweep"="Stop Sweep","dock"="Goto Dock"] | +| status | Number | Robot Cleaner - Status | Value mapping ["1"="Sweeping","2"="Idle","3"="Paused","4"="Error","5"="Go Charging","6"="Charging","7"="Mopping"] | +| fault | Number | Robot Cleaner - Device Fault | | +| battery-level | Number:Dimensionless | Battery - Battery Level | | +| charging-state | Number | Battery - Charging State | Value mapping ["1"="Charging","2"="Not Charging","5"="Go Charging"] | +| resetConsumable | String | Consumables Reset | Value mapping ["mainbrush-cleaner-reset-brush-life"="Reset Main Brush","sidebrush-cleaner-reset-brush-life"="Reset Side Brush","filter-reset-filter-life"="Reset Filter"] | +| brush-left-time | Number:Time | Main Cleaning Brush - Brush Left Time | | +| brush-life-level | Number:Dimensionless | Main Cleaning Brush - Brush Life Level | | +| brush-left-time1 | Number:Time | Side Cleaning Brush - Brush Left Time | | +| brush-life-level1 | Number:Dimensionless | Side Cleaning Brush - Brush Life Level | | +| filter-life-level | Number:Dimensionless | Filter - Filter Life Level | | +| filter-left-time | Number:Time | Filter - Filter Left Time | | +| work-mode | Number | Vacuum Extend - Work Mode | | +| cleaning-time | Number:Time | Vacuum Extend - Cleaning Time | | +| cleaning-area | Number:Area | Vacuum Extend - Cleaning Area | | +| cleaning-mode | Number | Vacuum Extend - Cleaning Mode | Value mapping ["0"="mode 0","1"="mode 1","2"="mode 2","3"="mode 3"] | +| mop-mode | Number | Vacuum Extend - Mop Mode | Value mapping ["1"="low water","2"="medium water","3"="high water"] | +| waterbox-status | Number | Vacuum Extend - Waterbox Status | Value mapping ["0"="Status 0","1"="Status 1"] | +| task-status | Number | Vacuum Extend - Task Status | Value mapping ["0"="Notask","1"="AutoClean","2"="CustomClean","3"="SelectAreanClean","4"="SpotArea"] | +| break-point-restart | Number | Vacuum Extend - Break Point Restart | Value mapping ["0"="Off","1"="On"] | +| carpet-press | Number | Vacuum Extend - Carpet Press | Value mapping ["0"="Off","1"="On"] | +| serial-number1 | String | Vacuum Extend - Serial Number | | +| clean-rags-tip | Number:Time | Vacuum Extend - Clean Rags Tip | | +| keep-sweeper-time | Number:Time | Vacuum Extend - Keep Sweeper Time | | +| faults | String | Vacuum Extend - Faults | | +| enable | Switch | Do Not Disturb - Enable | | +| start-time | String | Do Not Disturb - Start Time | | +| end-time | String | Do Not Disturb - End Time | | +| volume | Number:Dimensionless | Audio - Volume | | +| voice-packet-id | String | Audio - Voice Packet Id | | +| voice-change-state | String | Audio - Voice Change State | | +| time-zone | String | Time - Time Zone | | +| timer-clean | String | Time - Timer Clean | | +| first-clean-time | Number | Clean Logs - First Clean Time | | +| total-clean-time | Number:Time | Clean Logs - Total Clean Time | | +| total-clean-times | Number | Clean Logs - Total Clean Times | | +| total-clean-area | Number | Clean Logs - Total Clean Area | | + +### Mi Robot Vacuum-Mop 2 Pro+ (dreame.vacuum.p2041o) Channels + +| Channel | Type | Description | Comment | +|----------------------|----------------------|------------------------------------------|------------| +| actions | String | Actions | Value mapping ["vacuum-start-sweep"="Start Sweep","vacuum-stop-sweeping"="Stop Sweeping","battery-start-charge"="Start Charge","brush-cleaner-reset-brush-life"="Brush Cleaner Reset Brush Life","brush-cleaner-reset-brush-life"="Brush Cleaner Reset Brush Life","filter-reset-filter-life"="Filter Reset Filter Life","vacuum-extend-start-clean"="Vacuum Extend Start Clean","vacuum-extend-stop-clean"="Vacuum Extend Stop Clean","map-map-req"="Map Map Req","map-update-map"="Map Update Map","audio-position"="Audio Position","audio-play-sound"="Audio Play Sound","time-delete-timer"="Time Delete Timer"] | +| status | Number | Robot Cleaner - Status | Value mapping ["1"="Sweeping","2"="Idle","3"="Paused","4"="Error","5"="Go Charging","6"="Charging","7"="Mopping"] | +| fault | Number | Robot Cleaner - Device Fault | | +| mode | Number | Robot Cleaner - Mode | Value mapping ["0"="Silent","1"="Basic","2"="Strong","3"="Full Speed"] | +| battery_level | Number:Dimensionless | Battery - Battery Level | | +| charging_state | Number | Battery - Charging State | Value mapping ["1"="Charging","2"="Not Charging","5"="Go Charging"] | +| brush_left_time | Number:Time | Main Cleaning Brush - Brush Left Time | | +| brush_life_level | Number:Dimensionless | Main Cleaning Brush - Brush Life Level | | +| brush_left_time1 | Number:Time | Side Cleaning Brush - Brush Left Time | | +| brush_life_level1 | Number:Dimensionless | Side Cleaning Brush - Brush Life Level | | +| filter_life_level | Number:Dimensionless | Filter - Filter Life Level | | +| filter_left_time | Number:Time | Filter - Filter Left Time | | +| work_mode | Number | Vacuum Extend - Work Mode | | +| cleaning_time | Number:Time | Vacuum Extend - Cleaning Time | | +| cleaning-area | Number:Area | Vacuum Extend - Cleaning Area | | +| cleaning_mode | Number | Vacuum Extend - Cleaning Mode | Value mapping ["0"="mode 0","1"="mode 1","2"="mode 2","3"="mode 3"] | +| mop_mode | Number | Vacuum Extend - Mop Mode | Value mapping ["1"="low water","2"="medium water","3"="high water"] | +| waterbox_status | Number | Vacuum Extend - Waterbox Status | Value mapping ["0"="Status 0","1"="Status 1"] | +| task_status | Number | Vacuum Extend - Task Status | Value mapping ["0"="Notask","1"="AutoClean","2"="CustomClean","3"="SelectAreanClean","4"="SpotArea"] | +| break_point_restart | Number | Vacuum Extend - Break Point Restart | Value mapping ["0"="Off","1"="On"] | +| carpet_press | Number | Vacuum Extend - Carpet Press | Value mapping ["0"="Off","1"="On"] | +| serial_number | String | Vacuum Extend - Serial Number | | +| keep_sweeper_time | Number:Time | Vacuum Extend - Keep Sweeper Time | | +| faults | String | Vacuum Extend - Faults | | +| enable | Switch | Do Not Disturb - Enable | | +| start_time | String | Do Not Disturb - Start Time | | +| end_time | String | Do Not Disturb - End Time | | +| volume | Number:Dimensionless | Audio - Volume | | +| voice_packet_id | String | Audio - Voice Packet Id | | +| voice_change_state | String | Audio - Voice Change State | | +| time_zone | String | Time - Time Zone | | +| timer_clean | String | Time - Timer Clean | | +| first_clean_time | Number | Clean Logs - First Clean Time | | +| total_clean_time | Number:Time | Clean Logs - Total Clean Time | | +| total_clean_times | Number | Clean Logs - Total Clean Times | | +| total_clean_area | Number | Clean Logs - Total Clean Area | | +| save_map_status | Number | Vslam Extend - Save Map Status | Value mapping ["0"="Off","1"="On"] | + +### MOVA Z500 Robot Vacuum and Mop Cleaner (dreame.vacuum.p2156o) Channels + +| Channel | Type | Description | Comment | +|----------------------|----------------------|------------------------------------------|------------| +| actions | String | Actions | Value mapping ["vacuum-start-sweep"="Start Sweep","vacuum-stop-sweeping"="Stop Sweeping","battery-start-charge"="Start Charge","brush-cleaner-reset-brush-life"="Brush Cleaner Reset Brush Life","brush-cleaner-reset-brush-life"="Brush Cleaner Reset Brush Life","filter-reset-filter-life"="Filter Reset Filter Life","vacuum-extend-start-clean"="Vacuum Extend Start Clean","vacuum-extend-stop-clean"="Vacuum Extend Stop Clean","map-map-req"="Map Map Req","map-update-map"="Map Update Map","audio-position"="Audio Position","audio-play-sound"="Audio Play Sound","time-delete-timer"="Time Delete Timer"] | +| status | Number | Robot Cleaner - Status | Value mapping ["1"="Sweeping","2"="Idle","3"="Paused","4"="Error","5"="Go Charging","6"="Charging","7"="Mopping"] | +| fault | Number | Robot Cleaner - Device Fault | | +| mode | Number | Robot Cleaner - Mode | Value mapping ["0"="Silent","1"="Basic","2"="Strong","3"="Full Speed"] | +| battery_level | Number:Dimensionless | Battery - Battery Level | | +| charging_state | Number | Battery - Charging State | Value mapping ["1"="Charging","2"="Not Charging","5"="Go Charging"] | +| brush_left_time | Number:Time | Main Cleaning Brush - Brush Left Time | | +| brush_life_level | Number:Dimensionless | Main Cleaning Brush - Brush Life Level | | +| brush_left_time1 | Number:Time | Side Cleaning Brush - Brush Left Time | | +| brush_life_level1 | Number:Dimensionless | Side Cleaning Brush - Brush Life Level | | +| filter_life_level | Number:Dimensionless | Filter - Filter Life Level | | +| filter_left_time | Number:Time | Filter - Filter Left Time | | +| work_mode | Number | Vacuum Extend - Work Mode | | +| cleaning_time | Number:Time | Vacuum Extend - Cleaning Time | | +| cleaning-area | Number:Area | Vacuum Extend - Cleaning Area | | +| cleaning_mode | Number | Vacuum Extend - Cleaning Mode | Value mapping ["0"="mode 0","1"="mode 1","2"="mode 2","3"="mode 3"] | +| mop_mode | Number | Vacuum Extend - Mop Mode | Value mapping ["1"="low water","2"="medium water","3"="high water"] | +| waterbox_status | Number | Vacuum Extend - Waterbox Status | Value mapping ["0"="Status 0","1"="Status 1"] | +| task_status | Number | Vacuum Extend - Task Status | Value mapping ["0"="Notask","1"="AutoClean","2"="CustomClean","3"="SelectAreanClean","4"="SpotArea"] | +| break_point_restart | Number | Vacuum Extend - Break Point Restart | Value mapping ["0"="Off","1"="On"] | +| carpet_press | Number | Vacuum Extend - Carpet Press | Value mapping ["0"="Off","1"="On"] | +| serial_number | String | Vacuum Extend - Serial Number | | +| keep_sweeper_time | Number:Time | Vacuum Extend - Keep Sweeper Time | | +| faults | String | Vacuum Extend - Faults | | +| enable | Switch | Do Not Disturb - Enable | | +| start_time | String | Do Not Disturb - Start Time | | +| end_time | String | Do Not Disturb - End Time | | +| volume | Number:Dimensionless | Audio - Volume | | +| voice_packet_id | String | Audio - Voice Packet Id | | +| voice_change_state | String | Audio - Voice Change State | | +| time_zone | String | Time - Time Zone | | +| timer_clean | String | Time - Timer Clean | | +| first_clean_time | Number | Clean Logs - First Clean Time | | +| total_clean_time | Number:Time | Clean Logs - Total Clean Time | | +| total_clean_times | Number | Clean Logs - Total Clean Times | | +| total_clean_area | Number | Clean Logs - Total Clean Area | | +| save_map_status | Number | Vslam Extend - Save Map Status | Value mapping ["0"="Off","1"="On"] | + +### MOVA L600 Robot Vacuum and Mop Cleaner (dreame.vacuum.p2157) Channels + +| Channel | Type | Description | Comment | +|----------------------|----------------------|------------------------------------------|------------| +| vacuumaction | String | Vacuum Action | Value mapping ["sweep"="Sweep","stopsweep"="Stop Sweep","dock"="Goto Dock"] | +| status | Number | Robot Cleaner - Status | Value mapping ["1"="Sweeping","2"="Idle","3"="Paused","4"="Error","5"="Go Charging","6"="Charging","7"="Mopping"] | +| fault | Number | Robot Cleaner - Device Fault | | +| battery-level | Number:Dimensionless | Battery - Battery Level | | +| charging-state | Number | Battery - Charging State | Value mapping ["1"="Charging","2"="Not Charging","5"="Go Charging"] | +| resetConsumable | String | Consumables Reset | Value mapping ["mainbrush-cleaner-reset-brush-life"="Reset Main Brush","sidebrush-cleaner-reset-brush-life"="Reset Side Brush","filter-reset-filter-life"="Reset Filter"] | +| brush-left-time | Number:Time | Main Cleaning Brush - Brush Left Time | | +| brush-life-level | Number:Dimensionless | Main Cleaning Brush - Brush Life Level | | +| brush-left-time1 | Number:Time | Side Cleaning Brush - Brush Left Time | | +| brush-life-level1 | Number:Dimensionless | Side Cleaning Brush - Brush Life Level | | +| filter-life-level | Number:Dimensionless | Filter - Filter Life Level | | +| filter-left-time | Number:Time | Filter - Filter Left Time | | +| work-mode | Number | Vacuum Extend - Work Mode | | +| cleaning-time | Number:Time | Vacuum Extend - Cleaning Time | | +| cleaning-area | Number:Area | Vacuum Extend - Cleaning Area | | +| cleaning-mode | Number | Vacuum Extend - Cleaning Mode | Value mapping ["0"="mode 0","1"="mode 1","2"="mode 2","3"="mode 3"] | +| mop-mode | Number | Vacuum Extend - Mop Mode | Value mapping ["1"="low water","2"="medium water","3"="high water"] | +| waterbox-status | Number | Vacuum Extend - Waterbox Status | Value mapping ["0"="Status 0","1"="Status 1"] | +| task-status | Number | Vacuum Extend - Task Status | Value mapping ["0"="Notask","1"="AutoClean","2"="CustomClean","3"="SelectAreanClean","4"="SpotArea"] | | break-point-restart | Number | Vacuum Extend - Break Point Restart | Value mapping ["0"="Off","1"="On"] | | carpet-press | Number | Vacuum Extend - Carpet Press | Value mapping ["0"="Off","1"="On"] | | serial-number1 | String | Vacuum Extend - Serial Number | | @@ -2060,7 +2278,7 @@ e.g. `openhab:send actionCommand 'upd_timer["1498595904821", "on"]'` would enabl | elec-count | Number | Electricity - Count | | | clean | String | Maintenance - Clean | | | examine | String | Maintenance - Examine | | -| running-duration | Number:Duration | Maintenance - Running Duration | | +| running-duration | Number:Time | Maintenance - Running Duration | | | fan-percent | Number:Dimentionless | Fan Speed % | | | timer | String | Enhance - Timer | | @@ -2084,7 +2302,7 @@ e.g. `openhab:send actionCommand 'upd_timer["1498595904821", "on"]'` would enabl | elec-count | Number | Electricity - Count | | | clean | String | Maintenance - Clean | | | examine | String | Maintenance - Examine | | -| running-duration | Number:Duration | Maintenance - Running Duration | | +| running-duration | Number:Time | Maintenance - Running Duration | | | fan-percent | Number:Dimentionless | Fan Speed % | | | timer | String | Enhance - Timer | | @@ -2108,7 +2326,7 @@ e.g. `openhab:send actionCommand 'upd_timer["1498595904821", "on"]'` would enabl | elec-count | Number | Electricity - Count | | | clean | String | Maintenance - Clean | | | examine | String | Maintenance - Examine | | -| running-duration | Number:Duration | Maintenance - Running Duration | | +| running-duration | Number:Time | Maintenance - Running Duration | | | fan-percent | Number:Dimentionless | Fan Speed % | | | timer | String | Enhance - Timer | | @@ -2132,7 +2350,7 @@ e.g. `openhab:send actionCommand 'upd_timer["1498595904821", "on"]'` would enabl | elec-count | Number | Electricity - Count | | | clean | String | Maintenance - Clean | | | examine | String | Maintenance - Examine | | -| running-duration | Number:Duration | Maintenance - Running Duration | | +| running-duration | Number:Time | Maintenance - Running Duration | | | fan-percent | Number:Dimentionless | Fan Speed % | | | timer | String | Enhance - Timer | | @@ -2156,7 +2374,7 @@ e.g. `openhab:send actionCommand 'upd_timer["1498595904821", "on"]'` would enabl | elec-count | Number | Electricity - Count | | | clean | String | Maintenance - Clean | | | examine | String | Maintenance - Examine | | -| running-duration | Number:Duration | Maintenance - Running Duration | | +| running-duration | Number:Time | Maintenance - Running Duration | | | fan-percent | Number:Dimentionless | Fan Speed % | | | timer | String | Enhance - Timer | | @@ -2180,7 +2398,7 @@ e.g. `openhab:send actionCommand 'upd_timer["1498595904821", "on"]'` would enabl | elec-count | Number | Electricity - Count | | | clean | String | Maintenance - Clean | | | examine | String | Maintenance - Examine | | -| running-duration | Number:Duration | Maintenance - Running Duration | | +| running-duration | Number:Time | Maintenance - Running Duration | | | fan-percent | Number:Dimentionless | Fan Speed % | | | timer | String | Enhance - Timer | | @@ -2204,7 +2422,7 @@ e.g. `openhab:send actionCommand 'upd_timer["1498595904821", "on"]'` would enabl | elec-count | Number | Electricity - Count | | | clean | String | Maintenance - Clean | | | examine | String | Maintenance - Examine | | -| running-duration | Number:Duration | Maintenance - Running Duration | | +| running-duration | Number:Time | Maintenance - Running Duration | | | fan-percent | Number:Dimentionless | Fan Speed % | | | timer | String | Enhance - Timer | | @@ -2228,7 +2446,7 @@ e.g. `openhab:send actionCommand 'upd_timer["1498595904821", "on"]'` would enabl | elec-count | Number | Electricity - Count | | | clean | String | Maintenance - Clean | | | examine | String | Maintenance - Examine | | -| running-duration | Number:Duration | Maintenance - Running Duration | | +| running-duration | Number:Time | Maintenance - Running Duration | | | fan-percent | Number:Dimentionless | Fan Speed % | | | timer | String | Enhance - Timer | | @@ -2252,7 +2470,7 @@ e.g. `openhab:send actionCommand 'upd_timer["1498595904821", "on"]'` would enabl | elec-count | Number | Electricity - Count | | | clean | String | Maintenance - Clean | | | examine | String | Maintenance - Examine | | -| running-duration | Number:Duration | Maintenance - Running Duration | | +| running-duration | Number:Time | Maintenance - Running Duration | | | fan-percent | Number:Dimentionless | Fan Speed % | | | timer | String | Enhance - Timer | | @@ -2276,7 +2494,7 @@ e.g. `openhab:send actionCommand 'upd_timer["1498595904821", "on"]'` would enabl | elec-count | Number | Electricity - Count | | | clean | String | Maintenance - Clean | | | examine | String | Maintenance - Examine | | -| running-duration | Number:Duration | Maintenance - Running Duration | | +| running-duration | Number:Time | Maintenance - Running Duration | | | fan-percent | Number:Dimentionless | Fan Speed % | | | timer | String | Enhance - Timer | | @@ -2300,7 +2518,7 @@ e.g. `openhab:send actionCommand 'upd_timer["1498595904821", "on"]'` would enabl | elec-count | Number | Electricity - Count | | | clean | String | Maintenance - Clean | | | examine | String | Maintenance - Examine | | -| running-duration | Number:Duration | Maintenance - Running Duration | | +| running-duration | Number:Time | Maintenance - Running Duration | | | fan-percent | Number:Dimentionless | Fan Speed % | | | timer | String | Enhance - Timer | | @@ -2324,7 +2542,7 @@ e.g. `openhab:send actionCommand 'upd_timer["1498595904821", "on"]'` would enabl | elec-count | Number | Electricity - Count | | | clean | String | Maintenance - Clean | | | examine | String | Maintenance - Examine | | -| running-duration | Number:Duration | Maintenance - Running Duration | | +| running-duration | Number:Time | Maintenance - Running Duration | | | fan-percent | Number:Dimentionless | Fan Speed % | | | timer | String | Enhance - Timer | | @@ -2348,7 +2566,7 @@ e.g. `openhab:send actionCommand 'upd_timer["1498595904821", "on"]'` would enabl | elec-count | Number | Electricity - Count | | | clean | String | Maintenance - Clean | | | examine | String | Maintenance - Examine | | -| running-duration | Number:Duration | Maintenance - Running Duration | | +| running-duration | Number:Time | Maintenance - Running Duration | | | fan-percent | Number:Dimentionless | Fan Speed % | | | timer | String | Enhance - Timer | | @@ -2372,7 +2590,7 @@ e.g. `openhab:send actionCommand 'upd_timer["1498595904821", "on"]'` would enabl | elec-count | Number | Electricity - Count | | | clean | String | Maintenance - Clean | | | examine | String | Maintenance - Examine | | -| running-duration | Number:Duration | Maintenance - Running Duration | | +| running-duration | Number:Time | Maintenance - Running Duration | | | fan-percent | Number:Dimentionless | Fan Speed % | | | timer | String | Enhance - Timer | | @@ -2396,7 +2614,7 @@ e.g. `openhab:send actionCommand 'upd_timer["1498595904821", "on"]'` would enabl | elec-count | Number | Electricity - Count | | | clean | String | Maintenance - Clean | | | examine | String | Maintenance - Examine | | -| running-duration | Number:Duration | Maintenance - Running Duration | | +| running-duration | Number:Time | Maintenance - Running Duration | | | fan-percent | Number:Dimentionless | Fan Speed % | | | timer | String | Enhance - Timer | | @@ -2420,7 +2638,7 @@ e.g. `openhab:send actionCommand 'upd_timer["1498595904821", "on"]'` would enabl | elec-count | Number | Electricity - Count | | | clean | String | Maintenance - Clean | | | examine | String | Maintenance - Examine | | -| running-duration | Number:Duration | Maintenance - Running Duration | | +| running-duration | Number:Time | Maintenance - Running Duration | | | fan-percent | Number:Dimentionless | Fan Speed % | | | timer | String | Enhance - Timer | | @@ -2444,7 +2662,7 @@ e.g. `openhab:send actionCommand 'upd_timer["1498595904821", "on"]'` would enabl | elec-count | Number | Electricity - Count | | | clean | String | Maintenance - Clean | | | examine | String | Maintenance - Examine | | -| running-duration | Number:Duration | Maintenance - Running Duration | | +| running-duration | Number:Time | Maintenance - Running Duration | | | fan-percent | Number:Dimentionless | Fan Speed % | | | timer | String | Enhance - Timer | | @@ -2468,7 +2686,7 @@ e.g. `openhab:send actionCommand 'upd_timer["1498595904821", "on"]'` would enabl | elec-count | Number | Electricity - Count | | | clean | String | Maintenance - Clean | | | examine | String | Maintenance - Examine | | -| running-duration | Number:Duration | Maintenance - Running Duration | | +| running-duration | Number:Time | Maintenance - Running Duration | | | fan-percent | Number:Dimentionless | Fan Speed % | | | timer | String | Enhance - Timer | | @@ -2492,7 +2710,7 @@ e.g. `openhab:send actionCommand 'upd_timer["1498595904821", "on"]'` would enabl | elec-count | Number | Electricity - Count | | | clean | String | Maintenance - Clean | | | examine | String | Maintenance - Examine | | -| running-duration | Number:Duration | Maintenance - Running Duration | | +| running-duration | Number:Time | Maintenance - Running Duration | | | fan-percent | Number:Dimentionless | Fan Speed % | | | timer | String | Enhance - Timer | | @@ -2516,7 +2734,7 @@ e.g. `openhab:send actionCommand 'upd_timer["1498595904821", "on"]'` would enabl | elec-count | Number | Electricity - Count | | | clean | String | Maintenance - Clean | | | examine | String | Maintenance - Examine | | -| running-duration | Number:Duration | Maintenance - Running Duration | | +| running-duration | Number:Time | Maintenance - Running Duration | | | fan-percent | Number:Dimentionless | Fan Speed % | | | timer | String | Enhance - Timer | | @@ -2663,7 +2881,7 @@ e.g. `openhab:send actionCommand 'upd_timer["1498595904821", "on"]'` would enabl |----------------------|----------------------|------------------------------------------|------------| | power | Switch | Power | | | brightness | Dimmer | Brightness | | -| ambientBrightness | Number | Ambient Brightness | | +| ambientBrightness | Dimmer | Ambient Brightness | | | delayoff | Number:Time | Shutdown Timer | | | colorTemperature | Number | Color Temperature | | | colorMode | Number | Color Mode | | @@ -2759,7 +2977,7 @@ e.g. `openhab:send actionCommand 'upd_timer["1498595904821", "on"]'` would enabl |----------------------|----------------------|------------------------------------------|------------| | power | Switch | Power | | | brightness | Dimmer | Brightness | | -| ambientBrightness | Number | Ambient Brightness | | +| ambientBrightness | Dimmer | Ambient Brightness | | | delayoff | Number:Time | Shutdown Timer | | | colorTemperature | Number | Color Temperature | | | colorMode | Number | Color Mode | | @@ -4203,6 +4421,62 @@ e.g. `openhab:send actionCommand 'upd_timer["1498595904821", "on"]'` would enabl | temperature | Number:Temperature | Temperature | | | childlock | Switch | Child Lock | | +### Mi Air Purifier Pro H (zhimi.airpurifier.vb2) Channels + +| Channel | Type | Description | Comment | +|----------------------|----------------------|------------------------------------------|------------| +| actions | String | Actions | Value mapping ["button-toggle"="Toggle","button-toggle-mode"="Toggle Mode"] | +| fault | Number | Air Purifier - Device Fault | Value mapping ["0"="No faults","1"="m1_run","2"="m1_stuck","3"="no_sensor","4"="error_hum","5"="error_temp","6"="timer_error1","7"="timer_error2"] | +| on | Switch | Air Purifier - Power | | +| fan_level | Number | Air Purifier - Fan Level | Value mapping ["1"="Level1","2"="Level2","3"="Level3","0"="Sleep"] | +| mode | Number | Air Purifier - Mode | Value mapping ["0"="Auto","1"="Night","2"="Favourite","3"="Manual"] | +| pm2_5_density | Number | Environment - PM2 5 Density | | +| relative_humidity | Number:Dimensionless | Environment - Relative Humidity | | +| temperature | Number:Temperature | Environment - Temperature | | +| filter_life_level | Number:Dimensionless | Filter - Filter Life Level | | +| filter_used_time | Number:Time | Filter - Filter Used Time | | +| alarm | Switch | Alarm - Alarm | | +| volume | Number:Dimensionless | Alarm - Volume | | +| brightness | Number | Indicator Light - Brightness | Value mapping ["0"="brightest","1"="glimmer","2"="not bright"] | +| on1 | Switch | Indicator Light - Switch Status | | +| physical_controls_locked | Switch | Physical Control Locked - Physical Control Locked | | +| button_pressed | String | Button - Button_pressed | | +| filter_max_time | Number:Time | Filter Time - Filter Max Time | | +| filter_hour_used_debug | Number:Time | Filter Time - Filter Hour Used Debug | | +| m1_strong | Number | Motor Speed - M1 Strong | | +| m1_high | Number | Motor Speed - M1 High | | +| m1_med | Number | Motor Speed - M1 Med | | +| m1_med_l | Number | Motor Speed - M1 Med L | | +| m1_low | Number | Motor Speed - M1 Low | | +| m1_silent | Number | Motor Speed - M1 Silent | | +| m1_favorite | Number | Motor Speed - M1 Favorite | | +| motor1_speed | Number | Motor Speed - Motor1 Speed | | +| motor1_set_speed | Number | Motor Speed - Motor1 Set Speed | | +| favorite_level | Number | Motor Speed - Favorite Level | | +| use_time | Number:Time | Use Time - Use Time | | +| purify_volume | Number | Aqi - Purify Volume | | +| average_aqi | Number | Aqi - Average Aqi | | +| average_aqi_cnt | Number | Aqi - Average_aqi Read Times | | +| aqi_zone | String | Aqi - Aqi Zone | | +| sensor_state | Number | Aqi - Sensor State | Value mapping ["0"="waiting","1"="ready"] | +| aqi_goodh | Number | Aqi - Aqi Goodh | | +| aqi_runstate | Number | Aqi - Runstate | Value mapping ["0"="continue","1"="hold","2"="sleep"] | +| aqi_state | Number | Aqi - Aqi State | Value mapping ["0"="AQI_GOOD_L","1"="AQI_GOOD_H","2"="AQI_MID_L","3"="AQI_MID_H","4"="AQI_BAD_L","5"="AQI_BAD_H"] | +| rfid_tag | String | Rfid - Rfid Tag | | +| rfid_factory_id | String | Rfid - Rfid Factory Id | | +| rfid_product_id | String | Rfid - Rfid Product Id | | +| rfid_time | String | Rfid - Rfid Time | | +| rfid_serial_num | String | Rfid - Rfid Serial Num | | +| app_extra | Number | Others - App Extra | | +| main_channel | Number | Others - Main Channel | | +| slave_channel | Number | Others - Slave Channel | | +| cola | String | Others - Cola | | +| buttom_door | String | Others - Buttom Door | | +| reboot_cause | Number | Others - Reboot_cause | Value mapping ["0"="REASON_HW_BOOT","1"="REASON_USER_REBOOT","2"="REASON_UPDATE","3"="REASON_WDT"] | +| manual_level | Number | Others - Manual Level | Value mapping ["1"="level1","2"="level2","3"="level3"] | +| powertime | Number:duration | Others - Powertime | | +| country_code | Number | Others - Country Code | Value mapping ["91"="India","44"="UK","852"="Hong Kong","886"="Taiwan","82"="Korea"] | + ### Mi Standing Fan (zhimi.fan.sa1) Channels | Channel | Type | Description | Comment | @@ -4339,6 +4613,107 @@ e.g. `openhab:send actionCommand 'upd_timer["1498595904821", "on"]'` would enabl | naturalLevel | Number | Natural Level | | | move | String | Move Direction | | +### Smartmi Standing Fan 3 (zhimi.fan.za5) Channels + +| Channel | Type | Description | Comment | +|----------------------|----------------------|------------------------------------------|------------| +| on | Switch | Fan - Power | | +| fan_level | Number | Fan - Fan Level | Value mapping ["1"="Level 1","2"="Level 2","3"="Level 3","4"="Level 4"] | +| horizontal_swing | Switch | Fan - Horizontal Swing | | +| horizontal_angle | Number | Fan - Horizontal Angle | | +| mode | Number | Fan - Mode | Value mapping ["0"="Natural Wind","1"="Straight Wind"] | +| off_delay | Number | Fan - Power Off Delay | | +| anion | Switch | Fan - Anion | | +| physical_controls_locked | Switch | Physical Control Locked - Physical Control Locked | | +| brightness | Number:Dimensionless | Indicator Light - Brightness | | +| alarm | Switch | Alarm - Alarm | | +| relative_humidity | Number:Dimensionless | Environment - Relative Humidity | | +| temperature | Number:Temperature | Environment - Temperature | | +| button_press | Number | Custom Service - Button Press | Value mapping ["1"="power","2"="swing","0"="No Button Pressed"] | +| battery_state | Switch | Custom Service - Battery State | | +| speed_now | Number | Custom Service - Speed Now | | +| ac_state | Switch | Custom Service - Ac State | | +| speed_level | Number:Dimensionless | Custom Service - Speed Level | | + +### Mi Smart Space Heater S (zhimi.heater.ma2) Channels + +| Channel | Type | Description | Comment | +|----------------------|----------------------|------------------------------------------|------------| +| on | Switch | Heater - Switch Status | | +| fault | Number | Heater - Fault | Value mapping ["0"="No Error","1"="NTC Connect Error","2"="High Temperature Alarm","3"="EEPROM Error","4"="Multi Errors"] | +| target_temperature | Number:Temperature | Heater - Target Temperature | | +| countdown_time | Number:Time | Countdown - Countdown Time | | +| temperature | Number:Temperature | Environment - Temperature | | +| physical_controls_locked | Switch | Physical Control Locked - Physical Control Locked | | +| alarm | Switch | Alarm - Alarm | | +| brightness | Number:Dimensionless | Indicator Light - Brightness | Value mapping ["0"="Bright","1"="Dark","2"="Extinguished"] | +| hw_enable | Switch | Private Service - Hw Enable | | +| use_time | Number:Time | Private Service - Use Time | | + +### Mi Smart Baseboard Heater E (zhimi.heater.ma3) Channels + +| Channel | Type | Description | Comment | +|----------------------|----------------------|------------------------------------------|------------| +| actions | String | Actions | | +| on | Switch | Heater - Switch Status | | +| fault | Number | Heater - Fault | Value mapping ["0"="No Error","1"="NTC Connect Error","2"="High Temperature Alarm","3"="EEPROM Error","4"="Multi Errors"] | +| target_temperature | Number:Temperature | Heater - Target Temperature | | +| mode | Number | Heater - Mode | Value mapping ["0"="Auto","1"="LL Mode","2"="HH Mode"] | +| countdown_time | Number:Time | Countdown - Countdown Time | | +| temperature | Number:Temperature | Environment - Temperature | | +| physical_controls_locked | Switch | Physical Control Locked - Physical Control Locked | | +| alarm | Switch | Alarm - Alarm | | +| brightness | Number:Dimensionless | Indicator Light - Brightness | Value mapping ["0"="Bright","1"="Dark","2"="Extinguished"] | +| use_time | Number:Time | Private Service - Use Time | | + +### Mi Smart Space Heater S (zhimi.heater.mc2) Channels + +| Channel | Type | Description | Comment | +|----------------------|----------------------|------------------------------------------|------------| +| on | Switch | Heater - Power | | +| fault | Number | Heater - Device Fault | Value mapping ["0"="No Error","1"="NTC Connect Error","2"="High Temperature Alarm","3"="EEPROM Error","4"="Multi Errors"] | +| target_temperature | Number:Temperature | Heater - Target Temperature | | +| countdown_time | Number:Time | Countdown - Countdown Time | | +| temperature | Number:Temperature | Environment - Temperature | | +| physical_controls_locked | Switch | Physical Control Locked - Physical Control Locked | | +| alarm | Switch | Alarm - Alarm | | +| brightness | Number | Indicator Light - Brightness | Value mapping ["0"="Bright","1"="Dark","2"="Extinguished"] | +| hw_enable | Switch | Private Service - Hw Enable | | +| use_time | Number:Time | Private Service - Use Time | | +| country_code | Number | Private Service - Country Code | Value mapping ["0"="Unknown","1"="US","82"="KR","44"="EU","81"="JP","7"="RU","86"="CN","852"="HK","886"="TW","33"="FR"] | + +### Smartmi Smart Fan (zhimi.heater.na1) Channels + +| Channel | Type | Description | Comment | +|----------------------|----------------------|------------------------------------------|------------| +| on | Switch | Heater - Power | | +| fault | Number | Heater - Device Fault | Value mapping ["0"="No Error","1"="NTC Connect Error","2"="High Temperature Alarm","3"="EEPROM Error","4"="Multi Errors"] | +| heat_level | Number | Heater - Heat Level | Value mapping ["1"="High","2"="Low"] | +| mode | Number | Heater - Mode | Value mapping ["0"="Fan not swing","1"="Fan swing"] | +| alarm | Switch | Alarm - Alarm | | +| countdown_time | Number:Time | Countdown - Countdown Time | | +| brightness | Number | Indicator Light - Brightness | Value mapping ["0"="Bright","1"="Dark","2"="Extinguished"] | +| physical_controls_locked | Switch | Physical Control Locked - Physical Control Locked | | +| return_to_middle | Switch | Private Service - Return To Middle | | + +### Smartmi Smart Fan Heater (zhimi.heater.nb1) Channels + +| Channel | Type | Description | Comment | +|----------------------|----------------------|------------------------------------------|------------| +| on | Switch | Heater - Power | | +| fault | Number | Heater - Device Fault | Value mapping ["0"="No Error","1"="NTC Connect Error","2"="High Temperature Alarm","3"="EEPROM Error","4"="Multi Errors"] | +| heat_level | Number | Heater - Heat Level | Value mapping ["1"="High","2"="Low"] | +| mode | Number | Heater - Mode | Value mapping ["0"="Fan not swing","1"="Fan swing"] | +| target_temperature | Number:Temperature | Heater - Target Temperature | | +| temperature | Number:Temperature | Environment - Temperature | | +| alarm | Switch | Alarm - Alarm | | +| countdown_time | Number:Time | Countdown - Countdown Time | | +| brightness | Number | Indicator Light - Brightness | Value mapping ["0"="Bright","1"="Dark","2"="Extinguished"] | +| physical_controls_locked | Switch | Physical Control Locked - Physical Control Locked | | +| return_to_middle | Switch | Private Service - Return To Middle | | +| country_code | Number | Private Service - Country Code | Value mapping ["0"="Unknown","1"="US","82"="KR","44"="EU","81"="JP","7"="RU","86"="CN","852"="HK","886"="TW","33"="FR"] | +| hw_en | Switch | Private Service - Hw En | | + ### Smartmi Radiant Heater Smart Version (zhimi.heater.za1) Channels | Channel | Type | Description | Comment | @@ -4357,14 +4732,14 @@ e.g. `openhab:send actionCommand 'upd_timer["1498595904821", "on"]'` would enabl | Channel | Type | Description | Comment | |----------------------|----------------------|------------------------------------------|------------| -| fault | Number | Heater - Device Fault | | +| fault | Number | Heater - Device Fault | Value mapping ["0"="No Error","1"="NTC Connect Error","2"="High Temperature Alarm","3"="EEPROM Error","4"="Multi Errors"] | | on | Switch | Heater - Power | | | target-temperature | Number:Temperature | Heater - Target Temperature | | | alarm | Switch | Alarm - Alarm | | | countdown-time | Number:Time | Countdown - Countdown Time | | | relative-humidity | Number:Dimensionless | Environment - Relative Humidity | | | temperature | Number:Temperature | Environment - Temperature | | -| brightness | Dimmer | Indicator Light - Brightness | | +| brightness | Number | Indicator Light - Brightness | Value mapping ["0"="Bright","1"="Dark","2"="Extinguished"] | | physical-controls-locked | Switch | Physical Control Locked - Physical Controls Locked | | | use-time | Number:Time | Private-Service - Use Time | | @@ -4372,14 +4747,14 @@ e.g. `openhab:send actionCommand 'upd_timer["1498595904821", "on"]'` would enabl | Channel | Type | Description | Comment | |----------------------|----------------------|------------------------------------------|------------| -| fault | Number | Heater - Device Fault | | | on | Switch | Heater - Power | | +| fault | Number | Heater - Device Fault | Value mapping ["0"="No Error","1"="NTC Connect Error","2"="High Temperature Alarm","3"="EEPROM Error","4"="Multi Errors"] | | target-temperature | Number:Temperature | Heater - Target Temperature | | | alarm | Switch | Alarm - Alarm | | | countdown-time | Number:Time | Countdown - Countdown Time | | | relative-humidity | Number:Dimensionless | Environment - Relative Humidity | | | temperature | Number:Temperature | Environment - Temperature | | -| brightness | Dimmer | Indicator Light - Brightness | | +| brightness | Number | Indicator Light - Brightness | Value mapping ["0"="Bright","1"="Dark","2"="Extinguished"] | | physical-controls-locked | Switch | Physical Control Locked - Physical Controls Locked | | | use-time | Number:Time | Private-Service - Use Time | | | country-code | Number | Private-Service - Country-Code | Value mapping ["0"="Unknown","1"="US","82"="KR","44"="EU","81"="JP","7"="RU","86"="CN","852"="HK","886"="TW","33"="FR"] | @@ -4543,8 +4918,20 @@ Image map "Cleaning Map" (gVacLast) {channel="miio:vacuum:034F0E45:cleaning#map" Note: cleaning map is only available with cloud access. -Additionally depending on the capabilities of your robot vacuum other channels may be enabled at runtime +There are several advanced channels, which may be useful in rules (e.g. for individual room cleaning etc) +In case your vacuum does not support one of these commands, it will show "unsupported_method" for string channels or no value for numeric channels. + +| Type | Channel | Description | +|---------|-----------------------------------|----------------------------| +| Number | status#segment_status | Segment Status | +| Number | status#map_status | Map Box Status | +| Number | status#led_status | Led Box Status | +| String | info#carpet_mode | Carpet Mode details | +| String | info#fw_features | Firmware Features | +| String | info#room_mapping | Room Mapping details | +| String | info#multi_maps_list | Maps Listing details | +Additionally depending on the capabilities of your robot vacuum other channels may be enabled at runtime | Type | Channel | Description | |---------|-----------------------------------|----------------------------| @@ -4594,14 +4981,14 @@ note: Autogenerated example. Replace the id (plug) in the channel with your own. Group G_plug "Mi Smart Power Plug 2 (Wi-Fi and Bluetooth Gateway)" Switch on "Power" (G_plug) {channel="miio:basic:plug:on"} Number:Temperature temperature "Temperature" (G_plug) {channel="miio:basic:plug:temperature"} -Number:Duration working_time "Working Time" (G_plug) {channel="miio:basic:plug:working-time"} +Number:Time working_time "Working Time" (G_plug) {channel="miio:basic:plug:working-time"} Switch on1 "Indicator Light - Switch Status" (G_plug) {channel="miio:basic:plug:on1"} Number:Energy power_consumption "Daily Power Consumption" (G_plug) {channel="miio:basic:plug:power-consumption"} Number:Current electric_current "Power Consumption - Electric Current" (G_plug) {channel="miio:basic:plug:electric-current"} Number:ElectricPotential voltage "Power Consumption - Voltage" (G_plug) {channel="miio:basic:plug:voltage"} Number:Power electric_power "Current Power Consumption - Electric Power" (G_plug) {channel="miio:basic:plug:electric-power"} -Number:Duration on_duration "Imilab Timer - On Duration" (G_plug) {channel="miio:basic:plug:on-duration"} -Number:Duration off_duration "Imilab Timer - Off Duration" (G_plug) {channel="miio:basic:plug:off-duration"} +Number:Time on_duration "Imilab Timer - On Duration" (G_plug) {channel="miio:basic:plug:on-duration"} +Number:Time off_duration "Imilab Timer - Off Duration" (G_plug) {channel="miio:basic:plug:off-duration"} Number:Time countdown "Imilab Timer - Countdown" (G_plug) {channel="miio:basic:plug:countdown"} Switch task_switch "Imilab Timer - Task Switch" (G_plug) {channel="miio:basic:plug:task-switch"} Switch countdown_info "Imilab Timer - Countdown Info" (G_plug) {channel="miio:basic:plug:countdown-info"} @@ -4710,6 +5097,52 @@ String SerialNumber "Device Information-Device Serial Number" (G_plug) {channel= Switch On "Switch-Switch Status" (G_plug) {channel="miio:basic:plug:On"} ``` +### Mi Smart Antibacterial Humidifier (deerma.humidifier.jsq) item file lines + +note: Autogenerated example. Replace the id (humidifier) in the channel with your own. Replace `basic` with `generic` in the thing UID depending on how your thing was discovered. + +``` +Group G_humidifier "Mi Smart Antibacterial Humidifier" +Switch power "Power" (G_humidifier) {channel="miio:basic:humidifier:power"} +Number mode "Mode" (G_humidifier) {channel="miio:basic:humidifier:mode"} +Number:Dimensionless humidity "Humidity" (G_humidifier) {channel="miio:basic:humidifier:humidity"} +Number:Dimensionless humidity_set "Humidity Setting" (G_humidifier) {channel="miio:basic:humidifier:humidity_set"} +Switch led "LED indicator Light" (G_humidifier) {channel="miio:basic:humidifier:led"} +Switch sound "Notification Sounds" (G_humidifier) {channel="miio:basic:humidifier:sound"} +Number watertankstatus "Watertank Status" (G_humidifier) {channel="miio:basic:humidifier:watertankstatus"} +``` + +### Mi S Smart humidifer (deerma.humidifier.jsq1) item file lines + +note: Autogenerated example. Replace the id (humidifier) in the channel with your own. Replace `basic` with `generic` in the thing UID depending on how your thing was discovered. + +``` +Group G_humidifier "Mi S Smart humidifer " +Switch power "Power" (G_humidifier) {channel="miio:basic:humidifier:power"} +Number mode "Mode" (G_humidifier) {channel="miio:basic:humidifier:mode"} +Number:Dimensionless humidity "Humidity" (G_humidifier) {channel="miio:basic:humidifier:humidity"} +Number:Dimensionless humidity_set "Humidity Setting" (G_humidifier) {channel="miio:basic:humidifier:humidity_set"} +Switch led "LED indicator Light" (G_humidifier) {channel="miio:basic:humidifier:led"} +Switch sound "Notification Sounds" (G_humidifier) {channel="miio:basic:humidifier:sound"} +Number watertankstatus "Watertank Status" (G_humidifier) {channel="miio:basic:humidifier:watertankstatus"} +Switch wet_and_protect "Wet and Protect" (G_humidifier) {channel="miio:basic:humidifier:wet_and_protect"} +``` + +### Mi Smart Humidifier (deerma.humidifier.mjjsq) item file lines + +note: Autogenerated example. Replace the id (humidifier) in the channel with your own. Replace `basic` with `generic` in the thing UID depending on how your thing was discovered. + +``` +Group G_humidifier "Mi Smart Humidifier" +Switch power "Power" (G_humidifier) {channel="miio:basic:humidifier:power"} +Number mode "Mode" (G_humidifier) {channel="miio:basic:humidifier:mode"} +Number:Dimensionless humidity "Humidity" (G_humidifier) {channel="miio:basic:humidifier:humidity"} +Number:Dimensionless humidity_set "Humidity Setting" (G_humidifier) {channel="miio:basic:humidifier:humidity_set"} +Switch led "LED indicator Light" (G_humidifier) {channel="miio:basic:humidifier:led"} +Switch sound "Notification Sounds" (G_humidifier) {channel="miio:basic:humidifier:sound"} +Number watertankstatus "Watertank Status" (G_humidifier) {channel="miio:basic:humidifier:watertankstatus"} +``` + ### Mi Fresh Air Ventilator A1-150 (dmaker.airfresh.a1) item file lines note: Autogenerated example. Replace the id (airfresh) in the channel with your own. Replace `basic` with `generic` in the thing UID depending on how your thing was discovered. @@ -4987,6 +5420,186 @@ Number total_clean_times "Clean Logs - Total Clean Times" (G_vacuum) {channel="m Number total_clean_area "Clean Logs - Total Clean Area" (G_vacuum) {channel="miio:basic:vacuum:total-clean-area"} ``` +### Trouver Robot LDS Vacuum-Mop Finder (dreame.vacuum.p2036) item file lines + +note: Autogenerated example. Replace the id (vacuum) in the channel with your own. Replace `basic` with `generic` in the thing UID depending on how your thing was discovered. + +``` +Group G_vacuum "Trouver Robot LDS Vacuum-Mop Finder" +String vacuumaction "Vacuum Action" (G_vacuum) {channel="miio:basic:vacuum:vacuumaction"} +Number status "Robot Cleaner - Status" (G_vacuum) {channel="miio:basic:vacuum:status"} +Number fault "Robot Cleaner - Device Fault" (G_vacuum) {channel="miio:basic:vacuum:fault"} +Number:Dimensionless battery_level "Battery - Battery Level" (G_vacuum) {channel="miio:basic:vacuum:battery-level"} +Number charging_state "Battery - Charging State" (G_vacuum) {channel="miio:basic:vacuum:charging-state"} +String resetConsumable "Consumables Reset" (G_vacuum) {channel="miio:basic:vacuum:resetConsumable"} +Number:Time brush_left_time "Main Cleaning Brush - Brush Left Time" (G_vacuum) {channel="miio:basic:vacuum:brush-left-time"} +Number:Dimensionless brush_life_level "Main Cleaning Brush - Brush Life Level" (G_vacuum) {channel="miio:basic:vacuum:brush-life-level"} +Number:Time brush_left_time1 "Side Cleaning Brush - Brush Left Time" (G_vacuum) {channel="miio:basic:vacuum:brush-left-time1"} +Number:Dimensionless brush_life_level1 "Side Cleaning Brush - Brush Life Level" (G_vacuum) {channel="miio:basic:vacuum:brush-life-level1"} +Number:Dimensionless filter_life_level "Filter - Filter Life Level" (G_vacuum) {channel="miio:basic:vacuum:filter-life-level"} +Number:Time filter_left_time "Filter - Filter Left Time" (G_vacuum) {channel="miio:basic:vacuum:filter-left-time"} +Number work_mode "Vacuum Extend - Work Mode" (G_vacuum) {channel="miio:basic:vacuum:work-mode"} +Number:Time cleaning_time "Vacuum Extend - Cleaning Time" (G_vacuum) {channel="miio:basic:vacuum:cleaning-time"} +Number:Area cleaning_area "Vacuum Extend - Cleaning Area" (G_vacuum) {channel="miio:basic:vacuum:cleaning-area"} +Number cleaning_mode "Vacuum Extend - Cleaning Mode" (G_vacuum) {channel="miio:basic:vacuum:cleaning-mode"} +Number mop_mode "Vacuum Extend - Mop Mode" (G_vacuum) {channel="miio:basic:vacuum:mop-mode"} +Number waterbox_status "Vacuum Extend - Waterbox Status" (G_vacuum) {channel="miio:basic:vacuum:waterbox-status"} +Number task_status "Vacuum Extend - Task Status" (G_vacuum) {channel="miio:basic:vacuum:task-status"} +Number break_point_restart "Vacuum Extend - Break Point Restart" (G_vacuum) {channel="miio:basic:vacuum:break-point-restart"} +Number carpet_press "Vacuum Extend - Carpet Press" (G_vacuum) {channel="miio:basic:vacuum:carpet-press"} +String serial_number1 "Vacuum Extend - Serial Number" (G_vacuum) {channel="miio:basic:vacuum:serial-number1"} +Number:Time clean_rags_tip "Vacuum Extend - Clean Rags Tip" (G_vacuum) {channel="miio:basic:vacuum:clean-rags-tip"} +Number:Time keep_sweeper_time "Vacuum Extend - Keep Sweeper Time" (G_vacuum) {channel="miio:basic:vacuum:keep-sweeper-time"} +String faults "Vacuum Extend - Faults" (G_vacuum) {channel="miio:basic:vacuum:faults"} +Switch enable "Do Not Disturb - Enable" (G_vacuum) {channel="miio:basic:vacuum:enable"} +String start_time "Do Not Disturb - Start Time" (G_vacuum) {channel="miio:basic:vacuum:start-time"} +String end_time "Do Not Disturb - End Time" (G_vacuum) {channel="miio:basic:vacuum:end-time"} +Number:Dimensionless volume "Audio - Volume" (G_vacuum) {channel="miio:basic:vacuum:volume"} +String voice_packet_id "Audio - Voice Packet Id" (G_vacuum) {channel="miio:basic:vacuum:voice-packet-id"} +String voice_change_state "Audio - Voice Change State" (G_vacuum) {channel="miio:basic:vacuum:voice-change-state"} +String time_zone "Time - Time Zone" (G_vacuum) {channel="miio:basic:vacuum:time-zone"} +String timer_clean "Time - Timer Clean" (G_vacuum) {channel="miio:basic:vacuum:timer-clean"} +Number first_clean_time "Clean Logs - First Clean Time" (G_vacuum) {channel="miio:basic:vacuum:first-clean-time"} +Number:Time total_clean_time "Clean Logs - Total Clean Time" (G_vacuum) {channel="miio:basic:vacuum:total-clean-time"} +Number total_clean_times "Clean Logs - Total Clean Times" (G_vacuum) {channel="miio:basic:vacuum:total-clean-times"} +Number total_clean_area "Clean Logs - Total Clean Area" (G_vacuum) {channel="miio:basic:vacuum:total-clean-area"} +``` + +### Mi Robot Vacuum-Mop 2 Pro+ (dreame.vacuum.p2041o) item file lines + +note: Autogenerated example. Replace the id (vacuum) in the channel with your own. Replace `basic` with `generic` in the thing UID depending on how your thing was discovered. + +``` +Group G_vacuum "Mi Robot Vacuum-Mop 2 Pro+" +String actions "Actions" (G_vacuum) {channel="miio:basic:vacuum:actions"} +Number status "Robot Cleaner - Status" (G_vacuum) {channel="miio:basic:vacuum:status"} +Number fault "Robot Cleaner - Device Fault" (G_vacuum) {channel="miio:basic:vacuum:fault"} +Number mode "Robot Cleaner - Mode" (G_vacuum) {channel="miio:basic:vacuum:mode"} +Number:Dimensionless battery_level "Battery - Battery Level" (G_vacuum) {channel="miio:basic:vacuum:battery_level"} +Number charging_state "Battery - Charging State" (G_vacuum) {channel="miio:basic:vacuum:charging_state"} +Number:Time brush_left_time "Main Cleaning Brush - Brush Left Time" (G_vacuum) {channel="miio:basic:vacuum:brush_left_time"} +Number:Dimensionless brush_life_level "Main Cleaning Brush - Brush Life Level" (G_vacuum) {channel="miio:basic:vacuum:brush_life_level"} +Number:Time brush_left_time1 "Side Cleaning Brush - Brush Left Time" (G_vacuum) {channel="miio:basic:vacuum:brush_left_time1"} +Number:Dimensionless brush_life_level1 "Side Cleaning Brush - Brush Life Level" (G_vacuum) {channel="miio:basic:vacuum:brush_life_level1"} +Number:Dimensionless filter_life_level "Filter - Filter Life Level" (G_vacuum) {channel="miio:basic:vacuum:filter_life_level"} +Number:Time filter_left_time "Filter - Filter Left Time" (G_vacuum) {channel="miio:basic:vacuum:filter_left_time"} +Number work_mode "Vacuum Extend - Work Mode" (G_vacuum) {channel="miio:basic:vacuum:work_mode"} +Number:Time cleaning_time "Vacuum Extend - Cleaning Time" (G_vacuum) {channel="miio:basic:vacuum:cleaning_time"} +Number:Area cleaning_area "Vacuum Extend - Cleaning Area" (G_vacuum) {channel="miio:basic:vacuum:cleaning-area"} +Number cleaning_mode "Vacuum Extend - Cleaning Mode" (G_vacuum) {channel="miio:basic:vacuum:cleaning_mode"} +Number mop_mode "Vacuum Extend - Mop Mode" (G_vacuum) {channel="miio:basic:vacuum:mop_mode"} +Number waterbox_status "Vacuum Extend - Waterbox Status" (G_vacuum) {channel="miio:basic:vacuum:waterbox_status"} +Number task_status "Vacuum Extend - Task Status" (G_vacuum) {channel="miio:basic:vacuum:task_status"} +Number break_point_restart "Vacuum Extend - Break Point Restart" (G_vacuum) {channel="miio:basic:vacuum:break_point_restart"} +Number carpet_press "Vacuum Extend - Carpet Press" (G_vacuum) {channel="miio:basic:vacuum:carpet_press"} +String serial_number "Vacuum Extend - Serial Number" (G_vacuum) {channel="miio:basic:vacuum:serial_number"} +Number:Time keep_sweeper_time "Vacuum Extend - Keep Sweeper Time" (G_vacuum) {channel="miio:basic:vacuum:keep_sweeper_time"} +String faults "Vacuum Extend - Faults" (G_vacuum) {channel="miio:basic:vacuum:faults"} +Switch enable "Do Not Disturb - Enable" (G_vacuum) {channel="miio:basic:vacuum:enable"} +String start_time "Do Not Disturb - Start Time" (G_vacuum) {channel="miio:basic:vacuum:start_time"} +String end_time "Do Not Disturb - End Time" (G_vacuum) {channel="miio:basic:vacuum:end_time"} +Number:Dimensionless volume "Audio - Volume" (G_vacuum) {channel="miio:basic:vacuum:volume"} +String voice_packet_id "Audio - Voice Packet Id" (G_vacuum) {channel="miio:basic:vacuum:voice_packet_id"} +String voice_change_state "Audio - Voice Change State" (G_vacuum) {channel="miio:basic:vacuum:voice_change_state"} +String time_zone "Time - Time Zone" (G_vacuum) {channel="miio:basic:vacuum:time_zone"} +String timer_clean "Time - Timer Clean" (G_vacuum) {channel="miio:basic:vacuum:timer_clean"} +Number first_clean_time "Clean Logs - First Clean Time" (G_vacuum) {channel="miio:basic:vacuum:first_clean_time"} +Number:Time total_clean_time "Clean Logs - Total Clean Time" (G_vacuum) {channel="miio:basic:vacuum:total_clean_time"} +Number total_clean_times "Clean Logs - Total Clean Times" (G_vacuum) {channel="miio:basic:vacuum:total_clean_times"} +Number total_clean_area "Clean Logs - Total Clean Area" (G_vacuum) {channel="miio:basic:vacuum:total_clean_area"} +Number save_map_status "Vslam Extend - Save Map Status" (G_vacuum) {channel="miio:basic:vacuum:save_map_status"} +``` + +### MOVA Z500 Robot Vacuum and Mop Cleaner (dreame.vacuum.p2156o) item file lines + +note: Autogenerated example. Replace the id (vacuum) in the channel with your own. Replace `basic` with `generic` in the thing UID depending on how your thing was discovered. + +``` +Group G_vacuum "MOVA Z500 Robot Vacuum and Mop Cleaner" +String actions "Actions" (G_vacuum) {channel="miio:basic:vacuum:actions"} +Number status "Robot Cleaner - Status" (G_vacuum) {channel="miio:basic:vacuum:status"} +Number fault "Robot Cleaner - Device Fault" (G_vacuum) {channel="miio:basic:vacuum:fault"} +Number mode "Robot Cleaner - Mode" (G_vacuum) {channel="miio:basic:vacuum:mode"} +Number:Dimensionless battery_level "Battery - Battery Level" (G_vacuum) {channel="miio:basic:vacuum:battery_level"} +Number charging_state "Battery - Charging State" (G_vacuum) {channel="miio:basic:vacuum:charging_state"} +Number:Time brush_left_time "Main Cleaning Brush - Brush Left Time" (G_vacuum) {channel="miio:basic:vacuum:brush_left_time"} +Number:Dimensionless brush_life_level "Main Cleaning Brush - Brush Life Level" (G_vacuum) {channel="miio:basic:vacuum:brush_life_level"} +Number:Time brush_left_time1 "Side Cleaning Brush - Brush Left Time" (G_vacuum) {channel="miio:basic:vacuum:brush_left_time1"} +Number:Dimensionless brush_life_level1 "Side Cleaning Brush - Brush Life Level" (G_vacuum) {channel="miio:basic:vacuum:brush_life_level1"} +Number:Dimensionless filter_life_level "Filter - Filter Life Level" (G_vacuum) {channel="miio:basic:vacuum:filter_life_level"} +Number:Time filter_left_time "Filter - Filter Left Time" (G_vacuum) {channel="miio:basic:vacuum:filter_left_time"} +Number work_mode "Vacuum Extend - Work Mode" (G_vacuum) {channel="miio:basic:vacuum:work_mode"} +Number:Time cleaning_time "Vacuum Extend - Cleaning Time" (G_vacuum) {channel="miio:basic:vacuum:cleaning_time"} +Number:Area cleaning_area "Vacuum Extend - Cleaning Area" (G_vacuum) {channel="miio:basic:vacuum:cleaning-area"} +Number cleaning_mode "Vacuum Extend - Cleaning Mode" (G_vacuum) {channel="miio:basic:vacuum:cleaning_mode"} +Number mop_mode "Vacuum Extend - Mop Mode" (G_vacuum) {channel="miio:basic:vacuum:mop_mode"} +Number waterbox_status "Vacuum Extend - Waterbox Status" (G_vacuum) {channel="miio:basic:vacuum:waterbox_status"} +Number task_status "Vacuum Extend - Task Status" (G_vacuum) {channel="miio:basic:vacuum:task_status"} +Number break_point_restart "Vacuum Extend - Break Point Restart" (G_vacuum) {channel="miio:basic:vacuum:break_point_restart"} +Number carpet_press "Vacuum Extend - Carpet Press" (G_vacuum) {channel="miio:basic:vacuum:carpet_press"} +String serial_number "Vacuum Extend - Serial Number" (G_vacuum) {channel="miio:basic:vacuum:serial_number"} +Number:Time keep_sweeper_time "Vacuum Extend - Keep Sweeper Time" (G_vacuum) {channel="miio:basic:vacuum:keep_sweeper_time"} +String faults "Vacuum Extend - Faults" (G_vacuum) {channel="miio:basic:vacuum:faults"} +Switch enable "Do Not Disturb - Enable" (G_vacuum) {channel="miio:basic:vacuum:enable"} +String start_time "Do Not Disturb - Start Time" (G_vacuum) {channel="miio:basic:vacuum:start_time"} +String end_time "Do Not Disturb - End Time" (G_vacuum) {channel="miio:basic:vacuum:end_time"} +Number:Dimensionless volume "Audio - Volume" (G_vacuum) {channel="miio:basic:vacuum:volume"} +String voice_packet_id "Audio - Voice Packet Id" (G_vacuum) {channel="miio:basic:vacuum:voice_packet_id"} +String voice_change_state "Audio - Voice Change State" (G_vacuum) {channel="miio:basic:vacuum:voice_change_state"} +String time_zone "Time - Time Zone" (G_vacuum) {channel="miio:basic:vacuum:time_zone"} +String timer_clean "Time - Timer Clean" (G_vacuum) {channel="miio:basic:vacuum:timer_clean"} +Number first_clean_time "Clean Logs - First Clean Time" (G_vacuum) {channel="miio:basic:vacuum:first_clean_time"} +Number:Time total_clean_time "Clean Logs - Total Clean Time" (G_vacuum) {channel="miio:basic:vacuum:total_clean_time"} +Number total_clean_times "Clean Logs - Total Clean Times" (G_vacuum) {channel="miio:basic:vacuum:total_clean_times"} +Number total_clean_area "Clean Logs - Total Clean Area" (G_vacuum) {channel="miio:basic:vacuum:total_clean_area"} +Number save_map_status "Vslam Extend - Save Map Status" (G_vacuum) {channel="miio:basic:vacuum:save_map_status"} +``` + +### MOVA L600 Robot Vacuum and Mop Cleaner (dreame.vacuum.p2157) item file lines + +note: Autogenerated example. Replace the id (vacuum) in the channel with your own. Replace `basic` with `generic` in the thing UID depending on how your thing was discovered. + +``` +Group G_vacuum "MOVA L600 Robot Vacuum and Mop Cleaner" +String vacuumaction "Vacuum Action" (G_vacuum) {channel="miio:basic:vacuum:vacuumaction"} +Number status "Robot Cleaner - Status" (G_vacuum) {channel="miio:basic:vacuum:status"} +Number fault "Robot Cleaner - Device Fault" (G_vacuum) {channel="miio:basic:vacuum:fault"} +Number:Dimensionless battery_level "Battery - Battery Level" (G_vacuum) {channel="miio:basic:vacuum:battery-level"} +Number charging_state "Battery - Charging State" (G_vacuum) {channel="miio:basic:vacuum:charging-state"} +String resetConsumable "Consumables Reset" (G_vacuum) {channel="miio:basic:vacuum:resetConsumable"} +Number:Time brush_left_time "Main Cleaning Brush - Brush Left Time" (G_vacuum) {channel="miio:basic:vacuum:brush-left-time"} +Number:Dimensionless brush_life_level "Main Cleaning Brush - Brush Life Level" (G_vacuum) {channel="miio:basic:vacuum:brush-life-level"} +Number:Time brush_left_time1 "Side Cleaning Brush - Brush Left Time" (G_vacuum) {channel="miio:basic:vacuum:brush-left-time1"} +Number:Dimensionless brush_life_level1 "Side Cleaning Brush - Brush Life Level" (G_vacuum) {channel="miio:basic:vacuum:brush-life-level1"} +Number:Dimensionless filter_life_level "Filter - Filter Life Level" (G_vacuum) {channel="miio:basic:vacuum:filter-life-level"} +Number:Time filter_left_time "Filter - Filter Left Time" (G_vacuum) {channel="miio:basic:vacuum:filter-left-time"} +Number work_mode "Vacuum Extend - Work Mode" (G_vacuum) {channel="miio:basic:vacuum:work-mode"} +Number:Time cleaning_time "Vacuum Extend - Cleaning Time" (G_vacuum) {channel="miio:basic:vacuum:cleaning-time"} +Number:Area cleaning_area "Vacuum Extend - Cleaning Area" (G_vacuum) {channel="miio:basic:vacuum:cleaning-area"} +Number cleaning_mode "Vacuum Extend - Cleaning Mode" (G_vacuum) {channel="miio:basic:vacuum:cleaning-mode"} +Number mop_mode "Vacuum Extend - Mop Mode" (G_vacuum) {channel="miio:basic:vacuum:mop-mode"} +Number waterbox_status "Vacuum Extend - Waterbox Status" (G_vacuum) {channel="miio:basic:vacuum:waterbox-status"} +Number task_status "Vacuum Extend - Task Status" (G_vacuum) {channel="miio:basic:vacuum:task-status"} +Number break_point_restart "Vacuum Extend - Break Point Restart" (G_vacuum) {channel="miio:basic:vacuum:break-point-restart"} +Number carpet_press "Vacuum Extend - Carpet Press" (G_vacuum) {channel="miio:basic:vacuum:carpet-press"} +String serial_number1 "Vacuum Extend - Serial Number" (G_vacuum) {channel="miio:basic:vacuum:serial-number1"} +Number:Time clean_rags_tip "Vacuum Extend - Clean Rags Tip" (G_vacuum) {channel="miio:basic:vacuum:clean-rags-tip"} +Number:Time keep_sweeper_time "Vacuum Extend - Keep Sweeper Time" (G_vacuum) {channel="miio:basic:vacuum:keep-sweeper-time"} +String faults "Vacuum Extend - Faults" (G_vacuum) {channel="miio:basic:vacuum:faults"} +Switch enable "Do Not Disturb - Enable" (G_vacuum) {channel="miio:basic:vacuum:enable"} +String start_time "Do Not Disturb - Start Time" (G_vacuum) {channel="miio:basic:vacuum:start-time"} +String end_time "Do Not Disturb - End Time" (G_vacuum) {channel="miio:basic:vacuum:end-time"} +Number:Dimensionless volume "Audio - Volume" (G_vacuum) {channel="miio:basic:vacuum:volume"} +String voice_packet_id "Audio - Voice Packet Id" (G_vacuum) {channel="miio:basic:vacuum:voice-packet-id"} +String voice_change_state "Audio - Voice Change State" (G_vacuum) {channel="miio:basic:vacuum:voice-change-state"} +String time_zone "Time - Time Zone" (G_vacuum) {channel="miio:basic:vacuum:time-zone"} +String timer_clean "Time - Timer Clean" (G_vacuum) {channel="miio:basic:vacuum:timer-clean"} +Number first_clean_time "Clean Logs - First Clean Time" (G_vacuum) {channel="miio:basic:vacuum:first-clean-time"} +Number:Time total_clean_time "Clean Logs - Total Clean Time" (G_vacuum) {channel="miio:basic:vacuum:total-clean-time"} +Number total_clean_times "Clean Logs - Total Clean Times" (G_vacuum) {channel="miio:basic:vacuum:total-clean-times"} +Number total_clean_area "Clean Logs - Total Clean Area" (G_vacuum) {channel="miio:basic:vacuum:total-clean-area"} +``` + ### HUIZUO ARIES For Bedroom (huayi.light.ari013) item file lines note: Autogenerated example. Replace the id (light) in the channel with your own. Replace `basic` with `generic` in the thing UID depending on how your thing was discovered. @@ -6456,7 +7069,7 @@ Number:Energy electricity "Power consumption accumulation in kWh" (G_airconditio Number elec_count "Electricity - Count" (G_aircondition) {channel="miio:basic:aircondition:elec-count"} String clean "Maintenance - Clean" (G_aircondition) {channel="miio:basic:aircondition:clean"} String examine "Maintenance - Examine" (G_aircondition) {channel="miio:basic:aircondition:examine"} -Number:Duration running_duration "Maintenance - Running Duration" (G_aircondition) {channel="miio:basic:aircondition:running-duration"} +Number:Time running_duration "Maintenance - Running Duration" (G_aircondition) {channel="miio:basic:aircondition:running-duration"} Number:Dimentionless fan_percent "Fan Speed %" (G_aircondition) {channel="miio:basic:aircondition:fan-percent"} String timer "Enhance - Timer" (G_aircondition) {channel="miio:basic:aircondition:timer"} ``` @@ -6483,7 +7096,7 @@ Number:Energy electricity "Power consumption accumulation in kWh" (G_airconditio Number elec_count "Electricity - Count" (G_aircondition) {channel="miio:basic:aircondition:elec-count"} String clean "Maintenance - Clean" (G_aircondition) {channel="miio:basic:aircondition:clean"} String examine "Maintenance - Examine" (G_aircondition) {channel="miio:basic:aircondition:examine"} -Number:Duration running_duration "Maintenance - Running Duration" (G_aircondition) {channel="miio:basic:aircondition:running-duration"} +Number:Time running_duration "Maintenance - Running Duration" (G_aircondition) {channel="miio:basic:aircondition:running-duration"} Number:Dimentionless fan_percent "Fan Speed %" (G_aircondition) {channel="miio:basic:aircondition:fan-percent"} String timer "Enhance - Timer" (G_aircondition) {channel="miio:basic:aircondition:timer"} ``` @@ -6510,7 +7123,7 @@ Number:Energy electricity "Power consumption accumulation in kWh" (G_airconditio Number elec_count "Electricity - Count" (G_aircondition) {channel="miio:basic:aircondition:elec-count"} String clean "Maintenance - Clean" (G_aircondition) {channel="miio:basic:aircondition:clean"} String examine "Maintenance - Examine" (G_aircondition) {channel="miio:basic:aircondition:examine"} -Number:Duration running_duration "Maintenance - Running Duration" (G_aircondition) {channel="miio:basic:aircondition:running-duration"} +Number:Time running_duration "Maintenance - Running Duration" (G_aircondition) {channel="miio:basic:aircondition:running-duration"} Number:Dimentionless fan_percent "Fan Speed %" (G_aircondition) {channel="miio:basic:aircondition:fan-percent"} String timer "Enhance - Timer" (G_aircondition) {channel="miio:basic:aircondition:timer"} ``` @@ -6537,7 +7150,7 @@ Number:Energy electricity "Power consumption accumulation in kWh" (G_airconditio Number elec_count "Electricity - Count" (G_aircondition) {channel="miio:basic:aircondition:elec-count"} String clean "Maintenance - Clean" (G_aircondition) {channel="miio:basic:aircondition:clean"} String examine "Maintenance - Examine" (G_aircondition) {channel="miio:basic:aircondition:examine"} -Number:Duration running_duration "Maintenance - Running Duration" (G_aircondition) {channel="miio:basic:aircondition:running-duration"} +Number:Time running_duration "Maintenance - Running Duration" (G_aircondition) {channel="miio:basic:aircondition:running-duration"} Number:Dimentionless fan_percent "Fan Speed %" (G_aircondition) {channel="miio:basic:aircondition:fan-percent"} String timer "Enhance - Timer" (G_aircondition) {channel="miio:basic:aircondition:timer"} ``` @@ -6564,7 +7177,7 @@ Number:Energy electricity "Power consumption accumulation in kWh" (G_airconditio Number elec_count "Electricity - Count" (G_aircondition) {channel="miio:basic:aircondition:elec-count"} String clean "Maintenance - Clean" (G_aircondition) {channel="miio:basic:aircondition:clean"} String examine "Maintenance - Examine" (G_aircondition) {channel="miio:basic:aircondition:examine"} -Number:Duration running_duration "Maintenance - Running Duration" (G_aircondition) {channel="miio:basic:aircondition:running-duration"} +Number:Time running_duration "Maintenance - Running Duration" (G_aircondition) {channel="miio:basic:aircondition:running-duration"} Number:Dimentionless fan_percent "Fan Speed %" (G_aircondition) {channel="miio:basic:aircondition:fan-percent"} String timer "Enhance - Timer" (G_aircondition) {channel="miio:basic:aircondition:timer"} ``` @@ -6591,7 +7204,7 @@ Number:Energy electricity "Power consumption accumulation in kWh" (G_airconditio Number elec_count "Electricity - Count" (G_aircondition) {channel="miio:basic:aircondition:elec-count"} String clean "Maintenance - Clean" (G_aircondition) {channel="miio:basic:aircondition:clean"} String examine "Maintenance - Examine" (G_aircondition) {channel="miio:basic:aircondition:examine"} -Number:Duration running_duration "Maintenance - Running Duration" (G_aircondition) {channel="miio:basic:aircondition:running-duration"} +Number:Time running_duration "Maintenance - Running Duration" (G_aircondition) {channel="miio:basic:aircondition:running-duration"} Number:Dimentionless fan_percent "Fan Speed %" (G_aircondition) {channel="miio:basic:aircondition:fan-percent"} String timer "Enhance - Timer" (G_aircondition) {channel="miio:basic:aircondition:timer"} ``` @@ -6618,7 +7231,7 @@ Number:Energy electricity "Power consumption accumulation in kWh" (G_airconditio Number elec_count "Electricity - Count" (G_aircondition) {channel="miio:basic:aircondition:elec-count"} String clean "Maintenance - Clean" (G_aircondition) {channel="miio:basic:aircondition:clean"} String examine "Maintenance - Examine" (G_aircondition) {channel="miio:basic:aircondition:examine"} -Number:Duration running_duration "Maintenance - Running Duration" (G_aircondition) {channel="miio:basic:aircondition:running-duration"} +Number:Time running_duration "Maintenance - Running Duration" (G_aircondition) {channel="miio:basic:aircondition:running-duration"} Number:Dimentionless fan_percent "Fan Speed %" (G_aircondition) {channel="miio:basic:aircondition:fan-percent"} String timer "Enhance - Timer" (G_aircondition) {channel="miio:basic:aircondition:timer"} ``` @@ -6645,7 +7258,7 @@ Number:Energy electricity "Power consumption accumulation in kWh" (G_airconditio Number elec_count "Electricity - Count" (G_aircondition) {channel="miio:basic:aircondition:elec-count"} String clean "Maintenance - Clean" (G_aircondition) {channel="miio:basic:aircondition:clean"} String examine "Maintenance - Examine" (G_aircondition) {channel="miio:basic:aircondition:examine"} -Number:Duration running_duration "Maintenance - Running Duration" (G_aircondition) {channel="miio:basic:aircondition:running-duration"} +Number:Time running_duration "Maintenance - Running Duration" (G_aircondition) {channel="miio:basic:aircondition:running-duration"} Number:Dimentionless fan_percent "Fan Speed %" (G_aircondition) {channel="miio:basic:aircondition:fan-percent"} String timer "Enhance - Timer" (G_aircondition) {channel="miio:basic:aircondition:timer"} ``` @@ -6672,7 +7285,7 @@ Number:Energy electricity "Power consumption accumulation in kWh" (G_airconditio Number elec_count "Electricity - Count" (G_aircondition) {channel="miio:basic:aircondition:elec-count"} String clean "Maintenance - Clean" (G_aircondition) {channel="miio:basic:aircondition:clean"} String examine "Maintenance - Examine" (G_aircondition) {channel="miio:basic:aircondition:examine"} -Number:Duration running_duration "Maintenance - Running Duration" (G_aircondition) {channel="miio:basic:aircondition:running-duration"} +Number:Time running_duration "Maintenance - Running Duration" (G_aircondition) {channel="miio:basic:aircondition:running-duration"} Number:Dimentionless fan_percent "Fan Speed %" (G_aircondition) {channel="miio:basic:aircondition:fan-percent"} String timer "Enhance - Timer" (G_aircondition) {channel="miio:basic:aircondition:timer"} ``` @@ -6699,7 +7312,7 @@ Number:Energy electricity "Power consumption accumulation in kWh" (G_airconditio Number elec_count "Electricity - Count" (G_aircondition) {channel="miio:basic:aircondition:elec-count"} String clean "Maintenance - Clean" (G_aircondition) {channel="miio:basic:aircondition:clean"} String examine "Maintenance - Examine" (G_aircondition) {channel="miio:basic:aircondition:examine"} -Number:Duration running_duration "Maintenance - Running Duration" (G_aircondition) {channel="miio:basic:aircondition:running-duration"} +Number:Time running_duration "Maintenance - Running Duration" (G_aircondition) {channel="miio:basic:aircondition:running-duration"} Number:Dimentionless fan_percent "Fan Speed %" (G_aircondition) {channel="miio:basic:aircondition:fan-percent"} String timer "Enhance - Timer" (G_aircondition) {channel="miio:basic:aircondition:timer"} ``` @@ -6726,7 +7339,7 @@ Number:Energy electricity "Power consumption accumulation in kWh" (G_airconditio Number elec_count "Electricity - Count" (G_aircondition) {channel="miio:basic:aircondition:elec-count"} String clean "Maintenance - Clean" (G_aircondition) {channel="miio:basic:aircondition:clean"} String examine "Maintenance - Examine" (G_aircondition) {channel="miio:basic:aircondition:examine"} -Number:Duration running_duration "Maintenance - Running Duration" (G_aircondition) {channel="miio:basic:aircondition:running-duration"} +Number:Time running_duration "Maintenance - Running Duration" (G_aircondition) {channel="miio:basic:aircondition:running-duration"} Number:Dimentionless fan_percent "Fan Speed %" (G_aircondition) {channel="miio:basic:aircondition:fan-percent"} String timer "Enhance - Timer" (G_aircondition) {channel="miio:basic:aircondition:timer"} ``` @@ -6753,7 +7366,7 @@ Number:Energy electricity "Power consumption accumulation in kWh" (G_airconditio Number elec_count "Electricity - Count" (G_aircondition) {channel="miio:basic:aircondition:elec-count"} String clean "Maintenance - Clean" (G_aircondition) {channel="miio:basic:aircondition:clean"} String examine "Maintenance - Examine" (G_aircondition) {channel="miio:basic:aircondition:examine"} -Number:Duration running_duration "Maintenance - Running Duration" (G_aircondition) {channel="miio:basic:aircondition:running-duration"} +Number:Time running_duration "Maintenance - Running Duration" (G_aircondition) {channel="miio:basic:aircondition:running-duration"} Number:Dimentionless fan_percent "Fan Speed %" (G_aircondition) {channel="miio:basic:aircondition:fan-percent"} String timer "Enhance - Timer" (G_aircondition) {channel="miio:basic:aircondition:timer"} ``` @@ -6780,7 +7393,7 @@ Number:Energy electricity "Power consumption accumulation in kWh" (G_airconditio Number elec_count "Electricity - Count" (G_aircondition) {channel="miio:basic:aircondition:elec-count"} String clean "Maintenance - Clean" (G_aircondition) {channel="miio:basic:aircondition:clean"} String examine "Maintenance - Examine" (G_aircondition) {channel="miio:basic:aircondition:examine"} -Number:Duration running_duration "Maintenance - Running Duration" (G_aircondition) {channel="miio:basic:aircondition:running-duration"} +Number:Time running_duration "Maintenance - Running Duration" (G_aircondition) {channel="miio:basic:aircondition:running-duration"} Number:Dimentionless fan_percent "Fan Speed %" (G_aircondition) {channel="miio:basic:aircondition:fan-percent"} String timer "Enhance - Timer" (G_aircondition) {channel="miio:basic:aircondition:timer"} ``` @@ -6807,7 +7420,7 @@ Number:Energy electricity "Power consumption accumulation in kWh" (G_airconditio Number elec_count "Electricity - Count" (G_aircondition) {channel="miio:basic:aircondition:elec-count"} String clean "Maintenance - Clean" (G_aircondition) {channel="miio:basic:aircondition:clean"} String examine "Maintenance - Examine" (G_aircondition) {channel="miio:basic:aircondition:examine"} -Number:Duration running_duration "Maintenance - Running Duration" (G_aircondition) {channel="miio:basic:aircondition:running-duration"} +Number:Time running_duration "Maintenance - Running Duration" (G_aircondition) {channel="miio:basic:aircondition:running-duration"} Number:Dimentionless fan_percent "Fan Speed %" (G_aircondition) {channel="miio:basic:aircondition:fan-percent"} String timer "Enhance - Timer" (G_aircondition) {channel="miio:basic:aircondition:timer"} ``` @@ -6834,7 +7447,7 @@ Number:Energy electricity "Power consumption accumulation in kWh" (G_airconditio Number elec_count "Electricity - Count" (G_aircondition) {channel="miio:basic:aircondition:elec-count"} String clean "Maintenance - Clean" (G_aircondition) {channel="miio:basic:aircondition:clean"} String examine "Maintenance - Examine" (G_aircondition) {channel="miio:basic:aircondition:examine"} -Number:Duration running_duration "Maintenance - Running Duration" (G_aircondition) {channel="miio:basic:aircondition:running-duration"} +Number:Time running_duration "Maintenance - Running Duration" (G_aircondition) {channel="miio:basic:aircondition:running-duration"} Number:Dimentionless fan_percent "Fan Speed %" (G_aircondition) {channel="miio:basic:aircondition:fan-percent"} String timer "Enhance - Timer" (G_aircondition) {channel="miio:basic:aircondition:timer"} ``` @@ -6861,7 +7474,7 @@ Number:Energy electricity "Power consumption accumulation in kWh" (G_airconditio Number elec_count "Electricity - Count" (G_aircondition) {channel="miio:basic:aircondition:elec-count"} String clean "Maintenance - Clean" (G_aircondition) {channel="miio:basic:aircondition:clean"} String examine "Maintenance - Examine" (G_aircondition) {channel="miio:basic:aircondition:examine"} -Number:Duration running_duration "Maintenance - Running Duration" (G_aircondition) {channel="miio:basic:aircondition:running-duration"} +Number:Time running_duration "Maintenance - Running Duration" (G_aircondition) {channel="miio:basic:aircondition:running-duration"} Number:Dimentionless fan_percent "Fan Speed %" (G_aircondition) {channel="miio:basic:aircondition:fan-percent"} String timer "Enhance - Timer" (G_aircondition) {channel="miio:basic:aircondition:timer"} ``` @@ -6888,7 +7501,7 @@ Number:Energy electricity "Power consumption accumulation in kWh" (G_airconditio Number elec_count "Electricity - Count" (G_aircondition) {channel="miio:basic:aircondition:elec-count"} String clean "Maintenance - Clean" (G_aircondition) {channel="miio:basic:aircondition:clean"} String examine "Maintenance - Examine" (G_aircondition) {channel="miio:basic:aircondition:examine"} -Number:Duration running_duration "Maintenance - Running Duration" (G_aircondition) {channel="miio:basic:aircondition:running-duration"} +Number:Time running_duration "Maintenance - Running Duration" (G_aircondition) {channel="miio:basic:aircondition:running-duration"} Number:Dimentionless fan_percent "Fan Speed %" (G_aircondition) {channel="miio:basic:aircondition:fan-percent"} String timer "Enhance - Timer" (G_aircondition) {channel="miio:basic:aircondition:timer"} ``` @@ -6915,7 +7528,7 @@ Number:Energy electricity "Power consumption accumulation in kWh" (G_airconditio Number elec_count "Electricity - Count" (G_aircondition) {channel="miio:basic:aircondition:elec-count"} String clean "Maintenance - Clean" (G_aircondition) {channel="miio:basic:aircondition:clean"} String examine "Maintenance - Examine" (G_aircondition) {channel="miio:basic:aircondition:examine"} -Number:Duration running_duration "Maintenance - Running Duration" (G_aircondition) {channel="miio:basic:aircondition:running-duration"} +Number:Time running_duration "Maintenance - Running Duration" (G_aircondition) {channel="miio:basic:aircondition:running-duration"} Number:Dimentionless fan_percent "Fan Speed %" (G_aircondition) {channel="miio:basic:aircondition:fan-percent"} String timer "Enhance - Timer" (G_aircondition) {channel="miio:basic:aircondition:timer"} ``` @@ -6942,7 +7555,7 @@ Number:Energy electricity "Power consumption accumulation in kWh" (G_airconditio Number elec_count "Electricity - Count" (G_aircondition) {channel="miio:basic:aircondition:elec-count"} String clean "Maintenance - Clean" (G_aircondition) {channel="miio:basic:aircondition:clean"} String examine "Maintenance - Examine" (G_aircondition) {channel="miio:basic:aircondition:examine"} -Number:Duration running_duration "Maintenance - Running Duration" (G_aircondition) {channel="miio:basic:aircondition:running-duration"} +Number:Time running_duration "Maintenance - Running Duration" (G_aircondition) {channel="miio:basic:aircondition:running-duration"} Number:Dimentionless fan_percent "Fan Speed %" (G_aircondition) {channel="miio:basic:aircondition:fan-percent"} String timer "Enhance - Timer" (G_aircondition) {channel="miio:basic:aircondition:timer"} ``` @@ -6969,7 +7582,7 @@ Number:Energy electricity "Power consumption accumulation in kWh" (G_airconditio Number elec_count "Electricity - Count" (G_aircondition) {channel="miio:basic:aircondition:elec-count"} String clean "Maintenance - Clean" (G_aircondition) {channel="miio:basic:aircondition:clean"} String examine "Maintenance - Examine" (G_aircondition) {channel="miio:basic:aircondition:examine"} -Number:Duration running_duration "Maintenance - Running Duration" (G_aircondition) {channel="miio:basic:aircondition:running-duration"} +Number:Time running_duration "Maintenance - Running Duration" (G_aircondition) {channel="miio:basic:aircondition:running-duration"} Number:Dimentionless fan_percent "Fan Speed %" (G_aircondition) {channel="miio:basic:aircondition:fan-percent"} String timer "Enhance - Timer" (G_aircondition) {channel="miio:basic:aircondition:timer"} ``` @@ -7155,7 +7768,7 @@ note: Autogenerated example. Replace the id (light) in the channel with your own Group G_light "Yeelight LED Ceiling Light" Switch power "Power" (G_light) {channel="miio:basic:light:power"} Dimmer brightness "Brightness" (G_light) {channel="miio:basic:light:brightness"} -Number ambientBrightness "Ambient Brightness" (G_light) {channel="miio:basic:light:ambientBrightness"} +Dimmer ambientBrightness "Ambient Brightness" (G_light) {channel="miio:basic:light:ambientBrightness"} Number:Time delayoff "Shutdown Timer" (G_light) {channel="miio:basic:light:delayoff"} Number colorTemperature "Color Temperature" (G_light) {channel="miio:basic:light:colorTemperature"} Number colorMode "Color Mode" (G_light) {channel="miio:basic:light:colorMode"} @@ -7272,7 +7885,7 @@ note: Autogenerated example. Replace the id (light) in the channel with your own Group G_light "Yeelight Crystal Pendant Lamp" Switch power "Power" (G_light) {channel="miio:basic:light:power"} Dimmer brightness "Brightness" (G_light) {channel="miio:basic:light:brightness"} -Number ambientBrightness "Ambient Brightness" (G_light) {channel="miio:basic:light:ambientBrightness"} +Dimmer ambientBrightness "Ambient Brightness" (G_light) {channel="miio:basic:light:ambientBrightness"} Number:Time delayoff "Shutdown Timer" (G_light) {channel="miio:basic:light:delayoff"} Number colorTemperature "Color Temperature" (G_light) {channel="miio:basic:light:colorTemperature"} Number colorMode "Color Mode" (G_light) {channel="miio:basic:light:colorMode"} @@ -8945,6 +9558,65 @@ Number:Temperature temperature "Temperature" (G_airpurifier) {channel="miio:basi Switch childlock "Child Lock" (G_airpurifier) {channel="miio:basic:airpurifier:childlock"} ``` +### Mi Air Purifier Pro H (zhimi.airpurifier.vb2) item file lines + +note: Autogenerated example. Replace the id (airpurifier) in the channel with your own. Replace `basic` with `generic` in the thing UID depending on how your thing was discovered. + +``` +Group G_airpurifier "Mi Air Purifier Pro H" +String actions "Actions" (G_airpurifier) {channel="miio:basic:airpurifier:actions"} +Number fault "Air Purifier - Device Fault" (G_airpurifier) {channel="miio:basic:airpurifier:fault"} +Switch on "Air Purifier - Power" (G_airpurifier) {channel="miio:basic:airpurifier:on"} +Number fan_level "Air Purifier - Fan Level" (G_airpurifier) {channel="miio:basic:airpurifier:fan_level"} +Number mode "Air Purifier - Mode" (G_airpurifier) {channel="miio:basic:airpurifier:mode"} +Number pm2_5_density "Environment - PM2 5 Density" (G_airpurifier) {channel="miio:basic:airpurifier:pm2_5_density"} +Number:Dimensionless relative_humidity "Environment - Relative Humidity" (G_airpurifier) {channel="miio:basic:airpurifier:relative_humidity"} +Number:Temperature temperature "Environment - Temperature" (G_airpurifier) {channel="miio:basic:airpurifier:temperature"} +Number:Dimensionless filter_life_level "Filter - Filter Life Level" (G_airpurifier) {channel="miio:basic:airpurifier:filter_life_level"} +Number:Time filter_used_time "Filter - Filter Used Time" (G_airpurifier) {channel="miio:basic:airpurifier:filter_used_time"} +Switch alarm "Alarm - Alarm" (G_airpurifier) {channel="miio:basic:airpurifier:alarm"} +Number:Dimensionless volume "Alarm - Volume" (G_airpurifier) {channel="miio:basic:airpurifier:volume"} +Number brightness "Indicator Light - Brightness" (G_airpurifier) {channel="miio:basic:airpurifier:brightness"} +Switch on1 "Indicator Light - Switch Status" (G_airpurifier) {channel="miio:basic:airpurifier:on1"} +Switch physical_controls_locked "Physical Control Locked - Physical Control Locked" (G_airpurifier) {channel="miio:basic:airpurifier:physical_controls_locked"} +String button_pressed "Button - Button_pressed" (G_airpurifier) {channel="miio:basic:airpurifier:button_pressed"} +Number:Time filter_max_time "Filter Time - Filter Max Time" (G_airpurifier) {channel="miio:basic:airpurifier:filter_max_time"} +Number:Time filter_hour_used_debug "Filter Time - Filter Hour Used Debug" (G_airpurifier) {channel="miio:basic:airpurifier:filter_hour_used_debug"} +Number m1_strong "Motor Speed - M1 Strong" (G_airpurifier) {channel="miio:basic:airpurifier:m1_strong"} +Number m1_high "Motor Speed - M1 High" (G_airpurifier) {channel="miio:basic:airpurifier:m1_high"} +Number m1_med "Motor Speed - M1 Med" (G_airpurifier) {channel="miio:basic:airpurifier:m1_med"} +Number m1_med_l "Motor Speed - M1 Med L" (G_airpurifier) {channel="miio:basic:airpurifier:m1_med_l"} +Number m1_low "Motor Speed - M1 Low" (G_airpurifier) {channel="miio:basic:airpurifier:m1_low"} +Number m1_silent "Motor Speed - M1 Silent" (G_airpurifier) {channel="miio:basic:airpurifier:m1_silent"} +Number m1_favorite "Motor Speed - M1 Favorite" (G_airpurifier) {channel="miio:basic:airpurifier:m1_favorite"} +Number motor1_speed "Motor Speed - Motor1 Speed" (G_airpurifier) {channel="miio:basic:airpurifier:motor1_speed"} +Number motor1_set_speed "Motor Speed - Motor1 Set Speed" (G_airpurifier) {channel="miio:basic:airpurifier:motor1_set_speed"} +Number favorite_level "Motor Speed - Favorite Level" (G_airpurifier) {channel="miio:basic:airpurifier:favorite_level"} +Number:Time use_time "Use Time - Use Time" (G_airpurifier) {channel="miio:basic:airpurifier:use_time"} +Number purify_volume "Aqi - Purify Volume" (G_airpurifier) {channel="miio:basic:airpurifier:purify_volume"} +Number average_aqi "Aqi - Average Aqi" (G_airpurifier) {channel="miio:basic:airpurifier:average_aqi"} +Number average_aqi_cnt "Aqi - Average_aqi Read Times" (G_airpurifier) {channel="miio:basic:airpurifier:average_aqi_cnt"} +String aqi_zone "Aqi - Aqi Zone" (G_airpurifier) {channel="miio:basic:airpurifier:aqi_zone"} +Number sensor_state "Aqi - Sensor State" (G_airpurifier) {channel="miio:basic:airpurifier:sensor_state"} +Number aqi_goodh "Aqi - Aqi Goodh" (G_airpurifier) {channel="miio:basic:airpurifier:aqi_goodh"} +Number aqi_runstate "Aqi - Runstate" (G_airpurifier) {channel="miio:basic:airpurifier:aqi_runstate"} +Number aqi_state "Aqi - Aqi State" (G_airpurifier) {channel="miio:basic:airpurifier:aqi_state"} +String rfid_tag "Rfid - Rfid Tag" (G_airpurifier) {channel="miio:basic:airpurifier:rfid_tag"} +String rfid_factory_id "Rfid - Rfid Factory Id" (G_airpurifier) {channel="miio:basic:airpurifier:rfid_factory_id"} +String rfid_product_id "Rfid - Rfid Product Id" (G_airpurifier) {channel="miio:basic:airpurifier:rfid_product_id"} +String rfid_time "Rfid - Rfid Time" (G_airpurifier) {channel="miio:basic:airpurifier:rfid_time"} +String rfid_serial_num "Rfid - Rfid Serial Num" (G_airpurifier) {channel="miio:basic:airpurifier:rfid_serial_num"} +Number app_extra "Others - App Extra" (G_airpurifier) {channel="miio:basic:airpurifier:app_extra"} +Number main_channel "Others - Main Channel" (G_airpurifier) {channel="miio:basic:airpurifier:main_channel"} +Number slave_channel "Others - Slave Channel" (G_airpurifier) {channel="miio:basic:airpurifier:slave_channel"} +String cola "Others - Cola" (G_airpurifier) {channel="miio:basic:airpurifier:cola"} +String buttom_door "Others - Buttom Door" (G_airpurifier) {channel="miio:basic:airpurifier:buttom_door"} +Number reboot_cause "Others - Reboot_cause" (G_airpurifier) {channel="miio:basic:airpurifier:reboot_cause"} +Number manual_level "Others - Manual Level" (G_airpurifier) {channel="miio:basic:airpurifier:manual_level"} +Number:duration powertime "Others - Powertime" (G_airpurifier) {channel="miio:basic:airpurifier:powertime"} +Number country_code "Others - Country Code" (G_airpurifier) {channel="miio:basic:airpurifier:country_code"} +``` + ### Mi Standing Fan (zhimi.fan.sa1) item file lines note: Autogenerated example. Replace the id (fan) in the channel with your own. Replace `basic` with `generic` in the thing UID depending on how your thing was discovered. @@ -9102,6 +9774,125 @@ Number naturalLevel "Natural Level" (G_fan) {channel="miio:basic:fan:naturalLeve String move "Move Direction" (G_fan) {channel="miio:basic:fan:move"} ``` +### Smartmi Standing Fan 3 (zhimi.fan.za5) item file lines + +note: Autogenerated example. Replace the id (fan) in the channel with your own. Replace `basic` with `generic` in the thing UID depending on how your thing was discovered. + +``` +Group G_fan "Smartmi Standing Fan 3 " +Switch on "Fan - Power" (G_fan) {channel="miio:basic:fan:on"} +Number fan_level "Fan - Fan Level" (G_fan) {channel="miio:basic:fan:fan_level"} +Switch horizontal_swing "Fan - Horizontal Swing" (G_fan) {channel="miio:basic:fan:horizontal_swing"} +Number horizontal_angle "Fan - Horizontal Angle" (G_fan) {channel="miio:basic:fan:horizontal_angle"} +Number mode "Fan - Mode" (G_fan) {channel="miio:basic:fan:mode"} +Number off_delay "Fan - Power Off Delay" (G_fan) {channel="miio:basic:fan:off_delay"} +Switch anion "Fan - Anion" (G_fan) {channel="miio:basic:fan:anion"} +Switch physical_controls_locked "Physical Control Locked - Physical Control Locked" (G_fan) {channel="miio:basic:fan:physical_controls_locked"} +Number:Dimensionless brightness "Indicator Light - Brightness" (G_fan) {channel="miio:basic:fan:brightness"} +Switch alarm "Alarm - Alarm" (G_fan) {channel="miio:basic:fan:alarm"} +Number:Dimensionless relative_humidity "Environment - Relative Humidity" (G_fan) {channel="miio:basic:fan:relative_humidity"} +Number:Temperature temperature "Environment - Temperature" (G_fan) {channel="miio:basic:fan:temperature"} +Number button_press "Custom Service - Button Press" (G_fan) {channel="miio:basic:fan:button_press"} +Switch battery_state "Custom Service - Battery State" (G_fan) {channel="miio:basic:fan:battery_state"} +Number speed_now "Custom Service - Speed Now" (G_fan) {channel="miio:basic:fan:speed_now"} +Switch ac_state "Custom Service - Ac State" (G_fan) {channel="miio:basic:fan:ac_state"} +Number:Dimensionless speed_level "Custom Service - Speed Level" (G_fan) {channel="miio:basic:fan:speed_level"} +``` + +### Mi Smart Space Heater S (zhimi.heater.ma2) item file lines + +note: Autogenerated example. Replace the id (heater) in the channel with your own. Replace `basic` with `generic` in the thing UID depending on how your thing was discovered. + +``` +Group G_heater "Mi Smart Space Heater S" +Switch on "Heater - Switch Status" (G_heater) {channel="miio:basic:heater:on"} +Number fault "Heater - Fault" (G_heater) {channel="miio:basic:heater:fault"} +Number:Temperature target_temperature "Heater - Target Temperature" (G_heater) {channel="miio:basic:heater:target_temperature"} +Number:Time countdown_time "Countdown - Countdown Time" (G_heater) {channel="miio:basic:heater:countdown_time"} +Number:Temperature temperature "Environment - Temperature" (G_heater) {channel="miio:basic:heater:temperature"} +Switch physical_controls_locked "Physical Control Locked - Physical Control Locked" (G_heater) {channel="miio:basic:heater:physical_controls_locked"} +Switch alarm "Alarm - Alarm" (G_heater) {channel="miio:basic:heater:alarm"} +Number:Dimensionless brightness "Indicator Light - Brightness" (G_heater) {channel="miio:basic:heater:brightness"} +Switch hw_enable "Private Service - Hw Enable" (G_heater) {channel="miio:basic:heater:hw_enable"} +Number:Time use_time "Private Service - Use Time" (G_heater) {channel="miio:basic:heater:use_time"} +``` + +### Mi Smart Baseboard Heater E (zhimi.heater.ma3) item file lines + +note: Autogenerated example. Replace the id (heater) in the channel with your own. Replace `basic` with `generic` in the thing UID depending on how your thing was discovered. + +``` +Group G_heater "Mi Smart Baseboard Heater E" +String actions "Actions" (G_heater) {channel="miio:basic:heater:actions"} +Switch on "Heater - Switch Status" (G_heater) {channel="miio:basic:heater:on"} +Number fault "Heater - Fault" (G_heater) {channel="miio:basic:heater:fault"} +Number:Temperature target_temperature "Heater - Target Temperature" (G_heater) {channel="miio:basic:heater:target_temperature"} +Number mode "Heater - Mode" (G_heater) {channel="miio:basic:heater:mode"} +Number:Time countdown_time "Countdown - Countdown Time" (G_heater) {channel="miio:basic:heater:countdown_time"} +Number:Temperature temperature "Environment - Temperature" (G_heater) {channel="miio:basic:heater:temperature"} +Switch physical_controls_locked "Physical Control Locked - Physical Control Locked" (G_heater) {channel="miio:basic:heater:physical_controls_locked"} +Switch alarm "Alarm - Alarm" (G_heater) {channel="miio:basic:heater:alarm"} +Number:Dimensionless brightness "Indicator Light - Brightness" (G_heater) {channel="miio:basic:heater:brightness"} +Number:Time use_time "Private Service - Use Time" (G_heater) {channel="miio:basic:heater:use_time"} +``` + +### Mi Smart Space Heater S (zhimi.heater.mc2) item file lines + +note: Autogenerated example. Replace the id (heater) in the channel with your own. Replace `basic` with `generic` in the thing UID depending on how your thing was discovered. + +``` +Group G_heater "Mi Smart Space Heater S" +Switch on "Heater - Power" (G_heater) {channel="miio:basic:heater:on"} +Number fault "Heater - Device Fault" (G_heater) {channel="miio:basic:heater:fault"} +Number:Temperature target_temperature "Heater - Target Temperature" (G_heater) {channel="miio:basic:heater:target_temperature"} +Number:Time countdown_time "Countdown - Countdown Time" (G_heater) {channel="miio:basic:heater:countdown_time"} +Number:Temperature temperature "Environment - Temperature" (G_heater) {channel="miio:basic:heater:temperature"} +Switch physical_controls_locked "Physical Control Locked - Physical Control Locked" (G_heater) {channel="miio:basic:heater:physical_controls_locked"} +Switch alarm "Alarm - Alarm" (G_heater) {channel="miio:basic:heater:alarm"} +Number brightness "Indicator Light - Brightness" (G_heater) {channel="miio:basic:heater:brightness"} +Switch hw_enable "Private Service - Hw Enable" (G_heater) {channel="miio:basic:heater:hw_enable"} +Number:Time use_time "Private Service - Use Time" (G_heater) {channel="miio:basic:heater:use_time"} +Number country_code "Private Service - Country Code" (G_heater) {channel="miio:basic:heater:country_code"} +``` + +### Smartmi Smart Fan (zhimi.heater.na1) item file lines + +note: Autogenerated example. Replace the id (heater) in the channel with your own. Replace `basic` with `generic` in the thing UID depending on how your thing was discovered. + +``` +Group G_heater "Smartmi Smart Fan" +Switch on "Heater - Power" (G_heater) {channel="miio:basic:heater:on"} +Number fault "Heater - Device Fault" (G_heater) {channel="miio:basic:heater:fault"} +Number heat_level "Heater - Heat Level" (G_heater) {channel="miio:basic:heater:heat_level"} +Number mode "Heater - Mode" (G_heater) {channel="miio:basic:heater:mode"} +Switch alarm "Alarm - Alarm" (G_heater) {channel="miio:basic:heater:alarm"} +Number:Time countdown_time "Countdown - Countdown Time" (G_heater) {channel="miio:basic:heater:countdown_time"} +Number brightness "Indicator Light - Brightness" (G_heater) {channel="miio:basic:heater:brightness"} +Switch physical_controls_locked "Physical Control Locked - Physical Control Locked" (G_heater) {channel="miio:basic:heater:physical_controls_locked"} +Switch return_to_middle "Private Service - Return To Middle" (G_heater) {channel="miio:basic:heater:return_to_middle"} +``` + +### Smartmi Smart Fan Heater (zhimi.heater.nb1) item file lines + +note: Autogenerated example. Replace the id (heater) in the channel with your own. Replace `basic` with `generic` in the thing UID depending on how your thing was discovered. + +``` +Group G_heater "Smartmi Smart Fan Heater" +Switch on "Heater - Power" (G_heater) {channel="miio:basic:heater:on"} +Number fault "Heater - Device Fault" (G_heater) {channel="miio:basic:heater:fault"} +Number heat_level "Heater - Heat Level" (G_heater) {channel="miio:basic:heater:heat_level"} +Number mode "Heater - Mode" (G_heater) {channel="miio:basic:heater:mode"} +Number:Temperature target_temperature "Heater - Target Temperature" (G_heater) {channel="miio:basic:heater:target_temperature"} +Number:Temperature temperature "Environment - Temperature" (G_heater) {channel="miio:basic:heater:temperature"} +Switch alarm "Alarm - Alarm" (G_heater) {channel="miio:basic:heater:alarm"} +Number:Time countdown_time "Countdown - Countdown Time" (G_heater) {channel="miio:basic:heater:countdown_time"} +Number brightness "Indicator Light - Brightness" (G_heater) {channel="miio:basic:heater:brightness"} +Switch physical_controls_locked "Physical Control Locked - Physical Control Locked" (G_heater) {channel="miio:basic:heater:physical_controls_locked"} +Switch return_to_middle "Private Service - Return To Middle" (G_heater) {channel="miio:basic:heater:return_to_middle"} +Number country_code "Private Service - Country Code" (G_heater) {channel="miio:basic:heater:country_code"} +Switch hw_en "Private Service - Hw En" (G_heater) {channel="miio:basic:heater:hw_en"} +``` + ### Smartmi Radiant Heater Smart Version (zhimi.heater.za1) item file lines note: Autogenerated example. Replace the id (heater) in the channel with your own. Replace `basic` with `generic` in the thing UID depending on how your thing was discovered. @@ -9132,7 +9923,7 @@ Switch alarm "Alarm - Alarm" (G_heater) {channel="miio:basic:heater:alarm"} Number:Time countdown_time "Countdown - Countdown Time" (G_heater) {channel="miio:basic:heater:countdown-time"} Number:Dimensionless relative_humidity "Environment - Relative Humidity" (G_heater) {channel="miio:basic:heater:relative-humidity"} Number:Temperature temperature "Environment - Temperature" (G_heater) {channel="miio:basic:heater:temperature"} -Dimmer brightness "Indicator Light - Brightness" (G_heater) {channel="miio:basic:heater:brightness"} +Number brightness "Indicator Light - Brightness" (G_heater) {channel="miio:basic:heater:brightness"} Switch physical_controls_locked "Physical Control Locked - Physical Controls Locked" (G_heater) {channel="miio:basic:heater:physical-controls-locked"} Number:Time use_time "Private-Service - Use Time" (G_heater) {channel="miio:basic:heater:use-time"} ``` @@ -9143,14 +9934,14 @@ note: Autogenerated example. Replace the id (heater) in the channel with your ow ``` Group G_heater "Smartmi Smart Convector Heater 1S" -Number fault "Heater - Device Fault" (G_heater) {channel="miio:basic:heater:fault"} Switch on "Heater - Power" (G_heater) {channel="miio:basic:heater:on"} +Number fault "Heater - Device Fault" (G_heater) {channel="miio:basic:heater:fault"} Number:Temperature target_temperature "Heater - Target Temperature" (G_heater) {channel="miio:basic:heater:target-temperature"} Switch alarm "Alarm - Alarm" (G_heater) {channel="miio:basic:heater:alarm"} Number:Time countdown_time "Countdown - Countdown Time" (G_heater) {channel="miio:basic:heater:countdown-time"} Number:Dimensionless relative_humidity "Environment - Relative Humidity" (G_heater) {channel="miio:basic:heater:relative-humidity"} Number:Temperature temperature "Environment - Temperature" (G_heater) {channel="miio:basic:heater:temperature"} -Dimmer brightness "Indicator Light - Brightness" (G_heater) {channel="miio:basic:heater:brightness"} +Number brightness "Indicator Light - Brightness" (G_heater) {channel="miio:basic:heater:brightness"} Switch physical_controls_locked "Physical Control Locked - Physical Controls Locked" (G_heater) {channel="miio:basic:heater:physical-controls-locked"} Number:Time use_time "Private-Service - Use Time" (G_heater) {channel="miio:basic:heater:use-time"} Number country_code "Private-Service - Country-Code" (G_heater) {channel="miio:basic:heater:country-code"} diff --git a/bundles/org.openhab.binding.miio/src/main/java/org/openhab/binding/miio/internal/MiIoCommand.java b/bundles/org.openhab.binding.miio/src/main/java/org/openhab/binding/miio/internal/MiIoCommand.java index 447b51d1c5dd6..f16df318f268d 100644 --- a/bundles/org.openhab.binding.miio/src/main/java/org/openhab/binding/miio/internal/MiIoCommand.java +++ b/bundles/org.openhab.binding.miio/src/main/java/org/openhab/binding/miio/internal/MiIoCommand.java @@ -86,6 +86,15 @@ public enum MiIoCommand { REMOTE_END("app_rc_end"), REMOTE_MOVE("app_rc_move"), + GET_MAP_STATUS("get_map_status"), + GET_SEGMENT_STATUS("get_segment_status"), + GET_LED_STATUS("get_led_status"), + GET_CARPET_MODE("get_carpet_mode"), + GET_FW_FEATURES("get_fw_features"), + GET_CUSTOMIZED_CLEAN_MODE("get_customize_clean_mode"), + GET_MULTI_MAP_LIST("get_multi_maps_list"), + GET_ROOM_MAPPING("get_room_mapping"), + UNKNOWN(""); private final String command; diff --git a/bundles/org.openhab.binding.miio/src/main/java/org/openhab/binding/miio/internal/MiIoDevices.java b/bundles/org.openhab.binding.miio/src/main/java/org/openhab/binding/miio/internal/MiIoDevices.java index b3c224629f294..e998e004f4a1f 100644 --- a/bundles/org.openhab.binding.miio/src/main/java/org/openhab/binding/miio/internal/MiIoDevices.java +++ b/bundles/org.openhab.binding.miio/src/main/java/org/openhab/binding/miio/internal/MiIoDevices.java @@ -45,6 +45,8 @@ public enum MiIoDevices { CHUNMI_COOKER_PRESS1("chunmi.cooker.press1", "Mi IH Pressure Rice Cooker", THING_TYPE_UNSUPPORTED), CHUNMI_COOKER_PRESS2("chunmi.cooker.press2", "Mi IH Pressure Rice Cooker", THING_TYPE_UNSUPPORTED), CUCO_PLUG_CP1("cuco.plug.cp1", "Gosund Smart Plug", THING_TYPE_BASIC), + DEERMA_HUMIDIFIER_JSQ("deerma.humidifier.jsq", "Mi Smart Antibacterial Humidifier", THING_TYPE_BASIC), + DEERMA_HUMIDIFIER_JSQ1("deerma.humidifier.jsq1", "Mi S Smart humidifer ", THING_TYPE_BASIC), DEERMA_HUMIDIFIER_MJJSQ("deerma.humidifier.mjjsq", "Mi Smart Humidifier", THING_TYPE_BASIC), DMAKER_AIRFRESH_A1("dmaker.airfresh.a1", "Mi Fresh Air Ventilator A1-150", THING_TYPE_BASIC), DMAKER_AIRFRESH_T2017("dmaker.airfresh.t2017", "Mi Fresh Air Ventilator", THING_TYPE_BASIC), @@ -56,6 +58,10 @@ public enum MiIoDevices { DREAME_VACUUM_MC1808("dreame.vacuum.mc1808", "Mi Robot Vacuum Mop 1C STYTJ01ZHM", THING_TYPE_BASIC), DREAME_VACUUM_P2008("dreame.vacuum.p2008", "Dreame Robot Vacuum-Mop F9", THING_TYPE_BASIC), DREAME_VACUUM_P2009("dreame.vacuum.p2009", "Dreame Robot Vacuum D9 ", THING_TYPE_BASIC), + DREAME_VACUUM_P2036("dreame.vacuum.p2036", "Trouver Robot LDS Vacuum-Mop Finder", THING_TYPE_BASIC), + DREAME_VACUUM_P2041O("dreame.vacuum.p2041o", "Mi Robot Vacuum-Mop 2 Pro+", THING_TYPE_BASIC), + DREAME_VACUUM_P2156O("dreame.vacuum.p2156o", "MOVA Z500 Robot Vacuum and Mop Cleaner", THING_TYPE_BASIC), + DREAME_VACUUM_P2157("dreame.vacuum.p2157", "MOVA L600 Robot Vacuum and Mop Cleaner", THING_TYPE_BASIC), HUAYI_LIGHT_ARI013("huayi.light.ari013", "HUIZUO ARIES For Bedroom", THING_TYPE_BASIC), HUAYI_LIGHT_ARIES("huayi.light.aries", "HUIZUO ARIES For Living Room", THING_TYPE_BASIC), HUAYI_LIGHT_FANWY("huayi.light.fanwy", "HUIZUO Fan Light", THING_TYPE_BASIC), @@ -338,6 +344,7 @@ public enum MiIoDevices { ZHIMI_AIRPURIFIER_V5("zhimi.airpurifier.v5", "Mi Air Purifier v5", THING_TYPE_BASIC), ZHIMI_AIRPURIFIER_V6("zhimi.airpurifier.v6", "Mi Air Purifier Pro v6", THING_TYPE_BASIC), ZHIMI_AIRPURIFIER_V7("zhimi.airpurifier.v7", "Mi Air Purifier Pro v7", THING_TYPE_BASIC), + ZHIMI_AIRPURIFIER_VB2("zhimi.airpurifier.vb2", "Mi Air Purifier Pro H", THING_TYPE_BASIC), ZHIMI_AIRPURIFIER_VIRTUAL("zhimi.airpurifier.virtual", "Mi Air Purifier virtual", THING_TYPE_UNSUPPORTED), ZHIMI_AIRPURIFIER_VTL_M1("zhimi.airpurifier.vtl_m1", "Mi Air Purifier 2(Virtual)", THING_TYPE_UNSUPPORTED), ZHIMI_FAN_SA1("zhimi.fan.sa1", "Mi Standing Fan", THING_TYPE_BASIC), @@ -347,6 +354,12 @@ public enum MiIoDevices { ZHIMI_FAN_ZA1("zhimi.fan.za1", "Smartmi Inverter Pedestal Fan", THING_TYPE_BASIC), ZHIMI_FAN_ZA3("zhimi.fan.za3", "Smartmi Standing Fan 2", THING_TYPE_BASIC), ZHIMI_FAN_ZA4("zhimi.fan.za4", "Smartmi Standing Fan 2S", THING_TYPE_BASIC), + ZHIMI_FAN_ZA5("zhimi.fan.za5", "Smartmi Standing Fan 3 ", THING_TYPE_BASIC), + ZHIMI_HEATER_MA2("zhimi.heater.ma2", "Mi Smart Space Heater S", THING_TYPE_BASIC), + ZHIMI_HEATER_MA3("zhimi.heater.ma3", "Mi Smart Baseboard Heater E", THING_TYPE_BASIC), + ZHIMI_HEATER_MC2("zhimi.heater.mc2", "Mi Smart Space Heater S", THING_TYPE_BASIC), + ZHIMI_HEATER_NA1("zhimi.heater.na1", "Smartmi Smart Fan", THING_TYPE_BASIC), + ZHIMI_HEATER_NB1("zhimi.heater.nb1", "Smartmi Smart Fan Heater", THING_TYPE_BASIC), ZHIMI_HEATER_ZA1("zhimi.heater.za1", "Smartmi Radiant Heater Smart Version", THING_TYPE_BASIC), ZHIMI_HEATER_ZA2("zhimi.heater.za2", "Smartmi Smart Convector Heater 1S", THING_TYPE_BASIC), ZHIMI_HEATER_ZB1("zhimi.heater.zb1", "Smartmi Smart Convector Heater 1S", THING_TYPE_BASIC), diff --git a/bundles/org.openhab.binding.miio/src/main/java/org/openhab/binding/miio/internal/MiIoHandlerFactory.java b/bundles/org.openhab.binding.miio/src/main/java/org/openhab/binding/miio/internal/MiIoHandlerFactory.java index 129ce0a6b8277..08fa2c001702f 100644 --- a/bundles/org.openhab.binding.miio/src/main/java/org/openhab/binding/miio/internal/MiIoHandlerFactory.java +++ b/bundles/org.openhab.binding.miio/src/main/java/org/openhab/binding/miio/internal/MiIoHandlerFactory.java @@ -15,6 +15,8 @@ import static org.openhab.binding.miio.internal.MiIoBindingConstants.*; import java.util.Map; +import java.util.concurrent.Future; +import java.util.concurrent.RejectedExecutionException; import java.util.concurrent.ScheduledExecutorService; import org.eclipse.jdt.annotation.NonNullByDefault; @@ -35,7 +37,10 @@ import org.openhab.core.thing.type.ChannelTypeRegistry; import org.osgi.service.component.annotations.Activate; import org.osgi.service.component.annotations.Component; +import org.osgi.service.component.annotations.Deactivate; import org.osgi.service.component.annotations.Reference; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; /** * The {@link MiIoHandlerFactory} is responsible for creating things and thing @@ -49,17 +54,20 @@ public class MiIoHandlerFactory extends BaseThingHandlerFactory { private static final String THING_HANDLER_THREADPOOL_NAME = "thingHandler"; protected final ScheduledExecutorService scheduler = ThreadPoolManager .getScheduledPool(THING_HANDLER_THREADPOOL_NAME); - private MiIoDatabaseWatchService miIoDatabaseWatchService; private CloudConnector cloudConnector; private ChannelTypeRegistry channelTypeRegistry; private BasicChannelTypeProvider basicChannelTypeProvider; + private @Nullable Future scheduledTask; + private final Logger logger = LoggerFactory.getLogger(MiIoHandlerFactory.class); @Activate public MiIoHandlerFactory(@Reference ChannelTypeRegistry channelTypeRegistry, @Reference MiIoDatabaseWatchService miIoDatabaseWatchService, @Reference CloudConnector cloudConnector, @Reference BasicChannelTypeProvider basicChannelTypeProvider, Map properties) { this.miIoDatabaseWatchService = miIoDatabaseWatchService; + this.channelTypeRegistry = channelTypeRegistry; + this.basicChannelTypeProvider = basicChannelTypeProvider; this.cloudConnector = cloudConnector; @Nullable String username = (String) properties.get("username"); @@ -68,9 +76,23 @@ public MiIoHandlerFactory(@Reference ChannelTypeRegistry channelTypeRegistry, @Nullable String country = (String) properties.get("country"); cloudConnector.setCredentials(username, password, country); - scheduler.submit(() -> cloudConnector.isConnected()); - this.channelTypeRegistry = channelTypeRegistry; - this.basicChannelTypeProvider = basicChannelTypeProvider; + try { + if (!scheduler.isShutdown()) { + scheduledTask = scheduler.submit(() -> cloudConnector.isConnected()); + } else { + logger.debug("Unexpected: ScheduledExecutorService is shutdown."); + } + } catch (RejectedExecutionException e) { + logger.debug("Unexpected: ScheduledExecutorService task rejected.", e); + } + } + + @Deactivate + private void dispose() { + final Future scheduledTask = this.scheduledTask; + if (scheduledTask != null && !scheduledTask.isDone()) { + scheduledTask.cancel(true); + } } @Override diff --git a/bundles/org.openhab.binding.miio/src/main/java/org/openhab/binding/miio/internal/Utils.java b/bundles/org.openhab.binding.miio/src/main/java/org/openhab/binding/miio/internal/Utils.java index 9cc5c418a464f..00d141f628b4a 100644 --- a/bundles/org.openhab.binding.miio/src/main/java/org/openhab/binding/miio/internal/Utils.java +++ b/bundles/org.openhab.binding.miio/src/main/java/org/openhab/binding/miio/internal/Utils.java @@ -98,10 +98,9 @@ public static String obfuscateToken(String tokenString) { public static JsonObject convertFileToJSON(URL fileName) throws JsonIOException, JsonSyntaxException, JsonParseException, IOException, URISyntaxException, NoSuchFileException { JsonObject jsonObject = new JsonObject(); - JsonParser parser = new JsonParser(); try (InputStream inputStream = fileName.openStream(); InputStreamReader reader = new InputStreamReader(inputStream, StandardCharsets.UTF_8)) { - JsonElement jsonElement = parser.parse(reader); + JsonElement jsonElement = JsonParser.parseReader(reader); jsonObject = jsonElement.getAsJsonObject(); return jsonObject; } diff --git a/bundles/org.openhab.binding.miio/src/main/java/org/openhab/binding/miio/internal/basic/CommandParameterType.java b/bundles/org.openhab.binding.miio/src/main/java/org/openhab/binding/miio/internal/basic/CommandParameterType.java index c2a26b77012d5..7a379a581dda9 100644 --- a/bundles/org.openhab.binding.miio/src/main/java/org/openhab/binding/miio/internal/basic/CommandParameterType.java +++ b/bundles/org.openhab.binding.miio/src/main/java/org/openhab/binding/miio/internal/basic/CommandParameterType.java @@ -27,6 +27,7 @@ public enum CommandParameterType { ONOFFPARA("onoffpara"), ONOFFBOOL("onoffbool"), ONOFFBOOLSTRING("onoffboolstring"), + ONOFFNUMBER("onoffnumber"), STRING("string"), CUSTOMSTRING("customstring"), NUMBER("number"), diff --git a/bundles/org.openhab.binding.miio/src/main/java/org/openhab/binding/miio/internal/cloud/CloudLoginDTO.java b/bundles/org.openhab.binding.miio/src/main/java/org/openhab/binding/miio/internal/cloud/CloudLoginDTO.java index 859e78eb9bf17..712a533a5a0f4 100644 --- a/bundles/org.openhab.binding.miio/src/main/java/org/openhab/binding/miio/internal/cloud/CloudLoginDTO.java +++ b/bundles/org.openhab.binding.miio/src/main/java/org/openhab/binding/miio/internal/cloud/CloudLoginDTO.java @@ -12,8 +12,6 @@ */ package org.openhab.binding.miio.internal.cloud; -import org.jetbrains.annotations.NotNull; - import com.google.gson.annotations.Expose; import com.google.gson.annotations.SerializedName; @@ -64,23 +62,23 @@ public class CloudLoginDTO { @Expose private Object captchaUrl; - public @NotNull String getSsecurity() { + public String getSsecurity() { return ssecurity != null ? ssecurity : ""; } - public @NotNull String getUserId() { + public String getUserId() { return userId != null ? userId : ""; } - public @NotNull String getcUserId() { + public String getcUserId() { return cUserId != null ? cUserId : ""; } - public @NotNull String getPassToken() { + public String getPassToken() { return passToken != null ? passToken : ""; } - public @NotNull String getLocation() { + public String getLocation() { return location != null ? location : ""; } diff --git a/bundles/org.openhab.binding.miio/src/main/java/org/openhab/binding/miio/internal/cloud/MiCloudConnector.java b/bundles/org.openhab.binding.miio/src/main/java/org/openhab/binding/miio/internal/cloud/MiCloudConnector.java index cd076a9d5dde1..3ecabee39c9f1 100644 --- a/bundles/org.openhab.binding.miio/src/main/java/org/openhab/binding/miio/internal/cloud/MiCloudConnector.java +++ b/bundles/org.openhab.binding.miio/src/main/java/org/openhab/binding/miio/internal/cloud/MiCloudConnector.java @@ -74,7 +74,6 @@ public class MiCloudConnector { private static final TimeZone TZ = TimeZone.getDefault(); private static final DateTimeFormatter FORMATTER = DateTimeFormatter.ofPattern("OOOO"); private static final Gson GSON = new GsonBuilder().serializeNulls().create(); - private static final JsonParser PARSER = new JsonParser(); private final String clientId; @@ -156,7 +155,7 @@ public String getMapUrl(String vacuumMap, String country) throws MiCloudExceptio logger.trace("response: {}", mapResponse); String errorMsg = ""; try { - JsonElement response = PARSER.parse(mapResponse); + JsonElement response = JsonParser.parseString(mapResponse); if (response.isJsonObject()) { logger.debug("Received JSON message {}", response); if (response.getAsJsonObject().has("result") @@ -210,7 +209,7 @@ public List getDevices(String country) { final String response = getDeviceString(country); List devicesList = new ArrayList<>(); try { - final JsonElement resp = PARSER.parse(response); + final JsonElement resp = JsonParser.parseString(response); if (resp.isJsonObject()) { final JsonObject jor = resp.getAsJsonObject(); if (jor.has("result")) { @@ -413,7 +412,7 @@ private String loginStep1() throws InterruptedException, TimeoutException, Execu logger.trace("Xiaomi Login step 1 content response= {}", content); logger.trace("Xiaomi Login step 1 response = {}", responseStep1); try { - JsonElement resp = new JsonParser().parse(parseJson(content)); + JsonElement resp = JsonParser.parseString(parseJson(content)); if (resp.isJsonObject() && resp.getAsJsonObject().has("_sign")) { String sign = resp.getAsJsonObject().get("_sign").getAsString(); logger.trace("Xiaomi Login step 1 sign = {}", sign); @@ -457,7 +456,7 @@ private String loginStep2(String sign) throws MiIoCryptoException, InterruptedEx logger.trace("Xiaomi login step 2 response = {}", responseStep2); logger.trace("Xiaomi login step 2 content = {}", content2); - JsonElement resp2 = new JsonParser().parse(parseJson(content2)); + JsonElement resp2 = JsonParser.parseString(parseJson(content2)); CloudLoginDTO jsonResp = GSON.fromJson(resp2, CloudLoginDTO.class); if (jsonResp == null) { throw new MiCloudException("Error getting logon details from step 2: " + content2); diff --git a/bundles/org.openhab.binding.miio/src/main/java/org/openhab/binding/miio/internal/handler/MiIoAbstractHandler.java b/bundles/org.openhab.binding.miio/src/main/java/org/openhab/binding/miio/internal/handler/MiIoAbstractHandler.java index bdd6106fbd278..b38208451c01c 100644 --- a/bundles/org.openhab.binding.miio/src/main/java/org/openhab/binding/miio/internal/handler/MiIoAbstractHandler.java +++ b/bundles/org.openhab.binding.miio/src/main/java/org/openhab/binding/miio/internal/handler/MiIoAbstractHandler.java @@ -58,7 +58,6 @@ import com.google.gson.Gson; import com.google.gson.GsonBuilder; import com.google.gson.JsonObject; -import com.google.gson.JsonParser; /** * The {@link MiIoAbstractHandler} is responsible for handling commands, which are @@ -76,7 +75,6 @@ public abstract class MiIoAbstractHandler extends BaseThingHandler implements Mi protected MiIoDevices miDevice = MiIoDevices.UNKNOWN; protected boolean isIdentified; - protected final JsonParser parser = new JsonParser(); protected byte[] token = new byte[0]; protected @Nullable MiIoBindingConfiguration configuration; diff --git a/bundles/org.openhab.binding.miio/src/main/java/org/openhab/binding/miio/internal/handler/MiIoBasicHandler.java b/bundles/org.openhab.binding.miio/src/main/java/org/openhab/binding/miio/internal/handler/MiIoBasicHandler.java index bb7731f477ef2..cefd4469be068 100644 --- a/bundles/org.openhab.binding.miio/src/main/java/org/openhab/binding/miio/internal/handler/MiIoBasicHandler.java +++ b/bundles/org.openhab.binding.miio/src/main/java/org/openhab/binding/miio/internal/handler/MiIoBasicHandler.java @@ -73,6 +73,7 @@ import com.google.gson.JsonElement; import com.google.gson.JsonIOException; import com.google.gson.JsonObject; +import com.google.gson.JsonParser; import com.google.gson.JsonPrimitive; import com.google.gson.JsonSyntaxException; @@ -196,6 +197,8 @@ public void handleCommand(ChannelUID channelUID, Command receivedCommand) { value = new JsonPrimitive(boolCommand); } else if (paramType == CommandParameterType.ONOFFBOOLSTRING) { value = new JsonPrimitive(command == OnOffType.ON ? "true" : "false"); + } else if (paramType == CommandParameterType.ONOFFNUMBER) { + value = new JsonPrimitive(command == OnOffType.ON ? 1 : 0); } } else if (command instanceof DecimalType) { value = new JsonPrimitive(((DecimalType) command).toBigDecimal()); @@ -503,7 +506,8 @@ private boolean buildChannelStructure(String deviceName) { private void updatePropsFromJsonArray(MiIoSendCommand response) { JsonArray res = response.getResult().getAsJsonArray(); - JsonArray para = parser.parse(response.getCommandString()).getAsJsonObject().get("params").getAsJsonArray(); + JsonArray para = JsonParser.parseString(response.getCommandString()).getAsJsonObject().get("params") + .getAsJsonArray(); if (res.size() != para.size()) { logger.debug("Unexpected size different. Request size {}, response size {}. (Req: {}, Resp:{})", para.size(), res.size(), para, res); @@ -570,8 +574,13 @@ private void updateChannel(@Nullable MiIoBasicChannel basicChannel, String param updateState(basicChannel.getChannel(), new StringType(val.getAsString())); break; case "switch": - updateState(basicChannel.getChannel(), val.getAsString().toLowerCase().equals("on") - || val.getAsString().toLowerCase().equals("true") ? OnOffType.ON : OnOffType.OFF); + if (val.getAsJsonPrimitive().isNumber()) { + updateState(basicChannel.getChannel(), val.getAsInt() > 0 ? OnOffType.ON : OnOffType.OFF); + } else { + String strVal = val.getAsString().toLowerCase(); + updateState(basicChannel.getChannel(), + strVal.equals("on") || strVal.equals("true") ? OnOffType.ON : OnOffType.OFF); + } break; case "color": Color rgb = new Color(val.getAsInt()); diff --git a/bundles/org.openhab.binding.miio/src/main/java/org/openhab/binding/miio/internal/handler/MiIoVacuumHandler.java b/bundles/org.openhab.binding.miio/src/main/java/org/openhab/binding/miio/internal/handler/MiIoVacuumHandler.java index 3b3eba803aa5d..30fe0fbe84422 100644 --- a/bundles/org.openhab.binding.miio/src/main/java/org/openhab/binding/miio/internal/handler/MiIoVacuumHandler.java +++ b/bundles/org.openhab.binding.miio/src/main/java/org/openhab/binding/miio/internal/handler/MiIoVacuumHandler.java @@ -22,10 +22,14 @@ import java.time.Instant; import java.time.ZoneId; import java.time.ZonedDateTime; +import java.util.Collections; import java.util.Date; import java.util.Map.Entry; +import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.TimeUnit; +import java.util.stream.Collectors; +import java.util.stream.Stream; import javax.imageio.ImageIO; @@ -89,6 +93,12 @@ public class MiIoVacuumHandler extends MiIoAbstractHandler { private static final Gson GSON = new GsonBuilder().serializeNulls().create(); private final ChannelUID mapChannelUid; + private static final Set FEATURES_CHANNELS = Collections.unmodifiableSet(Stream + .of(RobotCababilities.SEGMENT_STATUS, RobotCababilities.MAP_STATUS, RobotCababilities.LED_STATUS, + RobotCababilities.CARPET_MODE, RobotCababilities.FW_FEATURES, RobotCababilities.ROOM_MAPPING, + RobotCababilities.MULTI_MAP_LIST, RobotCababilities.CUSTOMIZE_CLEAN_MODE) + .collect(Collectors.toSet())); + private ExpiringCache status; private ExpiringCache consumables; private ExpiringCache dnd; @@ -289,6 +299,7 @@ private boolean updateVacuumStatus(JsonObject statusData) { safeUpdateState(CHANNEL_IN_CLEANING, statusInfo.getInCleaning()); safeUpdateState(CHANNEL_MAP_PRESENT, statusInfo.getMapPresent()); if (statusInfo.getState() != null) { + stateId = statusInfo.getState(); StatusType state = StatusType.getType(statusInfo.getState()); updateState(CHANNEL_STATE, new StringType(state.getDescription())); updateState(CHANNEL_STATE_ID, new DecimalType(statusInfo.getState())); @@ -464,6 +475,11 @@ protected synchronized void updateData() { map.getValue(); } } + for (RobotCababilities cmd : FEATURES_CHANNELS) { + if (isLinked(cmd.getChannel())) { + sendCommand(cmd.getCommand()); + } + } } catch (Exception e) { logger.debug("Error while updating '{}': '{}", getThing().getUID().toString(), e.getLocalizedMessage()); } @@ -530,11 +546,54 @@ public void onMessageReceived(MiIoSendCommand response) { } } break; + case GET_MAP_STATUS: + case GET_SEGMENT_STATUS: + case GET_LED_STATUS: + updateNumericChannel(response); + break; + case GET_CARPET_MODE: + case GET_FW_FEATURES: + case GET_CUSTOMIZED_CLEAN_MODE: + case GET_MULTI_MAP_LIST: + case GET_ROOM_MAPPING: + for (RobotCababilities cmd : FEATURES_CHANNELS) { + if (response.getCommand().getCommand().contentEquals(cmd.getCommand())) { + updateState(cmd.getChannel(), new StringType(response.getResult().toString())); + break; + } + } + break; default: break; } } + private void updateNumericChannel(MiIoSendCommand response) { + RobotCababilities capabilityChannel = null; + for (RobotCababilities cmd : FEATURES_CHANNELS) { + if (response.getCommand().getCommand().contentEquals(cmd.getCommand())) { + capabilityChannel = cmd; + break; + } + } + if (capabilityChannel != null) { + if (response.getResult().isJsonArray() && response.getResult().getAsJsonArray().get(0).isJsonPrimitive()) { + try { + Integer stat = response.getResult().getAsJsonArray().get(0).getAsInt(); + updateState(capabilityChannel.getChannel(), new DecimalType(stat)); + return; + } catch (ClassCastException | IllegalStateException e) { + logger.debug("Could not update numeric channel {} with '{}': {}", capabilityChannel.getChannel(), + response.getResult(), e.getMessage()); + } + } else { + logger.debug("Could not update numeric channel {} with '{}': Not in expected format", + capabilityChannel.getChannel(), response.getResult()); + } + updateState(capabilityChannel.getChannel(), UnDefType.UNDEF); + } + } + private void setCapabilities(JsonObject statusResponse) { for (RobotCababilities capability : RobotCababilities.values()) { if (statusResponse.has(capability.getStatusFieldName())) { diff --git a/bundles/org.openhab.binding.miio/src/main/java/org/openhab/binding/miio/internal/robot/RobotCababilities.java b/bundles/org.openhab.binding.miio/src/main/java/org/openhab/binding/miio/internal/robot/RobotCababilities.java index 2d8d5dc339a62..d30eca08c4d32 100644 --- a/bundles/org.openhab.binding.miio/src/main/java/org/openhab/binding/miio/internal/robot/RobotCababilities.java +++ b/bundles/org.openhab.binding.miio/src/main/java/org/openhab/binding/miio/internal/robot/RobotCababilities.java @@ -23,23 +23,33 @@ @NonNullByDefault public enum RobotCababilities { - WATERBOX_STATUS("water_box_status", "status#water_box_status", "miio:water_box_status"), - LOCKSTATUS("lock_status", "status#lock_status", "miio:lock_status"), - WATERBOX_MODE("water_box_mode", "status#water_box_mode", "miio:water_box_mode"), - WATERBOX_CARRIAGE("water_box_carriage_status", "status#water_box_carriage_status", - "miio:water_box_carriage_status"), - MOP_FORBIDDEN("mop_forbidden_enable", "status#mop_forbidden_enable", "miio:mop_forbidden_enable"), - LOCATING("is_locating", "status#is_locating", "miio:is_locating"), - SEGMENT_CLEAN("", "actions#segment", "miio:segment"); + WATERBOX_STATUS("water_box_status", "status#water_box_status", "miio:water_box_status", ""), + LOCKSTATUS("lock_status", "status#lock_status", "miio:lock_status", ""), + WATERBOX_MODE("water_box_mode", "status#water_box_mode", "miio:water_box_mode", ""), + WATERBOX_CARRIAGE("water_box_carriage_status", "status#water_box_carriage_status", "miio:water_box_carriage_status", + ""), + MOP_FORBIDDEN("mop_forbidden_enable", "status#mop_forbidden_enable", "miio:mop_forbidden_enable", ""), + LOCATING("is_locating", "status#is_locating", "miio:is_locating", ""), + SEGMENT_STATUS("", "status#segment_status", "miio:segment_status", "get_segment_status"), + MAP_STATUS("", "status#map_status", "miio:map_status", "get_map_status"), + LED_STATUS("", "status#led_status", "miio:led_status", "get_led_status"), + CARPET_MODE("", "info#carpet_mode", "miio:carpet_mode", "get_carpet_mode"), + FW_FEATURES("", "info#fw_features", "miio:fw_features", "get_fw_features"), + ROOM_MAPPING("", "info#room_mapping", "miio:room_mapping", "get_room_mapping"), + MULTI_MAP_LIST("", "info#multi_maps_list", "miio:multi_maps_list", "get_multi_maps_list"), + CUSTOMIZE_CLEAN_MODE("", "info#customize_clean_mode", "miio:customize_clean_mode", "get_customize_clean_mode"), + SEGMENT_CLEAN("", "actions#segment", "miio:segment", ""); private final String statusFieldName; private final String channel; private final String channelType; + private final String command; - RobotCababilities(String statusKey, String channel, String channelType) { + RobotCababilities(String statusKey, String channel, String channelType, String command) { this.statusFieldName = statusKey; this.channel = channel; this.channelType = channelType; + this.command = command; } public String getStatusFieldName() { @@ -54,9 +64,14 @@ public ChannelTypeUID getChannelType() { return new ChannelTypeUID(channelType); } + public String getCommand() { + return command; + } + @Override public String toString() { - return String.format("Capability %s: status field name: '%s', channel: '%s', channeltype: '%s'.", this.name(), - statusFieldName, channel, channelType); + return String.format("Capability %s: status field name: '%s', channel: '%s', channeltype: '%s'%s%s.", + this.name(), statusFieldName, channel, channelType, command.isBlank() ? "" : ", custom command: ", + command); } } diff --git a/bundles/org.openhab.binding.miio/src/main/java/org/openhab/binding/miio/internal/transport/MiIoAsyncCommunication.java b/bundles/org.openhab.binding.miio/src/main/java/org/openhab/binding/miio/internal/transport/MiIoAsyncCommunication.java index 22cc0747ec94a..f4ebe2ce2739b 100644 --- a/bundles/org.openhab.binding.miio/src/main/java/org/openhab/binding/miio/internal/transport/MiIoAsyncCommunication.java +++ b/bundles/org.openhab.binding.miio/src/main/java/org/openhab/binding/miio/internal/transport/MiIoAsyncCommunication.java @@ -73,7 +73,6 @@ public class MiIoAsyncCommunication { private AtomicInteger id = new AtomicInteger(-1); private int timeDelta; private int timeStamp; - private final JsonParser parser; private @Nullable MessageSenderThread senderThread; private boolean connected; private ThingStatusDetail status = ThingStatusDetail.NONE; @@ -94,7 +93,6 @@ public MiIoAsyncCommunication(String ip, byte[] token, byte[] did, int id, int t this.timeout = timeout; this.cloudConnector = cloudConnector; setId(id); - parser = new JsonParser(); startReceiver(); } @@ -150,7 +148,7 @@ public int queueCommand(String command, String params, String cloudServer) } fullCommand.addProperty("id", cmdId); fullCommand.addProperty("method", command); - fullCommand.add("params", parser.parse(params)); + fullCommand.add("params", JsonParser.parseString(params)); MiIoSendCommand sendCmd = new MiIoSendCommand(cmdId, MiIoCommand.getCommand(command), fullCommand, cloudServer); concurrentLinkedQueue.add(sendCmd); @@ -188,7 +186,7 @@ MiIoSendCommand sendMiIoSendCommand(MiIoSendCommand miIoSendCommand) { // hack due to avoid invalid json errors from some misbehaving device firmwares decryptedResponse = decryptedResponse.replace(",,", ","); JsonElement response; - response = parser.parse(decryptedResponse); + response = JsonParser.parseString(decryptedResponse); if (!response.isJsonObject()) { errorMsg = "Received message is not a JSON object "; } else { diff --git a/bundles/org.openhab.binding.miio/src/main/resources/OH-INF/thing/vacuumThing.xml b/bundles/org.openhab.binding.miio/src/main/resources/OH-INF/thing/vacuumThing.xml index caee93a44ecf4..c9e5e713eedaa 100644 --- a/bundles/org.openhab.binding.miio/src/main/resources/OH-INF/thing/vacuumThing.xml +++ b/bundles/org.openhab.binding.miio/src/main/resources/OH-INF/thing/vacuumThing.xml @@ -15,6 +15,7 @@ + @@ -50,6 +51,9 @@ + + + @@ -95,6 +99,16 @@ + + + + + + + + + + @@ -152,6 +166,21 @@ + + Number + + + + + Number + + + + + Number + + + @@ -358,4 +387,29 @@ + + + + String + + + + + String + + + + + String + + + + + String + + + + String + + diff --git a/bundles/org.openhab.binding.miio/src/main/resources/database/chuangmi.plug.212a01-miot.json b/bundles/org.openhab.binding.miio/src/main/resources/database/chuangmi.plug.212a01-miot.json index 61b9dec71b05f..4827ef6cd6df4 100644 --- a/bundles/org.openhab.binding.miio/src/main/resources/database/chuangmi.plug.212a01-miot.json +++ b/bundles/org.openhab.binding.miio/src/main/resources/database/chuangmi.plug.212a01-miot.json @@ -54,7 +54,7 @@ "piid": 7, "friendlyName": "Working Time", "channel": "working-time", - "type": "Number:Duration", + "type": "Number:Time", "unit": "minutes", "stateDescription": { "minimum": 0, @@ -184,7 +184,7 @@ "piid": 1, "friendlyName": "Imilab Timer - On Duration", "channel": "on-duration", - "type": "Number:Duration", + "type": "Number:Time", "unit": "seconds", "stateDescription": { "minimum": 0, @@ -207,7 +207,7 @@ "piid": 2, "friendlyName": "Imilab Timer - Off Duration", "channel": "off-duration", - "type": "Number:Duration", + "type": "Number:Time", "unit": "seconds", "stateDescription": { "minimum": 0, diff --git a/bundles/org.openhab.binding.miio/src/main/resources/database/chuangmi.plug.v3.json b/bundles/org.openhab.binding.miio/src/main/resources/database/chuangmi.plug.v3.json index 1316189f2bab4..fbc7759e4d011 100644 --- a/bundles/org.openhab.binding.miio/src/main/resources/database/chuangmi.plug.v3.json +++ b/bundles/org.openhab.binding.miio/src/main/resources/database/chuangmi.plug.v3.json @@ -1,7 +1,9 @@ { "deviceMapping": { "id": [ - "chuangmi.plug.v3old" + "chuangmi.plug.v3", + "chuangmi.plug.hmi206", + "chuangmi.plug.hmi208" ], "channels": [ { @@ -13,14 +15,15 @@ "refresh": true, "actions": [ { - "command": "set_*", - "parameterType": "ONOFFPARA" + "command": "set_power", + "parameterType": "ONOFF" } ], "category": "switch", "tags": [ "Switch" - ] + ], + "readmeComment": "If this channel does not respond to on/off upgrade firmware" }, { "property": "usb_on", diff --git a/bundles/org.openhab.binding.miio/src/main/resources/database/chuangmi.plug.v3fw.json b/bundles/org.openhab.binding.miio/src/main/resources/database/chuangmi.plug.v3fw.json deleted file mode 100644 index 1999d01a2bcf6..0000000000000 --- a/bundles/org.openhab.binding.miio/src/main/resources/database/chuangmi.plug.v3fw.json +++ /dev/null @@ -1,79 +0,0 @@ -{ - "deviceMapping": { - "id": [ - "chuangmi.plug.v3", - "chuangmi.plug.v3fw", - "chuangmi.plug.hmi206", - "chuangmi.plug.hmi208" - ], - "channels": [ - { - "property": "power", - "friendlyName": "Power", - "channel": "power", - "channelType": "power", - "type": "Switch", - "refresh": true, - "actions": [ - { - "command": "set_power", - "parameterType": "ONOFF" - } - ], - "category": "switch", - "tags": [ - "Switch" - ], - "readmeComment": "If this channel does not respond to on/off replace the model with chuangmi.plug.v3old in the config or upgrade firmware" - }, - { - "property": "usb_on", - "friendlyName": "USB", - "channel": "usb", - "channelType": "usb", - "type": "Switch", - "refresh": true, - "ChannelGroup": "actions", - "actions": [ - { - "command": "set_usb_*", - "parameterType": "ONOFFPARA" - } - ] - }, - { - "property": "temperature", - "friendlyName": "Temperature", - "channel": "temperature", - "type": "Number:Temperature", - "unit": "CELCIUS", - "stateDescription": { - "pattern": "%.1f %unit%", - "readOnly": true - }, - "refresh": true, - "actions": [], - "category": "temperature", - "tags": [ - "Measurement", - "Temperature" - ] - }, - { - "property": "wifi_led", - "friendlyName": "Wifi LED", - "channel": "led", - "channelType": "led", - "type": "Switch", - "refresh": true, - "ChannelGroup": "actions", - "actions": [ - { - "command": "set_wifi_led", - "parameterType": "ONOFF" - } - ] - } - ] - } -} diff --git a/bundles/org.openhab.binding.miio/src/main/resources/database/deerma.humidifier.jsq1.json b/bundles/org.openhab.binding.miio/src/main/resources/database/deerma.humidifier.jsq1.json new file mode 100644 index 0000000000000..2a8a2a53149a6 --- /dev/null +++ b/bundles/org.openhab.binding.miio/src/main/resources/database/deerma.humidifier.jsq1.json @@ -0,0 +1,158 @@ +{ + "deviceMapping": { + "id": [ + "deerma.humidifier.jsq1" + ], + "maxProperties": 1, + "channels": [ + { + "property": "OnOff_State", + "friendlyName": "Power", + "channel": "power", + "type": "Switch", + "refresh": true, + "actions": [ + { + "command": "Set_OnOff", + "parameterType": "ONOFFNUMBER" + } + ], + "category": "switch", + "tags": [ + "Switch" + ] + }, + { + "property": "Humidifier_Gear", + "friendlyName": "Mode", + "channel": "mode", + "type": "Number", + "stateDescription": { + "options": [ + { + "value": "1", + "label": "Low" + }, + { + "value": "2", + "label": "Medium" + }, + { + "value": "3", + "label": "High" + }, + { + "value": "4", + "label": "Humidity " + } + ] + }, + "refresh": true, + "actions": [ + { + "command": "Set_HumidifierGears", + "parameterType": "NUMBER" + } + ] + }, + { + "property": "Humidity_Value", + "friendlyName": "Humidity", + "channel": "humidity", + "type": "Number:Dimensionless", + "unit": "PERCENT", + "stateDescription": { + "minimum": 0, + "maximum": 100, + "step": 1, + "pattern": "%.0f %unit%", + "readOnly": true + }, + "refresh": true, + "actions": [], + "category": "humidity", + "tags": [ + "Measurement", + "Humidity" + ] + }, + { + "property": "HumiSet_Value", + "friendlyName": "Humidity Setting", + "channel": "humidity_set", + "type": "Number:Dimensionless", + "unit": "PERCENT", + "stateDescription": { + "minimum": 0, + "maximum": 100, + "step": 1, + "pattern": "%.0f %unit%", + "readOnly": false + }, + "refresh": true, + "actions": [ + { + "command": "Set_HumiValue", + "parameterType": "NUMBER" + } + ], + "category": "humidity", + "tags": [ + "SetPoint", + "Humidity" + ] + }, + { + "property": "Led_State", + "friendlyName": "LED indicator Light", + "channel": "led", + "type": "Switch", + "refresh": true, + "actions": [ + { + "command": "SetLedState", + "parameterType": "ONOFFNUMBER" + } + ] + }, + { + "property": "TipSound_State", + "friendlyName": "Notification Sounds", + "channel": "sound", + "type": "Switch", + "refresh": true, + "actions": [ + { + "command": "SetTipSound_Status", + "parameterType": "ONOFFNUMBER" + } + ] + }, + { + "property": "watertankstatus", + "friendlyName": "Watertank Status", + "channel": "watertankstatus", + "type": "Number", + "stateDescription": { + "pattern": "%.0f", + "readOnly": true + }, + "refresh": true, + "actions": [] + }, + { + "property": "wet_and_protect", + "friendlyName": "Wet and Protect", + "channel": "wet_and_protect", + "type": "Switch", + "refresh": true, + "actions": [ + { + "command": "Set_wet_and_protect", + "parameterType": "ONOFFNUMBER" + } + ] + } + ] + } +} diff --git a/bundles/org.openhab.binding.miio/src/main/resources/database/deerma.humidifier.mjjsq.json b/bundles/org.openhab.binding.miio/src/main/resources/database/deerma.humidifier.mjjsq.json new file mode 100644 index 0000000000000..d3ce6308fc0ef --- /dev/null +++ b/bundles/org.openhab.binding.miio/src/main/resources/database/deerma.humidifier.mjjsq.json @@ -0,0 +1,146 @@ +{ + "deviceMapping": { + "id": [ + "deerma.humidifier.mjjsq", + "deerma.humidifier.jsq" + ], + "maxProperties": 1, + "channels": [ + { + "property": "OnOff_State", + "friendlyName": "Power", + "channel": "power", + "type": "Switch", + "refresh": true, + "actions": [ + { + "command": "Set_OnOff", + "parameterType": "ONOFFNUMBER" + } + ], + "category": "switch", + "tags": [ + "Switch" + ] + }, + { + "property": "Humidifier_Gear", + "friendlyName": "Mode", + "channel": "mode", + "type": "Number", + "stateDescription": { + "options": [ + { + "value": "1", + "label": "Low" + }, + { + "value": "2", + "label": "Medium" + }, + { + "value": "3", + "label": "High" + }, + { + "value": "4", + "label": "Humidity " + } + ] + }, + "refresh": true, + "actions": [ + { + "command": "Set_HumidifierGears", + "parameterType": "NUMBER" + } + ] + }, + { + "property": "Humidity_Value", + "friendlyName": "Humidity", + "channel": "humidity", + "type": "Number:Dimensionless", + "unit": "PERCENT", + "stateDescription": { + "minimum": 0, + "maximum": 100, + "step": 1, + "pattern": "%.0f %unit%", + "readOnly": true + }, + "refresh": true, + "actions": [], + "category": "humidity", + "tags": [ + "Measurement", + "Humidity" + ] + }, + { + "property": "HumiSet_Value", + "friendlyName": "Humidity Setting", + "channel": "humidity_set", + "type": "Number:Dimensionless", + "unit": "PERCENT", + "stateDescription": { + "minimum": 0, + "maximum": 100, + "step": 1, + "pattern": "%.0f %unit%", + "readOnly": false + }, + "refresh": true, + "actions": [ + { + "command": "Set_HumiValue", + "parameterType": "NUMBER" + } + ], + "category": "humidity", + "tags": [ + "SetPoint", + "Humidity" + ] + }, + { + "property": "Led_State", + "friendlyName": "LED indicator Light", + "channel": "led", + "type": "Switch", + "refresh": true, + "actions": [ + { + "command": "SetLedState", + "parameterType": "ONOFFNUMBER" + } + ] + }, + { + "property": "TipSound_State", + "friendlyName": "Notification Sounds", + "channel": "sound", + "type": "Switch", + "refresh": true, + "actions": [ + { + "command": "SetTipSound_Status", + "parameterType": "ONOFFNUMBER" + } + ] + }, + { + "property": "watertankstatus", + "friendlyName": "Watertank Status", + "channel": "watertankstatus", + "type": "Number", + "stateDescription": { + "pattern": "%.0f", + "readOnly": true + }, + "refresh": true, + "actions": [] + } + ] + } +} diff --git a/bundles/org.openhab.binding.miio/src/main/resources/database/dreame.vacuum.p2009-miot.json b/bundles/org.openhab.binding.miio/src/main/resources/database/dreame.vacuum.p2009-miot.json index c0c5d71dcc86e..3a62d30f89444 100644 --- a/bundles/org.openhab.binding.miio/src/main/resources/database/dreame.vacuum.p2009-miot.json +++ b/bundles/org.openhab.binding.miio/src/main/resources/database/dreame.vacuum.p2009-miot.json @@ -1,7 +1,9 @@ { "deviceMapping": { "id": [ - "dreame.vacuum.p2009" + "dreame.vacuum.p2009", + "dreame.vacuum.p2036", + "dreame.vacuum.p2157" ], "propertyMethod": "get_properties", "maxProperties": 1, @@ -12,7 +14,6 @@ "channel": "vacuumaction", "type": "String", "stateDescription": { - "readOnly": true, "options": [ { "value": "sweep", @@ -374,7 +375,7 @@ "minimum": 0, "maximum": 50, "step": 1, - "pattern": "%.1f", + "pattern": "%.0f", "readOnly": true }, "refresh": true, @@ -516,14 +517,33 @@ "channel": "task-status", "type": "Number", "stateDescription": { - "minimum": 0, - "maximum": 20, - "step": 1, - "pattern": "%.1f", - "readOnly": true + "readOnly": true, + "options": [ + { + "value": "0", + "label": "Notask" + }, + { + "value": "1", + "label": "AutoClean" + }, + { + "value": "2", + "label": "CustomClean" + }, + { + "value": "3", + "label": "SelectAreanClean" + }, + { + "value": "4", + "label": "SpotArea" + } + ] }, "refresh": true, - "actions": [] + "actions": [], + "readmeComment": "Value mapping [\"0\"\u003d\"Notask\",\"1\"\u003d\"AutoClean\",\"2\"\u003d\"CustomClean\",\"3\"\u003d\"SelectAreanClean\",\"4\"\u003d\"SpotArea\"]" }, { "property": "break-point-restart", diff --git a/bundles/org.openhab.binding.miio/src/main/resources/database/dreame.vacuum.p2156o-miot.json b/bundles/org.openhab.binding.miio/src/main/resources/database/dreame.vacuum.p2156o-miot.json new file mode 100644 index 0000000000000..c23afd413107a --- /dev/null +++ b/bundles/org.openhab.binding.miio/src/main/resources/database/dreame.vacuum.p2156o-miot.json @@ -0,0 +1,1025 @@ +{ + "deviceMapping": { + "id": [ + "dreame.vacuum.p2156o", + "dreame.vacuum.p2041o" + ], + "propertyMethod": "get_properties", + "maxProperties": 1, + "channels": [ + { + "property": "", + "friendlyName": "Actions", + "channel": "actions", + "type": "String", + "stateDescription": { + "options": [ + { + "value": "vacuum-start-sweep", + "label": "Start Sweep" + }, + { + "value": "vacuum-stop-sweeping", + "label": "Stop Sweeping" + }, + { + "value": "battery-start-charge", + "label": "Start Charge" + }, + { + "value": "brush-cleaner-reset-brush-life", + "label": "Brush Cleaner Reset Brush Life" + }, + { + "value": "brush-cleaner-reset-brush-life", + "label": "Brush Cleaner Reset Brush Life" + }, + { + "value": "filter-reset-filter-life", + "label": "Filter Reset Filter Life" + }, + { + "value": "vacuum-extend-start-clean", + "label": "Vacuum Extend Start Clean" + }, + { + "value": "vacuum-extend-stop-clean", + "label": "Vacuum Extend Stop Clean" + }, + { + "value": "map-map-req", + "label": "Map Map Req" + }, + { + "value": "map-update-map", + "label": "Map Update Map" + }, + { + "value": "audio-position", + "label": "Audio Position" + }, + { + "value": "audio-play-sound", + "label": "Audio Play Sound" + }, + { + "value": "time-delete-timer", + "label": "Time Delete Timer" + } + ] + }, + "actions": [ + { + "command": "action", + "parameterType": "EMPTY", + "siid": 2, + "aiid": 1, + "condition": { + "name": "matchValue", + "parameters": [ + { + "matchValue": "vacuum-start-sweep" + } + ] + } + }, + { + "command": "action", + "parameterType": "EMPTY", + "siid": 2, + "aiid": 2, + "condition": { + "name": "matchValue", + "parameters": [ + { + "matchValue": "vacuum-stop-sweeping" + } + ] + } + }, + { + "command": "action", + "parameterType": "EMPTY", + "siid": 3, + "aiid": 1, + "condition": { + "name": "matchValue", + "parameters": [ + { + "matchValue": "battery-start-charge" + } + ] + } + }, + { + "command": "action", + "parameterType": "EMPTY", + "siid": 9, + "aiid": 1, + "condition": { + "name": "matchValue", + "parameters": [ + { + "matchValue": "brush-cleaner-reset-brush-life" + } + ] + } + }, + { + "command": "action", + "parameterType": "EMPTY", + "siid": 10, + "aiid": 1, + "condition": { + "name": "matchValue", + "parameters": [ + { + "matchValue": "brush-cleaner-reset-brush-life" + } + ] + } + }, + { + "command": "action", + "parameterType": "EMPTY", + "siid": 11, + "aiid": 1, + "condition": { + "name": "matchValue", + "parameters": [ + { + "matchValue": "filter-reset-filter-life" + } + ] + } + }, + { + "command": "action", + "parameterType": "NUMBER", + "parameters": [ + 10.0 + ], + "siid": 4, + "aiid": 1, + "condition": { + "name": "matchValue", + "parameters": [ + { + "matchValue": "vacuum-extend-start-clean" + } + ] + } + }, + { + "command": "action", + "parameterType": "EMPTY", + "siid": 4, + "aiid": 2, + "condition": { + "name": "matchValue", + "parameters": [ + { + "matchValue": "vacuum-extend-stop-clean" + } + ] + } + }, + { + "command": "action", + "parameterType": "NUMBER", + "parameters": [ + 2.0 + ], + "siid": 6, + "aiid": 1, + "condition": { + "name": "matchValue", + "parameters": [ + { + "matchValue": "map-map-req" + } + ] + } + }, + { + "command": "action", + "parameterType": "NUMBER", + "parameters": [ + 4.0 + ], + "siid": 6, + "aiid": 2, + "condition": { + "name": "matchValue", + "parameters": [ + { + "matchValue": "map-update-map" + } + ] + } + }, + { + "command": "action", + "parameterType": "EMPTY", + "siid": 7, + "aiid": 1, + "condition": { + "name": "matchValue", + "parameters": [ + { + "matchValue": "audio-position" + } + ] + } + }, + { + "command": "action", + "parameterType": "EMPTY", + "siid": 7, + "aiid": 2, + "condition": { + "name": "matchValue", + "parameters": [ + { + "matchValue": "audio-play-sound" + } + ] + } + }, + { + "command": "action", + "parameterType": "NUMBER", + "parameters": [ + 3.0 + ], + "siid": 8, + "aiid": 1, + "condition": { + "name": "matchValue", + "parameters": [ + { + "matchValue": "time-delete-timer" + } + ] + } + } + ], + "readmeComment": "Value mapping [\"vacuum-start-sweep\"\u003d\"Start Sweep\",\"vacuum-stop-sweeping\"\u003d\"Stop Sweeping\",\"battery-start-charge\"\u003d\"Start Charge\",\"brush-cleaner-reset-brush-life\"\u003d\"Brush Cleaner Reset Brush Life\",\"brush-cleaner-reset-brush-life\"\u003d\"Brush Cleaner Reset Brush Life\",\"filter-reset-filter-life\"\u003d\"Filter Reset Filter Life\",\"vacuum-extend-start-clean\"\u003d\"Vacuum Extend Start Clean\",\"vacuum-extend-stop-clean\"\u003d\"Vacuum Extend Stop Clean\",\"map-map-req\"\u003d\"Map Map Req\",\"map-update-map\"\u003d\"Map Update Map\",\"audio-position\"\u003d\"Audio Position\",\"audio-play-sound\"\u003d\"Audio Play Sound\",\"time-delete-timer\"\u003d\"Time Delete Timer\"]" + }, + { + "property": "status", + "siid": 2, + "piid": 1, + "friendlyName": "Robot Cleaner - Status", + "channel": "status", + "type": "Number", + "stateDescription": { + "readOnly": true, + "options": [ + { + "value": "1", + "label": "Sweeping" + }, + { + "value": "2", + "label": "Idle" + }, + { + "value": "3", + "label": "Paused" + }, + { + "value": "4", + "label": "Error" + }, + { + "value": "5", + "label": "Go Charging" + }, + { + "value": "6", + "label": "Charging" + }, + { + "value": "7", + "label": "Mopping" + } + ] + }, + "refresh": true, + "actions": [], + "category": "status", + "tags": [ + "Status" + ], + "readmeComment": "Value mapping [\"1\"\u003d\"Sweeping\",\"2\"\u003d\"Idle\",\"3\"\u003d\"Paused\",\"4\"\u003d\"Error\",\"5\"\u003d\"Go Charging\",\"6\"\u003d\"Charging\",\"7\"\u003d\"Mopping\"]" + }, + { + "property": "fault", + "siid": 2, + "piid": 2, + "friendlyName": "Robot Cleaner - Device Fault", + "channel": "fault", + "type": "Number", + "stateDescription": { + "minimum": 0, + "maximum": 100, + "step": 1, + "pattern": "%.0f", + "readOnly": true + }, + "refresh": true, + "actions": [] + }, + { + "property": "mode", + "siid": 2, + "piid": 3, + "friendlyName": "Robot Cleaner - Mode", + "channel": "mode", + "type": "Number", + "stateDescription": { + "options": [ + { + "value": "0", + "label": "Silent" + }, + { + "value": "1", + "label": "Basic" + }, + { + "value": "2", + "label": "Strong" + }, + { + "value": "3", + "label": "Full Speed" + } + ] + }, + "refresh": true, + "actions": [ + { + "command": "set_properties", + "parameterType": "NUMBER" + } + ], + "readmeComment": "Value mapping [\"0\"\u003d\"Silent\",\"1\"\u003d\"Basic\",\"2\"\u003d\"Strong\",\"3\"\u003d\"Full Speed\"]" + }, + { + "property": "battery-level", + "siid": 3, + "piid": 1, + "friendlyName": "Battery - Battery Level", + "channel": "battery_level", + "type": "Number:Dimensionless", + "unit": "percentage", + "stateDescription": { + "minimum": 0, + "maximum": 100, + "step": 1, + "pattern": "%.0f %unit%", + "readOnly": true + }, + "refresh": true, + "actions": [] + }, + { + "property": "charging-state", + "siid": 3, + "piid": 2, + "friendlyName": "Battery - Charging State", + "channel": "charging_state", + "type": "Number", + "stateDescription": { + "readOnly": true, + "options": [ + { + "value": "1", + "label": "Charging" + }, + { + "value": "2", + "label": "Not Charging" + }, + { + "value": "5", + "label": "Go Charging" + } + ] + }, + "refresh": true, + "actions": [], + "readmeComment": "Value mapping [\"1\"\u003d\"Charging\",\"2\"\u003d\"Not Charging\",\"5\"\u003d\"Go Charging\"]" + }, + { + "property": "brush-left-time", + "siid": 9, + "piid": 1, + "friendlyName": "Main Cleaning Brush - Brush Left Time", + "channel": "brush_left_time", + "type": "Number:Time", + "unit": "hours", + "stateDescription": { + "minimum": 0, + "maximum": 300, + "step": 1, + "pattern": "%.0f %unit%", + "readOnly": true + }, + "refresh": true, + "actions": [] + }, + { + "property": "brush-life-level", + "siid": 9, + "piid": 2, + "friendlyName": "Main Cleaning Brush - Brush Life Level", + "channel": "brush_life_level", + "type": "Number:Dimensionless", + "unit": "percentage", + "stateDescription": { + "minimum": 0, + "maximum": 100, + "step": 1, + "pattern": "%.0f %unit%", + "readOnly": true + }, + "refresh": true, + "actions": [] + }, + { + "property": "brush-left-time1", + "siid": 10, + "piid": 1, + "friendlyName": "Side Cleaning Brush - Brush Left Time", + "channel": "brush_left_time1", + "type": "Number:Time", + "unit": "hours", + "stateDescription": { + "minimum": 0, + "maximum": 200, + "step": 1, + "pattern": "%.0f %unit%", + "readOnly": true + }, + "refresh": true, + "actions": [] + }, + { + "property": "brush-life-level1", + "siid": 10, + "piid": 2, + "friendlyName": "Side Cleaning Brush - Brush Life Level", + "channel": "brush_life_level1", + "type": "Number:Dimensionless", + "unit": "percentage", + "stateDescription": { + "minimum": 0, + "maximum": 100, + "step": 1, + "pattern": "%.0f %unit%", + "readOnly": true + }, + "refresh": true, + "actions": [] + }, + { + "property": "filter-life-level", + "siid": 11, + "piid": 1, + "friendlyName": "Filter - Filter Life Level", + "channel": "filter_life_level", + "type": "Number:Dimensionless", + "unit": "percentage", + "stateDescription": { + "minimum": 0, + "maximum": 100, + "step": 1, + "pattern": "%.0f %unit%", + "readOnly": true + }, + "refresh": true, + "actions": [] + }, + { + "property": "filter-left-time", + "siid": 11, + "piid": 2, + "friendlyName": "Filter - Filter Left Time", + "channel": "filter_left_time", + "type": "Number:Time", + "unit": "hours", + "stateDescription": { + "minimum": 0, + "maximum": 150, + "step": 1, + "pattern": "%.0f %unit%", + "readOnly": true + }, + "refresh": true, + "actions": [] + }, + { + "property": "work-mode", + "siid": 4, + "piid": 1, + "friendlyName": "Vacuum Extend - Work Mode", + "channel": "work_mode", + "type": "Number", + "stateDescription": { + "minimum": 0, + "maximum": 50, + "step": 1, + "pattern": "%.0f", + "readOnly": true + }, + "refresh": true, + "actions": [] + }, + { + "property": "cleaning-time", + "siid": 4, + "piid": 2, + "friendlyName": "Vacuum Extend - Cleaning Time", + "channel": "cleaning_time", + "type": "Number:Time", + "unit": "minutes", + "stateDescription": { + "minimum": 0, + "maximum": 32767, + "step": 1, + "pattern": "%.0f %unit%", + "readOnly": true + }, + "refresh": true, + "actions": [] + }, + { + "property": "cleaning-area", + "siid": 4, + "piid": 3, + "friendlyName": "Vacuum Extend - Cleaning Area", + "channel": "cleaning-area", + "type": "Number:Area", + "unit": "square_meter", + "stateDescription": { + "minimum": 0, + "maximum": 32767, + "step": 1, + "pattern": "%.1f %unit%", + "readOnly": true + }, + "refresh": true, + "actions": [] + }, + { + "property": "cleaning-mode", + "siid": 4, + "piid": 4, + "friendlyName": "Vacuum Extend - Cleaning Mode", + "channel": "cleaning_mode", + "type": "Number", + "stateDescription": { + "options": [ + { + "value": "0", + "label": "mode 0" + }, + { + "value": "1", + "label": "mode 1" + }, + { + "value": "2", + "label": "mode 2" + }, + { + "value": "3", + "label": "mode 3" + } + ] + }, + "refresh": true, + "actions": [ + { + "command": "set_properties", + "parameterType": "STRING" + } + ], + "readmeComment": "Value mapping [\"0\"\u003d\"mode 0\",\"1\"\u003d\"mode 1\",\"2\"\u003d\"mode 2\",\"3\"\u003d\"mode 3\"]" + }, + { + "property": "mop-mode", + "siid": 4, + "piid": 5, + "friendlyName": "Vacuum Extend - Mop Mode", + "channel": "mop_mode", + "type": "Number", + "stateDescription": { + "options": [ + { + "value": "1", + "label": "low water" + }, + { + "value": "2", + "label": "medium water" + }, + { + "value": "3", + "label": "high water" + } + ] + }, + "refresh": true, + "actions": [ + { + "command": "set_properties", + "parameterType": "STRING" + } + ], + "readmeComment": "Value mapping [\"1\"\u003d\"low water\",\"2\"\u003d\"medium water\",\"3\"\u003d\"high water\"]" + }, + { + "property": "waterbox-status", + "siid": 4, + "piid": 6, + "friendlyName": "Vacuum Extend - Waterbox Status", + "channel": "waterbox_status", + "type": "Number", + "stateDescription": { + "readOnly": true, + "options": [ + { + "value": "0", + "label": "Status 0" + }, + { + "value": "1", + "label": "Status 1" + } + ] + }, + "refresh": true, + "actions": [], + "readmeComment": "Value mapping [\"0\"\u003d\"Status 0\",\"1\"\u003d\"Status 1\"]" + }, + { + "property": "task-status", + "siid": 4, + "piid": 7, + "friendlyName": "Vacuum Extend - Task Status", + "channel": "task_status", + "type": "Number", + "stateDescription": { + "readOnly": true, + "options": [ + { + "value": "0", + "label": "Notask" + }, + { + "value": "1", + "label": "AutoClean" + }, + { + "value": "2", + "label": "CustomClean" + }, + { + "value": "3", + "label": "SelectAreanClean" + }, + { + "value": "4", + "label": "SpotArea" + } + ] + }, + "refresh": true, + "actions": [], + "readmeComment": "Value mapping [\"0\"\u003d\"Notask\",\"1\"\u003d\"AutoClean\",\"2\"\u003d\"CustomClean\",\"3\"\u003d\"SelectAreanClean\",\"4\"\u003d\"SpotArea\"]" + }, + { + "property": "break-point-restart", + "siid": 4, + "piid": 11, + "friendlyName": "Vacuum Extend - Break Point Restart", + "channel": "break_point_restart", + "type": "Number", + "stateDescription": { + "options": [ + { + "value": "0", + "label": "Off" + }, + { + "value": "1", + "label": "On" + } + ] + }, + "refresh": true, + "actions": [ + { + "command": "set_properties", + "parameterType": "NUMBER" + } + ], + "readmeComment": "Value mapping [\"0\"\u003d\"Off\",\"1\"\u003d\"On\"]" + }, + { + "property": "carpet-press", + "siid": 4, + "piid": 12, + "friendlyName": "Vacuum Extend - Carpet Press", + "channel": "carpet_press", + "type": "Number", + "stateDescription": { + "options": [ + { + "value": "0", + "label": "Off" + }, + { + "value": "1", + "label": "On" + } + ] + }, + "refresh": true, + "actions": [ + { + "command": "set_properties", + "parameterType": "NUMBER" + } + ], + "readmeComment": "Value mapping [\"0\"\u003d\"Off\",\"1\"\u003d\"On\"]" + }, + { + "property": "serial-number", + "siid": 4, + "piid": 14, + "friendlyName": "Vacuum Extend - Serial Number", + "channel": "serial_number", + "type": "String", + "stateDescription": { + "readOnly": true + }, + "refresh": true, + "actions": [] + }, + { + "property": "keep-sweeper-time", + "siid": 4, + "piid": 17, + "friendlyName": "Vacuum Extend - Keep Sweeper Time", + "channel": "keep_sweeper_time", + "type": "Number:Time", + "unit": "minutes", + "stateDescription": { + "minimum": -1, + "maximum": 1000000, + "step": 1, + "pattern": "%.0f %unit%", + "readOnly": true + }, + "refresh": true, + "actions": [] + }, + { + "property": "faults", + "siid": 4, + "piid": 18, + "friendlyName": "Vacuum Extend - Faults", + "channel": "faults", + "type": "String", + "stateDescription": { + "readOnly": true + }, + "refresh": true, + "actions": [] + }, + { + "property": "enable", + "siid": 5, + "piid": 1, + "friendlyName": "Do Not Disturb - Enable", + "channel": "enable", + "type": "Switch", + "refresh": true, + "actions": [ + { + "command": "set_properties", + "parameterType": "ONOFFBOOL" + } + ] + }, + { + "property": "start-time", + "siid": 5, + "piid": 2, + "friendlyName": "Do Not Disturb - Start Time", + "channel": "start_time", + "type": "String", + "refresh": true, + "actions": [ + { + "command": "set_properties", + "parameterType": "STRING" + } + ] + }, + { + "property": "end-time", + "siid": 5, + "piid": 3, + "friendlyName": "Do Not Disturb - End Time", + "channel": "end_time", + "type": "String", + "refresh": true, + "actions": [ + { + "command": "set_properties", + "parameterType": "STRING" + } + ] + }, + { + "property": "volume", + "siid": 7, + "piid": 1, + "friendlyName": "Audio - Volume", + "channel": "volume", + "type": "Number:Dimensionless", + "unit": "PERCENT", + "stateDescription": { + "minimum": 0, + "maximum": 100, + "step": 1, + "pattern": "%.0f" + }, + "refresh": true, + "actions": [ + { + "command": "set_properties", + "parameterType": "NUMBER" + } + ] + }, + { + "property": "voice-packet-id", + "siid": 7, + "piid": 2, + "friendlyName": "Audio - Voice Packet Id", + "channel": "voice_packet_id", + "type": "String", + "refresh": true, + "actions": [ + { + "command": "set_properties", + "parameterType": "STRING" + } + ] + }, + { + "property": "voice-change-state", + "siid": 7, + "piid": 3, + "friendlyName": "Audio - Voice Change State", + "channel": "voice_change_state", + "type": "String", + "stateDescription": { + "readOnly": true + }, + "refresh": true, + "actions": [] + }, + { + "property": "time-zone", + "siid": 8, + "piid": 1, + "friendlyName": "Time - Time Zone", + "channel": "time_zone", + "type": "String", + "stateDescription": { + "readOnly": true + }, + "refresh": true, + "actions": [] + }, + { + "property": "timer-clean", + "siid": 8, + "piid": 2, + "friendlyName": "Time - Timer Clean", + "channel": "timer_clean", + "type": "String", + "refresh": true, + "actions": [ + { + "command": "set_properties", + "parameterType": "STRING" + } + ] + }, + { + "property": "first-clean-time", + "siid": 12, + "piid": 1, + "friendlyName": "Clean Logs - First Clean Time", + "channel": "first_clean_time", + "type": "Number", + "stateDescription": { + "minimum": 0, + "step": 1, + "pattern": "%.0f", + "readOnly": true + }, + "refresh": true, + "actions": [] + }, + { + "property": "total-clean-time", + "siid": 12, + "piid": 2, + "friendlyName": "Clean Logs - Total Clean Time", + "channel": "total_clean_time", + "type": "Number:Time", + "unit": "minutes", + "stateDescription": { + "minimum": 0, + "step": 1, + "pattern": "%.0f %unit%", + "readOnly": true + }, + "refresh": true, + "actions": [] + }, + { + "property": "total-clean-times", + "siid": 12, + "piid": 3, + "friendlyName": "Clean Logs - Total Clean Times", + "channel": "total_clean_times", + "type": "Number", + "stateDescription": { + "minimum": 0, + "step": 1, + "pattern": "%.0f", + "readOnly": true + }, + "refresh": true, + "actions": [] + }, + { + "property": "total-clean-area", + "siid": 12, + "piid": 4, + "friendlyName": "Clean Logs - Total Clean Area", + "channel": "total_clean_area", + "type": "Number", + "stateDescription": { + "minimum": 0, + "step": 1, + "pattern": "%.0f", + "readOnly": true + }, + "refresh": true, + "actions": [] + }, + { + "property": "save-map-status", + "siid": 13, + "piid": 1, + "friendlyName": "Vslam Extend - Save Map Status", + "channel": "save_map_status", + "type": "Number", + "stateDescription": { + "options": [ + { + "value": "0", + "label": "Off" + }, + { + "value": "1", + "label": "On" + } + ] + }, + "refresh": true, + "actions": [ + { + "command": "set_properties", + "parameterType": "NUMBER" + } + ], + "readmeComment": "Value mapping [\"0\"\u003d\"Off\",\"1\"\u003d\"On\"]" + } + ], + "readmeComment": "Identified manual actions for execution\u003cbr /\u003e`action{\"did\":\"vacuum-start-sweep\",\"siid\":2,\"aiid\":1,\"in\":[]}`\u003cbr /\u003e`action{\"did\":\"vacuum-stop-sweeping\",\"siid\":2,\"aiid\":2,\"in\":[]}`\u003cbr /\u003e`action{\"did\":\"battery-start-charge\",\"siid\":3,\"aiid\":1,\"in\":[]}`\u003cbr /\u003e`action{\"did\":\"brush-cleaner-reset-brush-life\",\"siid\":9,\"aiid\":1,\"in\":[]}`\u003cbr /\u003e`action{\"did\":\"brush-cleaner-reset-brush-life\",\"siid\":10,\"aiid\":1,\"in\":[]}`\u003cbr /\u003e`action{\"did\":\"filter-reset-filter-life\",\"siid\":11,\"aiid\":1,\"in\":[]}`\u003cbr /\u003e`action{\"did\":\"vacuum-extend-start-clean\",\"siid\":4,\"aiid\":1,\"in\":[10.0]}`\u003cbr /\u003e`action{\"did\":\"vacuum-extend-stop-clean\",\"siid\":4,\"aiid\":2,\"in\":[]}`\u003cbr /\u003e`action{\"did\":\"map-map-req\",\"siid\":6,\"aiid\":1,\"in\":[2.0]}`\u003cbr /\u003e`action{\"did\":\"map-update-map\",\"siid\":6,\"aiid\":2,\"in\":[4.0]}`\u003cbr /\u003e`action{\"did\":\"audio-position\",\"siid\":7,\"aiid\":1,\"in\":[]}`\u003cbr /\u003e`action{\"did\":\"audio-play-sound\",\"siid\":7,\"aiid\":2,\"in\":[]}`\u003cbr /\u003e`action{\"did\":\"time-delete-timer\",\"siid\":8,\"aiid\":1,\"in\":[3.0]}`\u003cbr /\u003ePlease test and feedback if they are working to they can be linked to a channel.", + "experimental": true + } +} diff --git a/bundles/org.openhab.binding.miio/src/main/resources/database/xiaomi.aircondition.mc1-miot.json b/bundles/org.openhab.binding.miio/src/main/resources/database/xiaomi.aircondition.mc1-miot.json index 082e75a507306..e5fa1d519e047 100644 --- a/bundles/org.openhab.binding.miio/src/main/resources/database/xiaomi.aircondition.mc1-miot.json +++ b/bundles/org.openhab.binding.miio/src/main/resources/database/xiaomi.aircondition.mc1-miot.json @@ -405,7 +405,7 @@ "piid": 5, "friendlyName": "Maintenance - Running Duration", "channel": "running-duration", - "type": "Number:Duration", + "type": "Number:Time", "unit": "seconds", "stateDescription": { "minimum": 0, diff --git a/bundles/org.openhab.binding.miio/src/main/resources/database/zhimi.airpurifier.vb2-miot.json b/bundles/org.openhab.binding.miio/src/main/resources/database/zhimi.airpurifier.vb2-miot.json new file mode 100644 index 0000000000000..4e20dcae188de --- /dev/null +++ b/bundles/org.openhab.binding.miio/src/main/resources/database/zhimi.airpurifier.vb2-miot.json @@ -0,0 +1,1099 @@ +{ + "deviceMapping": { + "id": [ + "zhimi.airpurifier.vb2" + ], + "propertyMethod": "get_properties", + "maxProperties": 3, + "channels": [ + { + "property": "", + "friendlyName": "Actions", + "channel": "actions", + "type": "String", + "stateDescription": { + "options": [ + { + "value": "button-toggle", + "label": "Toggle" + }, + { + "value": "button-toggle-mode", + "label": "Toggle Mode" + } + ] + }, + "actions": [ + { + "command": "action", + "parameterType": "EMPTY", + "siid": 8, + "aiid": 1, + "condition": { + "name": "matchValue", + "parameters": [ + { + "matchValue": "button-toggle" + } + ] + } + }, + { + "command": "action", + "parameterType": "EMPTY", + "siid": 8, + "aiid": 2, + "condition": { + "name": "matchValue", + "parameters": [ + { + "matchValue": "button-toggle-mode" + } + ] + } + } + ], + "readmeComment": "Value mapping [\"button-toggle\"\u003d\"Toggle\",\"button-toggle-mode\"\u003d\"Toggle Mode\"]" + }, + { + "property": "fault", + "siid": 2, + "piid": 1, + "friendlyName": "Air Purifier - Device Fault", + "channel": "fault", + "type": "Number", + "stateDescription": { + "readOnly": true, + "options": [ + { + "value": "0", + "label": "No faults" + }, + { + "value": "1", + "label": "m1_run" + }, + { + "value": "2", + "label": "m1_stuck" + }, + { + "value": "3", + "label": "no_sensor" + }, + { + "value": "4", + "label": "error_hum" + }, + { + "value": "5", + "label": "error_temp" + }, + { + "value": "6", + "label": "timer_error1" + }, + { + "value": "7", + "label": "timer_error2" + } + ] + }, + "refresh": true, + "actions": [], + "readmeComment": "Value mapping [\"0\"\u003d\"No faults\",\"1\"\u003d\"m1_run\",\"2\"\u003d\"m1_stuck\",\"3\"\u003d\"no_sensor\",\"4\"\u003d\"error_hum\",\"5\"\u003d\"error_temp\",\"6\"\u003d\"timer_error1\",\"7\"\u003d\"timer_error2\"]" + }, + { + "property": "on", + "siid": 2, + "piid": 2, + "friendlyName": "Air Purifier - Power", + "channel": "on", + "type": "Switch", + "refresh": true, + "actions": [ + { + "command": "set_properties", + "parameterType": "ONOFFBOOL" + } + ] + }, + { + "property": "fan-level", + "siid": 2, + "piid": 4, + "friendlyName": "Air Purifier - Fan Level", + "channel": "fan_level", + "type": "Number", + "stateDescription": { + "options": [ + { + "value": "1", + "label": "Level1" + }, + { + "value": "2", + "label": "Level2" + }, + { + "value": "3", + "label": "Level3" + }, + { + "value": "0", + "label": "Sleep" + } + ] + }, + "refresh": true, + "actions": [ + { + "command": "set_properties", + "parameterType": "NUMBER" + } + ], + "readmeComment": "Value mapping [\"1\"\u003d\"Level1\",\"2\"\u003d\"Level2\",\"3\"\u003d\"Level3\",\"0\"\u003d\"Sleep\"]" + }, + { + "property": "mode", + "siid": 2, + "piid": 5, + "friendlyName": "Air Purifier - Mode", + "channel": "mode", + "type": "Number", + "stateDescription": { + "options": [ + { + "value": "0", + "label": "Auto" + }, + { + "value": "1", + "label": "Night" + }, + { + "value": "2", + "label": "Favourite" + }, + { + "value": "3", + "label": "Manual" + } + ] + }, + "refresh": true, + "actions": [ + { + "command": "set_properties", + "parameterType": "NUMBER" + } + ], + "readmeComment": "Value mapping [\"0\"\u003d\"Auto\",\"1\"\u003d\"Night\",\"2\"\u003d\"Favourite\",\"3\"\u003d\"Manual\"]" + }, + { + "property": "pm2.5-density", + "siid": 3, + "piid": 6, + "friendlyName": "Environment - PM2 5 Density", + "channel": "pm2_5_density", + "type": "Number", + "stateDescription": { + "minimum": 0, + "maximum": 600, + "step": 1, + "pattern": "%.1f", + "readOnly": true + }, + "refresh": true, + "actions": [] + }, + { + "property": "relative-humidity", + "siid": 3, + "piid": 7, + "friendlyName": "Environment - Relative Humidity", + "channel": "relative_humidity", + "type": "Number:Dimensionless", + "unit": "percentage", + "stateDescription": { + "minimum": 0, + "maximum": 100, + "step": 1, + "pattern": "%.0f %unit%", + "readOnly": true + }, + "refresh": true, + "actions": [] + }, + { + "property": "temperature", + "siid": 3, + "piid": 8, + "friendlyName": "Environment - Temperature", + "channel": "temperature", + "type": "Number:Temperature", + "unit": "CELCIUS", + "stateDescription": { + "minimum": -40, + "maximum": 125, + "pattern": "%.1f", + "readOnly": true + }, + "refresh": true, + "actions": [] + }, + { + "property": "filter-life-level", + "siid": 4, + "piid": 3, + "friendlyName": "Filter - Filter Life Level", + "channel": "filter_life_level", + "type": "Number:Dimensionless", + "unit": "percentage", + "stateDescription": { + "minimum": 0, + "maximum": 100, + "step": 1, + "pattern": "%.0f %unit%", + "readOnly": true + }, + "refresh": true, + "actions": [] + }, + { + "property": "filter-used-time", + "siid": 4, + "piid": 5, + "friendlyName": "Filter - Filter Used Time", + "channel": "filter_used_time", + "type": "Number:Time", + "unit": "hours", + "stateDescription": { + "minimum": 0, + "maximum": 18000, + "step": 1, + "pattern": "%.0f %unit%", + "readOnly": true + }, + "refresh": true, + "actions": [] + }, + { + "property": "alarm", + "siid": 5, + "piid": 1, + "friendlyName": "Alarm - Alarm", + "channel": "alarm", + "type": "Switch", + "refresh": true, + "actions": [ + { + "command": "set_properties", + "parameterType": "ONOFFBOOL" + } + ] + }, + { + "property": "volume", + "siid": 5, + "piid": 2, + "friendlyName": "Alarm - Volume", + "channel": "volume", + "type": "Number:Dimensionless", + "unit": "percentage", + "stateDescription": { + "minimum": 0, + "maximum": 100, + "step": 1, + "pattern": "%.0f %unit%" + }, + "refresh": true, + "actions": [ + { + "command": "set_properties", + "parameterType": "NUMBER" + } + ] + }, + { + "property": "brightness", + "siid": 6, + "piid": 1, + "friendlyName": "Indicator Light - Brightness", + "channel": "brightness", + "type": "Number", + "stateDescription": { + "options": [ + { + "value": "0", + "label": "brightest" + }, + { + "value": "1", + "label": "glimmer" + }, + { + "value": "2", + "label": "not bright" + } + ] + }, + "refresh": true, + "actions": [ + { + "command": "set_properties", + "parameterType": "NUMBER" + } + ], + "readmeComment": "Value mapping [\"0\"\u003d\"brightest\",\"1\"\u003d\"glimmer\",\"2\"\u003d\"not bright\"]" + }, + { + "property": "on1", + "siid": 6, + "piid": 6, + "friendlyName": "Indicator Light - Switch Status", + "channel": "on1", + "type": "Switch", + "refresh": true, + "actions": [ + { + "command": "set_properties", + "parameterType": "ONOFFBOOL" + } + ] + }, + { + "property": "physical-controls-locked", + "siid": 7, + "piid": 1, + "friendlyName": "Physical Control Locked - Physical Control Locked", + "channel": "physical_controls_locked", + "type": "Switch", + "refresh": true, + "actions": [ + { + "command": "set_properties", + "parameterType": "ONOFFBOOL" + } + ] + }, + { + "property": "button-pressed", + "siid": 8, + "piid": 1, + "friendlyName": "Button - Button_pressed", + "channel": "button_pressed", + "type": "String", + "stateDescription": { + "readOnly": true + }, + "refresh": true, + "actions": [] + }, + { + "property": "filter-max-time", + "siid": 9, + "piid": 1, + "friendlyName": "Filter Time - Filter Max Time", + "channel": "filter_max_time", + "type": "Number:Time", + "unit": "hours", + "stateDescription": { + "minimum": 2000, + "maximum": 8000, + "step": 1, + "pattern": "%.0f %unit%" + }, + "refresh": true, + "actions": [ + { + "command": "set_properties", + "parameterType": "NUMBER" + } + ] + }, + { + "property": "filter-hour-used-debug", + "siid": 9, + "piid": 2, + "friendlyName": "Filter Time - Filter Hour Used Debug", + "channel": "filter_hour_used_debug", + "type": "Number:Time", + "unit": "hours", + "stateDescription": { + "minimum": 0, + "maximum": 18000, + "step": 1, + "pattern": "%.0f %unit%" + }, + "refresh": true, + "actions": [ + { + "command": "set_properties", + "parameterType": "NUMBER" + } + ] + }, + { + "property": "m1-strong", + "siid": 10, + "piid": 1, + "friendlyName": "Motor Speed - M1 Strong", + "channel": "m1_strong", + "type": "Number", + "stateDescription": { + "minimum": 300, + "maximum": 2300, + "step": 10, + "pattern": "%.0f" + }, + "refresh": true, + "actions": [ + { + "command": "set_properties", + "parameterType": "NUMBER" + } + ] + }, + { + "property": "m1-high", + "siid": 10, + "piid": 2, + "friendlyName": "Motor Speed - M1 High", + "channel": "m1_high", + "type": "Number", + "stateDescription": { + "minimum": 300, + "maximum": 2300, + "step": 10, + "pattern": "%.0f" + }, + "refresh": true, + "actions": [ + { + "command": "set_properties", + "parameterType": "NUMBER" + } + ] + }, + { + "property": "m1-med", + "siid": 10, + "piid": 3, + "friendlyName": "Motor Speed - M1 Med", + "channel": "m1_med", + "type": "Number", + "stateDescription": { + "minimum": 300, + "maximum": 2300, + "step": 10, + "pattern": "%.0f" + }, + "refresh": true, + "actions": [ + { + "command": "set_properties", + "parameterType": "NUMBER" + } + ] + }, + { + "property": "m1-med-l", + "siid": 10, + "piid": 4, + "friendlyName": "Motor Speed - M1 Med L", + "channel": "m1_med_l", + "type": "Number", + "stateDescription": { + "minimum": 300, + "maximum": 2300, + "step": 10, + "pattern": "%.0f" + }, + "refresh": true, + "actions": [ + { + "command": "set_properties", + "parameterType": "NUMBER" + } + ] + }, + { + "property": "m1-low", + "siid": 10, + "piid": 5, + "friendlyName": "Motor Speed - M1 Low", + "channel": "m1_low", + "type": "Number", + "stateDescription": { + "minimum": 300, + "maximum": 2300, + "step": 10, + "pattern": "%.0f" + }, + "refresh": true, + "actions": [ + { + "command": "set_properties", + "parameterType": "NUMBER" + } + ] + }, + { + "property": "m1-silent", + "siid": 10, + "piid": 6, + "friendlyName": "Motor Speed - M1 Silent", + "channel": "m1_silent", + "type": "Number", + "stateDescription": { + "minimum": 300, + "maximum": 2300, + "step": 10, + "pattern": "%.0f" + }, + "refresh": true, + "actions": [ + { + "command": "set_properties", + "parameterType": "NUMBER" + } + ] + }, + { + "property": "m1-favorite", + "siid": 10, + "piid": 7, + "friendlyName": "Motor Speed - M1 Favorite", + "channel": "m1_favorite", + "type": "Number", + "stateDescription": { + "minimum": 300, + "maximum": 2300, + "step": 10, + "pattern": "%.0f", + "readOnly": true + }, + "refresh": true, + "actions": [] + }, + { + "property": "motor1-speed", + "siid": 10, + "piid": 8, + "friendlyName": "Motor Speed - Motor1 Speed", + "channel": "motor1_speed", + "type": "Number", + "stateDescription": { + "minimum": 0, + "maximum": 10000, + "step": 1, + "pattern": "%.0f", + "readOnly": true + }, + "refresh": true, + "actions": [] + }, + { + "property": "motor1-set-speed", + "siid": 10, + "piid": 9, + "friendlyName": "Motor Speed - Motor1 Set Speed", + "channel": "motor1_set_speed", + "type": "Number", + "stateDescription": { + "minimum": 0, + "maximum": 10000, + "step": 1, + "pattern": "%.0f", + "readOnly": true + }, + "refresh": true, + "actions": [] + }, + { + "property": "favorite-level", + "siid": 10, + "piid": 10, + "friendlyName": "Motor Speed - Favorite Level", + "channel": "favorite_level", + "type": "Number", + "stateDescription": { + "minimum": 0, + "maximum": 9, + "step": 1, + "pattern": "%.0f" + }, + "refresh": true, + "actions": [ + { + "command": "set_properties", + "parameterType": "NUMBER" + } + ] + }, + { + "property": "use-time", + "siid": 12, + "piid": 1, + "friendlyName": "Use Time - Use Time", + "channel": "use_time", + "type": "Number:Time", + "unit": "seconds", + "stateDescription": { + "minimum": 0, + "maximum": 2147483647, + "step": 1, + "pattern": "%.0f %unit%", + "readOnly": true + }, + "refresh": true, + "actions": [] + }, + { + "property": "purify-volume", + "siid": 13, + "piid": 1, + "friendlyName": "Aqi - Purify Volume", + "channel": "purify_volume", + "type": "Number", + "stateDescription": { + "minimum": 0, + "maximum": 2147483647, + "step": 1, + "pattern": "%.0f", + "readOnly": true + }, + "refresh": true, + "actions": [] + }, + { + "property": "average-aqi", + "siid": 13, + "piid": 2, + "friendlyName": "Aqi - Average Aqi", + "channel": "average_aqi", + "type": "Number", + "stateDescription": { + "minimum": 0, + "maximum": 600, + "step": 1, + "pattern": "%.0f", + "readOnly": true + }, + "refresh": true, + "actions": [] + }, + { + "property": "average-aqi-cnt", + "siid": 13, + "piid": 3, + "friendlyName": "Aqi - Average_aqi Read Times", + "channel": "average_aqi_cnt", + "type": "Number", + "stateDescription": { + "minimum": 0, + "maximum": 2147483647, + "step": 1, + "pattern": "%.0f", + "readOnly": true + }, + "refresh": true, + "actions": [] + }, + { + "property": "aqi-zone", + "siid": 13, + "piid": 4, + "friendlyName": "Aqi - Aqi Zone", + "channel": "aqi_zone", + "type": "String", + "stateDescription": { + "readOnly": true + }, + "refresh": true, + "actions": [] + }, + { + "property": "sensor-state", + "siid": 13, + "piid": 5, + "friendlyName": "Aqi - Sensor State", + "channel": "sensor_state", + "type": "Number", + "stateDescription": { + "readOnly": true, + "options": [ + { + "value": "0", + "label": "waiting" + }, + { + "value": "1", + "label": "ready" + } + ] + }, + "refresh": true, + "actions": [], + "readmeComment": "Value mapping [\"0\"\u003d\"waiting\",\"1\"\u003d\"ready\"]" + }, + { + "property": "aqi-goodh", + "siid": 13, + "piid": 6, + "friendlyName": "Aqi - Aqi Goodh", + "channel": "aqi_goodh", + "type": "Number", + "stateDescription": { + "minimum": 0, + "maximum": 255, + "step": 1, + "pattern": "%.0f" + }, + "refresh": true, + "actions": [ + { + "command": "set_properties", + "parameterType": "NUMBER" + } + ] + }, + { + "property": "aqi-runstate", + "siid": 13, + "piid": 7, + "friendlyName": "Aqi - Runstate", + "channel": "aqi_runstate", + "type": "Number", + "stateDescription": { + "readOnly": true, + "options": [ + { + "value": "0", + "label": "continue" + }, + { + "value": "1", + "label": "hold" + }, + { + "value": "2", + "label": "sleep" + } + ] + }, + "refresh": true, + "actions": [], + "readmeComment": "Value mapping [\"0\"\u003d\"continue\",\"1\"\u003d\"hold\",\"2\"\u003d\"sleep\"]" + }, + { + "property": "aqi-state", + "siid": 13, + "piid": 8, + "friendlyName": "Aqi - Aqi State", + "channel": "aqi_state", + "type": "Number", + "stateDescription": { + "readOnly": true, + "options": [ + { + "value": "0", + "label": "AQI_GOOD_L" + }, + { + "value": "1", + "label": "AQI_GOOD_H" + }, + { + "value": "2", + "label": "AQI_MID_L" + }, + { + "value": "3", + "label": "AQI_MID_H" + }, + { + "value": "4", + "label": "AQI_BAD_L" + }, + { + "value": "5", + "label": "AQI_BAD_H" + } + ] + }, + "refresh": true, + "actions": [], + "readmeComment": "Value mapping [\"0\"\u003d\"AQI_GOOD_L\",\"1\"\u003d\"AQI_GOOD_H\",\"2\"\u003d\"AQI_MID_L\",\"3\"\u003d\"AQI_MID_H\",\"4\"\u003d\"AQI_BAD_L\",\"5\"\u003d\"AQI_BAD_H\"]" + }, + { + "property": "rfid-tag", + "siid": 14, + "piid": 1, + "friendlyName": "Rfid - Rfid Tag", + "channel": "rfid_tag", + "type": "String", + "stateDescription": { + "readOnly": true + }, + "refresh": true, + "actions": [] + }, + { + "property": "rfid-factory-id", + "siid": 14, + "piid": 2, + "friendlyName": "Rfid - Rfid Factory Id", + "channel": "rfid_factory_id", + "type": "String", + "stateDescription": { + "readOnly": true + }, + "refresh": true, + "actions": [] + }, + { + "property": "rfid-product-id", + "siid": 14, + "piid": 3, + "friendlyName": "Rfid - Rfid Product Id", + "channel": "rfid_product_id", + "type": "String", + "stateDescription": { + "readOnly": true + }, + "refresh": true, + "actions": [] + }, + { + "property": "rfid-time", + "siid": 14, + "piid": 4, + "friendlyName": "Rfid - Rfid Time", + "channel": "rfid_time", + "type": "String", + "stateDescription": { + "readOnly": true + }, + "refresh": true, + "actions": [] + }, + { + "property": "rfid-serial-num", + "siid": 14, + "piid": 5, + "friendlyName": "Rfid - Rfid Serial Num", + "channel": "rfid_serial_num", + "type": "String", + "stateDescription": { + "readOnly": true + }, + "refresh": true, + "actions": [] + }, + { + "property": "app-extra", + "siid": 15, + "piid": 1, + "friendlyName": "Others - App Extra", + "channel": "app_extra", + "type": "Number", + "stateDescription": { + "minimum": 0, + "maximum": 2147483647, + "step": 1, + "pattern": "%.0f" + }, + "refresh": true, + "actions": [ + { + "command": "set_properties", + "parameterType": "NUMBER" + } + ] + }, + { + "property": "main-channel", + "siid": 15, + "piid": 2, + "friendlyName": "Others - Main Channel", + "channel": "main_channel", + "type": "Number", + "stateDescription": { + "minimum": 0, + "maximum": 2147483647, + "step": 1, + "pattern": "%.0f", + "readOnly": true + }, + "refresh": true, + "actions": [] + }, + { + "property": "slave-channel", + "siid": 15, + "piid": 3, + "friendlyName": "Others - Slave Channel", + "channel": "slave_channel", + "type": "Number", + "stateDescription": { + "minimum": 0, + "maximum": 2147483647, + "step": 1, + "pattern": "%.0f", + "readOnly": true + }, + "refresh": true, + "actions": [] + }, + { + "property": "cola", + "siid": 15, + "piid": 4, + "friendlyName": "Others - Cola", + "channel": "cola", + "type": "String", + "refresh": true, + "actions": [ + { + "command": "set_properties", + "parameterType": "STRING" + } + ] + }, + { + "property": "buttom-door", + "siid": 15, + "piid": 5, + "friendlyName": "Others - Buttom Door", + "channel": "buttom_door", + "type": "String", + "stateDescription": { + "readOnly": true + }, + "refresh": true, + "actions": [] + }, + { + "property": "reboot-cause", + "siid": 15, + "piid": 6, + "friendlyName": "Others - Reboot_cause", + "channel": "reboot_cause", + "type": "Number", + "stateDescription": { + "readOnly": true, + "options": [ + { + "value": "0", + "label": "REASON_HW_BOOT" + }, + { + "value": "1", + "label": "REASON_USER_REBOOT" + }, + { + "value": "2", + "label": "REASON_UPDATE" + }, + { + "value": "3", + "label": "REASON_WDT" + } + ] + }, + "refresh": true, + "actions": [], + "readmeComment": "Value mapping [\"0\"\u003d\"REASON_HW_BOOT\",\"1\"\u003d\"REASON_USER_REBOOT\",\"2\"\u003d\"REASON_UPDATE\",\"3\"\u003d\"REASON_WDT\"]" + }, + { + "property": "manual-level", + "siid": 15, + "piid": 7, + "friendlyName": "Others - Manual Level", + "channel": "manual_level", + "type": "Number", + "stateDescription": { + "readOnly": true, + "options": [ + { + "value": "1", + "label": "level1" + }, + { + "value": "2", + "label": "level2" + }, + { + "value": "3", + "label": "level3" + } + ] + }, + "refresh": true, + "actions": [], + "readmeComment": "Value mapping [\"1\"\u003d\"level1\",\"2\"\u003d\"level2\",\"3\"\u003d\"level3\"]" + }, + { + "property": "powertime", + "siid": 15, + "piid": 8, + "friendlyName": "Others - Powertime", + "channel": "powertime", + "type": "Number:duration", + "unit": "seconds", + "stateDescription": { + "minimum": 0, + "maximum": 2147483647, + "step": 1, + "pattern": "%.0f", + "readOnly": true + }, + "refresh": true, + "actions": [] + }, + { + "property": "country-code", + "siid": 15, + "piid": 9, + "friendlyName": "Others - Country Code", + "channel": "country_code", + "type": "Number", + "stateDescription": { + "options": [ + { + "value": "91", + "label": "India" + }, + { + "value": "44", + "label": "UK" + }, + { + "value": "852", + "label": "Hong Kong" + }, + { + "value": "886", + "label": "Taiwan" + }, + { + "value": "82", + "label": "Korea" + } + ] + }, + "refresh": true, + "actions": [ + { + "command": "set_properties", + "parameterType": "NUMBER" + } + ], + "readmeComment": "Value mapping [\"91\"\u003d\"India\",\"44\"\u003d\"UK\",\"852\"\u003d\"Hong Kong\",\"886\"\u003d\"Taiwan\",\"82\"\u003d\"Korea\"]" + } + ], + "experimental": false + } +} diff --git a/bundles/org.openhab.binding.miio/src/main/resources/database/zhimi.fan.za5-miot.json b/bundles/org.openhab.binding.miio/src/main/resources/database/zhimi.fan.za5-miot.json new file mode 100644 index 0000000000000..a5e92ab2d8670 --- /dev/null +++ b/bundles/org.openhab.binding.miio/src/main/resources/database/zhimi.fan.za5-miot.json @@ -0,0 +1,361 @@ +{ + "deviceMapping": { + "id": [ + "zhimi.fan.za5" + ], + "propertyMethod": "get_properties", + "maxProperties": 1, + "channels": [ + { + "property": "on", + "siid": 2, + "piid": 1, + "friendlyName": "Fan - Power", + "channel": "on", + "type": "Switch", + "refresh": true, + "actions": [ + { + "command": "set_properties", + "parameterType": "ONOFFBOOL" + } + ], + "category": "switch", + "tags": [ + "Switch" + ] + }, + { + "property": "fan-level", + "siid": 2, + "piid": 2, + "friendlyName": "Fan - Fan Level", + "channel": "fan_level", + "type": "Number", + "stateDescription": { + "options": [ + { + "value": "1", + "label": "Level 1" + }, + { + "value": "2", + "label": "Level 2" + }, + { + "value": "3", + "label": "Level 3" + }, + { + "value": "4", + "label": "Level 4" + } + ] + }, + "refresh": true, + "actions": [ + { + "command": "set_properties", + "parameterType": "NUMBER" + } + ], + "readmeComment": "Value mapping [\"1\"\u003d\"Level 1\",\"2\"\u003d\"Level 2\",\"3\"\u003d\"Level 3\",\"4\"\u003d\"Level 4\"]" + }, + { + "property": "horizontal-swing", + "siid": 2, + "piid": 3, + "friendlyName": "Fan - Horizontal Swing", + "channel": "horizontal_swing", + "type": "Switch", + "refresh": true, + "actions": [ + { + "command": "set_properties", + "parameterType": "ONOFFBOOL" + } + ] + }, + { + "property": "horizontal-angle", + "siid": 2, + "piid": 5, + "friendlyName": "Fan - Horizontal Angle", + "channel": "horizontal_angle", + "type": "Number", + "stateDescription": { + "minimum": 30, + "maximum": 120, + "step": 1, + "pattern": "%.0f" + }, + "refresh": true, + "actions": [ + { + "command": "set_properties", + "parameterType": "STRING" + } + ] + }, + { + "property": "mode", + "siid": 2, + "piid": 7, + "friendlyName": "Fan - Mode", + "channel": "mode", + "type": "Number", + "stateDescription": { + "options": [ + { + "value": "0", + "label": "Natural Wind" + }, + { + "value": "1", + "label": "Straight Wind" + } + ] + }, + "refresh": true, + "actions": [ + { + "command": "set_properties", + "parameterType": "NUMBER" + } + ], + "readmeComment": "Value mapping [\"0\"\u003d\"Natural Wind\",\"1\"\u003d\"Straight Wind\"]" + }, + { + "property": "off-delay", + "siid": 2, + "piid": 10, + "friendlyName": "Fan - Power Off Delay", + "channel": "off_delay", + "type": "Number", + "stateDescription": { + "minimum": 0, + "maximum": 36000, + "step": 1, + "pattern": "%.0f" + }, + "refresh": true, + "actions": [ + { + "command": "set_properties", + "parameterType": "STRING" + } + ] + }, + { + "property": "anion", + "siid": 2, + "piid": 11, + "friendlyName": "Fan - Anion", + "channel": "anion", + "type": "Switch", + "refresh": true, + "actions": [ + { + "command": "set_properties", + "parameterType": "ONOFFBOOL" + } + ] + }, + { + "property": "physical-controls-locked", + "siid": 3, + "piid": 1, + "friendlyName": "Physical Control Locked - Physical Control Locked", + "channel": "physical_controls_locked", + "type": "Switch", + "refresh": true, + "actions": [ + { + "command": "set_properties", + "parameterType": "ONOFFBOOL" + } + ] + }, + { + "property": "brightness", + "siid": 4, + "piid": 3, + "friendlyName": "Indicator Light - Brightness", + "channel": "brightness", + "type": "Number:Dimensionless", + "unit": "percentage", + "stateDescription": { + "minimum": 0, + "maximum": 100, + "step": 1, + "pattern": "%.0f %unit%" + }, + "refresh": true, + "actions": [ + { + "command": "set_properties", + "parameterType": "NUMBER" + } + ], + "category": "light", + "tags": [ + "Control", + "Light" + ] + }, + { + "property": "alarm", + "siid": 5, + "piid": 1, + "friendlyName": "Alarm - Alarm", + "channel": "alarm", + "type": "Switch", + "refresh": true, + "actions": [ + { + "command": "set_properties", + "parameterType": "ONOFFBOOL" + } + ] + }, + { + "property": "relative-humidity", + "siid": 7, + "piid": 1, + "friendlyName": "Environment - Relative Humidity", + "channel": "relative_humidity", + "type": "Number:Dimensionless", + "unit": "percentage", + "stateDescription": { + "minimum": 0, + "maximum": 100, + "step": 1, + "pattern": "%.0f %unit%", + "readOnly": true + }, + "refresh": true, + "actions": [], + "tags": [ + "Measurement", + "Humidity" + ] + }, + { + "property": "temperature", + "siid": 7, + "piid": 7, + "friendlyName": "Environment - Temperature", + "channel": "temperature", + "type": "Number:Temperature", + "unit": "celsius", + "stateDescription": { + "minimum": -30, + "maximum": 100, + "pattern": "%.1f %unit%", + "readOnly": true + }, + "refresh": true, + "actions": [], + "category": "temperature", + "tags": [ + "Setpoint", + "Temperature" + ] + }, + { + "property": "button-press", + "siid": 6, + "piid": 1, + "friendlyName": "Custom Service - Button Press", + "channel": "button_press", + "type": "Number", + "stateDescription": { + "readOnly": true, + "options": [ + { + "value": "1", + "label": "power" + }, + { + "value": "2", + "label": "swing" + }, + { + "value": "0", + "label": "No Button Pressed" + } + ] + }, + "refresh": true, + "actions": [], + "readmeComment": "Value mapping [\"1\"\u003d\"power\",\"2\"\u003d\"swing\",\"0\"\u003d\"No Button Pressed\"]" + }, + { + "property": "battery-state", + "siid": 6, + "piid": 2, + "friendlyName": "Custom Service - Battery State", + "channel": "battery_state", + "type": "Switch", + "stateDescription": { + "readOnly": true + }, + "refresh": true, + "actions": [] + }, + { + "property": "speed-now", + "siid": 6, + "piid": 4, + "friendlyName": "Custom Service - Speed Now", + "channel": "speed_now", + "type": "Number", + "stateDescription": { + "minimum": 0, + "maximum": 3000, + "step": 1, + "pattern": "%.0f", + "readOnly": true + }, + "refresh": true, + "actions": [] + }, + { + "property": "ac-state", + "siid": 6, + "piid": 5, + "friendlyName": "Custom Service - Ac State", + "channel": "ac_state", + "type": "Switch", + "stateDescription": { + "readOnly": true + }, + "refresh": true, + "actions": [] + }, + { + "property": "speed-level", + "siid": 6, + "piid": 8, + "friendlyName": "Custom Service - Speed Level", + "channel": "speed_level", + "type": "Number:Dimensionless", + "unit": "percent", + "stateDescription": { + "minimum": 1, + "maximum": 100, + "step": 1, + "pattern": "%.0f %unit%" + }, + "refresh": true, + "actions": [ + { + "command": "set_properties", + "parameterType": "NUMBER" + } + ] + } + ], + "experimental": true + } +} diff --git a/bundles/org.openhab.binding.miio/src/main/resources/database/zhimi.heater.ma2-miot.json b/bundles/org.openhab.binding.miio/src/main/resources/database/zhimi.heater.ma2-miot.json new file mode 100644 index 0000000000000..5976b2e2a7399 --- /dev/null +++ b/bundles/org.openhab.binding.miio/src/main/resources/database/zhimi.heater.ma2-miot.json @@ -0,0 +1,241 @@ +{ + "deviceMapping": { + "id": [ + "zhimi.heater.ma2" + ], + "propertyMethod": "get_properties", + "maxProperties": 1, + "channels": [ + { + "property": "on", + "siid": 2, + "piid": 1, + "friendlyName": "Heater - Switch Status", + "channel": "on", + "type": "Switch", + "refresh": true, + "actions": [ + { + "command": "set_properties", + "parameterType": "ONOFFBOOL" + } + ], + "category": "switch", + "tags": [ + "Switch" + ] + }, + { + "property": "fault", + "siid": 2, + "piid": 2, + "friendlyName": "Heater - Fault", + "channel": "fault", + "type": "Number", + "stateDescription": { + "readOnly": true, + "options": [ + { + "value": "0", + "label": "No Error" + }, + { + "value": "1", + "label": "NTC Connect Error" + }, + { + "value": "2", + "label": "High Temperature Alarm" + }, + { + "value": "3", + "label": "EEPROM Error" + }, + { + "value": "4", + "label": "Multi Errors" + } + ] + }, + "refresh": true, + "actions": [], + "readmeComment": "Value mapping [\"0\"\u003d\"No Error\",\"1\"\u003d\"NTC Connect Error\",\"2\"\u003d\"High Temperature Alarm\",\"3\"\u003d\"EEPROM Error\",\"4\"\u003d\"Multi Errors\"]" + }, + { + "property": "target-temperature", + "siid": 2, + "piid": 5, + "friendlyName": "Heater - Target Temperature", + "channel": "target_temperature", + "type": "Number:Temperature", + "unit": "celsius", + "stateDescription": { + "minimum": 18, + "maximum": 28, + "step": 1, + "pattern": "%.1f %unit%" + }, + "refresh": true, + "actions": [ + { + "command": "set_properties", + "parameterType": "NUMBER" + } + ], + "category": "temperature", + "tags": [ + "Setpoint", + "Temperature" + ] + }, + { + "property": "countdown-time", + "siid": 3, + "piid": 1, + "friendlyName": "Countdown - Countdown Time", + "channel": "countdown_time", + "type": "Number:Time", + "unit": "hours", + "stateDescription": { + "minimum": 0, + "maximum": 12, + "step": 1, + "pattern": "%.0f %unit%" + }, + "refresh": true, + "actions": [ + { + "command": "set_properties", + "parameterType": "STRING" + } + ] + }, + { + "property": "temperature", + "siid": 4, + "piid": 7, + "friendlyName": "Environment - Temperature", + "channel": "temperature", + "type": "Number:Temperature", + "unit": "celsius", + "stateDescription": { + "minimum": -30, + "maximum": 100, + "pattern": "%.1f %unit%", + "readOnly": true + }, + "refresh": true, + "actions": [], + "category": "temperature", + "tags": [ + "Measurement", + "Temperature" + ] + }, + { + "property": "physical-controls-locked", + "siid": 5, + "piid": 1, + "friendlyName": "Physical Control Locked - Physical Control Locked", + "channel": "physical_controls_locked", + "type": "Switch", + "refresh": true, + "actions": [ + { + "command": "set_properties", + "parameterType": "ONOFFBOOL" + } + ] + }, + { + "property": "alarm", + "siid": 6, + "piid": 1, + "friendlyName": "Alarm - Alarm", + "channel": "alarm", + "type": "Switch", + "refresh": true, + "actions": [ + { + "command": "set_properties", + "parameterType": "ONOFFBOOL" + } + ], + "category": "alarm" + }, + { + "property": "brightness", + "siid": 7, + "piid": 3, + "friendlyName": "Indicator Light - Brightness", + "channel": "brightness", + "type": "Number:Dimensionless", + "unit": "percentage", + "stateDescription": { + "options": [ + { + "value": "0", + "label": "Bright" + }, + { + "value": "1", + "label": "Dark" + }, + { + "value": "2", + "label": "Extinguished" + } + ] + }, + "refresh": true, + "actions": [ + { + "command": "set_properties", + "parameterType": "NUMBER" + } + ], + "category": "light", + "tags": [ + "Control", + "Light" + ], + "readmeComment": "Value mapping [\"0\"\u003d\"Bright\",\"1\"\u003d\"Dark\",\"2\"\u003d\"Extinguished\"]" + }, + { + "property": "hw-enable", + "siid": 8, + "piid": 8, + "friendlyName": "Private Service - Hw Enable", + "channel": "hw_enable", + "type": "Switch", + "refresh": true, + "actions": [ + { + "command": "set_properties", + "parameterType": "ONOFFBOOL" + } + ] + }, + { + "property": "use-time", + "siid": 8, + "piid": 9, + "friendlyName": "Private Service - Use Time", + "channel": "use_time", + "type": "Number:Time", + "unit": "seconds", + "stateDescription": { + "minimum": 0, + "maximum": 2147483647, + "step": 1, + "pattern": "%.0f %unit%", + "readOnly": true + }, + "refresh": true, + "actions": [], + "category": "time" + } + ], + "experimental": true + } +} diff --git a/bundles/org.openhab.binding.miio/src/main/resources/database/zhimi.heater.ma3-miot.json b/bundles/org.openhab.binding.miio/src/main/resources/database/zhimi.heater.ma3-miot.json new file mode 100644 index 0000000000000..ad79fffd912bd --- /dev/null +++ b/bundles/org.openhab.binding.miio/src/main/resources/database/zhimi.heater.ma3-miot.json @@ -0,0 +1,288 @@ +{ + "deviceMapping": { + "id": [ + "zhimi.heater.ma3" + ], + "propertyMethod": "get_properties", + "maxProperties": 1, + "channels": [ + { + "property": "", + "friendlyName": "Actions", + "channel": "actions", + "type": "String", + "stateDescription": { + "options": [ + { + "value": "private-service-toggle-switch", + "label": "Toggle Private Service" + } + ] + }, + "actions": [ + { + "command": "action", + "parameterType": "EMPTY", + "siid": 8, + "aiid": 1, + "condition": { + "name": "matchValue", + "parameters": [ + { + "matchValue": "private-service-toggle-switch" + } + ] + } + } + ] + }, + { + "property": "on", + "siid": 2, + "piid": 1, + "friendlyName": "Heater - Switch Status", + "channel": "on", + "type": "Switch", + "refresh": true, + "actions": [ + { + "command": "set_properties", + "parameterType": "ONOFFBOOL" + } + ], + "category": "switch", + "tags": [ + "Switch" + ] + }, + { + "property": "fault", + "siid": 2, + "piid": 2, + "friendlyName": "Heater - Fault", + "channel": "fault", + "type": "Number", + "stateDescription": { + "readOnly": true, + "options": [ + { + "value": "0", + "label": "No Error" + }, + { + "value": "1", + "label": "NTC Connect Error" + }, + { + "value": "2", + "label": "High Temperature Alarm" + }, + { + "value": "3", + "label": "EEPROM Error" + }, + { + "value": "4", + "label": "Multi Errors" + } + ] + }, + "refresh": true, + "actions": [], + "readmeComment": "Value mapping [\"0\"\u003d\"No Error\",\"1\"\u003d\"NTC Connect Error\",\"2\"\u003d\"High Temperature Alarm\",\"3\"\u003d\"EEPROM Error\",\"4\"\u003d\"Multi Errors\"]" + }, + { + "property": "target-temperature", + "siid": 2, + "piid": 5, + "friendlyName": "Heater - Target Temperature", + "channel": "target_temperature", + "type": "Number:Temperature", + "unit": "celsius", + "stateDescription": { + "minimum": 16, + "maximum": 28, + "step": 1, + "pattern": "%.1f %unit%" + }, + "refresh": true, + "actions": [ + { + "command": "set_properties", + "parameterType": "NUMBER" + } + ], + "category": "temperature", + "tags": [ + "Setpoint", + "Temperature" + ] + }, + { + "property": "mode", + "siid": 2, + "piid": 6, + "friendlyName": "Heater - Mode", + "channel": "mode", + "type": "Number", + "stateDescription": { + "options": [ + { + "value": "0", + "label": "Auto" + }, + { + "value": "1", + "label": "LL Mode" + }, + { + "value": "2", + "label": "HH Mode" + } + ] + }, + "refresh": true, + "actions": [ + { + "command": "set_properties", + "parameterType": "NUMBER" + } + ], + "readmeComment": "Value mapping [\"0\"\u003d\"Auto\",\"1\"\u003d\"LL Mode\",\"2\"\u003d\"HH Mode\"]" + }, + { + "property": "countdown-time", + "siid": 3, + "piid": 1, + "friendlyName": "Countdown - Countdown Time", + "channel": "countdown_time", + "type": "Number:Time", + "unit": "seconds", + "stateDescription": { + "minimum": 0, + "maximum": 43200, + "step": 1, + "pattern": "%.0f %unit%" + }, + "refresh": true, + "actions": [ + { + "command": "set_properties", + "parameterType": "STRING" + } + ] + }, + { + "property": "temperature", + "siid": 4, + "piid": 7, + "friendlyName": "Environment - Temperature", + "channel": "temperature", + "type": "Number:Temperature", + "unit": "celsius", + "stateDescription": { + "minimum": -30, + "maximum": 100, + "pattern": "%.1f %unit%", + "readOnly": true + }, + "refresh": true, + "actions": [], + "category": "temperature", + "tags": [ + "Measurement", + "Temperature" + ] + }, + { + "property": "physical-controls-locked", + "siid": 5, + "piid": 1, + "friendlyName": "Physical Control Locked - Physical Control Locked", + "channel": "physical_controls_locked", + "type": "Switch", + "refresh": true, + "actions": [ + { + "command": "set_properties", + "parameterType": "ONOFFBOOL" + } + ] + }, + { + "property": "alarm", + "siid": 6, + "piid": 1, + "friendlyName": "Alarm - Alarm", + "channel": "alarm", + "type": "Switch", + "refresh": true, + "actions": [ + { + "command": "set_properties", + "parameterType": "ONOFFBOOL" + } + ], + "category": "alarm" + }, + { + "property": "brightness", + "siid": 7, + "piid": 3, + "friendlyName": "Indicator Light - Brightness", + "channel": "brightness", + "type": "Number:Dimensionless", + "unit": "percentage", + "stateDescription": { + "options": [ + { + "value": "0", + "label": "Bright" + }, + { + "value": "1", + "label": "Dark" + }, + { + "value": "2", + "label": "Extinguished" + } + ] + }, + "refresh": true, + "actions": [ + { + "command": "set_properties", + "parameterType": "NUMBER" + } + ], + "category": "light", + "tags": [ + "Control", + "Light" + ], + "readmeComment": "Value mapping [\"0\"\u003d\"Bright\",\"1\"\u003d\"Dark\",\"2\"\u003d\"Extinguished\"]" + }, + { + "property": "use-time", + "siid": 8, + "piid": 9, + "friendlyName": "Private Service - Use Time", + "channel": "use_time", + "type": "Number:Time", + "unit": "seconds", + "stateDescription": { + "minimum": 0, + "maximum": 2147483647, + "step": 1, + "pattern": "%.0f %unit%", + "readOnly": true + }, + "refresh": true, + "actions": [] + } + ], + "readmeComment": "Identified manual actions for execution\u003cbr /\u003e`action{\"did\":\"private-service-toggle-switch\",\"siid\":8,\"aiid\":1,\"in\":[]}`\u003cbr /\u003ePlease test and feedback if they are working to they can be linked to a channel.", + "experimental": true + } +} diff --git a/bundles/org.openhab.binding.miio/src/main/resources/database/zhimi.heater.mc2-miot.json b/bundles/org.openhab.binding.miio/src/main/resources/database/zhimi.heater.mc2-miot.json new file mode 100644 index 0000000000000..4982172b49b4d --- /dev/null +++ b/bundles/org.openhab.binding.miio/src/main/resources/database/zhimi.heater.mc2-miot.json @@ -0,0 +1,299 @@ +{ + "deviceMapping": { + "id": [ + "zhimi.heater.mc2" + ], + "propertyMethod": "get_properties", + "maxProperties": 1, + "channels": [ + { + "property": "on", + "siid": 2, + "piid": 1, + "friendlyName": "Heater - Power", + "channel": "on", + "type": "Switch", + "refresh": true, + "actions": [ + { + "command": "set_properties", + "parameterType": "ONOFFBOOL" + } + ], + "category": "switch", + "tags": [ + "Switch" + ] + }, + { + "property": "fault", + "siid": 2, + "piid": 2, + "friendlyName": "Heater - Device Fault", + "channel": "fault", + "type": "Number", + "stateDescription": { + "readOnly": true, + "options": [ + { + "value": "0", + "label": "No Error" + }, + { + "value": "1", + "label": "NTC Connect Error" + }, + { + "value": "2", + "label": "High Temperature Alarm" + }, + { + "value": "3", + "label": "EEPROM Error" + }, + { + "value": "4", + "label": "Multi Errors" + } + ] + }, + "refresh": true, + "actions": [], + "readmeComment": "Value mapping [\"0\"\u003d\"No Error\",\"1\"\u003d\"NTC Connect Error\",\"2\"\u003d\"High Temperature Alarm\",\"3\"\u003d\"EEPROM Error\",\"4\"\u003d\"Multi Errors\"]" + }, + { + "property": "target-temperature", + "siid": 2, + "piid": 5, + "friendlyName": "Heater - Target Temperature", + "channel": "target_temperature", + "type": "Number:Temperature", + "unit": "celsius", + "stateDescription": { + "minimum": 18, + "maximum": 28, + "step": 1, + "pattern": "%.1f %unit%" + }, + "refresh": true, + "actions": [ + { + "command": "set_properties", + "parameterType": "NUMBER" + } + ], + "category": "temperature", + "tags": [ + "Setpoint", + "Temperature" + ] + }, + { + "property": "countdown-time", + "siid": 3, + "piid": 1, + "friendlyName": "Countdown - Countdown Time", + "channel": "countdown_time", + "type": "Number:Time", + "unit": "hours", + "stateDescription": { + "minimum": 0, + "maximum": 12, + "step": 1, + "pattern": "%.0f %unit%" + }, + "refresh": true, + "actions": [ + { + "command": "set_properties", + "parameterType": "STRING" + } + ] + }, + { + "property": "temperature", + "siid": 4, + "piid": 7, + "friendlyName": "Environment - Temperature", + "channel": "temperature", + "type": "Number:Temperature", + "unit": "celsius", + "stateDescription": { + "minimum": -30, + "maximum": 100, + "pattern": "%.1f %unit%", + "readOnly": true + }, + "refresh": true, + "actions": [], + "category": "temperature", + "tags": [ + "Measurement", + "Temperature" + ] + }, + { + "property": "physical-controls-locked", + "siid": 5, + "piid": 1, + "friendlyName": "Physical Control Locked - Physical Control Locked", + "channel": "physical_controls_locked", + "type": "Switch", + "refresh": true, + "actions": [ + { + "command": "set_properties", + "parameterType": "ONOFFBOOL" + } + ] + }, + { + "property": "alarm", + "siid": 6, + "piid": 1, + "friendlyName": "Alarm - Alarm", + "channel": "alarm", + "type": "Switch", + "refresh": true, + "actions": [ + { + "command": "set_properties", + "parameterType": "ONOFFBOOL" + } + ], + "category": "alarm" + }, + { + "property": "brightness", + "siid": 7, + "piid": 3, + "friendlyName": "Indicator Light - Brightness", + "channel": "brightness", + "type": "Number", + "stateDescription": { + "options": [ + { + "value": "0", + "label": "Bright" + }, + { + "value": "1", + "label": "Dark" + }, + { + "value": "2", + "label": "Extinguished" + } + ] + }, + "refresh": true, + "actions": [ + { + "command": "set_properties", + "parameterType": "NUMBER" + } + ], + "category": "light", + "tags": [ + "Control", + "Light" + ], + "readmeComment": "Value mapping [\"0\"\u003d\"Bright\",\"1\"\u003d\"Dark\",\"2\"\u003d\"Extinguished\"]" + }, + { + "property": "hw-enable", + "siid": 8, + "piid": 8, + "friendlyName": "Private Service - Hw Enable", + "channel": "hw_enable", + "type": "Switch", + "refresh": true, + "actions": [ + { + "command": "set_properties", + "parameterType": "ONOFFBOOL" + } + ] + }, + { + "property": "use-time", + "siid": 8, + "piid": 9, + "friendlyName": "Private Service - Use Time", + "channel": "use_time", + "type": "Number:Time", + "unit": "seconds", + "stateDescription": { + "minimum": 0, + "maximum": 2147483647, + "step": 1, + "pattern": "%.0f %unit%", + "readOnly": true + }, + "refresh": true, + "actions": [] + }, + { + "property": "country-code", + "siid": 8, + "piid": 10, + "friendlyName": "Private Service - Country Code", + "channel": "country_code", + "type": "Number", + "stateDescription": { + "options": [ + { + "value": "0", + "label": "Unknown" + }, + { + "value": "1", + "label": "US" + }, + { + "value": "82", + "label": "KR" + }, + { + "value": "44", + "label": "EU" + }, + { + "value": "81", + "label": "JP" + }, + { + "value": "7", + "label": "RU" + }, + { + "value": "86", + "label": "CN" + }, + { + "value": "852", + "label": "HK" + }, + { + "value": "886", + "label": "TW" + }, + { + "value": "33", + "label": "FR" + } + ] + }, + "refresh": true, + "actions": [ + { + "command": "set_properties", + "parameterType": "NUMBER" + } + ], + "readmeComment": "Value mapping [\"0\"\u003d\"Unknown\",\"1\"\u003d\"US\",\"82\"\u003d\"KR\",\"44\"\u003d\"EU\",\"81\"\u003d\"JP\",\"7\"\u003d\"RU\",\"86\"\u003d\"CN\",\"852\"\u003d\"HK\",\"886\"\u003d\"TW\",\"33\"\u003d\"FR\"]" + } + ], + "experimental": true + } +} diff --git a/bundles/org.openhab.binding.miio/src/main/resources/database/zhimi.heater.na1-miot.json b/bundles/org.openhab.binding.miio/src/main/resources/database/zhimi.heater.na1-miot.json new file mode 100644 index 0000000000000..3383fdae5b9a3 --- /dev/null +++ b/bundles/org.openhab.binding.miio/src/main/resources/database/zhimi.heater.na1-miot.json @@ -0,0 +1,228 @@ +{ + "deviceMapping": { + "id": [ + "zhimi.heater.na1" + ], + "propertyMethod": "get_properties", + "maxProperties": 1, + "channels": [ + { + "property": "on", + "siid": 2, + "piid": 2, + "friendlyName": "Heater - Power", + "channel": "on", + "type": "Switch", + "refresh": true, + "actions": [ + { + "command": "set_properties", + "parameterType": "ONOFFBOOL" + } + ], + "category": "switch", + "tags": [ + "Switch" + ] + }, + { + "property": "fault", + "siid": 2, + "piid": 1, + "friendlyName": "Heater - Device Fault", + "channel": "fault", + "type": "Number", + "stateDescription": { + "readOnly": true, + "options": [ + { + "value": "0", + "label": "No Error" + }, + { + "value": "1", + "label": "NTC Connect Error" + }, + { + "value": "2", + "label": "High Temperature Alarm" + }, + { + "value": "3", + "label": "EEPROM Error" + }, + { + "value": "4", + "label": "Multi Errors" + } + ] + }, + "refresh": true, + "actions": [], + "readmeComment": "Value mapping [\"0\"\u003d\"No Error\",\"1\"\u003d\"NTC Connect Error\",\"2\"\u003d\"High Temperature Alarm\",\"3\"\u003d\"EEPROM Error\",\"4\"\u003d\"Multi Errors\"]" + }, + { + "property": "heat-level", + "siid": 2, + "piid": 3, + "friendlyName": "Heater - Heat Level", + "channel": "heat_level", + "type": "Number", + "stateDescription": { + "options": [ + { + "value": "1", + "label": "High" + }, + { + "value": "2", + "label": "Low" + } + ] + }, + "refresh": true, + "actions": [ + { + "command": "set_properties", + "parameterType": "NUMBER" + } + ], + "readmeComment": "Value mapping [\"1\"\u003d\"High\",\"2\"\u003d\"Low\"]" + }, + { + "property": "mode", + "siid": 2, + "piid": 4, + "friendlyName": "Heater - Mode", + "channel": "mode", + "type": "Number", + "stateDescription": { + "options": [ + { + "value": "0", + "label": "Fan not swing" + }, + { + "value": "1", + "label": "Fan swing" + } + ] + }, + "refresh": true, + "actions": [ + { + "command": "set_properties", + "parameterType": "NUMBER" + } + ], + "readmeComment": "Value mapping [\"0\"\u003d\"Fan not swing\",\"1\"\u003d\"Fan swing\"]" + }, + { + "property": "alarm", + "siid": 3, + "piid": 1, + "friendlyName": "Alarm - Alarm", + "channel": "alarm", + "type": "Switch", + "refresh": true, + "actions": [ + { + "command": "set_properties", + "parameterType": "ONOFFBOOL" + } + ], + "category": "alarm" + }, + { + "property": "countdown-time", + "siid": 4, + "piid": 1, + "friendlyName": "Countdown - Countdown Time", + "channel": "countdown_time", + "type": "Number:Time", + "unit": "hours", + "stateDescription": { + "minimum": 0, + "maximum": 12, + "step": 1, + "pattern": "%.0f %unit%" + }, + "refresh": true, + "actions": [ + { + "command": "set_properties", + "parameterType": "STRING" + } + ] + }, + { + "property": "brightness", + "siid": 6, + "piid": 1, + "friendlyName": "Indicator Light - Brightness", + "channel": "brightness", + "type": "Number", + "stateDescription": { + "options": [ + { + "value": "0", + "label": "Bright" + }, + { + "value": "1", + "label": "Dark" + }, + { + "value": "2", + "label": "Extinguished" + } + ] + }, + "refresh": true, + "actions": [ + { + "command": "set_properties", + "parameterType": "NUMBER" + } + ], + "category": "light", + "tags": [ + "Control", + "Light" + ], + "readmeComment": "Value mapping [\"0\"\u003d\"Bright\",\"1\"\u003d\"Dark\",\"2\"\u003d\"Extinguished\"]" + }, + { + "property": "physical-controls-locked", + "siid": 7, + "piid": 1, + "friendlyName": "Physical Control Locked - Physical Control Locked", + "channel": "physical_controls_locked", + "type": "Switch", + "refresh": true, + "actions": [ + { + "command": "set_properties", + "parameterType": "ONOFFBOOL" + } + ] + }, + { + "property": "return-to-middle", + "siid": 8, + "piid": 3, + "friendlyName": "Private Service - Return To Middle", + "channel": "return_to_middle", + "type": "Switch", + "refresh": true, + "actions": [ + { + "command": "set_properties", + "parameterType": "ONOFFBOOL" + } + ] + } + ], + "experimental": true + } +} diff --git a/bundles/org.openhab.binding.miio/src/main/resources/database/zhimi.heater.nb1-miot.json b/bundles/org.openhab.binding.miio/src/main/resources/database/zhimi.heater.nb1-miot.json new file mode 100644 index 0000000000000..61248fa404d82 --- /dev/null +++ b/bundles/org.openhab.binding.miio/src/main/resources/database/zhimi.heater.nb1-miot.json @@ -0,0 +1,342 @@ +{ + "deviceMapping": { + "id": [ + "zhimi.heater.nb1" + ], + "propertyMethod": "get_properties", + "maxProperties": 1, + "channels": [ + { + "property": "on", + "siid": 2, + "piid": 2, + "friendlyName": "Heater - Power", + "channel": "on", + "type": "Switch", + "refresh": true, + "actions": [ + { + "command": "set_properties", + "parameterType": "ONOFFBOOL" + } + ], + "category": "switch", + "tags": [ + "Switch" + ] + }, + { + "property": "fault", + "siid": 2, + "piid": 1, + "friendlyName": "Heater - Device Fault", + "channel": "fault", + "type": "Number", + "stateDescription": { + "readOnly": true, + "options": [ + { + "value": "0", + "label": "No Error" + }, + { + "value": "1", + "label": "NTC Connect Error" + }, + { + "value": "2", + "label": "High Temperature Alarm" + }, + { + "value": "3", + "label": "EEPROM Error" + }, + { + "value": "4", + "label": "Multi Errors" + } + ] + }, + "refresh": true, + "actions": [], + "readmeComment": "Value mapping [\"0\"\u003d\"No Error\",\"1\"\u003d\"NTC Connect Error\",\"2\"\u003d\"High Temperature Alarm\",\"3\"\u003d\"EEPROM Error\",\"4\"\u003d\"Multi Errors\"]" + }, + { + "property": "heat-level", + "siid": 2, + "piid": 3, + "friendlyName": "Heater - Heat Level", + "channel": "heat_level", + "type": "Number", + "stateDescription": { + "options": [ + { + "value": "1", + "label": "High" + }, + { + "value": "2", + "label": "Low" + } + ] + }, + "refresh": true, + "actions": [ + { + "command": "set_properties", + "parameterType": "NUMBER" + } + ], + "readmeComment": "Value mapping [\"1\"\u003d\"High\",\"2\"\u003d\"Low\"]" + }, + { + "property": "mode", + "siid": 2, + "piid": 4, + "friendlyName": "Heater - Mode", + "channel": "mode", + "type": "Number", + "stateDescription": { + "options": [ + { + "value": "0", + "label": "Fan not swing" + }, + { + "value": "1", + "label": "Fan swing" + } + ] + }, + "refresh": true, + "actions": [ + { + "command": "set_properties", + "parameterType": "NUMBER" + } + ], + "readmeComment": "Value mapping [\"0\"\u003d\"Fan not swing\",\"1\"\u003d\"Fan swing\"]" + }, + { + "property": "target-temperature", + "siid": 2, + "piid": 5, + "friendlyName": "Heater - Target Temperature", + "channel": "target_temperature", + "type": "Number:Temperature", + "unit": "celsius", + "stateDescription": { + "minimum": 16, + "maximum": 30, + "step": 1, + "pattern": "%.1f %unit%" + }, + "refresh": true, + "actions": [ + { + "command": "set_properties", + "parameterType": "NUMBER" + } + ] + }, + { + "property": "temperature", + "siid": 9, + "piid": 7, + "friendlyName": "Environment - Temperature", + "channel": "temperature", + "type": "Number:Temperature", + "unit": "celsius", + "stateDescription": { + "minimum": -30, + "maximum": 100, + "pattern": "%.1f %unit%", + "readOnly": true + }, + "refresh": true, + "actions": [] + }, + { + "property": "alarm", + "siid": 3, + "piid": 1, + "friendlyName": "Alarm - Alarm", + "channel": "alarm", + "type": "Switch", + "refresh": true, + "actions": [ + { + "command": "set_properties", + "parameterType": "ONOFFBOOL" + } + ], + "category": "alarm" + }, + { + "property": "countdown-time", + "siid": 4, + "piid": 1, + "friendlyName": "Countdown - Countdown Time", + "channel": "countdown_time", + "type": "Number:Time", + "unit": "hours", + "stateDescription": { + "minimum": 0, + "maximum": 12, + "step": 1, + "pattern": "%.0f %unit%" + }, + "refresh": true, + "actions": [ + { + "command": "set_properties", + "parameterType": "STRING" + } + ] + }, + { + "property": "brightness", + "siid": 6, + "piid": 1, + "friendlyName": "Indicator Light - Brightness", + "channel": "brightness", + "type": "Number", + "stateDescription": { + "options": [ + { + "value": "0", + "label": "Bright" + }, + { + "value": "1", + "label": "Dark" + }, + { + "value": "2", + "label": "Extinguished" + } + ] + }, + "refresh": true, + "actions": [ + { + "command": "set_properties", + "parameterType": "NUMBER" + } + ], + "category": "light", + "tags": [ + "Control", + "Light" + ], + "readmeComment": "Value mapping [\"0\"\u003d\"Bright\",\"1\"\u003d\"Dark\",\"2\"\u003d\"Extinguished\"]" + }, + { + "property": "physical-controls-locked", + "siid": 7, + "piid": 1, + "friendlyName": "Physical Control Locked - Physical Control Locked", + "channel": "physical_controls_locked", + "type": "Switch", + "refresh": true, + "actions": [ + { + "command": "set_properties", + "parameterType": "ONOFFBOOL" + } + ] + }, + { + "property": "return-to-middle", + "siid": 8, + "piid": 3, + "friendlyName": "Private Service - Return To Middle", + "channel": "return_to_middle", + "type": "Switch", + "refresh": true, + "actions": [ + { + "command": "set_properties", + "parameterType": "ONOFFBOOL" + } + ] + }, + { + "property": "country-code", + "siid": 8, + "piid": 4, + "friendlyName": "Private Service - Country Code", + "channel": "country_code", + "type": "Number", + "stateDescription": { + "options": [ + { + "value": "0", + "label": "Unknown" + }, + { + "value": "1", + "label": "US" + }, + { + "value": "82", + "label": "KR" + }, + { + "value": "44", + "label": "EU" + }, + { + "value": "81", + "label": "JP" + }, + { + "value": "7", + "label": "RU" + }, + { + "value": "86", + "label": "CN" + }, + { + "value": "852", + "label": "HK" + }, + { + "value": "886", + "label": "TW" + }, + { + "value": "33", + "label": "FR" + } + ] + }, + "refresh": true, + "actions": [ + { + "command": "set_properties", + "parameterType": "NUMBER" + } + ], + "readmeComment": "Value mapping [\"0\"\u003d\"Unknown\",\"1\"\u003d\"US\",\"82\"\u003d\"KR\",\"44\"\u003d\"EU\",\"81\"\u003d\"JP\",\"7\"\u003d\"RU\",\"86\"\u003d\"CN\",\"852\"\u003d\"HK\",\"886\"\u003d\"TW\",\"33\"\u003d\"FR\"]" + }, + { + "property": "hw-en", + "siid": 8, + "piid": 5, + "friendlyName": "Private Service - Hw En", + "channel": "hw_en", + "type": "Switch", + "refresh": true, + "actions": [ + { + "command": "set_properties", + "parameterType": "ONOFFBOOL" + } + ] + } + ], + "experimental": true + } +} diff --git a/bundles/org.openhab.binding.miio/src/main/resources/database/zhimi.heater.za1.json b/bundles/org.openhab.binding.miio/src/main/resources/database/zhimi.heater.za1.json index 6c60317f43681..4dc9fb14e418d 100644 --- a/bundles/org.openhab.binding.miio/src/main/resources/database/zhimi.heater.za1.json +++ b/bundles/org.openhab.binding.miio/src/main/resources/database/zhimi.heater.za1.json @@ -10,7 +10,6 @@ "property": "power", "friendlyName": "Power", "channel": "power", - "channelType": "power", "type": "Switch", "refresh": true, "actions": [ @@ -69,7 +68,6 @@ "property": "buzzer", "friendlyName": "Buzzer Status", "channel": "buzzer", - "channelType": "buzzer", "type": "Switch", "refresh": true, "actions": [ @@ -96,7 +94,6 @@ "property": "child_lock", "friendlyName": "Child Lock", "channel": "childlock", - "channelType": "childlock", "type": "Switch", "refresh": true, "actions": [ @@ -141,7 +138,6 @@ "property": "use_time", "friendlyName": "Run Time", "channel": "usedhours", - "channelType": "usedhours", "type": "Number:Time", "unit": "hours", "refresh": true, diff --git a/bundles/org.openhab.binding.miio/src/main/resources/database/zhimi.heater.za2-miot.json b/bundles/org.openhab.binding.miio/src/main/resources/database/zhimi.heater.za2-miot.json index 46f853218ab18..8344f0a8ea9dd 100644 --- a/bundles/org.openhab.binding.miio/src/main/resources/database/zhimi.heater.za2-miot.json +++ b/bundles/org.openhab.binding.miio/src/main/resources/database/zhimi.heater.za2-miot.json @@ -12,10 +12,35 @@ "piid": 1, "friendlyName": "Heater - Device Fault", "channel": "fault", - "channelType": "miot_uint32", "type": "Number", + "stateDescription": { + "readOnly": true, + "options": [ + { + "value": "0", + "label": "No Error" + }, + { + "value": "1", + "label": "NTC\tConnect Error" + }, + { + "value": "2", + "label": "High Temperature Alarm" + }, + { + "value": "3", + "label": "EEPROM Error" + }, + { + "value": "4", + "label": "Multi Errors" + } + ] + }, "refresh": true, - "actions": [] + "actions": [], + "readmeComment": "Value mapping [\"0\"\u003d\"No Error\",\"1\"\u003d\"NTC\tConnect Error\",\"2\"\u003d\"High Temperature Alarm\",\"3\"\u003d\"EEPROM Error\",\"4\"\u003d\"Multi Errors\"]" }, { "property": "on", @@ -23,7 +48,6 @@ "piid": 2, "friendlyName": "Heater - Power", "channel": "on", - "channelType": "miot_bool", "type": "Switch", "refresh": true, "actions": [ @@ -43,9 +67,14 @@ "piid": 6, "friendlyName": "Heater - Target Temperature", "channel": "target-temperature", - "channelType": "ZhimiHeaterZa2_target-temperature", "type": "Number:Temperature", "unit": "CELCIUS", + "stateDescription": { + "minimum": 16, + "maximum": 28, + "step": 1, + "pattern": "%.1f %unit%" + }, "refresh": true, "actions": [ { @@ -65,7 +94,6 @@ "piid": 1, "friendlyName": "Alarm - Alarm", "channel": "alarm", - "channelType": "miot_bool", "type": "Switch", "refresh": true, "actions": [ @@ -82,9 +110,14 @@ "piid": 1, "friendlyName": "Countdown - Countdown Time", "channel": "countdown-time", - "channelType": "time", "type": "Number:Time", "unit": "hours", + "stateDescription": { + "minimum": 0, + "maximum": 8, + "step": 1, + "pattern": "%.0f %unit%" + }, "refresh": true, "actions": [ { @@ -101,6 +134,13 @@ "channel": "relative-humidity", "type": "Number:Dimensionless", "unit": "PERCENT", + "stateDescription": { + "minimum": 0, + "maximum": 100, + "step": 1, + "pattern": "%.0f %unit%", + "readOnly": true + }, "refresh": true, "actions": [], "tags": [ @@ -117,6 +157,8 @@ "type": "Number:Temperature", "unit": "CELCIUS", "stateDescription": { + "minimum": -30, + "maximum": 100, "pattern": "%.1f %unit%", "readOnly": true }, @@ -134,7 +176,23 @@ "piid": 1, "friendlyName": "Indicator Light - Brightness", "channel": "brightness", - "type": "Dimmer", + "type": "Number", + "stateDescription": { + "options": [ + { + "value": "0", + "label": "Bright" + }, + { + "value": "1", + "label": "Dark" + }, + { + "value": "2", + "label": "Extinguished" + } + ] + }, "refresh": true, "actions": [ { @@ -146,7 +204,8 @@ "tags": [ "Control", "Light" - ] + ], + "readmeComment": "Value mapping [\"0\"\u003d\"Bright\",\"1\"\u003d\"Dark\",\"2\"\u003d\"Extinguished\"]" }, { "property": "physical-controls-locked", @@ -154,7 +213,6 @@ "piid": 1, "friendlyName": "Physical Control Locked - Physical Controls Locked", "channel": "physical-controls-locked", - "channelType": "miot_bool", "type": "Switch", "refresh": true, "actions": [ @@ -170,9 +228,15 @@ "piid": 7, "friendlyName": "Private-Service - Use Time", "channel": "use-time", - "channelType": "time", "type": "Number:Time", "unit": "seconds", + "stateDescription": { + "minimum": 0, + "maximum": -1, + "step": 1, + "pattern": "%.0f %unit%", + "readOnly": true + }, "refresh": true, "actions": [], "category": "time" diff --git a/bundles/org.openhab.binding.miio/src/main/resources/database/zhimi.heater.zb1-miot.json b/bundles/org.openhab.binding.miio/src/main/resources/database/zhimi.heater.zb1-miot.json index e9d63466afc07..1513658d28d32 100644 --- a/bundles/org.openhab.binding.miio/src/main/resources/database/zhimi.heater.zb1-miot.json +++ b/bundles/org.openhab.binding.miio/src/main/resources/database/zhimi.heater.zb1-miot.json @@ -6,24 +6,12 @@ "propertyMethod": "get_properties", "maxProperties": 1, "channels": [ - { - "property": "fault", - "siid": 2, - "piid": 1, - "friendlyName": "Heater - Device Fault", - "channel": "fault", - "channelType": "miot_uint32", - "type": "Number", - "refresh": true, - "actions": [] - }, { "property": "on", "siid": 2, "piid": 2, "friendlyName": "Heater - Power", "channel": "on", - "channelType": "miot_bool", "type": "Switch", "refresh": true, "actions": [ @@ -37,15 +25,56 @@ "Switch" ] }, + { + "property": "fault", + "siid": 2, + "piid": 1, + "friendlyName": "Heater - Device Fault", + "channel": "fault", + "type": "Number", + "stateDescription": { + "readOnly": true, + "options": [ + { + "value": "0", + "label": "No Error" + }, + { + "value": "1", + "label": "NTC\tConnect Error" + }, + { + "value": "2", + "label": "High Temperature Alarm" + }, + { + "value": "3", + "label": "EEPROM Error" + }, + { + "value": "4", + "label": "Multi Errors" + } + ] + }, + "refresh": true, + "actions": [], + "readmeComment": "Value mapping [\"0\"\u003d\"No Error\",\"1\"\u003d\"NTC\tConnect Error\",\"2\"\u003d\"High Temperature Alarm\",\"3\"\u003d\"EEPROM Error\",\"4\"\u003d\"Multi Errors\"]" + }, { "property": "target-temperature", "siid": 2, "piid": 6, "friendlyName": "Heater - Target Temperature", "channel": "target-temperature", - "channelType": "ZhimiHeaterZa2_target-temperature", "type": "Number:Temperature", - "unit": "CELCIUS", + "unit": "celsius", + "stateDescription": { + "minimum": 16, + "maximum": 28, + "step": 1, + "pattern": "%.1f %unit%" + }, "refresh": true, "actions": [ { @@ -65,7 +94,6 @@ "piid": 1, "friendlyName": "Alarm - Alarm", "channel": "alarm", - "channelType": "miot_bool", "type": "Switch", "refresh": true, "actions": [ @@ -82,9 +110,14 @@ "piid": 1, "friendlyName": "Countdown - Countdown Time", "channel": "countdown-time", - "channelType": "time", "type": "Number:Time", "unit": "hours", + "stateDescription": { + "minimum": 0, + "maximum": 8, + "step": 1, + "pattern": "%.0f %unit%" + }, "refresh": true, "actions": [ { @@ -101,6 +134,13 @@ "channel": "relative-humidity", "type": "Number:Dimensionless", "unit": "PERCENT", + "stateDescription": { + "minimum": 0, + "maximum": 100, + "step": 1, + "pattern": "%.0f %unit%", + "readOnly": true + }, "refresh": true, "actions": [], "tags": [ @@ -117,6 +157,8 @@ "type": "Number:Temperature", "unit": "CELCIUS", "stateDescription": { + "minimum": -30, + "maximum": 100, "pattern": "%.1f %unit%", "readOnly": true }, @@ -134,7 +176,23 @@ "piid": 1, "friendlyName": "Indicator Light - Brightness", "channel": "brightness", - "type": "Dimmer", + "type": "Number", + "stateDescription": { + "options": [ + { + "value": "0", + "label": "Bright" + }, + { + "value": "1", + "label": "Dark" + }, + { + "value": "2", + "label": "Extinguished" + } + ] + }, "refresh": true, "actions": [ { @@ -146,7 +204,8 @@ "tags": [ "Control", "Light" - ] + ], + "readmeComment": "Value mapping [\"0\"\u003d\"Bright\",\"1\"\u003d\"Dark\",\"2\"\u003d\"Extinguished\"]" }, { "property": "physical-controls-locked", @@ -154,7 +213,6 @@ "piid": 1, "friendlyName": "Physical Control Locked - Physical Controls Locked", "channel": "physical-controls-locked", - "channelType": "miot_bool", "type": "Switch", "refresh": true, "actions": [ @@ -170,12 +228,17 @@ "piid": 7, "friendlyName": "Private-Service - Use Time", "channel": "use-time", - "channelType": "time", "type": "Number:Time", "unit": "seconds", + "stateDescription": { + "minimum": 0, + "maximum": 2147483647, + "step": 1, + "pattern": "%.0f %unit%", + "readOnly": true + }, "refresh": true, - "actions": [], - "category": "time" + "actions": [] }, { "property": "country-code", @@ -183,7 +246,6 @@ "piid": 8, "friendlyName": "Private-Service - Country-Code", "channel": "country-code", - "channelType": "ZhimiHeaterZb1_country-code", "type": "Number", "stateDescription": { "readOnly": true, diff --git a/bundles/org.openhab.binding.miio/src/main/resources/misc/device_names.json b/bundles/org.openhab.binding.miio/src/main/resources/misc/device_names.json index 572e2d6ca7979..d1fa2c964c7c7 100644 --- a/bundles/org.openhab.binding.miio/src/main/resources/misc/device_names.json +++ b/bundles/org.openhab.binding.miio/src/main/resources/misc/device_names.json @@ -1,4 +1,5 @@ { + "090615.aircondition.lnk": "PTX central air conditioning controller", "090615.curtain.1mcu01": "One meter intelligent curtain", "090615.curtain.jldj03": "PTX Rolling curtain", "090615.curtain.jxdj02": " PTX mechanical roller motor", @@ -16,9 +17,14 @@ "090615.curtain.zsdj82": "ZS intelligent curtain motor", "090615.light.demo2": "PTX intelligent heating and cooling lamp belt (mesh)", "090615.light.mlig01": "PTX intelligent downlight (mesh)", + "090615.light.pipadd": "Crackle smart light band (mesh)", + "090615.light.pipatd": "Crackle smart anti glare lamp (mesh)", "090615.plug.pipa86": "Crackle intelligent switch two three plug", "090615.plug.plus01": "Intelligent 86 socket(WIFI)", "090615.plug.plus02": "Intelligent Mobile Plug (WIFI)", + "090615.switch.hmb012": "H intelligent single key switch (mesh)", + "090615.switch.hmb022": "H intelligent double key switch (mesh)", + "090615.switch.hmb032": "H intelligent three key switch (mesh)", "090615.switch.mesw1": "PTX Mesh intelligent one switch", "090615.switch.mesw2": "PTX Mesh intelligent two switch", "090615.switch.mesw3": "PTX Mesh intelligent three switch", @@ -33,6 +39,7 @@ "090615.switch.ptxtc1": "PTX intelligent touch switch on (mesh)", "090615.switch.ptxtc2": "PTX intelligent touch two on (mesh)", "090615.switch.ptxtc3": "PTX intelligent touch switch three on (mesh)", + "090615.switch.qksw32": "PTX card access switch (WiFi)", "090615.switch.smb1s1": "Sumi B1 Bluetooth mesh switch (single key)", "090615.switch.smb1s2": "Sumi B1 Bluetooth mesh switch (double key)", "090615.switch.smb1s3": "Sumi B1 Bluetooth mesh switch (three key)", @@ -108,6 +115,9 @@ "aok98.curtain.aok68": "AOK AM68 Smart Curtain", "aok98.curtain.qlam50": "qinglinkQ302 Smart Curtain", "app.light.wynd1": "Nnuodu Intelligent Living Room Lamp", + "arnoo.curtain.awcc10": "智能平开窗帘电机", + "arnoo.curtain.awcc11": "智能平开窗帘电机", + "arnoo.humidifier.aahz10": "智能加湿器", "arnoo.light.cct01": "WiFi 60W 调光调色ç¯", "arnoo.light.dim01": "WiFi 60W 调光ç¯", "arnoo.light.rgbw01": "WiFi 60W 调光调颜色ç¯", @@ -131,6 +141,7 @@ "babai.curtain.bb82mj": "Babuy Smart Curtain", "babai.curtain.kyx850": "KYX Smart Curtain", "babai.curtain.lb100a": "LANBOO Smart Curtain", + "babai.curtain.m515e": "Zemismart smart curtain motor", "babai.curtain.mtx850": "MTX Smart Curtain", "babai.curtain.nh5810": "NanHoo Smart Curtain", "babai.curtain.yilc3": "Yi-LOCK Smart Curtain C3", @@ -157,6 +168,11 @@ "beihao.airer.l9": "Lisheng clothes-horse", "beihao.airer.l9xf": "JIajueshi clothes-horse", "beihao.airer.lyj08": "Ai Smart Clothes Dryer", + "bemfa.fan.fan": "风扇", + "bemfa.light.be01": "巴法", + "bemfa.light.bemfa": "巴法云", + "bemfa.plug.be002": "巴法æ’座", + "bemfa.sensor_ht.dht11": "传感器", "bgdz.light.test3": "light", "bgdz.plug.chazuo": "outlet", "binthe.curtain.bcm": "BINTHEN Smart Curtain", @@ -167,6 +183,7 @@ "bj352.waterpuri.k10": "352Water purifier K10", "bj352.waterpuri.s100cm": "352WaterPurifierS100", "bjdm.airfresh.test01": "LOMEDIQI Intelligent automatic air purifier", + "bkrobo.bed.t1": "Blupeace Air Massage Mattress Topper", "blink.airmonitor.bs0001": "BlinkerAirDetector", "blink.light.bl0001": "BlinkerSmartLight", "blink.plug.bp0001": "BlinkerSmartPlug", @@ -208,17 +225,26 @@ "bull.switch.s312b": "G27_智能开关_三开_中键", "bull.switch.s312c": "G27_智能开关_三开_下键", "bymiot.aircondition.ir2": "未æ¥å±…空调控制器(红外版)", + "bymiot.aircondition.v1": "未æ¥å±…中央空调控制器", "bymiot.curtain.v2": "未æ¥å±…电动窗帘", "bymiot.gateway.v1": "ä¼ä¸šæœ‰çº¿æ™ºèƒ½ç½‘å…³", "bymiot.gateway.v2": "ä¼ä¸šæœ‰çº¿æ™ºèƒ½ç½‘å…³2.0版", "bymiot.motion.v1": "未æ¥å±…人体移动传感器", + "bymiot.sensor_occupy.v3": "未æ¥å±…强电æ’å¡å–电", + "bymiot.switch.1keyv1": "未æ¥å±…å•é”®å¼ºç”µå¼€å…³", + "bymiot.switch.2keyv1": "未æ¥å±…åŒé”®å¼ºç”µå¼€å…³", + "bymiot.switch.3keyv1": "未æ¥å±…三键强电开关", + "bymiot.switch.4keyv1": "未æ¥å±…四键强电开关", + "bymiot.switch.6keyv1": "未æ¥å±…六键强电开关", "bzhome.curtain.sz050": "窗帘电机", "bzhome.plug.sz070": "æ’座转æ¢å™¨", "bzhome.switch.sz010": "开关", + "candor.wine_cool.a": "Candor Wine Cooler(80L/275L)", "cchip.light.l1": "Air Surface ceiling light", "cchome.motion.v1": "人体感应器", "cchome.switch.86l1v1": "Smart Switch(Single)", "cchome.switch.86l2v1": "Smart Switch(Double)", + "cchome.switch.86l3v1": "Smart Switch(Three)", "cchome.tow_w.wyj001": "Bathroom Rack", "cchome.wopener.tcq001": "Motor Controller", "cgllc.airm.cgdn1": "Qingping Air Monitor Lite", @@ -252,6 +278,7 @@ "chuangmi.camera.025b02": "IMILAB Security Camera N Series", "chuangmi.camera.029a02": "Mi 360° Home Security Camera 2K", "chuangmi.camera.036a02": "IMILAB Home Security Camera Y2", + "chuangmi.camera.038a02": "IMILAB C21", "chuangmi.camera.27a02": "IMILAB C10", "chuangmi.camera.ip026c": "Mi 360° Home Security Camera 1080p Essential", "chuangmi.camera.ip029a": "Mi 360° Home Security Camera 2K", @@ -268,6 +295,7 @@ "chuangmi.camera.ipc019e": "IMI Home Security Camera A1", "chuangmi.camera.ipc020": "IMILAB Security Camera N Series", "chuangmi.camera.ipc021": "Mi 360° Home Security Camera 2K Pro", + "chuangmi.camera.ipc022": "Mi 360° Home Security AI Camera", "chuangmi.camera.v2": "Mi Home Security Camera 360°", "chuangmi.camera.v3": "IMI 1080P Home Security Camera", "chuangmi.camera.v4": "IMI Home Security Camera mini", @@ -340,6 +368,7 @@ "chunmi.waterpuri.800f3": "Mi Water Purifier H800G", "chunmi.ysj.tsa1": "Mi Countertop Filtered Water Dispenser", "cleargrass.sensor_ht.dk1": "Mi Temperature and Humidity Monitor", + "clk.light.basket": "WuZuoTiLan", "cmcc.aircondition.x12": "中国移动空调伴侣CMCC-X12", "cmcc.plug.x11": "中国移动智能æ’座CMCC-X11", "coc.airpurifier.tk": "离å­å¡”/é™éŸ³æ— è€—æ空气净化器-骑士", @@ -476,6 +505,10 @@ "dicook.cooker.wfz4003": "intelligent cooking robot", "difeis.curtain.d1": "Difeis D1 Motor", "difeis.ven_fan.dql04h": "Difeisi Air Cool", + "dikair.light.wy0a01": "Dan Fei Nuo Smart Light", + "dikair.light.wy0a02": "Xuan Yi Smart Light", + "dikair.light.wy0a03": "Yan Tai Scene Smart Llight", + "dikair.light.wy0a04": "Yi Tang Smart Light", "dmaker.airfresh.a1": "Mi Fresh Air Ventilator A1-150", "dmaker.airfresh.dm2018": "DreamMaker Fresh Air Ventilator", "dmaker.airfresh.t2017": "Mi Fresh Air Ventilator", @@ -515,16 +548,21 @@ "dreame.vacuum.mc1808": "Mi Robot Vacuum Mop", "dreame.vacuum.p2008": "Dreame Robot Vacuum-Mop F9", "dreame.vacuum.p2009": "Dreame Robot Vacuum D9 ", + "dreame.vacuum.p2029": "Dreame Bot L10 Pro", "dreame.vacuum.p2036": "Trouver Robot LDS Vacuum-Mop Finder", "dreame.vacuum.p2041": "Mi Robot Vacuum-Mop 1T", "dreame.vacuum.p2041o": "Mi Robot Vacuum-Mop 2 Pro+", + "dreame.vacuum.p2156o": "MOVA Z500 Robot Vacuum and Mop Cleaner", + "dreame.vacuum.p2157": "MOVA L600 Robot Vacuum and Mop Cleaner", "dsm.lock.h3": "DESSMANN smart lock-Di H3", "dsm.lock.q3": "Q3", "dsm.lock.r5": "DESSMANN Facial recognition smart lock-Di R5", "dtr.magic_touch.211mgr": "P5B Intelligent Neck Massager", "dtr.magic_touch.p6b": "PGG Neck Massager P6", + "dtr.magic_touch.p7": "PGG Neck Massager P7", "dun.cateye.nknk500": "DUN Smart Doorbell", "duoqin.safe.pbfv01": "Privacy box for finger vein identification", + "dwdz.switch.sw0a01": "Scene mesh breaker DBS", "ecloud.airc.eq": "智能空调", "ecloud.airfresh.eq": "新风", "ecloud.curtain.eq": "窗帘", @@ -585,6 +623,7 @@ "ezhome.switch.zhyapp03": "touch-panel", "ezhome.switch.zhyapp06": "Reset-Switch", "ezhome.tv.yy1002": "云接入电视", + "fawad.airrtc.40011": "FOWAD Floorheating Thermostat", "fawad.airrtc.fwd20011": "FOWAD thermostat controller", "fbs.airmonitor.pth02": "AIR QUALITY TESTER", "feibit.aircondition.zrc": "IR blaster", @@ -676,6 +715,7 @@ "gmn.light.wy0a02": "Gomanni Scene two color downlight", "gmn.light.wy0w01": "Gomanni Scene two color ceiling lamp", "gmn.light.wyfan1": "Gomanni G1 series bedroom fan lamp", + "gmrii.curtain.gt30": "GMIRI Curtain GT30TV/P", "gmwl.light.cibdo0410ga": "gmwlznd", "golden.curtain.c01": "curtain", "golden.kettle.wd01ei": "drinking", @@ -775,6 +815,7 @@ "huayi.light.peg093": "HUIZUO PEGASUS For Bedroom", "huayi.light.pis123": "HUIZUO PISCES For Bedroom", "huayi.light.pisces": "HUIZUO PISCES For Living Room", + "huayi.light.rgb205": "HUIZUO Light Board", "huayi.light.tau023": "HUIZUO TAURUS For Bedroom", "huayi.light.taurus": "HUIZUO TAURUS For Living Room", "huayi.light.vir063": "HUIZUO VIRGO For Bedroom", @@ -792,10 +833,12 @@ "huazhu.airc.v2": "空调控制器", "huazhu.curtain.v2": "窗帘电机", "huazhu.switch.1keyv2": "å•é”®å¼€å…³", + "huazhu.tv.ir1": "电视控制器", "huihe.switch.plug": "smart pulg", "huohe.lock.m1": "M1Lock", "huoman.litter_box.co1": "CO1", "hutlon.lock.v0001": "Huitailong ultra-thin screen fingerprint intelligent lock U1", + "hwzn.light.wy0a01": "Xihui smart ceiling lamp", "hxiot.light.ha1": "智能ç¯æŽ§(1è·¯å•äº®åº¦ç‰ˆ)", "hxrcj.hood.e5": "烟机", "hxrcj.oven.e5": "蒸箱", @@ -809,6 +852,11 @@ "idelan.aircondition.v2": "Jinxing Smart Air Conditioner", "ihealth.bp.bpm1": "iHealth Sphygmomanometer", "ihealth.bpm.kd5907": "Andon Smart Sphygmomanometer", + "ihome.aircondition.eaac": "空调", + "ihome.curtain.escc": "窗帘", + "ihome.light.estk": "智能照明", + "ihome.plug.essp": "æ’座", + "ihome.switch.switch": "开关", "ijomoo.toilet.zs320": "Smart Toilet Lid-ZS320T", "ikea.light.led1536g5": "IKEA E14 white spectrum", "ikea.light.led1537r6": "IKEA GU10 white spectrum", @@ -817,7 +865,20 @@ "ikea.light.led1623g12": "IKEA E27 warm white", "ikea.light.led1649c5": "IKEA E14 warm white", "ikea.light.led1650r5": "IKEA GU10 warm white", + "ikecin.aircondition.ir1": "空调", + "ikecin.airfresh.95": "智能新风控制", + "ikecin.airrtc.29": "智能温控器", + "ikecin.airrtc.76": "智能温控器", + "ikecin.airrtc.84": "智能温控器", + "ikecin.airrtc.87": "智能温控器", + "ikecin.airrtc.90": "智能温控器", + "ikecin.airrtc.93": "智能温控器", + "ikecin.airrtc.96": "智能温控器", + "ikecin.airrtc.99": "智能温控器", "ikecin.plug.6": "智能开关", + "ikecin.stb.ir3": "机顶盒", + "ikecin.switch.ir10": "开关", + "ikecin.tv.ir2": "电视", "ikonke.light.kkbulb": "bulb", "ikonke.light.kklight": "智能炫彩ç¯", "ikonke.plug.k2": "k2 smart plug", @@ -847,13 +908,20 @@ "isa.camera.virtual": "å°æ–¹æ™ºèƒ½æ‘„åƒæœºç»„", "isa.magnet.dw2hl": "Mi Door and Window Sensor 2", "isa.router.mr01hl": "HuaLai Xiao Fang Mesh router gateway", + "isa.switch.kg03hl": "Mi Smart Triple One Way Wall Switch with Display", "isleep.blanket.hs2001": "LETSLEEP Water Heating Mattress HS2001", "iwarm.aircondition.gt": "å°æ²ƒç²¾çµ", "iwarm.waterheater.l1p24": "å°æ²ƒå£æŒ‚炉", "janshi.magic_touch.g2": "G2 Spinal comfort neck massager", + "jieman.magic_touch.js01": "Jishu Intelligent cervical spine massage instrument", "jieman.magic_touch.js78": "Jishu Intelligent Cervical Massage", + "jieman.magic_touch.kdl0": "Kangduoli Smart Massager", "jieman.magic_touch.ms9": "Intelligent cervical massage instrument", + "jieman.magic_touch.newf": "New for Smart Massager", + "jieman.magic_touch.tfs2": "Tefeishi Smart Massager", + "jieman.magic_touch.tsf1": "Tefeishi Smart Massager", "jieman.magic_touch.ys01": "Yashen Intelligent cervical massage instrument", + "jieman.magic_touch.ys02": "Yashen Smart Massager", "jihisi.light.wy0a01": "JIHISI Intelligent lamp", "jilian.aircondition.a0": "mini-aircondition", "jilian.curtain.fm1": "Geeklink_Curtain", @@ -929,6 +997,7 @@ "kejia.airer.krq": "COURAGE Smart Airer", "kejia.airer.krqpro": "Courage Smart AirerPRO", "kejia.airer.mzn": "MI Smart·Electric Airer", + "kejia.airer.mznpro": "Mi Smart AirerPRO", "kejia.airer.th001": "Smart drying rack", "kiwik.aircondition.irac": "空调红外é¥æŽ§å™¨", "kiwik.curtain.kaw": "窗帘电机", @@ -995,6 +1064,7 @@ "lcrmcr.safe.ms30mp": "CRMCR Annuo Smart Safe PRO", "lcrmcr.safe.ms55kn": "CRMCR Kanuo Smart Safe", "lcrmcr.safe.ms80b": "Carberry Face Recognition Safe", + "ldsn.curtain.awcc10": "智能窗帘电机", "ldsn.light.2bpd18": "抱朴(DIM PP-有线版)", "ldsn.light.2hld20": "花轮(DIM11头-é¥æŽ§ç‰ˆï¼‰", "ldsn.light.2hsd15": "怀素(DIMå•å¤´-触控\u0026é¥æŽ§ç‰ˆï¼‰", @@ -1005,12 +1075,16 @@ "ldsn.light.2yfd19": "一方(DIM )", "ldsn.light.2ygd05": "摇光(DIM大版-é¥æŽ§ç‰ˆï¼‰", "ldsn.light.3ygd05": "摇光(DIM大版-é¥æŽ§ç‰ˆï¼‰", + "ldsn.light.hsxd01": "怀素å¸é¡¶ç¯", + "ldsn.light.hsxd02": "怀素å¸é¡¶ç¯", + "ldsn.light.ygxd01": "摇光å¸é¡¶ç¯", "leishi.bhf_light.yuba02": "NVC Smart Bath-Heater", "leishi.light.eps112": "NVC LED Smart Ceiling Lamp", "leishi.light.eps113": "DongDong Babysbreath LED Ceiling Lamp", "leishi.light.eps115": "NVC Nordic Style Light", "leishi.light.eps116": "NVC Pendant lamp", "leishi.light.eps117": "NVC Spotlight", + "leishi.light.eps118": "NVC LED Smart Ceiling Lamp", "leishi.light.esp114": "NVC DieYing LED ceiling lamp", "leishi.light.fan01": "NVC Fan Light", "leishi.light.nest": "NVC LED Nest Ceiling Lamp", @@ -1020,6 +1094,7 @@ "leishi.light.wy0a02": "NVC Smart down lamp", "leishi.light.wy0a04": "NVC Smart bulb ", "leishi.light.wy0a05": "NVC Smart panel light ", + "leishi.light.wy0a06": "NVC Intelligent Lighting", "leishi.light.wy0b01": "NVC Smart Desk lamp", "leishi.light.wy0c01": "NVC Smart Ceiling Lamp(ZhiRui)", "leishi.light.wy0c02": "NVC Smart Ceiling Lamp(ZhiZhen)", @@ -1037,6 +1112,9 @@ "lemesh.light.wy0c07": "Scene mesh color temperature lamp TM series", "lemesh.light.wy0c08": "Scene mesh color temperature lamp", "lemesh.switch.sw0a01": "Scene mesh breaker", + "lemesh.switch.sw1a02": "One-click Smart Switch Mesh version", + "lemesh.switch.sw2a02": "Two-button Smart Switch(Mesh)", + "lemesh.switch.sw3a02": "Three-button Smart Switch(Mesh)", "leshi.curtain.v0001": "Scene Curtain WIFI X", "leshi.light.wy0b01": "Scenario WIFI dual color light", "leshi.light.wyfan": "Scene intelligent fan lamp WiFi", @@ -1156,6 +1234,7 @@ "lumi.curtain.hagl05": "Xiaomiyoupin Curtain Controller (Wi-Fi)", "lumi.curtain.hagl08": "Aqara Curtain Controller A1", "lumi.curtain.hmcn01": "Mi Smart Motorized Curtain", + "lumi.curtain.hocn01": "好åšæŽ¨çª—器", "lumi.curtain.l07": "智能窗帘电机C2", "lumi.curtain.naq2": "智能窗帘", "lumi.curtain.nes1": "智能窗帘电机(Zigbeeå¼€åˆå¸˜ç‰ˆï¼‰", @@ -1328,6 +1407,7 @@ "lumi.switch.v11": "墙å£å¼€å…³ï¼ˆé›¶ç«çº¿åŒé”®ç‰ˆï¼‰", "lumi.vibration.aq1": "Aqara Vibration Sensor", "lumi.weather.v1": "Aqara Temperature and Humidity Sensor", + "lumi.wopener.hocn02": "好åšå¼€çª—器", "lwkj.bed.5110": "智能床", "lwkj.curtain.2a10": "智能窗帘", "lwkj.light.2010": "彩色ç¯", @@ -1335,6 +1415,7 @@ "lwkj.light.2210": "å•è‰²ç¯", "lwkj.switch.2410": "智能æ’座", "lxk.curtain.clmb": "窗帘é¢æ¿", + "lxk.curtain.cs06": "自动开åˆå¸˜CS06", "lxk.curtain.dj01w": "窗帘电机", "lxk.hood.n909i": "烟机", "lxk.hood.n913i": "烟机", @@ -1353,6 +1434,7 @@ "madv.cateye.dlowlplus": "Dling Smart Video Doorbell Plus", "madv.cateye.dlowlse": "Dling Smart Video Doorbell C3", "madv.cateye.dlowlse2": "Dling Smart Video Doorbell C5", + "madv.cateye.mi2gt": "Mi Smart Video Doorbell 2", "madv.cateye.miowl": "Mi Smart Video Doorbell", "madv.cateye.miowlv2": "Mi Smart Video Doorbell 2", "madv.cateye.miowlv2l": "Mi Smart Video Doorbell 2 Lite", @@ -1366,6 +1448,8 @@ "maxway.light.mx101": "maxway_light", "mbzn.curtain.m20051": "智能窗帘", "mengye.plug.rmtsw": "电脑远程开关机å¡", + "mhaq.aircondition.miair": "三è±é‡å·¥æµ·å°”·智能空调", + "mhaq.airfresh.miwind": "三è±é‡å·¥æµ·å°”·æ’温新风机", "mhiot.light.me27w": "WiFi智能ç¯å¤´", "mhiot.plug.mp01w": "WiFi智能æ’座", "mhiot.plug.ms86w": "WiFi智能墙å£æ’座", @@ -1472,6 +1556,7 @@ "morfun.kettle.mf809": "MORFUN Smart Instant Heating Water Dispenser", "morfun.ysj.mf208": "MORFUN Smart Instant Heating Water Dispenser MF208", "moyu.washer.s1hm": "Moyu Smart Baby Washing Machine", + "mpe.bed.bed01": "MPE智能床", "mpe.bed.mpe": "MPE智能床", "mpkx.curtain.050001": "智能窗帘", "mpkx.light.04000a": "ç¯å¸¦", @@ -1501,6 +1586,7 @@ "mxiang.cateye.xmcatt1": "Xiaomo Smart Peep Hole", "nbczwl.airer.airer": "Smart Airer", "nbczwl.airer.kfe01": "KFE", + "nbczwl.airer.zbs01": "ZBS", "nbym.curtain.yznv1": "Cloud intelligent electric curtain", "nhy.airrtc.v1": "Golan Denmark Heating", "nhy.rtc.pexrtc730": "丹麦格澜空调", @@ -1508,13 +1594,14 @@ "ninebot.scooter.v1": "Electric Scooter", "ninebot.scooter.v2": "Mi Electric Scooter Pro", "ninebot.scooter.v3": "Mi Electric Scooter 1S", - "ninebot.scooter.v4": "Mi Electric Scooter Pro 2", + "ninebot.scooter.v4": "Mi Electric Scooter Pro 2 series", "ninebot.scooter.v5": "Mi Electric Scooter Essential", "ninebot.scooter.v6": "Mi Electric Scooter 1S", "nnleaf.light.panels": "Light Panels", "nnleaf.light.strips": "Light Strips", "nnleaf.light.ulp": "Nanoleaf ULP", "noc196.light.mdctd9": "Midian Intelligent Bedside Healthy Lamp", + "noc196.light.mdyctd": "MIDIAN NO BLUE LIGHT MUSIC LAMP", "nuwa.robot.minikiwi": "Nuwa Robotics Danny Robot", "nuwa.robot.nb1": "KebbiAir", "nvc.light.c209": "雷士å¸é¡¶ç¯", @@ -1599,6 +1686,8 @@ "pair.light.3": "开关", "pair.light.4": "调光ç¯", "pair.light.5": "å¯è°ƒåŒè‰²æ¸©ç¯", + "pak.light.pak002": "pak ceiling lamp", + "pak.light.pak005": "Pak LED Smart Home Ceiling Lamp", "pak.light.pak01": "Pak LED Smart Ceiling Lamp", "park.switch.fp509": "车ä½æ¥æ™ºèƒ½åœ°é”", "permay.switch.kgkgk": "开关", @@ -1664,6 +1753,8 @@ "pwzn.relay.apple": "16 relays module", "pwzn.relay.banana": "16 relays module modbus version", "pze.light.wy0a01": "Puzhuoer Smart Light", + "qdhkl.aircondition.md01": "Sumi central air conditioning controller", + "qhzm.light.wy0a01": "Qinghe Smart Light", "qicyc.bike.tdp02z": "qicybike", "qicyc.bike.xmdzlzxc01qj": "Mi Smart Electric Folding Bike", "qike.bhf_light.qk201801": "Smart Bath(Basic)", @@ -1707,6 +1798,7 @@ "roidmi.cleaner.v2": "ROIDMI Cordless Vacuum Cleaner NEX2", "roidmi.cleaner.v382": "ROIDMI Cordless Vacuum Cleaner NEX2 Pro", "roidmi.vacuum.v1": "ROIDMI Handheld Vacuum Cleaner", + "roidmi.vacuum.v60": "ROIDMI EVE", "rokid.robot.alien": "Rokid Alien", "rokid.robot.alien2": "Rokid Alien", "rokid.robot.me": "Rokid Me", @@ -1746,9 +1838,19 @@ "scishare.coffee.s1102": "SCISHARE Smart Capsule Coffee Machine", "scmkcz.curtain.chcl": "curtain", "scmkcz.light.chll": "light", + "scmkcz.light.cr2l": "åŒè‰²ç¯", + "scmkcz.light.cr2lv2": "åŒè‰²ç¯", + "scmkcz.light.cr2pv2": "调光调色开关", + "scmkcz.light.crsl": "å•è‰²ç¯", + "scmkcz.light.crslv2": "å•è‰²ç¯", "scmkcz.light.mwl2m": "风扇ç¯", "scmkcz.plug.chcz": "outlet", + "scmkcz.switch.2msw": "åŒè·¯å¼€å…³", + "scmkcz.switch.3msw": "三路开关", + "scmkcz.switch.4msw": "多路开关", "scmkcz.switch.chsw": "switch", + "scmkcz.switch.cr2p": "调光调色开关", + "scmkcz.switch.scene": "场景", "sfl.light.aaas": "FSL AI ceiling lamp", "sfl.light.wl215": "5w调光调色筒ç¯", "shanhe.switch.1": "善禾物è”", @@ -1759,9 +1861,11 @@ "shjszn.lock.kx": "Justree Smart Lock Kx", "shuii.humidifier.jsq001": "The fog free of humidifier", "shuii.humidifier.jsq002": "Zero-fog low-temperature evaporative humidifier (upgrade)", + "shy.heater.h": "智能温控", "sig.curtain.welink": "å¾®è”å°æ™º-窗帘", "silen.fryer.sck501": "Silencare AirFryer 1.8L", "silen.fryer.sck505": "Silencare AirFryer", + "silen.mfcp.sck307": "Silencare Grill", "simon.aircondition.ac": "空调", "simon.aircondition.bst": "Bus空调", "simon.aircondition.bvt": "Bus新风温控器", @@ -1795,10 +1899,12 @@ "skyrc.airp.su001": "Petoneer Smart Odor Eliminator-Pro", "skyrc.airpurifier.pur": "Petoneer AirMaster", "skyrc.feeder.fed": " Petoneer Nutri Mini Feeder", + "skyrc.feeder.vdfeed": "Nutri Vision Mini", "skyrc.feeder.vfed": " Petoneer Nutri Vision", "skyrc.pet_waterer.fre1": "Fresco Mini Plus", "smartj.curtain.sjd82p": "SmartJoy Intelligent curtain motor (WiFi Pro)", "smartj.curtain.sjdt82": "SmartJoy intelligent curtain motor", + "smartj.light.sjdlds": "SmartJoy Zhizhen Dimmer (Bluetooth Mesh version)", "smartj.plug.sjsc86": "Smartjoy smart wall outlet (WiFi version)", "smartj.switch.sjlh01": "SmartJoy zhijian wall switch (one key WiFi)", "smartj.switch.sjlh02": "SmartJoy zhijian wall switch (two key WiFi)", @@ -1825,6 +1931,8 @@ "soocare.toothbrush.m1s": "Mi Smart Electric Toothbrush T500", "soocare.toothbrush.mc1": "Mi Kids Electric Toothbrush", "soocare.toothbrush.x3": "Soocare Electric Toothbrush", + "srkj.aircondition.v001": "空调", + "srkj.curtain.cl01": "窗帘", "srkj.light.test": "调光ç¯", "srkj.switch.sw1": "开关", "srkj.waterheater.re01": "热水器", @@ -1974,6 +2082,7 @@ "tuya.stb.tystb01": "STB", "tuya.switch.tyws02": "Wi-Fi Switch", "tuya.tv.tytv01": "TV", + "twzm.light.wy0a01": "Taiwo Magnetic attraction lamp", "txdd.wifispeaker.x1": "Tencent Smart Display", "tyzhjt.aircondition.air": "å°å°ç©ºè°ƒ", "tyzhjt.airpurifier.001": "空气净化器", @@ -2094,6 +2203,7 @@ "viomi.cooker.v2": "Viomi Rice Cooker (4L Premium Edition)", "viomi.cooker.v4": "Viomi Smart IH Rice Cooker 3L", "viomi.cooker.v5": "Viomi Smart Rice Cooker 3L", + "viomi.curtain.v1": "Viomi Internet curtain motor (Wi-Fi)", "viomi.dishwasher.m01": "Mi Smart Built-in Dishwasher (8 Dining Sets)", "viomi.dishwasher.m02": "Mi Smart Dishwasher (4 Dining Sets)", "viomi.dishwasher.v01": "Viomi Dishwasher", @@ -2103,6 +2213,7 @@ "viomi.dishwasher.v07": "Viomi Smart Protable dishwasher", "viomi.dishwasher.v08": "Viomi Smart dishwahser 8 settings 0803A", "viomi.dishwasher.v09": "Viomi Smart dishwahser 4 settings 0402", + "viomi.dishwasher.v12": "viomi smart dishwasher 8 settings 0803B", "viomi.fan.v5": "Viomi ButterflyFan DC", "viomi.fridge.b1": "Viomi Smart Refrigerator(SBS 380L)", "viomi.fridge.b3": "Viomi Refrigerator(Cross Door 408L)", @@ -2133,6 +2244,9 @@ "viomi.fridge.u39": "Viomi Refrigerator (Cross Door 485L)", "viomi.fridge.u4": "Viomi Smart Refrigerator iLive (side-by-side 483L)", "viomi.fridge.u40": "Viomi Refrigerator iLive (French Door 416L)", + "viomi.fridge.u41": "Viomi Refrigerator (SBS 639L)", + "viomi.fridge.u42": "Viomi Refrigerator (Cross Door 509L)", + "viomi.fridge.u43": "Viomi Refrigerator (French Door 445L)", "viomi.fridge.u44": "Viomi Refrigerator (Cross Door 513L)", "viomi.fridge.u45": "Viomi Refrigerator (French Door 508L)", "viomi.fridge.u6": "Viomi Smart Refrigerator iLive (4-door 486L)", @@ -2193,12 +2307,15 @@ "viomi.oven.so2": "VIOMI Internet steaming and baking all-in-one machine queen (embedded)", "viomi.oven.so3": "Viomi Smart Steaming and Baking Machine King Pro(Build-in)", "viomi.steriliser.v1": "Viomi Disinfection Cabinet (Build-in)", + "viomi.switch.s1": "云米无线开关inkRock(å•é”®ç‰ˆï¼‰", "viomi.vacuum.v11": "Viomi V-SLAM Robot Vacuum", "viomi.vacuum.v12": "Viomi Cleanning Robot X2", "viomi.vacuum.v13": "Viomi V3", - "viomi.vacuum.v17": "viomi Alpha 1A", + "viomi.vacuum.v17": "Viomi Robot Vacuum-Mop JC", "viomi.vacuum.v18": "Viomi S9", "viomi.vacuum.v19": "Viomi SE", + "viomi.vacuum.v20": "VIOMI FIERCE-UV", + "viomi.vacuum.v21": "VIOMI ALPHA-UV", "viomi.vacuum.v3": "Viomi sweeper PRO", "viomi.vacuum.v6": "Viomi Cleaning Robot", "viomi.vacuum.v7": "Mi Robot Vacuum-Mop P", @@ -2215,6 +2332,7 @@ "viomi.washer.v13": "Viomi Washer \u0026 Dryer Neo (10kg)", "viomi.washer.v14": "Viomi Washing Machine Neo (10kg)", "viomi.washer.v17": "Viomi Washer\u0026Dryer Eyebot(11kg)", + "viomi.washer.v18": "Viomi Washer\u0026Dryer Eyebot Face 11kg", "viomi.washer.v19": "Viomi Washer\u0026Dryer Neo(10kg DD)", "viomi.washer.v20": "Viomi Washer\u0026Dryer(10kg Navi Version)", "viomi.washer.v21": "Viomi Washing Machine(10kg Navi Version)", @@ -2224,6 +2342,7 @@ "viomi.washer.v26": "Viomi Washer\u0026Dryer Neo2 Pro(10kg DD)", "viomi.washer.v27": "Viomi Washing Machine Neo2 Pro(10kg DD)", "viomi.washer.v28": "Viomi Washer \u0026 Dryer Neo2 (10kg)", + "viomi.washer.v32": "Viomi Washer Eyebot(12KG)", "viomi.washer.v4": "Viomi Washing Drying Machine (8KG)", "viomi.washer.v5": "Viomi Washing Drying Machine (10KG)", "viomi.washer.v6": "Viomi Washer\u0026Dryer Rose 9kg", @@ -2299,6 +2418,7 @@ "wlank.light.001": "调光ç¯", "wlank.light.002": "RGBç¯", "wlank.light.light": "ç¯å…‰", + "worth.plug.787076": "智能æ’座", "wxzn.switch.801438": "开关é¢æ¿", "xbyk.switch.xh01s": "云开关", "xckj.dishwasher.idw01": "Ocooker The Dishwasher", @@ -2312,6 +2432,16 @@ "xckj.waterpuri.ihwp01": "Ocooker Water Purifier", "xckj.waterpuri.js01": "Circle kitchen household water purifier", "xgds.light.wy0a01": "Romne Intelligent lamp", + "xgds.light.wy0a02": "Romne smart ceiling lamp", + "xhuan.light.wy0a01": "Sumi Ruying Light Strip", + "xhuan.light.wy0a02": "Sumi Downlight Lite", + "xhuan.light.wy0a03": "Sumi Magnetic Floodlight", + "xhuan.light.wy0a04": "Sumi Magnetic Grille Lamp", + "xhuan.light.wy0a05": "Sumi Magnetic Spotlight", + "xhuan.light.wy0a06": "Sumi Ruguang Downlight", + "xhuan.light.wy0a07": "Sumi Ruguang Spotlight", + "xhuan.light.wy0a08": "Sumi Bulb Light Lite", + "xhzm.light.wy0a01": "Xinhong ceiling lamp", "xiaomi.aircondition.c10": "Mi Smart Ultra Electricity Saving Vertical Air Conditioner (2HP/Inverter/New China Energy Label Level 1)", "xiaomi.aircondition.c11": "Mi Smart Ultra Electricity Saving Vertical Air Conditioner (3HP/Inverter/New China Energy Label Level 1)", "xiaomi.aircondition.c14": "Mi Smart Ultra Electricity Saving Vertical Air Conditioner (3HP/Inverter/New China Energy Label Level 3)", @@ -2368,6 +2498,7 @@ "xiaomi.router.r4ac": "Mi Router 4A", "xiaomi.router.r4c": "Mi Router 4Q", "xiaomi.router.r4cm": "Mi Router 4C", + "xiaomi.router.ra50": "Redmi路由器AX5 京东云无线å®", "xiaomi.router.ra67": "Redmi路由器AX5", "xiaomi.router.ra69": "Redmi路由器AX6", "xiaomi.router.ra72": "å°ç±³è·¯ç”±å™¨AX6000", @@ -2530,7 +2661,10 @@ "yeelink.light.ceil31": "Yeelight Shaohua Ceiling Light A2002", "yeelink.light.ceil32": "Yeelight Ceiling Light E2001", "yeelink.light.ceil33": "Yeelight Ceiling Light for Children C2002", + "yeelink.light.ceil34": "Mi Smart LED Ceiling Light (350mm)", "yeelink.light.ceila": "Yeelight LED Ceiling Light Pro", + "yeelink.light.ceilb": "Yeelight Arwen Ceiling Light 450S/550S", + "yeelink.light.ceilc": "Yeelight Arwen Ceiling Light 450C/550C", "yeelink.light.ceiling1": "Yeelight Ceiling Light", "yeelink.light.ceiling10": "Yeelight Crystal Pendant Lamp", "yeelink.light.ceiling11": "Yeelight Ceiling Light 320 1S", @@ -2562,12 +2696,16 @@ "yeelink.light.color5": "Mi Smart LED Bulb Essential (White and Color)", "yeelink.light.color8": "Yeelight LED Bulb 1S(Color)", "yeelink.light.colora": "Yeelight Smart LED Bulb 1SE (color)", + "yeelink.light.colorb": "Yeelight LED smart bulb W3(Multicolor)", + "yeelink.light.colorc": "Yeelight GU10 smart bulb W1(multicolor)", "yeelink.light.ct2": "Yeelight LED Bulb (Tunable)", + "yeelink.light.cta": "Yeelight LED smart bulb W3(tunable white)", "yeelink.light.dn2grp": "Mi Mesh Downlight", "yeelink.light.dnlight2": "Yeelight Mesh LED Downlight", "yeelink.light.fancl1": "Yeelight Smart Ceiling Fan", "yeelink.light.fancl2": "Yeelight Smart Ceiling Fan S2001", "yeelink.light.fancl5": "Yeelight Smart Ceiling Fan C900", + "yeelink.light.fancl6": "Yeelight Smart Ceiling Fan C1060", "yeelink.light.lamp1": "Mi LED Desk Lamp", "yeelink.light.lamp10": "Yeelight Star Floor Lamp", "yeelink.light.lamp15": "Yeelight Screen Light Bar", @@ -2591,6 +2729,8 @@ "yeelink.light.mono4": "Yeelight LED Bulb 1S(Dimmable)", "yeelink.light.mono5": "Yeelight LED Filament Bulb", "yeelink.light.mono6": "Mi Smart LED Bulb", + "yeelink.light.monoa": "Yeelight LED smart bulb W3(dimmable)", + "yeelink.light.monob": "Yeelight GU10 Smart Bulb W1(dimmable)", "yeelink.light.nl1": "Mi Motion-Activated Night Light 2 (Bluetooth)", "yeelink.light.panel1": "Yeelight Whiteglow Panel Light", "yeelink.light.panel3": "Yeelight Haobai LED Panel light Pro", @@ -2629,6 +2769,7 @@ "ymj.light.wyymj1": "EMG Top halo ceiling lamp", "ymt.flowerpot.v1": "Yimitian smart food", "yongqi.aircondition.ac": "空调", + "yongqi.airfresh.fw": "新风", "yongqi.curtain.curt": "窗帘", "yongqi.switch.yq0001": "开关", "yonsz.aircondition.air": "红外空调", @@ -2646,6 +2787,7 @@ "yszj.aircondition.f0025": "空调é¥æŽ§å™¨", "yszj.bed.5110": "智能床垫", "yszj.curtain.2a10": "智能窗帘", + "yszj.curtain.2a10a": "智能窗帘2", "yszj.light.2010": "彩色ç¯", "yszj.light.2110": "å¸é¡¶ç¯", "yszj.light.2210": "å•è‰²ç¯", @@ -2684,6 +2826,8 @@ "yunmi.waterpuri.f4": "Viomi Smart Water purifier Easy3 (400G)", "yunmi.waterpuri.f5": "Viomi Smart Water purifier Easy3 (600G)", "yunmi.waterpuri.f6": "Viomi Smart Water purifier Easy3 (800G)", + "yunmi.waterpuri.f7": "viomi smart water purifier Fast3 1000G", + "yunmi.waterpuri.f8": "viomi smart water purifier Surging 1000G", "yunmi.waterpuri.lx11": "Mi Water Purifier C1 (Triple Setting)", "yunmi.waterpuri.lx12": "Mi Water Purifier S1", "yunmi.waterpuri.lx14": "Mi Water Purifier H1000G", @@ -2700,6 +2844,10 @@ "yunmi.waterpuri.s11": "Viomi Smart Water purifier Blues (400G)", "yunmi.waterpuri.s12": "Viomi Smart Water purifier Blues (600G)", "yunmi.waterpuri.s14": "云米·泉先互è”网净水器 å°ç™½é¾™ 600G", + "yunmi.waterpuri.s15": "viomi smart water purifier dolphin 400G", + "yunmi.waterpuri.s16": "viomi smart water purifier dolphin 600G", + "yunmi.waterpuri.s17": "viomi smart water purifier Fast3 800G", + "yunmi.waterpuri.s18": "viomi smart water purifier Mee 600G", "yunmi.waterpuri.s3": "Viomi smart water purifier SE(400G)", "yunmi.waterpuri.s4": "Viomi smart water purifier S2(500G)", "yunmi.waterpuri.s5": "Viomi smart water purifier S2(600G)", @@ -2847,6 +2995,7 @@ "zhuyun.curtain.1": "电机控制", "zhuyun.light.22": "智能LEDç¯", "zhuyun.light.zy22": "智能LEDç¯3", + "zichen.light.wy0a01": "Xiao ran all copper Smart Light", "zigma.airp.aerio": "Zigma空气净化器系列", "zigma.vacuum.laser": "Zigma扫地机系列", "zimi.clock.myk01": "Mi AI Alarm", diff --git a/bundles/org.openhab.binding.miio/src/test/java/org/openhab/binding/miio/internal/ReadmeHelper.java b/bundles/org.openhab.binding.miio/src/test/java/org/openhab/binding/miio/internal/ReadmeHelper.java index c14d4cbf2ef59..6366c51210969 100644 --- a/bundles/org.openhab.binding.miio/src/test/java/org/openhab/binding/miio/internal/ReadmeHelper.java +++ b/bundles/org.openhab.binding.miio/src/test/java/org/openhab/binding/miio/internal/ReadmeHelper.java @@ -292,8 +292,7 @@ JsonObject convertFileToJSON(String fileName) { JsonObject jsonObject = new JsonObject(); try { - JsonParser parser = new JsonParser(); - JsonElement jsonElement = parser.parse(new FileReader(fileName)); + JsonElement jsonElement = JsonParser.parseReader(new FileReader(fileName)); jsonObject = jsonElement.getAsJsonObject(); } catch (FileNotFoundException e) { // diff --git a/bundles/org.openhab.binding.millheat/src/main/java/org/openhab/binding/millheat/internal/client/RequestLogger.java b/bundles/org.openhab.binding.millheat/src/main/java/org/openhab/binding/millheat/internal/client/RequestLogger.java index 742a9a5dc3cc9..2b1e4f245ff04 100644 --- a/bundles/org.openhab.binding.millheat/src/main/java/org/openhab/binding/millheat/internal/client/RequestLogger.java +++ b/bundles/org.openhab.binding.millheat/src/main/java/org/openhab/binding/millheat/internal/client/RequestLogger.java @@ -41,12 +41,10 @@ public final class RequestLogger { private final Logger logger = LoggerFactory.getLogger(RequestLogger.class); private final AtomicLong nextId = new AtomicLong(); - private final JsonParser parser; private final Gson gson; private final String prefix; public RequestLogger(final String prefix, final Gson gson) { - this.parser = new JsonParser(); this.gson = gson; this.prefix = prefix; } @@ -124,7 +122,7 @@ public Request listenTo(final Request request) { private String reformatJson(final String jsonString) { try { - final JsonElement json = parser.parse(jsonString); + final JsonElement json = JsonParser.parseString(jsonString); return gson.toJson(json); } catch (final JsonSyntaxException e) { logger.debug("Could not reformat malformed JSON due to '{}'", e.getMessage()); diff --git a/bundles/org.openhab.binding.modbus.e3dc/README.md b/bundles/org.openhab.binding.modbus.e3dc/README.md index 0fad11771f86e..01499f575fde2 100644 --- a/bundles/org.openhab.binding.modbus.e3dc/README.md +++ b/bundles/org.openhab.binding.modbus.e3dc/README.md @@ -85,32 +85,32 @@ The E3DC device offers quite an amount of channels. For clustering 4 Channel Gro ### Channel Group _Power Block_ -| Channel Label | Channel Group ID | Channel ID | Type | Description | -|-------------------------------|-------------------|------------------------------|------------------------|------------------------------| -| PV Output | power | pv-power-supply | Number:Power | Photovoltaic Power Production | -| Battery Discharge | power | battery-power-supply | Number:Power | Battery discharges and provides Power | -| Battery Charge | power | battery-power-consumption | Number:Power | Battery charges and consumes Power | -| Household Consumption | power | household-power-consumption | Number:Power | Household consuming Power | -| Grid Power Consumption | power | grid-power-consumption | Number:Power | Grid Power is needed in order to satisfy your overall Power consumption | -| Grid Power Supply | power | grid-power-supply | Number:Power | More Photovoltaic Power is produced than needed. Additional Power is provided towards the Grid | -| External Power Supply | power | external-power-supply | Number:Power | Power produced by an external device which is attached to your E3DC device | -| Wallbox Power Consumption | power | wallbox-power-consumption | Number:Power | Power consumption of attached Wallboxes | -| Wallbox PV Power Consumption | power | wallbox-pv-power-consumption | Number:Power | Photovoltaic Power consumption (PV plus Battery) of attached Wallboxes | -| Autarky | power | autarky-channel | Number:Dimensionless | Your current Autarky Level in Percent | -| Self Consumption | power | self-consumption | Number:Dimensionless | Your current Photovoltaic Self Consumption Level in Percent | -| Battery State Of Charge | power | battery-soc | Number:Dimensionless | Charge Level of your attached Battery in Percent | +| Channel Label | Channel Group ID | Channel ID | Type | Description | +|-------------------------------|-------------------|------------------------------|------------------------|----------------------------------------------------------------------------------------| +| PV Output | power | pv-power-supply | Number:Power | Photovoltaic Power Production | +| Battery Discharge | power | battery-power-supply | Number:Power | Battery discharges and provides Power | +| Battery Charge | power | battery-power-consumption | Number:Power | Battery charges and consumes Power | +| Household Consumption | power | household-power-consumption | Number:Power | Household consuming Power | +| Grid Power Consumption | power | grid-power-consumption | Number:Power | More Photovoltaic Power is produced than needed. Additional Power is consumed by Grid | +| Grid Power Supply | power | grid-power-supply | Number:Power | Grid Power is needed in order to satisfy your overall Power consumption | +| External Power Supply | power | external-power-supply | Number:Power | Power produced by an external device which is attached to your E3DC device | +| Wallbox Power Consumption | power | wallbox-power-consumption | Number:Power | Power consumption of attached Wallboxes | +| Wallbox PV Power Consumption | power | wallbox-pv-power-consumption | Number:Power | Photovoltaic Power consumption (PV plus Battery) of attached Wallboxes | +| Autarky | power | autarky-channel | Number:Dimensionless | Your current Autarky Level in Percent | +| Self Consumption | power | self-consumption | Number:Dimensionless | Your current Photovoltaic Self Consumption Level in Percent | +| Battery State Of Charge | power | battery-soc | Number:Dimensionless | Charge Level of your attached Battery in Percent | ### Channel Group _String Details Block_ -| Channel Label | Channel Group ID | Channel ID | Type | Description | -|-----------------------|------------------|--------------------|---------------------------|------------------------------| -| String 1 Potential | strings | string1-dc-voltage | Number:ElectricPotential | Voltage on String 1 | -| String 2 Potential | strings | string2-dc-voltage | Number:ElectricPotential | Voltage on String 2 | -| String 3 Potential | strings | string3-dc-voltage | Number:ElectricPotential | Voltage on String 3 | -| String 1 Current | strings | string1-dc-current | Number:ElectricCurrent | Current on String 1 | -| String 2 Current | strings | string2-dc-current | Number:ElectricCurrent | Current on String 2 | -| String 3 Current | strings | string3-dc-current | Number:ElectricCurrent | Current on String 3 | +| Channel Label | Channel Group ID | Channel ID | Type | Description | +|-----------------------|------------------|--------------------|---------------------------|----------------------------| +| String 1 Potential | strings | string1-dc-voltage | Number:ElectricPotential | Voltage on String 1 | +| String 2 Potential | strings | string2-dc-voltage | Number:ElectricPotential | Voltage on String 2 | +| String 3 Potential | strings | string3-dc-voltage | Number:ElectricPotential | Voltage on String 3 | +| String 1 Current | strings | string1-dc-current | Number:ElectricCurrent | Current on String 1 | +| String 2 Current | strings | string2-dc-current | Number:ElectricCurrent | Current on String 2 | +| String 3 Current | strings | string3-dc-current | Number:ElectricCurrent | Current on String 3 | | String 1 Power | strings | string1-dc-output | Number:Power | Power produced by String 1 | | String 2 Power | strings | string2-dc-output | Number:Power | Power produced by String 2 | | String 3 Power | strings | string3-dc-output | Number:Power | Power produced by String 3 | @@ -175,18 +175,18 @@ String E3DC_ModelName "E3DC Model" (e3dc) String E3DC_Firmware "E3DC Modbus ID" (e3dc) { channel="modbus:e3dc:device:powerplant:info#firmware-release" } String E3DC_SerialNumber "E3DC Modbus ID" (e3dc) { channel="modbus:e3dc:device:powerplant:info#serial-number" } -Number:Power E3DC_PVPower "E3DC PV Power" (e3dc,persist) { channel="modbus:e3dc:device:powerplant:power#pv-power-supply" } -Number:Power E3DC_BatteryDischarge "E3DC Battery Discharge" (e3dc,persist) { channel="modbus:e3dc:device:powerplant:power#battery-power-supply" } -Number:Power E3DC_BatteryCharge "E3DC Battery Charge" (e3dc,persist) { channel="modbus:e3dc:device:powerplant:power#battery-power-consumption" } -Number:Power E3DC_Household "E3DC Household Consumption" (e3dc,persist) { channel="modbus:e3dc:device:powerplant:power#household-power-consumption" } -Number:Power E3DC_GridConsumption "E3DC Grid Consumption" (e3dc,persist) { channel="modbus:e3dc:device:powerplant:power#grid-power-consumption" } -Number:Power E3DC_GridSupply "E3DC Grid Supply " (e3dc,persist) { channel="modbus:e3dc:device:powerplant:power#grid-power-supply" } -Number:Power E3DC_ExternalSupply "E3DC External Supply" (e3dc,persist) { channel="modbus:e3dc:device:powerplant:power#external-power-supply" } -Number:Power E3DC_WallboxConsumption "E3DC Wallbox Consumption" (e3dc,persist) { channel="modbus:e3dc:device:powerplant:power#wallbox-power-consumption" } -Number:Power E3DC_WallboxPVConsumption "E3DC Wallbox PV Consumption" (e3dc) { channel="modbus:e3dc:device:powerplant:power#wallbox-pv-power-consumption" } -Number:Dimensionless E3DC_AutarkyLevel "E3DC Autarky Level" (e3dc) { channel="modbus:e3dc:device:powerplant:power#autarky" } -Number:Dimensionless E3DC_SelfConsumptionLevel "E3DC Self Consumption Level" (e3dc) { channel="modbus:e3dc:device:powerplant:power#self-consumption" } -Number:Dimensionless E3DC_BatterySOC "E3DC Battery SOC" (e3dc,persist) { channel="modbus:e3dc:device:powerplant:power#battery-soc" } +Number:Power E3DC_PVPower "E3DC PV Power" (e3dc,persist) { channel="modbus:e3dc:device:powerplant:power#pv-power-supply" } +Number:Power E3DC_BatteryDischarge "E3DC Battery Discharge" (e3dc,persist) { channel="modbus:e3dc:device:powerplant:power#battery-power-supply" } +Number:Power E3DC_BatteryCharge "E3DC Battery Charge" (e3dc,persist) { channel="modbus:e3dc:device:powerplant:power#battery-power-consumption" } +Number:Power E3DC_Household "E3DC Household Consumption" (e3dc,persist) { channel="modbus:e3dc:device:powerplant:power#household-power-consumption" } +Number:Power E3DC_GridConsumption "E3DC Power to Grid" (e3dc,persist) { channel="modbus:e3dc:device:powerplant:power#grid-power-consumption" } +Number:Power E3DC_GridSupply "E3DC Power from Grid" (e3dc,persist) { channel="modbus:e3dc:device:powerplant:power#grid-power-supply" } +Number:Power E3DC_ExternalSupply "E3DC External Supply" (e3dc,persist) { channel="modbus:e3dc:device:powerplant:power#external-power-supply" } +Number:Power E3DC_WallboxConsumption "E3DC Wallbox Consumption" (e3dc,persist) { channel="modbus:e3dc:device:powerplant:power#wallbox-power-consumption" } +Number:Power E3DC_WallboxPVConsumption "E3DC Wallbox PV Consumption" (e3dc) { channel="modbus:e3dc:device:powerplant:power#wallbox-pv-power-consumption" } +Number:Dimensionless E3DC_AutarkyLevel "E3DC Autarky Level" (e3dc) { channel="modbus:e3dc:device:powerplant:power#autarky" } +Number:Dimensionless E3DC_SelfConsumptionLevel "E3DC Self Consumption Level" (e3dc) { channel="modbus:e3dc:device:powerplant:power#self-consumption" } +Number:Dimensionless E3DC_BatterySOC "E3DC Battery SOC" (e3dc,persist) { channel="modbus:e3dc:device:powerplant:power#battery-soc" } Switch E3DC_WB_Available "E3DC WB available" (e3dc) { channel="modbus:e3dc-wallbox:device:powerplant:wallbox0:wb-available" } Switch E3DC_WB_Sunmode "E3DC WB Sunmode" (e3dc) { channel="modbus:e3dc-wallbox:device:powerplant:wallbox0:wb-sunmode" } @@ -332,7 +332,7 @@ I like the Grafana approach and I used the [InfluxDB & Grafana Tutorial](https:/ from the Community to set this up. I prepared my machine and I'm quite pleased with the results. - + In the above picture there are two graphs @@ -368,6 +368,6 @@ Items { Having these values in the timeline you're able to cross check how the forecast influences the Photovoltaic Production. - + I personally would like to have more steering control of the E3DC to react on such forecast e.g. "stop charging the car if it gets too cloudy" diff --git a/bundles/org.openhab.binding.modbus.e3dc/src/main/resources/OH-INF/i18n/e3dc_de.properties b/bundles/org.openhab.binding.modbus.e3dc/src/main/resources/OH-INF/i18n/e3dc_de.properties index 2fc6bb963ceb0..ff76bc4546159 100644 --- a/bundles/org.openhab.binding.modbus.e3dc/src/main/resources/OH-INF/i18n/e3dc_de.properties +++ b/bundles/org.openhab.binding.modbus.e3dc/src/main/resources/OH-INF/i18n/e3dc_de.properties @@ -59,8 +59,8 @@ channel-type.modbus.pv-power-supply-channel.label = Photovoltaik Leistung channel-type.modbus.battery-power-supply-channel.label = Batterie Entladen channel-type.modbus.battery-power-consumption-channel.label = Batterie Laden channel-type.modbus.household-power-consumption-channel.label = Haushalt Verbrauch -channel-type.modbus.grid-power-consumption-channel.label = Stromnetz Verbrauch -channel-type.modbus.grid-power-supply-channel.label = Stromnetz Einspeisung +channel-type.modbus.grid-power-consumption-channel.label = Stromnetz Einspeisung +channel-type.modbus.grid-power-supply-channel.label = Stromnetz Verbrauch channel-type.modbus.external-power-supply-channel.label = Externer Stromproduzent channel-type.modbus.wallbox-power-consumption-channel.label = Wallbox Ladestrom channel-type.modbus.wallbox-pv-power-consumption-channel.label = Wallbox Photovoltaik Ladestrom diff --git a/bundles/org.openhab.binding.modbus.e3dc/src/main/resources/OH-INF/thing/power-channel-types.xml b/bundles/org.openhab.binding.modbus.e3dc/src/main/resources/OH-INF/thing/power-channel-types.xml index 2c607b1138b87..57035ee7b0702 100644 --- a/bundles/org.openhab.binding.modbus.e3dc/src/main/resources/OH-INF/thing/power-channel-types.xml +++ b/bundles/org.openhab.binding.modbus.e3dc/src/main/resources/OH-INF/thing/power-channel-types.xml @@ -26,12 +26,12 @@ Number:Power - Grid Power is needed in order to satisfy your overall Power consumption + More Photovoltaic Power is produced than needed. Additional Power is consumed by Grid Number:Power - More Photovoltaic Power is produced than needed. Additional Power is provided towards the Grid + Grid Power is needed in order to satisfy your overall Power consumption Number:Power diff --git a/bundles/org.openhab.binding.modbus.sunspec/src/main/resources/OH-INF/thing/inverter-channel-types.xml b/bundles/org.openhab.binding.modbus.sunspec/src/main/resources/OH-INF/thing/inverter-channel-types.xml index fa0506bbc02a8..0ee3ccb91824a 100644 --- a/bundles/org.openhab.binding.modbus.sunspec/src/main/resources/OH-INF/thing/inverter-channel-types.xml +++ b/bundles/org.openhab.binding.modbus.sunspec/src/main/resources/OH-INF/thing/inverter-channel-types.xml @@ -20,14 +20,14 @@ Number:ElectricPotential This phase's AC voltage relative to the next phase - + Number:ElectricPotential This phase's AC voltage relative to N line - + diff --git a/bundles/org.openhab.binding.modbus/README.md b/bundles/org.openhab.binding.modbus/README.md index c6f561aa443c1..ae30bbe53e4f2 100644 --- a/bundles/org.openhab.binding.modbus/README.md +++ b/bundles/org.openhab.binding.modbus/README.md @@ -146,7 +146,7 @@ Basic parameters | stopBits | text | ✓ | | Stop bits. Valid values are: `"1.0"`, `"1.5"`, `"2.0"`. | | | parity | text | ✓ | | Parity. Valid values are: `"none"`, `"even"`, `"odd"`. | | | dataBits | integer | ✓ | | Data bits. Valid values are: `5`, `6`, `7` and `8`. | | -| encoding | text | ✓ | | Encoding. Valid values are: `"ascii"`, `"rtu"`, `"bin"`. | | +| encoding | text | | `"rtu"` | Encoding. Valid values are: `"ascii"`, `"rtu"`, `"bin"`. | | | echo | boolean | | `false` | Flag for setting the RS485 echo mode. This controls whether we should try to read back whatever we send on the line, before reading the response. Valid values are: `true`, `false`. | | Advanced parameters diff --git a/bundles/org.openhab.binding.modbus/src/main/java/org/openhab/binding/modbus/internal/config/ModbusSerialConfiguration.java b/bundles/org.openhab.binding.modbus/src/main/java/org/openhab/binding/modbus/internal/config/ModbusSerialConfiguration.java index 32804d38c4ecc..8e81973a0ec52 100644 --- a/bundles/org.openhab.binding.modbus/src/main/java/org/openhab/binding/modbus/internal/config/ModbusSerialConfiguration.java +++ b/bundles/org.openhab.binding.modbus/src/main/java/org/openhab/binding/modbus/internal/config/ModbusSerialConfiguration.java @@ -24,19 +24,19 @@ @NonNullByDefault public class ModbusSerialConfiguration { private @Nullable String port; - private int id; + private int id = 1; private int baud; private @Nullable String stopBits; private @Nullable String parity; private int dataBits; - private @Nullable String encoding; + private String encoding = "rtu"; private boolean echo; - private int receiveTimeoutMillis; - private @Nullable String flowControlIn; - private @Nullable String flowControlOut; - private int timeBetweenTransactionsMillis; - private int connectMaxTries; - private int connectTimeoutMillis; + private int receiveTimeoutMillis = 1500; + private String flowControlIn = "none"; + private String flowControlOut = "none"; + private int timeBetweenTransactionsMillis = 35; + private int connectMaxTries = 1; + private int connectTimeoutMillis = 10_000; private boolean enableDiscovery; public @Nullable String getPort() { diff --git a/bundles/org.openhab.binding.modbus/src/main/java/org/openhab/binding/modbus/internal/config/ModbusTcpConfiguration.java b/bundles/org.openhab.binding.modbus/src/main/java/org/openhab/binding/modbus/internal/config/ModbusTcpConfiguration.java index 28ff1d2a2c991..f565f2082a58f 100644 --- a/bundles/org.openhab.binding.modbus/src/main/java/org/openhab/binding/modbus/internal/config/ModbusTcpConfiguration.java +++ b/bundles/org.openhab.binding.modbus/src/main/java/org/openhab/binding/modbus/internal/config/ModbusTcpConfiguration.java @@ -25,12 +25,12 @@ public class ModbusTcpConfiguration { private @Nullable String host; private int port; - private int id; - private int timeBetweenTransactionsMillis; + private int id = 1; + private int timeBetweenTransactionsMillis = 60; private int timeBetweenReconnectMillis; - private int connectMaxTries; + private int connectMaxTries = 1; private int reconnectAfterMillis; - private int connectTimeoutMillis; + private int connectTimeoutMillis = 10_000; private boolean enableDiscovery; private boolean rtuEncoded; diff --git a/bundles/org.openhab.binding.modbus/src/main/java/org/openhab/binding/modbus/internal/handler/AbstractModbusEndpointThingHandler.java b/bundles/org.openhab.binding.modbus/src/main/java/org/openhab/binding/modbus/internal/handler/AbstractModbusEndpointThingHandler.java index ceb2e7f8f5b47..35e6dd1c605db 100644 --- a/bundles/org.openhab.binding.modbus/src/main/java/org/openhab/binding/modbus/internal/handler/AbstractModbusEndpointThingHandler.java +++ b/bundles/org.openhab.binding.modbus/src/main/java/org/openhab/binding/modbus/internal/handler/AbstractModbusEndpointThingHandler.java @@ -45,7 +45,7 @@ public abstract class AbstractModbusEndpointThingHandler 100) { + updateChannel(CHANNEL_VOLUME, UnDefType.UNDEF); + } else { + updateChannel(CHANNEL_VOLUME, new PercentType(volume)); + } State newControlState = UnDefType.UNDEF; switch (status.getState()) { diff --git a/bundles/org.openhab.binding.mqtt.espmilighthub/src/main/java/org/openhab/binding/mqtt/espmilighthub/internal/handler/EspMilightHubHandler.java b/bundles/org.openhab.binding.mqtt.espmilighthub/src/main/java/org/openhab/binding/mqtt/espmilighthub/internal/handler/EspMilightHubHandler.java index 6182f79266451..c2566c264cc9a 100644 --- a/bundles/org.openhab.binding.mqtt.espmilighthub/src/main/java/org/openhab/binding/mqtt/espmilighthub/internal/handler/EspMilightHubHandler.java +++ b/bundles/org.openhab.binding.mqtt.espmilighthub/src/main/java/org/openhab/binding/mqtt/espmilighthub/internal/handler/EspMilightHubHandler.java @@ -347,10 +347,7 @@ public void connectMQTT() { "Bridge is missing or offline, you need to setup a working MQTT broker first."); return; } - ThingUID thingUID = localBridge.getBridgeUID(); - if (thingUID == null) { - return; - } + ThingUID thingUID = localBridge.getUID(); Thing thing = thingRegistry.get(thingUID); if (thing == null) { updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.BRIDGE_UNINITIALIZED, diff --git a/bundles/org.openhab.binding.mqtt.generic/src/main/java/org/openhab/binding/mqtt/generic/ChannelState.java b/bundles/org.openhab.binding.mqtt.generic/src/main/java/org/openhab/binding/mqtt/generic/ChannelState.java index 878a01d83bfed..88138a9c05e1e 100644 --- a/bundles/org.openhab.binding.mqtt.generic/src/main/java/org/openhab/binding/mqtt/generic/ChannelState.java +++ b/bundles/org.openhab.binding.mqtt.generic/src/main/java/org/openhab/binding/mqtt/generic/ChannelState.java @@ -21,7 +21,6 @@ import java.util.concurrent.ScheduledFuture; import java.util.concurrent.TimeUnit; -import org.apache.commons.lang.StringUtils; import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.Nullable; import org.openhab.binding.mqtt.generic.values.TextValue; @@ -78,7 +77,7 @@ public ChannelState(ChannelConfig config, ChannelUID channelUID, Value cachedVal this.channelStateUpdateListener = channelStateUpdateListener; this.channelUID = channelUID; this.cachedValue = cachedValue; - this.readOnly = StringUtils.isBlank(config.commandTopic); + this.readOnly = config.commandTopic.isBlank(); } public boolean isReadOnly() { @@ -242,7 +241,7 @@ public boolean isStateful() { */ public CompletableFuture<@Nullable Void> stop() { final MqttBrokerConnection connection = this.connection; - if (connection != null && StringUtils.isNotBlank(config.stateTopic)) { + if (connection != null && !config.stateTopic.isBlank()) { return connection.unsubscribe(config.stateTopic, this).thenRun(this::internalStop); } else { internalStop(); @@ -297,7 +296,7 @@ private void receivedOrTimeout() { this.connection = connection; - if (StringUtils.isBlank(config.stateTopic)) { + if (config.stateTopic.isBlank()) { return CompletableFuture.completedFuture(null); } diff --git a/bundles/org.openhab.binding.mqtt.generic/src/main/java/org/openhab/binding/mqtt/generic/internal/handler/GenericMQTTThingHandler.java b/bundles/org.openhab.binding.mqtt.generic/src/main/java/org/openhab/binding/mqtt/generic/internal/handler/GenericMQTTThingHandler.java index 3a991d9471f16..8cc05429ec1b6 100644 --- a/bundles/org.openhab.binding.mqtt.generic/src/main/java/org/openhab/binding/mqtt/generic/internal/handler/GenericMQTTThingHandler.java +++ b/bundles/org.openhab.binding.mqtt.generic/src/main/java/org/openhab/binding/mqtt/generic/internal/handler/GenericMQTTThingHandler.java @@ -21,7 +21,7 @@ import java.util.stream.Collectors; import java.util.stream.Stream; -import org.apache.commons.lang.StringUtils; +import org.apache.commons.lang3.StringUtils; import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.Nullable; import org.openhab.binding.mqtt.generic.AbstractMQTTThingHandler; @@ -164,9 +164,8 @@ public void initialize() { Value value = ValueFactory.createValueState(channelConfig, channelTypeUID.getId()); ChannelState channelState = createChannelState(channelConfig, channel.getUID(), value); channelStateByChannelUID.put(channel.getUID(), channelState); - StateDescription description = value - .createStateDescription(StringUtils.isBlank(channelConfig.commandTopic)).build() - .toStateDescription(); + StateDescription description = value.createStateDescription(channelConfig.commandTopic.isBlank()) + .build().toStateDescription(); if (description != null) { stateDescProvider.setDescription(channel.getUID(), description); } diff --git a/bundles/org.openhab.binding.mqtt.generic/src/main/java/org/openhab/binding/mqtt/generic/values/TextValue.java b/bundles/org.openhab.binding.mqtt.generic/src/main/java/org/openhab/binding/mqtt/generic/values/TextValue.java index 9b5f3aab601fc..1e4ffa3c0d9e4 100644 --- a/bundles/org.openhab.binding.mqtt.generic/src/main/java/org/openhab/binding/mqtt/generic/values/TextValue.java +++ b/bundles/org.openhab.binding.mqtt.generic/src/main/java/org/openhab/binding/mqtt/generic/values/TextValue.java @@ -12,12 +12,13 @@ */ package org.openhab.binding.mqtt.generic.values; +import static java.util.function.Predicate.not; + import java.util.Collections; import java.util.Set; import java.util.stream.Collectors; import java.util.stream.Stream; -import org.apache.commons.lang.StringUtils; import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.Nullable; import org.openhab.core.library.CoreItemFactory; @@ -45,7 +46,7 @@ public class TextValue extends Value { */ public TextValue(String[] states) { super(CoreItemFactory.STRING, Collections.singletonList(StringType.class)); - Set s = Stream.of(states).filter(e -> StringUtils.isNotBlank(e)).collect(Collectors.toSet()); + Set s = Stream.of(states).filter(not(String::isBlank)).collect(Collectors.toSet()); if (!s.isEmpty()) { this.states = s; } else { diff --git a/bundles/org.openhab.binding.mqtt.generic/src/main/java/org/openhab/binding/mqtt/generic/values/ValueFactory.java b/bundles/org.openhab.binding.mqtt.generic/src/main/java/org/openhab/binding/mqtt/generic/values/ValueFactory.java index ff8002c07a2af..57e593e7b837c 100644 --- a/bundles/org.openhab.binding.mqtt.generic/src/main/java/org/openhab/binding/mqtt/generic/values/ValueFactory.java +++ b/bundles/org.openhab.binding.mqtt.generic/src/main/java/org/openhab/binding/mqtt/generic/values/ValueFactory.java @@ -12,7 +12,6 @@ */ package org.openhab.binding.mqtt.generic.values; -import org.apache.commons.lang.StringUtils; import org.eclipse.jdt.annotation.NonNullByDefault; import org.openhab.binding.mqtt.generic.ChannelConfig; import org.openhab.binding.mqtt.generic.internal.MqttBindingConstants; @@ -35,7 +34,7 @@ public static Value createValueState(ChannelConfig config, String channelTypeID) Value value; switch (channelTypeID) { case MqttBindingConstants.STRING: - value = StringUtils.isBlank(config.allowedStates) ? new TextValue() + value = config.allowedStates.isBlank() ? new TextValue() : new TextValue(config.allowedStates.split(",")); break; case MqttBindingConstants.DATETIME: diff --git a/bundles/org.openhab.binding.mqtt.homeassistant/src/main/java/org/openhab/binding/mqtt/homeassistant/generic/internal/MqttThingHandlerFactory.java b/bundles/org.openhab.binding.mqtt.homeassistant/src/main/java/org/openhab/binding/mqtt/homeassistant/generic/internal/MqttThingHandlerFactory.java index 8961fe6026eba..f8862fc7dd6e8 100644 --- a/bundles/org.openhab.binding.mqtt.homeassistant/src/main/java/org/openhab/binding/mqtt/homeassistant/generic/internal/MqttThingHandlerFactory.java +++ b/bundles/org.openhab.binding.mqtt.homeassistant/src/main/java/org/openhab/binding/mqtt/homeassistant/generic/internal/MqttThingHandlerFactory.java @@ -16,7 +16,6 @@ import java.util.stream.Collectors; import java.util.stream.Stream; -import org.apache.commons.lang.StringUtils; import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.Nullable; import org.openhab.binding.mqtt.generic.MqttChannelTypeProvider; @@ -54,8 +53,8 @@ public boolean supportsThingType(ThingTypeUID thingTypeUID) { } private boolean isHomeassistantDynamicType(ThingTypeUID thingTypeUID) { - return StringUtils.equals(MqttBindingConstants.BINDING_ID, thingTypeUID.getBindingId()) - && StringUtils.startsWith(thingTypeUID.getId(), MqttBindingConstants.HOMEASSISTANT_MQTT_THING.getId()); + return MqttBindingConstants.BINDING_ID.equals(thingTypeUID.getBindingId()) + && thingTypeUID.getId().startsWith(MqttBindingConstants.HOMEASSISTANT_MQTT_THING.getId()); } @Activate diff --git a/bundles/org.openhab.binding.mqtt.homeassistant/src/main/java/org/openhab/binding/mqtt/homeassistant/internal/BaseChannelConfiguration.java b/bundles/org.openhab.binding.mqtt.homeassistant/src/main/java/org/openhab/binding/mqtt/homeassistant/internal/BaseChannelConfiguration.java index 854de33b85d17..bb17b03530cf1 100644 --- a/bundles/org.openhab.binding.mqtt.homeassistant/src/main/java/org/openhab/binding/mqtt/homeassistant/internal/BaseChannelConfiguration.java +++ b/bundles/org.openhab.binding.mqtt.homeassistant/src/main/java/org/openhab/binding/mqtt/homeassistant/internal/BaseChannelConfiguration.java @@ -16,7 +16,6 @@ import java.util.Map; import java.util.Objects; -import org.apache.commons.lang.StringUtils; import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.Nullable; import org.openhab.core.thing.Thing; @@ -103,9 +102,9 @@ static class Device { protected @Nullable String name; protected @Nullable String sw_version; - @Nullable - public String getId() { - return StringUtils.join(identifiers, "_"); + public @Nullable String getId() { + List identifiers = this.identifiers; + return identifiers == null ? null : String.join("_", identifiers); } } diff --git a/bundles/org.openhab.binding.mqtt.homeassistant/src/main/java/org/openhab/binding/mqtt/homeassistant/internal/CChannel.java b/bundles/org.openhab.binding.mqtt.homeassistant/src/main/java/org/openhab/binding/mqtt/homeassistant/internal/CChannel.java index 4eca5244e56ba..cffc7027d25b0 100644 --- a/bundles/org.openhab.binding.mqtt.homeassistant/src/main/java/org/openhab/binding/mqtt/homeassistant/internal/CChannel.java +++ b/bundles/org.openhab.binding.mqtt.homeassistant/src/main/java/org/openhab/binding/mqtt/homeassistant/internal/CChannel.java @@ -16,7 +16,6 @@ import java.util.concurrent.CompletableFuture; import java.util.concurrent.ScheduledExecutorService; -import org.apache.commons.lang.StringUtils; import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.Nullable; import org.openhab.binding.mqtt.generic.ChannelConfigBuilder; @@ -149,9 +148,9 @@ public Builder stateTopic(@Nullable String state_topic) { public Builder stateTopic(@Nullable String state_topic, @Nullable String... templates) { this.state_topic = state_topic; - if (StringUtils.isNotBlank(state_topic)) { + if (state_topic != null && !state_topic.isBlank()) { for (String template : templates) { - if (StringUtils.isNotBlank(template)) { + if (template != null && !template.isBlank()) { this.templateIn = template; break; } @@ -204,7 +203,8 @@ public CChannel build(boolean addToComponent) { .withCommandTopic(command_topic).makeTrigger(trigger).build(), channelUID, valueState, channelStateUpdateListener); - if (StringUtils.isBlank(state_topic) || this.trigger) { + String localStateTopic = state_topic; + if (localStateTopic == null || localStateTopic.isBlank() || this.trigger) { type = ChannelTypeBuilder.trigger(channelTypeUID, label) .withConfigDescriptionURI(URI.create(MqttBindingConstants.CONFIG_HA_CHANNEL)).build(); } else { diff --git a/bundles/org.openhab.binding.mqtt.homeassistant/src/main/java/org/openhab/binding/mqtt/homeassistant/internal/ChannelConfigurationTypeAdapterFactory.java b/bundles/org.openhab.binding.mqtt.homeassistant/src/main/java/org/openhab/binding/mqtt/homeassistant/internal/ChannelConfigurationTypeAdapterFactory.java index 563e39fe89421..5d61e981aeb87 100644 --- a/bundles/org.openhab.binding.mqtt.homeassistant/src/main/java/org/openhab/binding/mqtt/homeassistant/internal/ChannelConfigurationTypeAdapterFactory.java +++ b/bundles/org.openhab.binding.mqtt.homeassistant/src/main/java/org/openhab/binding/mqtt/homeassistant/internal/ChannelConfigurationTypeAdapterFactory.java @@ -15,7 +15,6 @@ import java.io.IOException; import java.lang.reflect.Field; -import org.apache.commons.lang.StringUtils; import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.Nullable; @@ -119,7 +118,7 @@ private void expandTidleInTopics(BaseChannelConfiguration config) { final String oldValue = (String) field.get(config); String newValue = oldValue; - if (StringUtils.isNotBlank(oldValue)) { + if (oldValue != null && !oldValue.isBlank()) { if (oldValue.charAt(0) == '~') { newValue = tilde + oldValue.substring(1); } else if (oldValue.charAt(oldValue.length() - 1) == '~') { diff --git a/bundles/org.openhab.binding.mqtt.homeassistant/src/main/java/org/openhab/binding/mqtt/homeassistant/internal/ComponentLock.java b/bundles/org.openhab.binding.mqtt.homeassistant/src/main/java/org/openhab/binding/mqtt/homeassistant/internal/ComponentLock.java index fb78f52358bac..aad866a2d155e 100644 --- a/bundles/org.openhab.binding.mqtt.homeassistant/src/main/java/org/openhab/binding/mqtt/homeassistant/internal/ComponentLock.java +++ b/bundles/org.openhab.binding.mqtt.homeassistant/src/main/java/org/openhab/binding/mqtt/homeassistant/internal/ComponentLock.java @@ -12,7 +12,6 @@ */ package org.openhab.binding.mqtt.homeassistant.internal; -import org.apache.commons.lang.StringUtils; import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.Nullable; import org.openhab.binding.mqtt.generic.values.OnOffValue; @@ -46,7 +45,7 @@ public ComponentLock(CFactory.ComponentConfiguration componentConfiguration) { super(componentConfiguration, ChannelConfiguration.class); // We do not support all HomeAssistant quirks - if (channelConfiguration.optimistic && StringUtils.isNotBlank(channelConfiguration.state_topic)) { + if (channelConfiguration.optimistic && !channelConfiguration.state_topic.isBlank()) { throw new UnsupportedOperationException("Component:Lock does not support forced optimistic mode"); } diff --git a/bundles/org.openhab.binding.mqtt.homeassistant/src/main/java/org/openhab/binding/mqtt/homeassistant/internal/ComponentSensor.java b/bundles/org.openhab.binding.mqtt.homeassistant/src/main/java/org/openhab/binding/mqtt/homeassistant/internal/ComponentSensor.java index e3769c002868a..2e18a3a2dd6f1 100644 --- a/bundles/org.openhab.binding.mqtt.homeassistant/src/main/java/org/openhab/binding/mqtt/homeassistant/internal/ComponentSensor.java +++ b/bundles/org.openhab.binding.mqtt.homeassistant/src/main/java/org/openhab/binding/mqtt/homeassistant/internal/ComponentSensor.java @@ -15,7 +15,6 @@ import java.util.List; import java.util.regex.Pattern; -import org.apache.commons.lang.StringUtils; import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.Nullable; import org.openhab.binding.mqtt.generic.ChannelStateUpdateListener; @@ -61,7 +60,7 @@ public ComponentSensor(CFactory.ComponentConfiguration componentConfiguration) { String uom = channelConfiguration.unit_of_measurement; - if (uom != null && StringUtils.isNotBlank(uom)) { + if (uom != null && !uom.isBlank()) { value = new NumberValue(null, null, null, uom); } else { value = new TextValue(); diff --git a/bundles/org.openhab.binding.mqtt.homeassistant/src/main/java/org/openhab/binding/mqtt/homeassistant/internal/ComponentSwitch.java b/bundles/org.openhab.binding.mqtt.homeassistant/src/main/java/org/openhab/binding/mqtt/homeassistant/internal/ComponentSwitch.java index 3326572f65d40..87f179d132b27 100644 --- a/bundles/org.openhab.binding.mqtt.homeassistant/src/main/java/org/openhab/binding/mqtt/homeassistant/internal/ComponentSwitch.java +++ b/bundles/org.openhab.binding.mqtt.homeassistant/src/main/java/org/openhab/binding/mqtt/homeassistant/internal/ComponentSwitch.java @@ -12,7 +12,6 @@ */ package org.openhab.binding.mqtt.homeassistant.internal; -import org.apache.commons.lang.StringUtils; import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.Nullable; import org.openhab.binding.mqtt.generic.values.OnOffValue; @@ -52,9 +51,9 @@ public ComponentSwitch(CFactory.ComponentConfiguration componentConfiguration) { super(componentConfiguration, ChannelConfiguration.class); boolean optimistic = channelConfiguration.optimistic != null ? channelConfiguration.optimistic - : StringUtils.isBlank(channelConfiguration.state_topic); + : channelConfiguration.state_topic.isBlank(); - if (optimistic && StringUtils.isNotBlank(channelConfiguration.state_topic)) { + if (optimistic && !channelConfiguration.state_topic.isBlank()) { throw new UnsupportedOperationException("Component:Switch does not support forced optimistic mode"); } diff --git a/bundles/org.openhab.binding.mqtt.homeassistant/src/main/java/org/openhab/binding/mqtt/homeassistant/internal/HaID.java b/bundles/org.openhab.binding.mqtt.homeassistant/src/main/java/org/openhab/binding/mqtt/homeassistant/internal/HaID.java index 48eba3bab940b..c7f895cd4557e 100644 --- a/bundles/org.openhab.binding.mqtt.homeassistant/src/main/java/org/openhab/binding/mqtt/homeassistant/internal/HaID.java +++ b/bundles/org.openhab.binding.mqtt.homeassistant/src/main/java/org/openhab/binding/mqtt/homeassistant/internal/HaID.java @@ -15,7 +15,6 @@ import java.util.ArrayList; import java.util.Collection; -import org.apache.commons.lang.StringUtils; import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.Nullable; import org.openhab.core.config.core.Configuration; @@ -93,7 +92,7 @@ private HaID(String baseTopic, String objectID, String nodeID, String component) private static final String createTopic(HaID id) { StringBuilder str = new StringBuilder(); str.append(id.baseTopic).append('/').append(id.component).append('/'); - if (StringUtils.isNotBlank(id.nodeID)) { + if (!id.nodeID.isBlank()) { str.append(id.nodeID).append('/'); } str.append(id.objectID).append('/'); @@ -175,7 +174,7 @@ public static Collection fromConfig(HandlerConfiguration config) { */ public String toShortTopic() { String objectID = this.objectID; - if (StringUtils.isNotBlank(nodeID)) { + if (!nodeID.isBlank()) { objectID = nodeID + "/" + objectID; } @@ -192,10 +191,10 @@ public String getGroupId(@Nullable final String uniqueId) { String result = uniqueId; // the null test is only here so the compile knows, result is not null afterwards - if (result == null || StringUtils.isBlank(result)) { + if (result == null || result.isBlank()) { StringBuilder str = new StringBuilder(); - if (StringUtils.isNotBlank(nodeID)) { + if (!nodeID.isBlank()) { str.append(nodeID).append('_'); } str.append(objectID).append('_').append(component); diff --git a/bundles/org.openhab.binding.mqtt/src/main/java/org/openhab/binding/mqtt/handler/BrokerHandler.java b/bundles/org.openhab.binding.mqtt/src/main/java/org/openhab/binding/mqtt/handler/BrokerHandler.java index 91d803dd43a71..f26cd46b8407f 100644 --- a/bundles/org.openhab.binding.mqtt/src/main/java/org/openhab/binding/mqtt/handler/BrokerHandler.java +++ b/bundles/org.openhab.binding.mqtt/src/main/java/org/openhab/binding/mqtt/handler/BrokerHandler.java @@ -19,7 +19,6 @@ import javax.net.ssl.TrustManager; -import org.apache.commons.lang.StringUtils; import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.Nullable; import org.openhab.binding.mqtt.internal.ssl.Pin; @@ -60,10 +59,12 @@ public void connectionStateChanged(MqttConnectionState state, @Nullable Throwabl super.connectionStateChanged(state, error); // Store generated client ID if none was set by the user final MqttBrokerConnection connection = this.connection; - if (connection != null && state == MqttConnectionState.CONNECTED && StringUtils.isBlank(config.clientID)) { - config.clientID = connection.getClientId(); + String clientID = config.clientID; + if (connection != null && state == MqttConnectionState.CONNECTED && (clientID == null || clientID.isBlank())) { + clientID = connection.getClientId(); + config.clientID = clientID; Configuration editConfig = editConfiguration(); - editConfig.put("clientid", config.clientID); + editConfig.put("clientid", clientID); updateConfiguration(editConfig); } } @@ -147,7 +148,7 @@ protected void assignSSLContextProvider(BrokerHandlerConfig config, MqttBrokerCo if (config.certificatepin) { try { Pin pin; - if (StringUtils.isBlank(config.certificate)) { + if (config.certificate.isBlank()) { pin = Pin.LearningPin(PinType.CERTIFICATE_TYPE); } else { String[] split = config.certificate.split(":"); @@ -165,7 +166,7 @@ protected void assignSSLContextProvider(BrokerHandlerConfig config, MqttBrokerCo if (config.publickeypin) { try { Pin pin; - if (StringUtils.isBlank(config.publickey)) { + if (config.publickey.isBlank()) { pin = Pin.LearningPin(PinType.PUBLIC_KEY_TYPE); } else { String[] split = config.publickey.split(":"); @@ -190,7 +191,7 @@ protected void assignSSLContextProvider(BrokerHandlerConfig config, MqttBrokerCo */ protected MqttBrokerConnection createBrokerConnection() throws IllegalArgumentException { String host = config.host; - if (StringUtils.isBlank(host) || host == null) { + if (host == null || host.isBlank()) { throw new IllegalArgumentException("Host is empty!"); } @@ -199,7 +200,7 @@ protected MqttBrokerConnection createBrokerConnection() throws IllegalArgumentEx final String username = config.username; final String password = config.password; - if (StringUtils.isNotBlank(username) && password != null) { + if (username != null && !username.isBlank() && password != null) { connection.setCredentials(username, password); // Empty passwords are allowed } diff --git a/bundles/org.openhab.binding.myq/NOTICE b/bundles/org.openhab.binding.myq/NOTICE new file mode 100644 index 0000000000000..38d625e349232 --- /dev/null +++ b/bundles/org.openhab.binding.myq/NOTICE @@ -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 diff --git a/bundles/org.openhab.binding.myq/README.md b/bundles/org.openhab.binding.myq/README.md new file mode 100644 index 0000000000000..a8094c3546967 --- /dev/null +++ b/bundles/org.openhab.binding.myq/README.md @@ -0,0 +1,68 @@ +# MyQ Binding + +This binding integrates with the [The Chamberlain Group MyQ](https://www.myq.com) cloud service. It allows monitoring and control over [MyQ](https://www.myq.com) enabled garage doors manufactured by LiftMaster, Chamberlain and Craftsman. + +## Supported Things + +### Account + +This represents the MyQ cloud account and uses the same credentials needed when using the MyQ mobile application. + +ThingTypeUID: `account` + +### Garage Door + +This represents a garage door associated with an account. Multiple garage doors are supported. + +ThingTypeUID: `garagedoor` + +### Lamp + +This represents a lamp associated with an account. Multiple lamps are supported. + +ThingTypeUID: `lamp` + +## Discovery + +Once an account has been added, garage doors and lamps will automatically be discovered and added to the inbox. + +## Channels + +| Channel | Item Type | Thing Type | States | +|---------------|---------------|------------------|--------------------------------------------------------| +| status | String | garagedoor | opening, closed, closing, stopped, transition, unknown | +| rollershutter | Rollershutter | garagedoor | UP, DOWN, 0%, 100% | +| switch | Switch | garagedoor, lamp | ON (open), OFF (closed) + +## Full Example + +### Thing Configuration + +```xtend +Bridge myq:account:home "MyQ Account" [ username="foo@bar.com", password="secret", refreshInterval=60 ] { + Thing garagedoor abcd12345 "MyQ Garage Door" [ serialNumber="abcd12345" ] + Thing lamp efgh6789 "MyQ Lamp" [ serialNumber="efgh6789" ] +} +``` + +### Items + +```xtend +String MyQGarageDoor1Status "Door Status [%s]" {channel = "myq:garagedoor:home:abcd12345:status"} +Switch MyQGarageDoor1Switch "Door Switch [%s]" {channel = "myq:garagedoor:home:abcd12345:switch"} +Rollershutter MyQGarageDoor1Rollershutter "Door Rollershutter [%s]" {channel = "myq:garagedoor:home:abcd12345:rollershutter"} +Switch MyQGarageDoorLamp "Lamp [%s]" {channel = "myq:lamp:home:efgh6789:switch"} +} +``` + +### Sitemaps + +```xtend +sitemap MyQ label="MyQ Demo Sitemap" { + Frame label="Garage Door" { + String item=MyQGarageDoor1Status + Switch item=MyQGarageDoor1Switch + Rollershutter item=MyQGarageDoor1Rollershutter + } +} +``` diff --git a/bundles/org.openhab.binding.myq/pom.xml b/bundles/org.openhab.binding.myq/pom.xml new file mode 100644 index 0000000000000..a0e8b2e06cdc3 --- /dev/null +++ b/bundles/org.openhab.binding.myq/pom.xml @@ -0,0 +1,17 @@ + + + + 4.0.0 + + + org.openhab.addons.bundles + org.openhab.addons.reactor.bundles + 3.1.0-SNAPSHOT + + + org.openhab.binding.myq + + openHAB Add-ons :: Bundles :: MyQ Binding + + diff --git a/bundles/org.openhab.binding.myq/src/main/feature/feature.xml b/bundles/org.openhab.binding.myq/src/main/feature/feature.xml new file mode 100644 index 0000000000000..60382373bb63b --- /dev/null +++ b/bundles/org.openhab.binding.myq/src/main/feature/feature.xml @@ -0,0 +1,9 @@ + + + mvn:org.openhab.core.features.karaf/org.openhab.core.features.karaf.openhab-core/${ohc.version}/xml/features + + + openhab-runtime-base + mvn:org.openhab.addons.bundles/org.openhab.binding.myq/${project.version} + + diff --git a/bundles/org.openhab.binding.myq/src/main/java/org/openhab/binding/myq/internal/MyQBindingConstants.java b/bundles/org.openhab.binding.myq/src/main/java/org/openhab/binding/myq/internal/MyQBindingConstants.java new file mode 100644 index 0000000000000..3e8cb305720ab --- /dev/null +++ b/bundles/org.openhab.binding.myq/src/main/java/org/openhab/binding/myq/internal/MyQBindingConstants.java @@ -0,0 +1,38 @@ +/** + * Copyright (c) 2010-2021 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.myq.internal; + +import java.util.Set; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.openhab.core.thing.ThingTypeUID; + +/** + * The {@link MyQBindingConstants} class defines common constants, which are + * used across the whole binding. + * + * @author Dan Cunningham - Initial contribution + */ +@NonNullByDefault +public class MyQBindingConstants { + + public static final String BINDING_ID = "myq"; + + public static final ThingTypeUID THING_TYPE_ACCOUNT = new ThingTypeUID(BINDING_ID, "account"); + public static final ThingTypeUID THING_TYPE_GARAGEDOOR = new ThingTypeUID(BINDING_ID, "garagedoor"); + public static final ThingTypeUID THING_TYPE_LAMP = new ThingTypeUID(BINDING_ID, "lamp"); + public static final Set SUPPORTED_THING_TYPES_UIDS = Set.of(THING_TYPE_ACCOUNT, THING_TYPE_GARAGEDOOR, + THING_TYPE_LAMP); + public static final Set SUPPORTED_DISCOVERY_THING_TYPES_UIDS = Set.of(THING_TYPE_GARAGEDOOR, + THING_TYPE_LAMP); +} diff --git a/bundles/org.openhab.binding.myq/src/main/java/org/openhab/binding/myq/internal/MyQDiscoveryService.java b/bundles/org.openhab.binding.myq/src/main/java/org/openhab/binding/myq/internal/MyQDiscoveryService.java new file mode 100644 index 0000000000000..883d2988cf736 --- /dev/null +++ b/bundles/org.openhab.binding.myq/src/main/java/org/openhab/binding/myq/internal/MyQDiscoveryService.java @@ -0,0 +1,97 @@ +/** + * Copyright (c) 2010-2021 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.myq.internal; + +import static org.openhab.binding.myq.internal.MyQBindingConstants.BINDING_ID; + +import java.util.Set; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; +import org.openhab.binding.myq.internal.dto.DevicesDTO; +import org.openhab.binding.myq.internal.handler.MyQAccountHandler; +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.config.discovery.DiscoveryService; +import org.openhab.core.thing.Thing; +import org.openhab.core.thing.ThingTypeUID; +import org.openhab.core.thing.ThingUID; +import org.openhab.core.thing.binding.ThingHandler; +import org.openhab.core.thing.binding.ThingHandlerService; + +/** + * The {@link MyQDiscoveryService} is responsible for discovering MyQ things + * + * @author Dan Cunningham - Initial contribution + */ +@NonNullByDefault +public class MyQDiscoveryService extends AbstractDiscoveryService implements DiscoveryService, ThingHandlerService { + + private static final Set SUPPORTED_DISCOVERY_THING_TYPES_UIDS = Set + .of(MyQBindingConstants.THING_TYPE_GARAGEDOOR, MyQBindingConstants.THING_TYPE_LAMP); + private @Nullable MyQAccountHandler accountHandler; + + public MyQDiscoveryService() { + super(SUPPORTED_DISCOVERY_THING_TYPES_UIDS, 1, true); + } + + @Override + public Set getSupportedThingTypes() { + return SUPPORTED_DISCOVERY_THING_TYPES_UIDS; + } + + @Override + public void startScan() { + MyQAccountHandler accountHandler = this.accountHandler; + if (accountHandler != null) { + DevicesDTO devices = accountHandler.devicesCache(); + if (devices != null) { + devices.items.forEach(device -> { + ThingTypeUID thingTypeUID = new ThingTypeUID(BINDING_ID, device.deviceFamily); + if (SUPPORTED_DISCOVERY_THING_TYPES_UIDS.contains(thingTypeUID)) { + ThingUID thingUID = new ThingUID(thingTypeUID, accountHandler.getThing().getUID(), + device.serialNumber.toLowerCase()); + DiscoveryResult result = DiscoveryResultBuilder.create(thingUID).withLabel("MyQ " + device.name) + .withProperty(Thing.PROPERTY_SERIAL_NUMBER, thingUID.getId()) + .withRepresentationProperty(Thing.PROPERTY_SERIAL_NUMBER) + .withBridge(accountHandler.getThing().getUID()).build(); + thingDiscovered(result); + } + }); + } + } + } + + @Override + public void setThingHandler(ThingHandler handler) { + if (handler instanceof MyQAccountHandler) { + accountHandler = (MyQAccountHandler) handler; + } + } + + @Override + public @Nullable ThingHandler getThingHandler() { + return accountHandler; + } + + @Override + public void activate() { + super.activate(null); + } + + @Override + public void deactivate() { + super.deactivate(); + } +} diff --git a/bundles/org.openhab.binding.myq/src/main/java/org/openhab/binding/myq/internal/MyQHandlerFactory.java b/bundles/org.openhab.binding.myq/src/main/java/org/openhab/binding/myq/internal/MyQHandlerFactory.java new file mode 100644 index 0000000000000..2d01eee7788ae --- /dev/null +++ b/bundles/org.openhab.binding.myq/src/main/java/org/openhab/binding/myq/internal/MyQHandlerFactory.java @@ -0,0 +1,73 @@ +/** + * Copyright (c) 2010-2021 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.myq.internal; + +import static org.openhab.binding.myq.internal.MyQBindingConstants.*; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; +import org.eclipse.jetty.client.HttpClient; +import org.openhab.binding.myq.internal.handler.MyQAccountHandler; +import org.openhab.binding.myq.internal.handler.MyQGarageDoorHandler; +import org.openhab.binding.myq.internal.handler.MyQLampHandler; +import org.openhab.core.io.net.http.HttpClientFactory; +import org.openhab.core.thing.Bridge; +import org.openhab.core.thing.Thing; +import org.openhab.core.thing.ThingTypeUID; +import org.openhab.core.thing.binding.BaseThingHandlerFactory; +import org.openhab.core.thing.binding.ThingHandler; +import org.openhab.core.thing.binding.ThingHandlerFactory; +import org.osgi.service.component.annotations.Activate; +import org.osgi.service.component.annotations.Component; +import org.osgi.service.component.annotations.Reference; + +/** + * The {@link MyQHandlerFactory} is responsible for creating things and thing + * handlers. + * + * @author Dan Cunningham - Initial contribution + */ +@NonNullByDefault +@Component(configurationPid = "binding.myq", service = ThingHandlerFactory.class) +public class MyQHandlerFactory extends BaseThingHandlerFactory { + private final HttpClient httpClient; + + @Activate + public MyQHandlerFactory(final @Reference HttpClientFactory httpClientFactory) { + this.httpClient = httpClientFactory.getCommonHttpClient(); + } + + @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_ACCOUNT.equals(thingTypeUID)) { + return new MyQAccountHandler((Bridge) thing, httpClient); + } + + if (THING_TYPE_GARAGEDOOR.equals(thingTypeUID)) { + return new MyQGarageDoorHandler(thing); + } + + if (THING_TYPE_LAMP.equals(thingTypeUID)) { + return new MyQLampHandler(thing); + } + + return null; + } +} diff --git a/bundles/org.openhab.binding.myq/src/main/java/org/openhab/binding/myq/internal/config/MyQAccountConfiguration.java b/bundles/org.openhab.binding.myq/src/main/java/org/openhab/binding/myq/internal/config/MyQAccountConfiguration.java new file mode 100644 index 0000000000000..b6f3d23c2524d --- /dev/null +++ b/bundles/org.openhab.binding.myq/src/main/java/org/openhab/binding/myq/internal/config/MyQAccountConfiguration.java @@ -0,0 +1,27 @@ +/** + * Copyright (c) 2010-2021 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.myq.internal.config; + +import org.eclipse.jdt.annotation.NonNullByDefault; + +/** + * The {@link MyQAccountConfiguration} class contains fields mapping thing configuration parameters. + * + * @author Dan Cunningham - Initial contribution + */ +@NonNullByDefault +public class MyQAccountConfiguration { + public String username = ""; + public String password = ""; + public Integer refreshInterval = 60; +} diff --git a/bundles/org.openhab.binding.myq/src/main/java/org/openhab/binding/myq/internal/config/MyQDeviceConfiguration.java b/bundles/org.openhab.binding.myq/src/main/java/org/openhab/binding/myq/internal/config/MyQDeviceConfiguration.java new file mode 100644 index 0000000000000..8cab68231271e --- /dev/null +++ b/bundles/org.openhab.binding.myq/src/main/java/org/openhab/binding/myq/internal/config/MyQDeviceConfiguration.java @@ -0,0 +1,25 @@ +/** + * Copyright (c) 2010-2021 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.myq.internal.config; + +import org.eclipse.jdt.annotation.NonNullByDefault; + +/** + * The {@link MyQDeviceConfiguration} class contains fields mapping thing configuration parameters. + * + * @author Dan Cunningham - Initial contribution + */ +@NonNullByDefault +public class MyQDeviceConfiguration { + public String serialNumber = ""; +} diff --git a/bundles/org.openhab.binding.myq/src/main/java/org/openhab/binding/myq/internal/dto/AccountDTO.java b/bundles/org.openhab.binding.myq/src/main/java/org/openhab/binding/myq/internal/dto/AccountDTO.java new file mode 100644 index 0000000000000..2957918320957 --- /dev/null +++ b/bundles/org.openhab.binding.myq/src/main/java/org/openhab/binding/myq/internal/dto/AccountDTO.java @@ -0,0 +1,38 @@ +/** + * Copyright (c) 2010-2021 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.myq.internal.dto; + +/** + * The {@link AccountDTO} entity from the MyQ API + * + * @author Dan Cunningham - Initial contribution + */ +public class AccountDTO { + + public UsersDTO users; + public Boolean admin; + public AccountInfoDTO account; + public String analyticsId; + public String userId; + public String userName; + public String email; + public String firstName; + public String lastName; + public String cultureCode; + public AddressDTO address; + public TimeZoneDTO timeZone; + public Boolean mailingListOptIn; + public Boolean requestAccountLinkInfo; + public String phone; + public Boolean diagnosticDataOptIn; +} diff --git a/bundles/org.openhab.binding.myq/src/main/java/org/openhab/binding/myq/internal/dto/AccountInfoDTO.java b/bundles/org.openhab.binding.myq/src/main/java/org/openhab/binding/myq/internal/dto/AccountInfoDTO.java new file mode 100644 index 0000000000000..193f464d044bb --- /dev/null +++ b/bundles/org.openhab.binding.myq/src/main/java/org/openhab/binding/myq/internal/dto/AccountInfoDTO.java @@ -0,0 +1,24 @@ +/** + * Copyright (c) 2010-2021 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.myq.internal.dto; + +/** + * The {@link AccountInfoDTO} entity from the MyQ API + * + * @author Dan Cunningham - Initial contribution + */ +public class AccountInfoDTO { + + public String href; + public String id; +} diff --git a/bundles/org.openhab.binding.myq/src/main/java/org/openhab/binding/myq/internal/dto/ActionDTO.java b/bundles/org.openhab.binding.myq/src/main/java/org/openhab/binding/myq/internal/dto/ActionDTO.java new file mode 100644 index 0000000000000..a38aa1e075d1b --- /dev/null +++ b/bundles/org.openhab.binding.myq/src/main/java/org/openhab/binding/myq/internal/dto/ActionDTO.java @@ -0,0 +1,28 @@ +/** + * Copyright (c) 2010-2021 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.myq.internal.dto; + +/** + * The {@link ActionDTO} entity from the MyQ API + * + * @author Dan Cunningham - Initial contribution + */ +public class ActionDTO { + + public ActionDTO(String actionType) { + super(); + this.actionType = actionType; + } + + public String actionType; +} diff --git a/bundles/org.openhab.binding.myq/src/main/java/org/openhab/binding/myq/internal/dto/AddressDTO.java b/bundles/org.openhab.binding.myq/src/main/java/org/openhab/binding/myq/internal/dto/AddressDTO.java new file mode 100644 index 0000000000000..1c4d858ff8b08 --- /dev/null +++ b/bundles/org.openhab.binding.myq/src/main/java/org/openhab/binding/myq/internal/dto/AddressDTO.java @@ -0,0 +1,26 @@ +/** + * Copyright (c) 2010-2021 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.myq.internal.dto; + +/** + * The {@link AddressDTO} entity from the MyQ API + * + * @author Dan Cunningham - Initial contribution + */ +public class AddressDTO { + + public String addressLine1; + public String city; + public String postalCode; + public CountryDTO country; +} diff --git a/bundles/org.openhab.binding.myq/src/main/java/org/openhab/binding/myq/internal/dto/CountryDTO.java b/bundles/org.openhab.binding.myq/src/main/java/org/openhab/binding/myq/internal/dto/CountryDTO.java new file mode 100644 index 0000000000000..594a1ee32e7c0 --- /dev/null +++ b/bundles/org.openhab.binding.myq/src/main/java/org/openhab/binding/myq/internal/dto/CountryDTO.java @@ -0,0 +1,25 @@ +/** + * Copyright (c) 2010-2021 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.myq.internal.dto; + +/** + * The {@link CountryDTO} entity from the MyQ API + * + * @author Dan Cunningham - Initial contribution + */ +public class CountryDTO { + + public String code; + public Boolean isEEACountry; + public String href; +} diff --git a/bundles/org.openhab.binding.myq/src/main/java/org/openhab/binding/myq/internal/dto/DeviceDTO.java b/bundles/org.openhab.binding.myq/src/main/java/org/openhab/binding/myq/internal/dto/DeviceDTO.java new file mode 100644 index 0000000000000..426d691b2fcfb --- /dev/null +++ b/bundles/org.openhab.binding.myq/src/main/java/org/openhab/binding/myq/internal/dto/DeviceDTO.java @@ -0,0 +1,31 @@ +/** + * Copyright (c) 2010-2021 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.myq.internal.dto; + +/** + * The {@link DeviceDTO} entity from the MyQ API + * + * @author Dan Cunningham - Initial contribution + */ +public class DeviceDTO { + public String href; + public String serialNumber; + public String deviceFamily; + public String devicePlatform; + public String deviceType; + public String name; + public String createdDate; + public DeviceStateDTO state; + public String parentDevice; + public String parentDeviceId; +} diff --git a/bundles/org.openhab.binding.myq/src/main/java/org/openhab/binding/myq/internal/dto/DeviceStateDTO.java b/bundles/org.openhab.binding.myq/src/main/java/org/openhab/binding/myq/internal/dto/DeviceStateDTO.java new file mode 100644 index 0000000000000..169119b8c6116 --- /dev/null +++ b/bundles/org.openhab.binding.myq/src/main/java/org/openhab/binding/myq/internal/dto/DeviceStateDTO.java @@ -0,0 +1,56 @@ +/** + * Copyright (c) 2010-2021 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.myq.internal.dto; + +import java.util.List; + +/** + * The {@link DeviceStateDTO} entity from the MyQ API + * + * @author Dan Cunningham - Initial contribution + */ +public class DeviceStateDTO { + + public Boolean gdoLockConnected; + public Boolean attachedWorkLightErrorPresent; + public String doorState; + public String lampState; + public String open; + public String close; + public String lastUpdate; + public String passthroughInterval; + public String doorAjarInterval; + public String invalidCredentialWindow; + public String invalidShutoutPeriod; + public Boolean isUnattendedOpenAllowed; + public Boolean isUnattendedCloseAllowed; + public String auxRelayDelay; + public Boolean useAuxRelay; + public String auxRelayBehavior; + public Boolean rexFiresDoor; + public Boolean commandChannelReportStatus; + public Boolean controlFromBrowser; + public Boolean reportForced; + public Boolean reportAjar; + public Integer maxInvalidAttempts; + public Boolean online; + public String lastStatus; + public String firmwareVersion; + public Boolean homekitCapable; + public Boolean homekitEnabled; + public String learn; + public Boolean learnMode; + public String updatedDate; + public List physicalDevices = null; + public Boolean pendingBootloadAbandoned; +} diff --git a/bundles/org.openhab.binding.myq/src/main/java/org/openhab/binding/myq/internal/dto/DevicesDTO.java b/bundles/org.openhab.binding.myq/src/main/java/org/openhab/binding/myq/internal/dto/DevicesDTO.java new file mode 100644 index 0000000000000..f170101c81dda --- /dev/null +++ b/bundles/org.openhab.binding.myq/src/main/java/org/openhab/binding/myq/internal/dto/DevicesDTO.java @@ -0,0 +1,26 @@ +/** + * Copyright (c) 2010-2021 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.myq.internal.dto; + +import java.util.List; + +/** + * The {@link DevicesDTO} entity from the MyQ API + * + * @author Dan Cunningham - Initial contribution + */ +public class DevicesDTO { + public String href; + public Integer count; + public List items; +} diff --git a/bundles/org.openhab.binding.myq/src/main/java/org/openhab/binding/myq/internal/dto/LoginRequestDTO.java b/bundles/org.openhab.binding.myq/src/main/java/org/openhab/binding/myq/internal/dto/LoginRequestDTO.java new file mode 100644 index 0000000000000..8b2eaf54014c6 --- /dev/null +++ b/bundles/org.openhab.binding.myq/src/main/java/org/openhab/binding/myq/internal/dto/LoginRequestDTO.java @@ -0,0 +1,30 @@ +/** + * Copyright (c) 2010-2021 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.myq.internal.dto; + +/** + * The {@link LoginRequestDTO} entity from the MyQ API + * + * @author Dan Cunningham - Initial contribution + */ +public class LoginRequestDTO { + + public LoginRequestDTO(String username, String password) { + super(); + this.username = username; + this.password = password; + } + + public String username; + public String password; +} diff --git a/bundles/org.openhab.binding.myq/src/main/java/org/openhab/binding/myq/internal/dto/LoginResponseDTO.java b/bundles/org.openhab.binding.myq/src/main/java/org/openhab/binding/myq/internal/dto/LoginResponseDTO.java new file mode 100644 index 0000000000000..2dfcd637d21bb --- /dev/null +++ b/bundles/org.openhab.binding.myq/src/main/java/org/openhab/binding/myq/internal/dto/LoginResponseDTO.java @@ -0,0 +1,22 @@ +/** + * Copyright (c) 2010-2021 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.myq.internal.dto; + +/** + * The {@link LoginResponseDTO} entity from the MyQ API + * + * @author Dan Cunningham - Initial contribution + */ +public class LoginResponseDTO { + public String securityToken; +} diff --git a/bundles/org.openhab.binding.myq/src/main/java/org/openhab/binding/myq/internal/dto/TimeZoneDTO.java b/bundles/org.openhab.binding.myq/src/main/java/org/openhab/binding/myq/internal/dto/TimeZoneDTO.java new file mode 100644 index 0000000000000..e324225ecdd11 --- /dev/null +++ b/bundles/org.openhab.binding.myq/src/main/java/org/openhab/binding/myq/internal/dto/TimeZoneDTO.java @@ -0,0 +1,24 @@ +/** + * Copyright (c) 2010-2021 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.myq.internal.dto; + +/** + * The {@link TimeZoneDTO} entity from the MyQ API + * + * @author Dan Cunningham - Initial contribution + */ +public class TimeZoneDTO { + + public String id; + public String name; +} diff --git a/bundles/org.openhab.binding.myq/src/main/java/org/openhab/binding/myq/internal/dto/UsersDTO.java b/bundles/org.openhab.binding.myq/src/main/java/org/openhab/binding/myq/internal/dto/UsersDTO.java new file mode 100644 index 0000000000000..c988dba883888 --- /dev/null +++ b/bundles/org.openhab.binding.myq/src/main/java/org/openhab/binding/myq/internal/dto/UsersDTO.java @@ -0,0 +1,23 @@ +/** + * Copyright (c) 2010-2021 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.myq.internal.dto; + +/** + * The {@link UsersDTO} entity from the MyQ API + * + * @author Dan Cunningham - Initial contribution + */ +public class UsersDTO { + + public String href; +} diff --git a/bundles/org.openhab.binding.myq/src/main/java/org/openhab/binding/myq/internal/handler/MyQAccountHandler.java b/bundles/org.openhab.binding.myq/src/main/java/org/openhab/binding/myq/internal/handler/MyQAccountHandler.java new file mode 100644 index 0000000000000..f9dc5f11bcb0f --- /dev/null +++ b/bundles/org.openhab.binding.myq/src/main/java/org/openhab/binding/myq/internal/handler/MyQAccountHandler.java @@ -0,0 +1,344 @@ +/** + * Copyright (c) 2010-2021 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.myq.internal.handler; + +import static org.openhab.binding.myq.internal.MyQBindingConstants.*; + +import java.util.Collection; +import java.util.Collections; +import java.util.Random; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.Future; +import java.util.concurrent.TimeUnit; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; +import org.eclipse.jetty.client.HttpClient; +import org.eclipse.jetty.client.api.ContentProvider; +import org.eclipse.jetty.client.api.Request; +import org.eclipse.jetty.client.api.Result; +import org.eclipse.jetty.client.util.BufferingResponseListener; +import org.eclipse.jetty.client.util.StringContentProvider; +import org.eclipse.jetty.http.HttpMethod; +import org.eclipse.jetty.http.HttpStatus; +import org.openhab.binding.myq.internal.MyQDiscoveryService; +import org.openhab.binding.myq.internal.config.MyQAccountConfiguration; +import org.openhab.binding.myq.internal.dto.AccountDTO; +import org.openhab.binding.myq.internal.dto.ActionDTO; +import org.openhab.binding.myq.internal.dto.DevicesDTO; +import org.openhab.binding.myq.internal.dto.LoginRequestDTO; +import org.openhab.binding.myq.internal.dto.LoginResponseDTO; +import org.openhab.core.thing.Bridge; +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.ThingTypeUID; +import org.openhab.core.thing.binding.BaseBridgeHandler; +import org.openhab.core.thing.binding.ThingHandler; +import org.openhab.core.thing.binding.ThingHandlerService; +import org.openhab.core.types.Command; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.google.gson.FieldNamingPolicy; +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; +import com.google.gson.JsonSyntaxException; + +/** + * The {@link MyQAccountHandler} is responsible for communicating with the MyQ API based on an account. + * + * @author Dan Cunningham - Initial contribution + */ +@NonNullByDefault +public class MyQAccountHandler extends BaseBridgeHandler { + private static final String BASE_URL = "https://api.myqdevice.com/api"; + private static final Integer RAPID_REFRESH_SECONDS = 5; + private final Logger logger = LoggerFactory.getLogger(MyQAccountHandler.class); + private final Gson gsonUpperCase = new GsonBuilder().setFieldNamingPolicy(FieldNamingPolicy.UPPER_CAMEL_CASE) + .create(); + private final Gson gsonLowerCase = new GsonBuilder() + .setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES).create(); + private @Nullable Future normalPollFuture; + private @Nullable Future rapidPollFuture; + private @Nullable String securityToken; + private @Nullable AccountDTO account; + private @Nullable DevicesDTO devicesCache; + private Integer normalRefreshSeconds = 60; + private HttpClient httpClient; + private String username = ""; + private String password = ""; + private String userAgent = ""; + + public MyQAccountHandler(Bridge bridge, HttpClient httpClient) { + super(bridge); + this.httpClient = httpClient; + } + + @Override + public void handleCommand(ChannelUID channelUID, Command command) { + } + + @Override + public void initialize() { + MyQAccountConfiguration config = getConfigAs(MyQAccountConfiguration.class); + normalRefreshSeconds = config.refreshInterval; + username = config.username; + password = config.password; + // MyQ can get picky about blocking user agents apparently + userAgent = MyQAccountHandler.randomString(40); + securityToken = null; + updateStatus(ThingStatus.UNKNOWN); + restartPolls(false); + } + + @Override + public void dispose() { + stopPolls(); + } + + @Override + public Collection> getServices() { + return Collections.singleton(MyQDiscoveryService.class); + } + + @Override + public void childHandlerInitialized(ThingHandler childHandler, Thing childThing) { + DevicesDTO localDeviceCaches = devicesCache; + if (localDeviceCaches != null && childHandler instanceof MyQDeviceHandler) { + MyQDeviceHandler handler = (MyQDeviceHandler) childHandler; + localDeviceCaches.items.stream() + .filter(d -> ((MyQDeviceHandler) childHandler).getSerialNumber().equalsIgnoreCase(d.serialNumber)) + .findFirst().ifPresent(handler::handleDeviceUpdate); + } + } + + /** + * Sends an action to the MyQ API + * + * @param serialNumber + * @param action + */ + public void sendAction(String serialNumber, String action) { + AccountDTO localAccount = account; + if (localAccount != null) { + try { + HttpResult result = sendRequest( + String.format("%s/v5.1/Accounts/%s/Devices/%s/actions", BASE_URL, localAccount.account.id, + serialNumber), + HttpMethod.PUT, securityToken, + new StringContentProvider(gsonLowerCase.toJson(new ActionDTO(action))), "application/json"); + if (HttpStatus.isSuccess(result.responseCode)) { + restartPolls(true); + } else { + logger.debug("Failed to send action {} : {}", action, result.content); + } + } catch (InterruptedException e) { + } + } + } + + /** + * Last known state of MyQ Devices + * + * @return cached MyQ devices + */ + public @Nullable DevicesDTO devicesCache() { + return devicesCache; + } + + private void stopPolls() { + stopNormalPoll(); + stopRapidPoll(); + } + + private synchronized void stopNormalPoll() { + stopFuture(normalPollFuture); + normalPollFuture = null; + } + + private synchronized void stopRapidPoll() { + stopFuture(rapidPollFuture); + rapidPollFuture = null; + } + + private void stopFuture(@Nullable Future future) { + if (future != null) { + future.cancel(true); + } + } + + private synchronized void restartPolls(boolean rapid) { + stopPolls(); + if (rapid) { + normalPollFuture = scheduler.scheduleWithFixedDelay(this::normalPoll, 35, normalRefreshSeconds, + TimeUnit.SECONDS); + rapidPollFuture = scheduler.scheduleWithFixedDelay(this::rapidPoll, 3, RAPID_REFRESH_SECONDS, + TimeUnit.SECONDS); + } else { + normalPollFuture = scheduler.scheduleWithFixedDelay(this::normalPoll, 0, normalRefreshSeconds, + TimeUnit.SECONDS); + } + } + + private void normalPoll() { + stopRapidPoll(); + fetchData(); + } + + private void rapidPoll() { + fetchData(); + } + + private synchronized void fetchData() { + try { + if (securityToken == null) { + login(); + if (securityToken != null) { + getAccount(); + } + } + if (securityToken != null) { + getDevices(); + } + } catch (InterruptedException e) { + } + } + + private void login() throws InterruptedException { + HttpResult result = sendRequest(BASE_URL + "/v5/Login", HttpMethod.POST, null, + new StringContentProvider(gsonUpperCase.toJson(new LoginRequestDTO(username, password))), + "application/json"); + LoginResponseDTO loginResponse = parseResultAndUpdateStatus(result, gsonUpperCase, LoginResponseDTO.class); + if (loginResponse != null) { + securityToken = loginResponse.securityToken; + } else { + securityToken = null; + if (thing.getStatusInfo().getStatusDetail() == ThingStatusDetail.CONFIGURATION_ERROR) { + // bad credentials, stop trying to login + stopPolls(); + } + } + } + + private void getAccount() throws InterruptedException { + HttpResult result = sendRequest(BASE_URL + "/v5/My?expand=account", HttpMethod.GET, securityToken, null, null); + account = parseResultAndUpdateStatus(result, gsonUpperCase, AccountDTO.class); + } + + private void getDevices() throws InterruptedException { + AccountDTO localAccount = account; + if (localAccount == null) { + return; + } + HttpResult result = sendRequest(String.format("%s/v5.1/Accounts/%s/Devices", BASE_URL, localAccount.account.id), + HttpMethod.GET, securityToken, null, null); + DevicesDTO devices = parseResultAndUpdateStatus(result, gsonLowerCase, DevicesDTO.class); + if (devices != null) { + devicesCache = devices; + devices.items.forEach(device -> { + ThingTypeUID thingTypeUID = new ThingTypeUID(BINDING_ID, device.deviceFamily); + if (SUPPORTED_DISCOVERY_THING_TYPES_UIDS.contains(thingTypeUID)) { + for (Thing thing : getThing().getThings()) { + ThingHandler handler = thing.getHandler(); + if (handler != null && ((MyQDeviceHandler) handler).getSerialNumber() + .equalsIgnoreCase(device.serialNumber)) { + ((MyQDeviceHandler) handler).handleDeviceUpdate(device); + } + } + } + }); + } + } + + private synchronized HttpResult sendRequest(String url, HttpMethod method, @Nullable String token, + @Nullable ContentProvider content, @Nullable String contentType) throws InterruptedException { + try { + Request request = httpClient.newRequest(url).method(method) + .header("MyQApplicationId", "JVM/G9Nwih5BwKgNCjLxiFUQxQijAebyyg8QUHr7JOrP+tuPb8iHfRHKwTmDzHOu") + .header("ApiVersion", "5.1").header("BrandId", "2").header("Culture", "en").agent(userAgent) + .timeout(10, TimeUnit.SECONDS); + if (token != null) { + request = request.header("SecurityToken", token); + } + if (content != null & contentType != null) { + request = request.content(content, contentType); + } + // use asyc jetty as the API service will response with a 401 error when credentials are wrong, + // but not a WWW-Authenticate header which causes Jetty to throw a generic execution exception which + // prevents us from knowing the response code + logger.trace("Sending {} to {}", request.getMethod(), request.getURI()); + final CompletableFuture futureResult = new CompletableFuture<>(); + request.send(new BufferingResponseListener() { + @NonNullByDefault({}) + @Override + public void onComplete(Result result) { + futureResult.complete(new HttpResult(result.getResponse().getStatus(), getContentAsString())); + } + }); + HttpResult result = futureResult.get(); + logger.trace("Account Response - status: {} content: {}", result.responseCode, result.content); + return result; + } catch (ExecutionException e) { + return new HttpResult(0, e.getMessage()); + } + } + + @Nullable + private T parseResultAndUpdateStatus(HttpResult result, Gson parser, Class classOfT) { + if (HttpStatus.isSuccess(result.responseCode)) { + try { + T responseObject = parser.fromJson(result.content, classOfT); + if (responseObject != null) { + if (getThing().getStatus() != ThingStatus.ONLINE) { + updateStatus(ThingStatus.ONLINE); + } + return responseObject; + } + } catch (JsonSyntaxException e) { + updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, + "Invalid JSON Response " + result.content); + } + } else if (result.responseCode == HttpStatus.UNAUTHORIZED_401) { + updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, + "Unauthorized - Check Credentials"); + } else { + updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, + "Invalid Response Code " + result.responseCode + " : " + result.content); + } + return null; + } + + private class HttpResult { + public final int responseCode; + public @Nullable String content; + + public HttpResult(int responseCode, @Nullable String content) { + this.responseCode = responseCode; + this.content = content; + } + } + + private static String randomString(int length) { + int low = 97; // a-z + int high = 122; // A-Z + StringBuilder sb = new StringBuilder(length); + Random random = new Random(); + for (int i = 0; i < length; i++) { + sb.append((char) (low + (int) (random.nextFloat() * (high - low + 1)))); + } + return sb.toString(); + } +} diff --git a/bundles/org.openhab.binding.myq/src/main/java/org/openhab/binding/myq/internal/handler/MyQDeviceHandler.java b/bundles/org.openhab.binding.myq/src/main/java/org/openhab/binding/myq/internal/handler/MyQDeviceHandler.java new file mode 100644 index 0000000000000..030d780bea5af --- /dev/null +++ b/bundles/org.openhab.binding.myq/src/main/java/org/openhab/binding/myq/internal/handler/MyQDeviceHandler.java @@ -0,0 +1,28 @@ +/** + * Copyright (c) 2010-2021 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.myq.internal.handler; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.openhab.binding.myq.internal.dto.DeviceDTO; + +/** + * The {@link MyQDeviceHandler} is responsible for handling device updates + * + * @author Dan Cunningham - Initial contribution + */ +@NonNullByDefault +public interface MyQDeviceHandler { + public void handleDeviceUpdate(DeviceDTO device); + + public String getSerialNumber(); +} diff --git a/bundles/org.openhab.binding.myq/src/main/java/org/openhab/binding/myq/internal/handler/MyQGarageDoorHandler.java b/bundles/org.openhab.binding.myq/src/main/java/org/openhab/binding/myq/internal/handler/MyQGarageDoorHandler.java new file mode 100644 index 0000000000000..c83c4acdabdad --- /dev/null +++ b/bundles/org.openhab.binding.myq/src/main/java/org/openhab/binding/myq/internal/handler/MyQGarageDoorHandler.java @@ -0,0 +1,131 @@ +/** + * Copyright (c) 2010-2021 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.myq.internal.handler; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; +import org.openhab.binding.myq.internal.MyQBindingConstants; +import org.openhab.binding.myq.internal.config.MyQDeviceConfiguration; +import org.openhab.binding.myq.internal.dto.DeviceDTO; +import org.openhab.core.library.types.OnOffType; +import org.openhab.core.library.types.PercentType; +import org.openhab.core.library.types.StringType; +import org.openhab.core.library.types.UpDownType; +import org.openhab.core.thing.Bridge; +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.thing.binding.BridgeHandler; +import org.openhab.core.types.Command; +import org.openhab.core.types.RefreshType; +import org.openhab.core.types.UnDefType; + +/** + * The {@link MyQGarageDoorHandler} is responsible for handling commands for a garage door thing, which are + * sent to one of the channels. + * + * @author Dan Cunningham - Initial contribution + */ +@NonNullByDefault +public class MyQGarageDoorHandler extends BaseThingHandler implements MyQDeviceHandler { + private @Nullable DeviceDTO deviceState; + private String serialNumber; + + public MyQGarageDoorHandler(Thing thing) { + super(thing); + serialNumber = getConfigAs(MyQDeviceConfiguration.class).serialNumber; + } + + @Override + public void initialize() { + updateStatus(ThingStatus.UNKNOWN); + } + + @Override + public void handleCommand(ChannelUID channelUID, Command command) { + if (command instanceof RefreshType) { + updateState(); + return; + } + Bridge bridge = getBridge(); + final DeviceDTO localState = deviceState; + if (bridge != null && localState != null) { + BridgeHandler handler = bridge.getHandler(); + if (handler != null) { + String cmd = null; + if (command instanceof OnOffType) { + cmd = command == OnOffType.ON ? "open" : "close"; + } + if (command instanceof UpDownType) { + cmd = command == UpDownType.UP ? "open" : "close"; + } + if (command instanceof PercentType) { + cmd = ((PercentType) command).as(UpDownType.class) == UpDownType.UP ? "open" : "close"; + } + if (command instanceof StringType) { + cmd = command.toString(); + } + if (cmd != null) { + ((MyQAccountHandler) handler).sendAction(localState.serialNumber, cmd); + } + } + } + } + + @Override + public String getSerialNumber() { + return serialNumber; + } + + protected void updateState() { + final DeviceDTO localState = deviceState; + if (localState != null) { + String doorState = localState.state.doorState; + updateState("status", new StringType(doorState)); + switch (doorState) { + case "open": + case "opening": + case "closing": + case "stopped": + case "transition": + updateState("switch", OnOffType.ON); + updateState("rollershutter", UpDownType.UP); + break; + case "closed": + updateState("switch", OnOffType.OFF); + updateState("rollershutter", UpDownType.DOWN); + break; + default: + updateState("switch", UnDefType.UNDEF); + updateState("rollershutter", UnDefType.UNDEF); + break; + } + } + } + + @Override + public void handleDeviceUpdate(DeviceDTO device) { + if (!MyQBindingConstants.THING_TYPE_GARAGEDOOR.getId().equals(device.deviceFamily)) { + return; + } + deviceState = device; + if (device.state.online) { + updateStatus(ThingStatus.ONLINE); + updateState(); + } else { + updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, "Device reports as offline"); + } + } +} diff --git a/bundles/org.openhab.binding.myq/src/main/java/org/openhab/binding/myq/internal/handler/MyQLampHandler.java b/bundles/org.openhab.binding.myq/src/main/java/org/openhab/binding/myq/internal/handler/MyQLampHandler.java new file mode 100644 index 0000000000000..e7988bae00ee4 --- /dev/null +++ b/bundles/org.openhab.binding.myq/src/main/java/org/openhab/binding/myq/internal/handler/MyQLampHandler.java @@ -0,0 +1,98 @@ +/** + * Copyright (c) 2010-2021 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.myq.internal.handler; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; +import org.openhab.binding.myq.internal.MyQBindingConstants; +import org.openhab.binding.myq.internal.config.MyQDeviceConfiguration; +import org.openhab.binding.myq.internal.dto.DeviceDTO; +import org.openhab.core.library.types.OnOffType; +import org.openhab.core.thing.Bridge; +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.thing.binding.BridgeHandler; +import org.openhab.core.types.Command; +import org.openhab.core.types.RefreshType; + +/** + * The {@link MyQLampHandler} is responsible for handling commands for a lamp thing, which are + * sent to one of the channels. + * + * @author Dan Cunningham - Initial contribution + */ +@NonNullByDefault +public class MyQLampHandler extends BaseThingHandler implements MyQDeviceHandler { + private @Nullable DeviceDTO deviceState; + private String serialNumber; + + public MyQLampHandler(Thing thing) { + super(thing); + serialNumber = getConfigAs(MyQDeviceConfiguration.class).serialNumber; + } + + @Override + public void initialize() { + updateStatus(ThingStatus.UNKNOWN); + } + + @Override + public void handleCommand(ChannelUID channelUID, Command command) { + if (command instanceof RefreshType) { + updateState(); + return; + } + + if (command instanceof OnOffType) { + Bridge bridge = getBridge(); + final DeviceDTO localState = deviceState; + if (bridge != null && localState != null) { + BridgeHandler handler = bridge.getHandler(); + if (handler != null) { + ((MyQAccountHandler) handler).sendAction(localState.serialNumber, + command == OnOffType.ON ? "turnon" : "turnoff"); + } + } + } + } + + @Override + public String getSerialNumber() { + return serialNumber; + } + + protected void updateState() { + final DeviceDTO localState = deviceState; + if (localState != null) { + String lampState = localState.state.lampState; + updateState("switch", "on".equals(lampState) ? OnOffType.ON : OnOffType.OFF); + } + } + + @Override + public void handleDeviceUpdate(DeviceDTO device) { + if (!MyQBindingConstants.THING_TYPE_LAMP.getId().equals(device.deviceFamily)) { + return; + } + deviceState = device; + if (device.state.online) { + updateStatus(ThingStatus.ONLINE); + updateState(); + } else { + updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, "Device reports as offline"); + } + } +} diff --git a/bundles/org.openhab.binding.myq/src/main/resources/OH-INF/binding/binding.xml b/bundles/org.openhab.binding.myq/src/main/resources/OH-INF/binding/binding.xml new file mode 100644 index 0000000000000..b024ec7451b50 --- /dev/null +++ b/bundles/org.openhab.binding.myq/src/main/resources/OH-INF/binding/binding.xml @@ -0,0 +1,9 @@ + + + + MyQ Binding + The MyQ binding allows monitoring and control of garage doors that are MyQ enabled. + + diff --git a/bundles/org.openhab.binding.myq/src/main/resources/OH-INF/config/config.xml b/bundles/org.openhab.binding.myq/src/main/resources/OH-INF/config/config.xml new file mode 100644 index 0000000000000..beaf772f2412f --- /dev/null +++ b/bundles/org.openhab.binding.myq/src/main/resources/OH-INF/config/config.xml @@ -0,0 +1,37 @@ + + + + + + Account username + + + + Account password + password + + + + Specifies the refresh interval in seconds + 60 + + + + + + + Serial number of the garage door + + + + + + + Serial number of the lamp + + + + diff --git a/bundles/org.openhab.binding.myq/src/main/resources/OH-INF/thing/thing-types.xml b/bundles/org.openhab.binding.myq/src/main/resources/OH-INF/thing/thing-types.xml new file mode 100644 index 0000000000000..dbe68946840e5 --- /dev/null +++ b/bundles/org.openhab.binding.myq/src/main/resources/OH-INF/thing/thing-types.xml @@ -0,0 +1,67 @@ + + + + + + MyQ Cloud Account + + + + + + + + + MyQ Garage Door + + + + + + serialNumber + + + + + + + + + MyQ Lamp + + + + serialNumber + + + + + String + + + + + + + + + + + + + + + Switch + + + + Rollershutter + + + + Switch + + + diff --git a/bundles/org.openhab.binding.mystrom/README.md b/bundles/org.openhab.binding.mystrom/README.md index a4de343bb95e6..a0389947e8f20 100644 --- a/bundles/org.openhab.binding.mystrom/README.md +++ b/bundles/org.openhab.binding.mystrom/README.md @@ -9,6 +9,9 @@ This bundle adds the following thing types: | Thing | ThingTypeID | Description | | ------------------ | ----------- | -------------------------------------------------- | | myStrom Smart Plug | mystromplug | A myStrom smart plug | +| myStrom Bulb | mystrombulb | A myStrom bulb | + +According to the myStrom API documentation all request specific to the myStrom Bulb are also work on the LED strip. ## Discovery @@ -24,13 +27,37 @@ The following parameters are valid for all thing types: | hostname | string | yes | localhost | The IP address or hostname of the myStrom smart plug | | refresh | integer | no | 10 | Poll interval in seconds. Increase this if you encounter connection errors | +## Properties + +In addition to the configuration a myStrom thing has the following properties. +The properties are updated during initialize. +Disabling/enabling the thing can be used to update the properties. + +| Property-Name | Description | +| ------------- | --------------------------------------------------------------------- | +| version | Current firmware version | +| type | The type of the device (i.e. bulb = 102) | +| ssid | SSID of the currently connected network | +| ip | Current ip address | +| mask | Mask of the current network | +| gateway | Gateway of the current network | +| dns | DNS of the current network | +| static | Whether or not the ip address is static | +| connected | Whether or not the device is connected to the internet | +| mac | The mac address of the bridge in upper case letters without delimiter | + ## Channels -| Channel ID | Item Type | Read only | Description | -| ---------------- | -------------------- | --------- | ------------------------------------------------------------- | -| switch | Switch | false | Turn the smart plug on or off | -| power | Number:Power | true | The currently delivered power | -| temperature | Number:Temperature | true | The temperature at the plug | +| Channel ID | Item Type | Read only | Description | Thing types supporting this channel | +| ---------------- | -------------------- | --------- | --------------------------------------------------------------------- |-------------------------------------| +| switch | Switch | false | Turn the device on or off | mystromplug, mystrombulb | +| power | Number:Power | true | The currently delivered power | mystromplug, mystrombulb | +| temperature | Number:Temperature | true | The temperature at the plug | mystromplug | +| color | Color | false | The color we set the bulb to (mode 'hsv') | mystrombulb | +| colorTemperature | Dimmer | false | The color temperature of the bulb in mode 'mono' (percentage) | mystrombulb | +| brightness | Dimmer | false | The brightness of the bulb in mode 'mono' | mystrombulb | +| ramp | Number:Time | false | Transition time from the light’s current state to the new state. [ms] | mystrombulb | +| mode | String | false | The color mode we want the Bulb to set to (rgb, hsv or mono) | mystrombulb | ## Full Example diff --git a/bundles/org.openhab.binding.mystrom/src/main/java/org/openhab/binding/mystrom/internal/AbstractMyStromHandler.java b/bundles/org.openhab.binding.mystrom/src/main/java/org/openhab/binding/mystrom/internal/AbstractMyStromHandler.java new file mode 100644 index 0000000000000..1be559b58b42b --- /dev/null +++ b/bundles/org.openhab.binding.mystrom/src/main/java/org/openhab/binding/mystrom/internal/AbstractMyStromHandler.java @@ -0,0 +1,159 @@ +/** + * Copyright (c) 2010-2021 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.mystrom.internal; + +import static org.openhab.binding.mystrom.internal.MyStromBindingConstants.PROPERTY_CONNECTED; +import static org.openhab.binding.mystrom.internal.MyStromBindingConstants.PROPERTY_DNS; +import static org.openhab.binding.mystrom.internal.MyStromBindingConstants.PROPERTY_GW; +import static org.openhab.binding.mystrom.internal.MyStromBindingConstants.PROPERTY_IP; +import static org.openhab.binding.mystrom.internal.MyStromBindingConstants.PROPERTY_LAST_REFRESH; +import static org.openhab.binding.mystrom.internal.MyStromBindingConstants.PROPERTY_MAC; +import static org.openhab.binding.mystrom.internal.MyStromBindingConstants.PROPERTY_MASK; +import static org.openhab.binding.mystrom.internal.MyStromBindingConstants.PROPERTY_SSID; +import static org.openhab.binding.mystrom.internal.MyStromBindingConstants.PROPERTY_STATIC; +import static org.openhab.binding.mystrom.internal.MyStromBindingConstants.PROPERTY_TYPE; +import static org.openhab.binding.mystrom.internal.MyStromBindingConstants.PROPERTY_VERSION; + +import java.text.DateFormat; +import java.util.Calendar; +import java.util.Locale; +import java.util.Map; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.ScheduledFuture; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; +import org.eclipse.jetty.client.HttpClient; +import org.eclipse.jetty.client.api.ContentResponse; +import org.eclipse.jetty.client.api.Request; +import org.eclipse.jetty.client.util.StringContentProvider; +import org.eclipse.jetty.http.HttpHeader; +import org.eclipse.jetty.http.HttpMethod; +import org.eclipse.jetty.http.HttpStatus; +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 com.google.gson.Gson; + +/** + * The {@link AbstractMyStromHandler} is responsible for handling commands, which are + * sent to one of the channels. + * + * @author Frederic Chastagnol - Initial contribution + */ +@NonNullByDefault +public abstract class AbstractMyStromHandler extends BaseThingHandler { + protected static final String COMMUNICATION_ERROR = "Error while communicating to the myStrom plug: "; + protected static final String HTTP_REQUEST_URL_PREFIX = "http://"; + + protected final HttpClient httpClient; + protected String hostname = ""; + protected String mac = ""; + + private @Nullable ScheduledFuture pollingJob; + protected final Gson gson = new Gson(); + + public AbstractMyStromHandler(Thing thing, HttpClient httpClient) { + super(thing); + this.httpClient = httpClient; + } + + @Override + public final void initialize() { + MyStromConfiguration config = getConfigAs(MyStromConfiguration.class); + this.hostname = HTTP_REQUEST_URL_PREFIX + config.hostname; + + updateStatus(ThingStatus.UNKNOWN); + scheduler.schedule(this::initializeInternal, 0, TimeUnit.SECONDS); + } + + @Override + public final void dispose() { + ScheduledFuture pollingJob = this.pollingJob; + if (pollingJob != null) { + pollingJob.cancel(true); + this.pollingJob = null; + } + super.dispose(); + } + + private void updateProperties() throws MyStromException { + String json = sendHttpRequest(HttpMethod.GET, "/api/v1/info", null); + MyStromDeviceInfo deviceInfo = gson.fromJson(json, MyStromDeviceInfo.class); + if (deviceInfo == null) { + throw new MyStromException("Cannot retrieve device info from myStrom device " + getThing().getUID()); + } + this.mac = deviceInfo.mac; + Map properties = editProperties(); + properties.put(PROPERTY_MAC, deviceInfo.mac); + properties.put(PROPERTY_VERSION, deviceInfo.version); + properties.put(PROPERTY_TYPE, Long.toString(deviceInfo.type)); + properties.put(PROPERTY_SSID, deviceInfo.ssid); + properties.put(PROPERTY_IP, deviceInfo.ip); + properties.put(PROPERTY_MASK, deviceInfo.mask); + properties.put(PROPERTY_GW, deviceInfo.gw); + properties.put(PROPERTY_DNS, deviceInfo.dns); + properties.put(PROPERTY_STATIC, Boolean.toString(deviceInfo.staticState)); + properties.put(PROPERTY_CONNECTED, Boolean.toString(deviceInfo.connected)); + Calendar calendar = Calendar.getInstance(); + DateFormat formatter = DateFormat.getDateTimeInstance(DateFormat.SHORT, DateFormat.MEDIUM, Locale.getDefault()); + properties.put(PROPERTY_LAST_REFRESH, formatter.format(calendar.getTime())); + updateProperties(properties); + } + + /** + * Calls the API with the given http method, request path and actual data. + * + * @param method the http method to make the call with + * @param path The path of the API endpoint + * @param requestData the actual raw data to send in the request body, may be {@code null} + * @return String contents of the response for the GET request. + * @throws MyStromException Throws on communication error + */ + protected final String sendHttpRequest(HttpMethod method, String path, @Nullable String requestData) + throws MyStromException { + String url = hostname + path; + try { + Request request = httpClient.newRequest(url).timeout(10, TimeUnit.SECONDS).method(method); + if (requestData != null) { + request = request.content(new StringContentProvider(requestData)).header(HttpHeader.CONTENT_TYPE, + "application/x-www-form-urlencoded"); + } + ContentResponse response = request.send(); + if (response.getStatus() != HttpStatus.OK_200) { + throw new MyStromException("Error sending HTTP " + method + " request to " + url + + ". Got response code: " + response.getStatus()); + } + return response.getContentAsString(); + } catch (InterruptedException | TimeoutException | ExecutionException e) { + throw new MyStromException(COMMUNICATION_ERROR + e.getMessage()); + } + } + + private void initializeInternal() { + try { + updateProperties(); + updateStatus(ThingStatus.ONLINE); + MyStromConfiguration config = getConfigAs(MyStromConfiguration.class); + pollingJob = scheduler.scheduleWithFixedDelay(this::pollDevice, 0, config.refresh, TimeUnit.SECONDS); + } catch (MyStromException e) { + updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, e.getMessage()); + } + } + + protected abstract void pollDevice(); +} diff --git a/bundles/org.openhab.binding.mystrom/src/main/java/org/openhab/binding/mystrom/internal/MyStromBindingConstants.java b/bundles/org.openhab.binding.mystrom/src/main/java/org/openhab/binding/mystrom/internal/MyStromBindingConstants.java index 99969fc837b9a..8e73faf290216 100644 --- a/bundles/org.openhab.binding.mystrom/src/main/java/org/openhab/binding/mystrom/internal/MyStromBindingConstants.java +++ b/bundles/org.openhab.binding.mystrom/src/main/java/org/openhab/binding/mystrom/internal/MyStromBindingConstants.java @@ -20,6 +20,7 @@ * used across the whole binding. * * @author Paul Frank - Initial contribution + * @author Frederic Chastagnol - Add constants for myStrom bulb support */ @NonNullByDefault public class MyStromBindingConstants { @@ -30,9 +31,36 @@ public class MyStromBindingConstants { // List of all Thing Type UIDs public static final ThingTypeUID THING_TYPE_PLUG = new ThingTypeUID(BINDING_ID, "mystromplug"); + public static final ThingTypeUID THING_TYPE_BULB = new ThingTypeUID(BINDING_ID, "mystrombulb"); // List of all Channel ids public static final String CHANNEL_SWITCH = "switch"; public static final String CHANNEL_POWER = "power"; public static final String CHANNEL_TEMPERATURE = "temperature"; + public static final String CHANNEL_COLOR = "color"; + public static final String CHANNEL_RAMP = "ramp"; + public static final String CHANNEL_MODE = "mode"; + public static final String CHANNEL_COLOR_TEMPERATURE = "colorTemperature"; + public static final String CHANNEL_BRIGHTNESS = "brightness"; + + // Config + public static final String CONFIG_MAC = "mac"; + + // List of all Properties + public static final String PROPERTY_MAC = "mac"; + public static final String PROPERTY_VERSION = "version"; + public static final String PROPERTY_TYPE = "type"; + public static final String PROPERTY_SSID = "ssid"; + public static final String PROPERTY_IP = "ip"; + public static final String PROPERTY_MASK = "mask"; + public static final String PROPERTY_GW = "gw"; + public static final String PROPERTY_DNS = "dns"; + public static final String PROPERTY_STATIC = "static"; + public static final String PROPERTY_CONNECTED = "connected"; + public static final String PROPERTY_LAST_REFRESH = "lastRefresh"; + + // myStrom Bulb modes + public static final String RGB = "rgb"; + public static final String HSV = "hsv"; + public static final String MONO = "mono"; } diff --git a/bundles/org.openhab.binding.mystrom/src/main/java/org/openhab/binding/mystrom/internal/MyStromBulbHandler.java b/bundles/org.openhab.binding.mystrom/src/main/java/org/openhab/binding/mystrom/internal/MyStromBulbHandler.java new file mode 100644 index 0000000000000..259136291438e --- /dev/null +++ b/bundles/org.openhab.binding.mystrom/src/main/java/org/openhab/binding/mystrom/internal/MyStromBulbHandler.java @@ -0,0 +1,297 @@ +/** + * Copyright (c) 2010-2021 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.mystrom.internal; + +import static org.openhab.binding.mystrom.internal.MyStromBindingConstants.CHANNEL_BRIGHTNESS; +import static org.openhab.binding.mystrom.internal.MyStromBindingConstants.CHANNEL_COLOR; +import static org.openhab.binding.mystrom.internal.MyStromBindingConstants.CHANNEL_COLOR_TEMPERATURE; +import static org.openhab.binding.mystrom.internal.MyStromBindingConstants.CHANNEL_MODE; +import static org.openhab.binding.mystrom.internal.MyStromBindingConstants.CHANNEL_POWER; +import static org.openhab.binding.mystrom.internal.MyStromBindingConstants.CHANNEL_RAMP; +import static org.openhab.binding.mystrom.internal.MyStromBindingConstants.CHANNEL_SWITCH; +import static org.openhab.binding.mystrom.internal.MyStromBindingConstants.HSV; +import static org.openhab.binding.mystrom.internal.MyStromBindingConstants.MONO; +import static org.openhab.binding.mystrom.internal.MyStromBindingConstants.RGB; +import static org.openhab.core.library.unit.Units.SECOND; +import static org.openhab.core.library.unit.Units.WATT; + +import java.lang.reflect.Type; +import java.time.Duration; +import java.util.HashMap; +import java.util.Map; +import java.util.Objects; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; +import org.eclipse.jetty.client.HttpClient; +import org.eclipse.jetty.http.HttpMethod; +import org.eclipse.jetty.util.Fields; +import org.openhab.core.cache.ExpiringCache; +import org.openhab.core.library.types.DecimalType; +import org.openhab.core.library.types.HSBType; +import org.openhab.core.library.types.OnOffType; +import org.openhab.core.library.types.PercentType; +import org.openhab.core.library.types.QuantityType; +import org.openhab.core.library.types.StringType; +import org.openhab.core.library.unit.MetricPrefix; +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.types.Command; +import org.openhab.core.types.RefreshType; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.google.gson.reflect.TypeToken; + +/** + * The {@link MyStromBulbHandler} is responsible for handling commands, which are + * sent to one of the channels. + * + * @author Frederic Chastagnol - Initial contribution + */ +@NonNullByDefault +public class MyStromBulbHandler extends AbstractMyStromHandler { + + private static final Type DEVICE_INFO_MAP_TYPE = new TypeToken>() { + }.getType(); + + private final Logger logger = LoggerFactory.getLogger(MyStromBulbHandler.class); + + private final ExpiringCache> cache = new ExpiringCache<>( + Duration.ofSeconds(3), this::getReport); + + private PercentType lastBrightness = PercentType.HUNDRED; + private PercentType lastColorTemperature = new PercentType(50); + private String lastMode = MONO; + private HSBType lastColor = HSBType.WHITE; + + public MyStromBulbHandler(Thing thing, HttpClient httpClient) { + super(thing, httpClient); + } + + @Override + public void handleCommand(ChannelUID channelUID, Command command) { + try { + if (command instanceof RefreshType) { + pollDevice(); + } else { + String sResp = null; + switch (channelUID.getId()) { + case CHANNEL_SWITCH: + if (command instanceof OnOffType) { + sResp = sendToBulb(command == OnOffType.ON ? "on" : "off", null, null, null); + } + break; + case CHANNEL_COLOR: + if (command instanceof HSBType) { + if (Objects.equals(((HSBType) command).as(OnOffType.class), OnOffType.OFF)) { + sResp = sendToBulb("off", null, null, null); + } else { + String hsv = command.toString().replaceAll(",", ";"); + sResp = sendToBulb("on", hsv, null, HSV); + } + } + break; + case CHANNEL_BRIGHTNESS: + if (command instanceof PercentType) { + if (Objects.equals(((PercentType) command).as(OnOffType.class), OnOffType.OFF)) { + sResp = sendToBulb("off", null, null, null); + } else { + if (lastMode.equals(MONO)) { + String mono = convertPercentageToMyStromCT(lastColorTemperature) + ";" + + command.toString(); + sResp = sendToBulb("on", mono, null, MONO); + } else { + String hsv = lastColor.getHue().intValue() + ";" + lastColor.getSaturation() + ";" + + command.toString(); + sResp = sendToBulb("on", hsv, null, HSV); + } + } + } + break; + case CHANNEL_COLOR_TEMPERATURE: + if (command instanceof PercentType) { + String mono = convertPercentageToMyStromCT((PercentType) command) + ";" + + lastBrightness.toString(); + sResp = sendToBulb("on", mono, null, MONO); + } + break; + case CHANNEL_RAMP: + if (command instanceof DecimalType) { + sResp = sendToBulb(null, null, command.toString(), null); + } + break; + case CHANNEL_MODE: + if (command instanceof StringType) { + sResp = sendToBulb(null, null, null, command.toString()); + } + break; + default: + } + + if (sResp != null) { + Map report = gson.fromJson(sResp, DEVICE_INFO_MAP_TYPE); + if (report != null) { + report.entrySet().stream().filter(e -> e.getKey().equals(mac)).findFirst() + .ifPresent(info -> updateDevice(info.getValue())); + } + } + } + } catch (MyStromException e) { + logger.warn("Error while handling command {}", e.getMessage()); + } + } + + private @Nullable Map getReport() { + try { + String returnContent = sendHttpRequest(HttpMethod.GET, "/api/v1/device", null); + Map report = gson.fromJson(returnContent, DEVICE_INFO_MAP_TYPE); + updateStatus(ThingStatus.ONLINE); + return report; + } catch (MyStromException e) { + updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, e.getMessage()); + return null; + } + } + + @Override + protected void pollDevice() { + Map report = cache.getValue(); + if (report != null) { + report.entrySet().stream().filter(e -> e.getKey().equals(mac)).findFirst() + .ifPresent(info -> updateDevice(info.getValue())); + } + } + + private void updateDevice(@Nullable MyStromBulbResponse deviceInfo) { + if (deviceInfo != null) { + updateState(CHANNEL_SWITCH, deviceInfo.on ? OnOffType.ON : OnOffType.OFF); + updateState(CHANNEL_RAMP, QuantityType.valueOf(deviceInfo.ramp, MetricPrefix.MILLI(SECOND))); + if (deviceInfo instanceof MyStromDeviceSpecificInfo) { + updateState(CHANNEL_POWER, QuantityType.valueOf(((MyStromDeviceSpecificInfo) deviceInfo).power, WATT)); + } + if (deviceInfo.on) { + try { + lastMode = deviceInfo.mode; + long numSemicolon = deviceInfo.color.chars().filter(c -> c == ';').count(); + if (numSemicolon == 1 && deviceInfo.mode.equals(MONO)) { + String[] xy = deviceInfo.color.split(";"); + lastColorTemperature = new PercentType(convertMyStromCTToPercentage(xy[0])); + lastBrightness = PercentType.valueOf(xy[1]); + lastColor = new HSBType(lastColor.getHue() + ",0," + lastBrightness); + updateState(CHANNEL_COLOR_TEMPERATURE, lastColorTemperature); + } else if (numSemicolon == 2 && deviceInfo.mode.equals(HSV)) { + lastColor = HSBType.valueOf(deviceInfo.color.replaceAll(";", ",")); + lastBrightness = lastColor.getBrightness(); + } else if (!deviceInfo.color.equals("") && deviceInfo.mode.equals(RGB)) { + int r = Integer.parseInt(deviceInfo.color.substring(2, 4), 16); + int g = Integer.parseInt(deviceInfo.color.substring(4, 6), 16); + int b = Integer.parseInt(deviceInfo.color.substring(6, 8), 16); + lastColor = HSBType.fromRGB(r, g, b); + lastBrightness = lastColor.getBrightness(); + } + updateState(CHANNEL_COLOR, lastColor); + updateState(CHANNEL_BRIGHTNESS, lastBrightness); + updateState(CHANNEL_MODE, StringType.valueOf(lastMode)); + } catch (IllegalArgumentException e) { + logger.warn("Error while updating {}", e.getMessage()); + } + } + } + } + + /** + * Given a URL and a set parameters, send a HTTP POST request to the URL location + * created by the URL and parameters. + * + * @param action The action we want to take (on,off or toggle) + * @param color The color we set the bulb to (When using RGBW mode the first two hex numbers are used for the + * white channel! hsv is of form ;;) + * @param ramp Transition time from the light’s current state to the new state. [ms] + * @param mode The color mode we want the Bulb to set to (rgb or hsv or mono) + * @return String contents of the response for the GET request. + * @throws MyStromException Throws on communication error + */ + private String sendToBulb(@Nullable String action, @Nullable String color, @Nullable String ramp, + @Nullable String mode) throws MyStromException { + Fields fields = new Fields(); + if (action != null) { + fields.put("action", action); + } + if (color != null) { + fields.put("color", color); + } + if (ramp != null) { + fields.put("ramp", ramp); + } + if (mode != null) { + fields.put("mode", mode); + } + StringBuilder builder = new StringBuilder(fields.getSize() * 32); + for (Fields.Field field : fields) { + for (String value : field.getValues()) { + if (builder.length() > 0) { + builder.append("&"); + } + builder.append(field.getName()).append("=").append(value); + } + } + return sendHttpRequest(HttpMethod.POST, "/api/v1/device/" + mac, builder.toString()); + } + + /** + * Convert the color temperature from myStrom (1-18) to openHAB (percentage) + * + * @param ctValue Color temperature in myStrom: "1" = warm to "18" = cold. + * @return Color temperature (0-100%). 0% is the coldest setting. + * @throws NumberFormatException if the argument is not an integer + */ + private int convertMyStromCTToPercentage(String ctValue) throws NumberFormatException { + int ct = Integer.parseInt(ctValue); + return Math.round((18 - limitColorTemperature(ct)) / 17F * 100F); + } + + /** + * Convert the color temperature from openHAB (percentage) to myStrom (1-18) + * + * @param colorTemperature Color temperature from openHab. 0 = coldest, 100 = warmest + * @return Color temperature from myStrom. 1 = warmest, 18 = coldest + */ + private String convertPercentageToMyStromCT(PercentType colorTemperature) { + int ct = 18 - Math.round(colorTemperature.floatValue() * 17F / 100F); + return Integer.toString(limitColorTemperature(ct)); + } + + private int limitColorTemperature(int colorTemperature) { + return Math.max(1, Math.min(colorTemperature, 18)); + } + + private static class MyStromBulbResponse { + public boolean on; + public String color = ""; + public String mode = ""; + public long ramp; + + @Override + public String toString() { + return "MyStromBulbResponse{" + "on=" + on + ", color='" + color + '\'' + ", mode='" + mode + '\'' + + ", ramp=" + ramp + '}'; + } + } + + private static class MyStromDeviceSpecificInfo extends MyStromBulbResponse { + public double power; + } +} diff --git a/bundles/org.openhab.binding.mystrom/src/main/java/org/openhab/binding/mystrom/internal/MyStromDeviceInfo.java b/bundles/org.openhab.binding.mystrom/src/main/java/org/openhab/binding/mystrom/internal/MyStromDeviceInfo.java new file mode 100644 index 0000000000000..893b464bf6965 --- /dev/null +++ b/bundles/org.openhab.binding.mystrom/src/main/java/org/openhab/binding/mystrom/internal/MyStromDeviceInfo.java @@ -0,0 +1,37 @@ +/** + * Copyright (c) 2010-2021 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.mystrom.internal; + +import org.eclipse.jdt.annotation.NonNullByDefault; + +import com.google.gson.annotations.SerializedName; + +/** + * The {@link MyStromDeviceInfo} class contains fields mapping thing thing properties + * + * @author Frederic Chastagnol - Initial contribution + */ +@NonNullByDefault +public class MyStromDeviceInfo { + public String version = ""; + public String mac = ""; + public long type; + public String ssid = ""; + public String ip = ""; + public String mask = ""; + public String gw = ""; + public String dns = ""; + @SerializedName("static") + public boolean staticState = false; + public boolean connected = false; +} diff --git a/bundles/org.openhab.binding.mystrom/src/main/java/org/openhab/binding/mystrom/internal/MyStromHandlerFactory.java b/bundles/org.openhab.binding.mystrom/src/main/java/org/openhab/binding/mystrom/internal/MyStromHandlerFactory.java index d0a2aeb669cab..3c47ee92f4637 100644 --- a/bundles/org.openhab.binding.mystrom/src/main/java/org/openhab/binding/mystrom/internal/MyStromHandlerFactory.java +++ b/bundles/org.openhab.binding.mystrom/src/main/java/org/openhab/binding/mystrom/internal/MyStromHandlerFactory.java @@ -12,9 +12,9 @@ */ package org.openhab.binding.mystrom.internal; +import static org.openhab.binding.mystrom.internal.MyStromBindingConstants.THING_TYPE_BULB; import static org.openhab.binding.mystrom.internal.MyStromBindingConstants.THING_TYPE_PLUG; -import java.util.Collections; import java.util.Set; import org.eclipse.jdt.annotation.NonNullByDefault; @@ -34,14 +34,15 @@ * handlers. * * @author Paul Frank - Initial contribution + * @author Frederic Chastagnol - Add support for myStrom bulb */ @NonNullByDefault @Component(configurationPid = "binding.mystrom", service = ThingHandlerFactory.class) public class MyStromHandlerFactory extends BaseThingHandlerFactory { - private static final Set SUPPORTED_THING_TYPES_UIDS = Collections.singleton(THING_TYPE_PLUG); + private static final Set SUPPORTED_THING_TYPES_UIDS = Set.of(THING_TYPE_PLUG, THING_TYPE_BULB); - private HttpClientFactory httpClientFactory; + private final HttpClientFactory httpClientFactory; @Activate public MyStromHandlerFactory(@Reference HttpClientFactory httpClientFactory) { @@ -58,7 +59,9 @@ public boolean supportsThingType(ThingTypeUID thingTypeUID) { ThingTypeUID thingTypeUID = thing.getThingTypeUID(); if (THING_TYPE_PLUG.equals(thingTypeUID)) { - return new MyStromHandler(thing, httpClientFactory.getCommonHttpClient()); + return new MyStromPlugHandler(thing, httpClientFactory.getCommonHttpClient()); + } else if (THING_TYPE_BULB.equals(thingTypeUID)) { + return new MyStromBulbHandler(thing, httpClientFactory.getCommonHttpClient()); } return null; diff --git a/bundles/org.openhab.binding.mystrom/src/main/java/org/openhab/binding/mystrom/internal/MyStromHandler.java b/bundles/org.openhab.binding.mystrom/src/main/java/org/openhab/binding/mystrom/internal/MyStromPlugHandler.java similarity index 53% rename from bundles/org.openhab.binding.mystrom/src/main/java/org/openhab/binding/mystrom/internal/MyStromHandler.java rename to bundles/org.openhab.binding.mystrom/src/main/java/org/openhab/binding/mystrom/internal/MyStromPlugHandler.java index 191a45fa8cc19..6bdb9e130dade 100644 --- a/bundles/org.openhab.binding.mystrom/src/main/java/org/openhab/binding/mystrom/internal/MyStromHandler.java +++ b/bundles/org.openhab.binding.mystrom/src/main/java/org/openhab/binding/mystrom/internal/MyStromPlugHandler.java @@ -19,15 +19,11 @@ import static org.openhab.core.library.unit.Units.WATT; import java.time.Duration; -import java.util.concurrent.ExecutionException; -import java.util.concurrent.ScheduledFuture; import java.util.concurrent.TimeUnit; -import java.util.concurrent.TimeoutException; import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.Nullable; import org.eclipse.jetty.client.HttpClient; -import org.eclipse.jetty.client.api.ContentResponse; import org.eclipse.jetty.http.HttpMethod; import org.openhab.core.cache.ExpiringCache; import org.openhab.core.library.types.OnOffType; @@ -36,22 +32,20 @@ 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.slf4j.Logger; import org.slf4j.LoggerFactory; -import com.google.gson.Gson; - /** - * The {@link MyStromHandler} is responsible for handling commands, which are + * The {@link MyStromPlugHandler} is responsible for handling commands, which are * sent to one of the channels. * * @author Paul Frank - Initial contribution + * @author Frederic Chastagnol - Extends from new abstract class */ @NonNullByDefault -public class MyStromHandler extends BaseThingHandler { +public class MyStromPlugHandler extends AbstractMyStromHandler { private static class MyStromReport { @@ -60,22 +54,12 @@ private static class MyStromReport { public float temperature; } - private static final int HTTP_OK_CODE = 200; - private static final String COMMUNICATION_ERROR = "Error while communicating to the myStrom plug: "; - private static final String HTTP_REQUEST_URL_PREFIX = "http://"; - - private final Logger logger = LoggerFactory.getLogger(MyStromHandler.class); - - private HttpClient httpClient; - private String hostname = ""; + private final Logger logger = LoggerFactory.getLogger(MyStromPlugHandler.class); - private @Nullable ScheduledFuture pollingJob; - private ExpiringCache cache = new ExpiringCache<>(Duration.ofSeconds(3), this::getReport); - private final Gson gson = new Gson(); + private final ExpiringCache cache = new ExpiringCache<>(Duration.ofSeconds(3), this::getReport); - public MyStromHandler(Thing thing, HttpClient httpClient) { - super(thing); - this.httpClient = httpClient; + public MyStromPlugHandler(Thing thing, HttpClient httpClient) { + super(thing, httpClient); } @Override @@ -85,7 +69,7 @@ public void handleCommand(ChannelUID channelUID, Command command) { pollDevice(); } else { if (command instanceof OnOffType && CHANNEL_SWITCH.equals(channelUID.getId())) { - sendHttpGet("relay?state=" + (command == OnOffType.ON ? "1" : "0")); + sendHttpRequest(HttpMethod.GET, "/relay?state=" + (command == OnOffType.ON ? "1" : "0"), null); scheduler.schedule(this::pollDevice, 500, TimeUnit.MILLISECONDS); } } @@ -96,7 +80,7 @@ public void handleCommand(ChannelUID channelUID, Command command) { private @Nullable MyStromReport getReport() { try { - String returnContent = sendHttpGet("report"); + String returnContent = sendHttpRequest(HttpMethod.GET, "/report", null); MyStromReport report = gson.fromJson(returnContent, MyStromReport.class); updateStatus(ThingStatus.ONLINE); return report; @@ -106,7 +90,8 @@ public void handleCommand(ChannelUID channelUID, Command command) { } } - private void pollDevice() { + @Override + protected void pollDevice() { MyStromReport report = cache.getValue(); if (report != null) { updateState(CHANNEL_SWITCH, report.relay ? OnOffType.ON : OnOffType.OFF); @@ -114,46 +99,4 @@ private void pollDevice() { updateState(CHANNEL_TEMPERATURE, QuantityType.valueOf(report.temperature, CELSIUS)); } } - - @Override - public void initialize() { - MyStromConfiguration config = getConfigAs(MyStromConfiguration.class); - this.hostname = HTTP_REQUEST_URL_PREFIX + config.hostname; - - updateStatus(ThingStatus.UNKNOWN); - pollingJob = scheduler.scheduleWithFixedDelay(this::pollDevice, 0, config.refresh, TimeUnit.SECONDS); - } - - @Override - public void dispose() { - if (pollingJob != null) { - pollingJob.cancel(true); - pollingJob = null; - } - super.dispose(); - } - - /** - * Given a URL and a set parameters, send a HTTP GET request to the URL location - * created by the URL and parameters. - * - * @param url The URL to send a GET request to. - * @return String contents of the response for the GET request. - * @throws Exception - */ - public String sendHttpGet(String action) throws MyStromException { - String url = hostname + "/" + action; - ContentResponse response = null; - try { - response = httpClient.newRequest(url).timeout(10, TimeUnit.SECONDS).method(HttpMethod.GET).send(); - } catch (InterruptedException | TimeoutException | ExecutionException e) { - throw new MyStromException(COMMUNICATION_ERROR + e.getMessage()); - } - - if (response.getStatus() != HTTP_OK_CODE) { - throw new MyStromException( - "Error sending HTTP GET request to " + url + ". Got response code: " + response.getStatus()); - } - return response.getContentAsString(); - } } diff --git a/bundles/org.openhab.binding.mystrom/src/main/resources/OH-INF/thing/thing-types.xml b/bundles/org.openhab.binding.mystrom/src/main/resources/OH-INF/thing/thing-types.xml index 52735564eb0c8..f9c9f8bf3c1ce 100644 --- a/bundles/org.openhab.binding.mystrom/src/main/resources/OH-INF/thing/thing-types.xml +++ b/bundles/org.openhab.binding.mystrom/src/main/resources/OH-INF/thing/thing-types.xml @@ -14,6 +14,21 @@ + + + + + + + + + + + + + + mac + @@ -30,11 +45,59 @@ + + + Controls the myStrom bulb + + + + + + + + + + + + + + + + + + + + + + + + + mac + + + + + The host name or IP address of the myStrom bulb. + network-address + localhost + true + + + + Specifies the refresh interval in seconds. + 10 + true + + + + + + Number:Power The current power delivered by the plug - + @@ -43,4 +106,25 @@ The current temperature at the plug + + + Number:Time + + Transition time from the light’s current state to the new state. + + + + + String + + The color mode we want the Bulb to set to + + + + + + + + + diff --git a/bundles/org.openhab.binding.nanoleaf/README.md b/bundles/org.openhab.binding.nanoleaf/README.md index ec2435cb03504..47ba4db643fb8 100644 --- a/bundles/org.openhab.binding.nanoleaf/README.md +++ b/bundles/org.openhab.binding.nanoleaf/README.md @@ -11,35 +11,30 @@ The binding uses the [Nanoleaf OpenAPI](https://forum.nanoleaf.me/docs/openapi), ## Supported Things -Nanoleaf provides a bunch of devices of which some are connected to Wifi whereas other use the new Thread Technology. This binding only supports devices that are connected to Wifi. +Nanoleaf provides a bunch of devices of which some are connected to Wifi whereas other use the new Thread Technology. This binding only supports devices that are connected through Wifi. Currently Nanoleaf's "Light Panels" and "Canvas" devices are supported. -Note that only specific types do support the touch functionality, so the binding needs to check these types. The binding supports two thing types: controller and lightpanel. -The controller thing is the bridge for the individually attached panels/canvas and can be perceived as the nanoleaf device at the wall as a whole (either called "light panels" or "canvas" by Nanoleaf). +The controller thing is the bridge for the individually attached panels/canvas and can be perceived as the Nanoleaf device at the wall as a whole (either called "light panels" or "canvas" by Nanoleaf). With the controller thing you can control channels which affect all panels, e.g. selecting effects or setting the brightness. The lightpanel (singular) thing controls one of the individual panels/canvas that are connected to each other. Each individual panel has therefore its own id assigned to it. -You can set the **color** for each panel or turn it on (white) or off (black) and in the case of a nanoleaf canvas you can even detect single and double **touch events** related to an individual panel which opens a whole new world of controlling any other device within your openHAB environment. +You can set the **color** for each panel and in the case of a Nanoleaf canvas you can even detect single and double **touch events** related to an individual panel which opens a whole new world of controlling any other device within your openHAB environment. | Nanoleaf Name | Type | Description | supported | touch support | | ---------------------- | ---- | ---------------------------------------------------------- | --------- | ------------- | | Light Panels | NL22 | Triangles 1st Generation | X | (-) | | Shapes Triangle | NL42 | Triangles 2nd Generation (rounded edges) | X | X | -| Shapes Hexagon | NL42 | Triangles 2nd Generation (rounded edges) | (X) | (X) | +| Shapes Hexagon | NL42 | Hexagons | X | X | | Shapes Mini Triangles | ?? | Mini Triangles | ? | ? | | Canvas | NL29 | Squares | X | X | x = Supported (x) = Supported but only tested by community (-) = unknown (no device available to test) -Note: In case of major changes of a binding (like adding more features to a thing) it becomes necessary to delete your things due to the things not being compatible anymore. -Don't worry too much though as they will be easily redetected and nothing really is lost. -Just make sure that you delete them and rediscover as described below. - ## Discovery **Adding the Controller as a Thing** @@ -73,13 +68,16 @@ In this case: - stop and then start openHAB - Rediscover like described above -**Knowing which panel has which id** +### Panel Layout + +Unfortunately it is not easy to find out which panel gets which id, and this becomes pretty important if you have lots of them and want to assign rules. -Unfortunately it is not easy to find out which panel gets which id, and this becomes pretty important if you have lots of them and want to assign rules. -Don't worry as the binding comes with some helpful support in the background the canvas type (this is only provided for the canvas device because triangles can have weird layouts that are hard to express in a log output) +For canvas that use square panels, you can request the layout through a console command: -- Set up a switch item with the channel panelLayout on the controller (see NanoRetrieveLayout below) and set the switch to true -- look out for something like "Panel layout and ids" in the openHAB logs. Below that you will see a panel layout similar to +``` +openhab:nanoleaf layout [] +``` +The `thingUID` is an optional parameter. If it is not provided, the command loops through all Nanoleaf controller things it can find and prints the layout for each of them. Compare the following output with the right picture at the beginning of the article @@ -93,9 +91,7 @@ Compare the following output with the right picture at the beginning of the arti 41451 ``` - -Disclaimer: this works best with square devices and not necessarily well with triangles due to the more geometrically flexible layout. - + ## Thing Configuration The controller thing has the following parameters: @@ -106,7 +102,7 @@ The controller thing has the following parameters: | port | Port number of the light panels contoller. Default is 16021 | | authToken | The authentication token received from the controller after successful pairing. | | refreshInterval | Interval in seconds to refresh the state of the light panels settings. Default is 60. | -| deviceType | (readOnly) defines the type: lightpanels (triangle) or canvas (square) | +| deviceType | Defines the type `lightpanels` (triangle) or `canvas` (square or hexagon) | The lightpanel thing has the following parameters: @@ -115,7 +111,7 @@ The lightpanel thing has the following parameters: | id | ID assigned by the controller to the individual panel (e.g. 158) | The IDs of the individual panels can be determined by starting another scan once the controller is configured and online. -This will add all connected panels with their IDs to the inbox. +This discovers all connected panels with their IDs. ## Channels @@ -123,8 +119,7 @@ The controller bridge has the following channels: | Channel | Item Type | Description | Read Only | |---------------------|-----------|------------------------------------------------------------------------|-----------| -| power | Switch | Power state of the light panels | No | -| color | Color | Color of all light panels | No | +| color | Color | Color, power and brightness of all light panels | No | | colorTemperature | Dimmer | Color temperature (in percent) of all light panels | No | | colorTemperatureAbs | Number | Color temperature (in Kelvin, 1200 to 6500) of all light panels | No | | colorMode | String | Color mode of the light panels | Yes | @@ -132,48 +127,39 @@ The controller bridge has the following channels: | rhythmState | Switch | Connection state of the rhythm module | Yes | | rhythmActive | Switch | Activity state of the rhythm module | Yes | | rhythmMode | Number | Sound source for the rhythm module. 0=Microphone, 1=Aux cable | No | -| panelLayout | Switch | Set to true will log out panel layout (returns to off automatically | No | A lightpanel thing has the following channels: -| Channel | Item Type | Description | Read Only | +| Channel | Type | Description | Read Only | |---------------------|-----------|------------------------------------------------------------------------|-----------| -| panelColor | Color | Color of the individual light panel | No | -| singleTap | Switch | [Canvas Only] Is set when the user taps that panel once (1 second pulse) | Yes | -| doubleTap | Switch | [Canvas Only] Is set when the user taps that panel twice (1 second pulse) | Yes | - -**color and panelColor** +| color | Color | Color of the individual light panel | No | +| tap | Trigger | [Canvas Only] Sends events of gestures. Currently, these are SHORT_PRESSED and DOUBLE_PRESSED events. | Yes | -The color and panelColor channels support full color control with hue, saturation and brightness values. +The color channels support full color control with hue, saturation and brightness values. For example, brightness of *all* panels at once can be controlled by defining a dimmer item for the color channel of the *controller thing*. -The same applies to the panelColor channel of an individual lightpanel thing. - -What might not be obvious and even maybe confusing is the fact that brightness and color use the *same* channel but two different *itemTypes*. While the Color-itemtype controls the color, the Dimmer-itemtype controls the brightness on the same channel. +The same applies to the color channel of an individual lightpanel. **Limitations assigning specific colors on individual panels:** - Due to the way the API of the nanoleaf is designed, each time a color is assigned to a panel, it will be directly sent to that panel. The result is that if you send colors to several panels more or less at the same time, they will not be set at the same time but one after the other and rather appear like a sequence but as a one shot. -- Another important limitation is that individual panels cannot be set while a dynamic effect is running on the panel which means that the following happens - - As soon as you set an individual panel a so called "static effect" is created which replaces the chosen dynamic effect. You can even see that in the nanoleaf app that shows that a static effect is now running. - - Unfortunately, at least at the moment, the colors of the current state cannot be retrieved due to the high frequency of color changes that cannot be read quickly enough from the canvas, so all panels go to OFF - - The the first panelColor command is applied to that panel (and of course then all subsequent commands) - - The fact that it is called a static effect does not mean that you cannot create animations. The Rainbow rule below shows a good example for the whole canvas. Just replace the controller item with a panel item and you will get the rainbow effect with an individual panel. +- Another important limitation is that individual panels cannot be set while a dynamic effect is running on the panel which means that as soon as you set an individual panel the "static effect" is set, which disables the chosen dynamic effect. The nanoleaf app shows that a static effect is now running, too. +- The colors of the current state cannot be retrieved due to the high frequency of color changes that cannot be read quickly enough from the canvas, so all panels go to OFF +- The first panelColor command is applied to that panel (and of course then all subsequent commands) +- The fact that it is called a static effect does not mean that you cannot create animations. The Rainbow rule below shows a good example for the whole canvas. Just replace the controller item with a panel item and you will get the rainbow effect with an individual panel. **Touch Support** -Nanoleaf's Canvas introduces a whole new experience by adding touch support to it. This allows single and double taps on individual panels to be detected and then processed via rules to further control any other device! +Nanoleaf's Canvas introduces a whole new experience by supporting touch. This allows single and double taps on individual panels to be detected and processed via rules. Note that even gestures like up, down, left, right are sent but can only be detected on the whole set of panels and not on an individual panel. These four gestures are not yet supported by the binding but may be added in a later release. -To detect single and double taps the panel's have been extended to have two additional channels named singleTap and doubleTap which act like switches that are turned on as soon as a tap type is detected. +To detect single and double taps the panels have been extended to have two additional channels named singleTap and doubleTap which act like switches that are turned on as soon as a tap type is detected. These switches then act as a pulse to further control anything else via rules. -If a panel is tapped the switch is set to ON and automatically reset to OFF after 1 second (this may be configured in the future) to simulate a pulse. A rule can easily detect the transition from OFF to ON and later detect another tap as it is automatically reset by the binding. See the example below on Panel 2. - Keep in mind that the double tap is used as an already built-in functionality by default when you buy the nanoleaf: it switches all panels (hence the controller) to on or off like a light switch for all the panels at once. To circumvent that - Within the nanoleaf app go to the dashboard and choose your device. Enter the settings for that device by clicking the cog icon in the upper right corner. - Enable "Touch Gesture" and assign the gestures you want to happen but set the double tap to unassigned. -- To still have the possibility to switch on the whole canvas device with all its panels by double tapping a specific panel, you can easily write a rule that triggers on the double tap channel of that panel and then toggles the Power Channel of the controller. See the example below on Panel 1. +- To still have the possibility to switch on the whole canvas device with all its panels by double tapping a specific panel, you can easily write a rule that triggers on the tap channel of that panel and then sends an ON to the color channel of the controller. See the example below on Panel 1. More details can be found in the full example below. @@ -190,7 +176,7 @@ Bridge nanoleaf:controller:MyLightPanels @ "mylocation" [ address="192.168.1.100 } ``` -If you define your device statically in the thing file, autodiscovery of the same thing is suppressed by using +If you define your device statically in the thing file, auto-discovery of the same thing is suppressed by using * the [address="..." ] of the controller * and the [id=123] of the lightpanel @@ -208,13 +194,13 @@ e.g. via command line `curl --location --request POST 'http://
:16021/ap ### nanoleaf.items -Note: If you did autodiscover your things and items: +Note: If you auto-discovered your things and items: - A controller item looks like nanoleaf:controller:F0ED4F9351AF:power where F0ED4F9351AF is the id of the controller that has been automatically assigned by the binding. - A panel item looks like nanoleaf:lightpanel:F0ED4F9351AF:39755:singleTap where 39755 is the id of the panel that has been automatically assigned by the binding. ``` -Switch NanoleafPower "Nanoleaf" { channel="nanoleaf:controller:MyLightPanels:power" } +Switch NanoleafPower "Nanoleaf" { channel="nanoleaf:controller:MyLightPanels:color" } Color NanoleafColor "Color" { channel="nanoleaf:controller:MyLightPanels:color" } Dimmer NanoleafBrightness "Brightness [%.0f]" { channel="nanoleaf:controller:MyLightPanels:color" } String NanoleafHue "Hue [%s]" @@ -226,15 +212,11 @@ String NanoleafEffect "Effect" { channel="nanoleaf:controller:MyLightPanels:effe Switch NanoleafRhythmState "Rhythm connected [MAP(nanoleaf.map):%s]" { channel="nanoleaf:controller:MyLightPanels:rhythmState" } Switch NanoleafRhythmActive "Rhythm active [MAP(nanoleaf.map):%s]" { channel="nanoleaf:controller:MyLightPanels:rhythmActive" } Number NanoleafRhythmSource "Rhythm source [%s]" { channel="nanoleaf:controller:MyLightPanels:rhythmMode" } -Switch NanoRetrieveLayout "Nano Layout" { channel="nanoleaf:controller:D81E7A7E424E:panelLayout" } // note that the next to items use the exact same channel but the two different types Color and Dimmer to control different parameters -Color Panel1Color "Panel 1" { channel="nanoleaf:lightpanel:MyLightPanels:135:panelColor" } -Dimmer Panel1Brightness "Panel 1" { channel="nanoleaf:lightpanel:MyLightPanels:135:panelColor" } -Switch Panel1DoubleTap "Toggle device on and off" { channel="nanoleaf:lightpanel:MyLightPanels:135:doubleTap" } -Switch Panel2Color "Panel 2" { channel="nanoleaf:lightpanel:MyLightPanels:158:panelColor" } -Switch Panel2SingleTap "Panel 2 Single Tap" { channel="nanoleaf:lightpanel:MyLightPanels:158:singleTap" } -Switch Panel2DoubleTap "Panel 2 Double Tap" { channel="nanoleaf:lightpanel:MyLightPanels:158:doubleTap" } +Color PanelColor "Panel 1" { channel="nanoleaf:lightpanel:MyLightPanels:135:color" } +Dimmer Panel1Brightness "Panel 1" { channel="nanoleaf:lightpanel:MyLightPanels:135:color" } +Switch Panel2Color "Panel 2" { channel="nanoleaf:lightpanel:MyLightPanels:158:color" } Switch NanoleafRainbowScene "Show Rainbow Scene" ``` @@ -252,11 +234,10 @@ sitemap nanoleaf label="Nanoleaf" Slider item=NanoleafColorTemp Setpoint item=NanoleafColorTempAbs step=100 minValue=1200 maxValue=6500 Text item=NanoleafColorMode - Selection item=NanoleafEffect mappings=["Color Burst"="Color Burst", "Fireworks" = "Fireworks", "Flames" = "Flames", "Forest" = "Forest", "Inner Peace" = "Inner Peace", "Meteor Shower" = "Meteor Shower", "Nemo" = "Nemo", "Northern Lights" = "Northern Lights", "Paint Splatter" = "Paint Splatter", "Pulse Pop Beats" = "Pulse Pop Beats", "Rhythmic Northern Lights" = "Rhythmic Northern Lights", "Ripple" = "Ripple", "Romantic" = "Romantic", "Snowfall" = "Snowfall", "Sound Bar" = "Sound Bar", "Streaking Notes" = "Streaking Notes", "moonlight" = "Moonlight", "*Static*" = "Color (single panels)", "*Dynamic*" = "Color (all panels)" ] + Selection item=NanoleafEffect Text item=NanoleafRhythmState Text item=NanoleafRhythmActive Selection item=NanoleafRhythmSource mappings=[0="Microphone", 1="Aux"] - Switch item=NanoRetrieveLayout } Frame label="Panels" { @@ -271,9 +252,6 @@ sitemap nanoleaf label="Nanoleaf" } ``` -Note: The mappings to effects in the selection item are specific for each Nanoleaf installation and should be adapted accordingly. -Only the effects "\*Static\*" and "\*Dynamic\*" are predefined by the controller and should always be present in the mappings. - ### nanoleaf.rules ``` @@ -313,8 +291,7 @@ end rule "Nanoleaf canvas touch detection Panel 2" when - Item Panel2SingleTap changed from NULL to ON or - Item Panel2SingleTap changed from OFF to ON + Channel "nanoleaf:lightpanel:MyLightPanels:158:tap" triggered SHORT_PRESS then logInfo("CanvasTouch", "Nanoleaf Canvas Panel 2 was touched once") @@ -327,8 +304,7 @@ end rule "Nanoleaf double tap toggles power of device" when - Item Panel1DoubleTap changed from NULL to ON or - Item Panel1DoubleTap changed from OFF to ON + Channel "nanoleaf:lightpanel:MyLightPanels:135:tap" triggered DOUBLE_PRESS then logInfo("CanvasTouch", "Nanoleaf Canvas Panel 1 was touched twice. Toggle Power of whole canvas.") diff --git a/bundles/org.openhab.binding.nanoleaf/src/main/java/org/openhab/binding/nanoleaf/internal/NanoleafBindingConstants.java b/bundles/org.openhab.binding.nanoleaf/src/main/java/org/openhab/binding/nanoleaf/internal/NanoleafBindingConstants.java index 57933199d4641..6a4b08f7da80b 100644 --- a/bundles/org.openhab.binding.nanoleaf/src/main/java/org/openhab/binding/nanoleaf/internal/NanoleafBindingConstants.java +++ b/bundles/org.openhab.binding.nanoleaf/src/main/java/org/openhab/binding/nanoleaf/internal/NanoleafBindingConstants.java @@ -27,7 +27,7 @@ @NonNullByDefault public class NanoleafBindingConstants { - private static final String BINDING_ID = "nanoleaf"; + public static final String BINDING_ID = "nanoleaf"; // List of all Thing Type UIDs public static final ThingTypeUID THING_TYPE_CONTROLLER = new ThingTypeUID(BINDING_ID, "controller"); @@ -44,7 +44,6 @@ public class NanoleafBindingConstants { public static final String CONFIG_PANEL_ID = "id"; // List of controller channels - public static final String CHANNEL_POWER = "power"; public static final String CHANNEL_COLOR = "color"; public static final String CHANNEL_COLOR_TEMPERATURE = "colorTemperature"; public static final String CHANNEL_COLOR_TEMPERATURE_ABS = "colorTemperatureAbs"; @@ -53,12 +52,10 @@ public class NanoleafBindingConstants { public static final String CHANNEL_RHYTHM_STATE = "rhythmState"; public static final String CHANNEL_RHYTHM_ACTIVE = "rhythmActive"; public static final String CHANNEL_RHYTHM_MODE = "rhythmMode"; - public static final String CHANNEL_PANEL_LAYOUT = "panelLayout"; // List of light panel channels - public static final String CHANNEL_PANEL_COLOR = "panelColor"; - public static final String CHANNEL_PANEL_SINGLE_TAP = "singleTap"; - public static final String CHANNEL_PANEL_DOUBLE_TAP = "doubleTap"; + public static final String CHANNEL_PANEL_COLOR = "color"; + public static final String CHANNEL_PANEL_TAP = "tap"; // Nanoleaf OpenAPI URLs public static final String API_V1_BASE_URL = "/api/v1"; diff --git a/bundles/org.openhab.binding.nanoleaf/src/main/java/org/openhab/binding/nanoleaf/internal/NanoleafHandlerFactory.java b/bundles/org.openhab.binding.nanoleaf/src/main/java/org/openhab/binding/nanoleaf/internal/NanoleafHandlerFactory.java index 8c7f2a32e5663..f942a10f0d698 100644 --- a/bundles/org.openhab.binding.nanoleaf/src/main/java/org/openhab/binding/nanoleaf/internal/NanoleafHandlerFactory.java +++ b/bundles/org.openhab.binding.nanoleaf/src/main/java/org/openhab/binding/nanoleaf/internal/NanoleafHandlerFactory.java @@ -15,9 +15,6 @@ import static org.openhab.binding.nanoleaf.internal.NanoleafBindingConstants.*; import java.util.Collections; -import java.util.HashMap; -import java.util.Hashtable; -import java.util.Map; import java.util.Set; import java.util.stream.Collectors; import java.util.stream.Stream; @@ -25,19 +22,15 @@ import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.Nullable; import org.eclipse.jetty.client.HttpClient; -import org.openhab.binding.nanoleaf.internal.discovery.NanoleafPanelsDiscoveryService; import org.openhab.binding.nanoleaf.internal.handler.NanoleafControllerHandler; import org.openhab.binding.nanoleaf.internal.handler.NanoleafPanelHandler; -import org.openhab.core.config.discovery.DiscoveryService; import org.openhab.core.io.net.http.HttpClientFactory; import org.openhab.core.thing.Bridge; import org.openhab.core.thing.Thing; import org.openhab.core.thing.ThingTypeUID; -import org.openhab.core.thing.ThingUID; import org.openhab.core.thing.binding.BaseThingHandlerFactory; import org.openhab.core.thing.binding.ThingHandler; import org.openhab.core.thing.binding.ThingHandlerFactory; -import org.osgi.framework.ServiceRegistration; import org.osgi.service.component.annotations.Activate; import org.osgi.service.component.annotations.Component; import org.osgi.service.component.annotations.Reference; @@ -49,6 +42,7 @@ * and panel (thing) handlers. * * @author Martin Raepple - Initial contribution + * @author Kai Kreuzer - made discovery a handler service */ @NonNullByDefault @Component(configurationPid = "binding.nanoleaf", service = ThingHandlerFactory.class) @@ -58,7 +52,6 @@ public class NanoleafHandlerFactory extends BaseThingHandlerFactory { .unmodifiableSet(Stream.of(THING_TYPE_LIGHT_PANEL, THING_TYPE_CONTROLLER).collect(Collectors.toSet())); private final Logger logger = LoggerFactory.getLogger(NanoleafHandlerFactory.class); - private final Map> discoveryServiceRegs = new HashMap<>(); private final HttpClient httpClient; @Activate @@ -77,7 +70,6 @@ public boolean supportsThingType(ThingTypeUID thingTypeUID) { if (THING_TYPE_CONTROLLER.equals(thingTypeUID)) { NanoleafControllerHandler handler = new NanoleafControllerHandler((Bridge) thing, httpClient); - registerDiscoveryService(handler); logger.debug("Nanoleaf controller handler created."); return handler; } else if (THING_TYPE_LIGHT_PANEL.equals(thingTypeUID)) { @@ -87,30 +79,4 @@ public boolean supportsThingType(ThingTypeUID thingTypeUID) { } return null; } - - @Override - protected void removeHandler(ThingHandler thingHandler) { - if (thingHandler instanceof NanoleafControllerHandler) { - unregisterDiscoveryService(thingHandler.getThing()); - logger.debug("Nanoleaf controller handler removed."); - } - } - - private synchronized void registerDiscoveryService(NanoleafControllerHandler bridgeHandler) { - NanoleafPanelsDiscoveryService discoveryService = new NanoleafPanelsDiscoveryService(bridgeHandler); - discoveryServiceRegs.put(bridgeHandler.getThing().getUID(), - bundleContext.registerService(DiscoveryService.class.getName(), discoveryService, new Hashtable<>())); - logger.debug("Discovery service for panels registered."); - } - - @SuppressWarnings("null") - private synchronized void unregisterDiscoveryService(Thing thing) { - @Nullable - ServiceRegistration serviceReg = discoveryServiceRegs.remove(thing.getUID()); - // would require null check but "if (response!=null)" throws warning on comoile time :´-( - if (serviceReg != null) { - serviceReg.unregister(); - } - logger.debug("Discovery service for panels removed."); - } } diff --git a/bundles/org.openhab.binding.nanoleaf/src/main/java/org/openhab/binding/nanoleaf/internal/OpenAPIUtils.java b/bundles/org.openhab.binding.nanoleaf/src/main/java/org/openhab/binding/nanoleaf/internal/OpenAPIUtils.java index 1875d474f7afe..af7658cb52c61 100644 --- a/bundles/org.openhab.binding.nanoleaf/src/main/java/org/openhab/binding/nanoleaf/internal/OpenAPIUtils.java +++ b/bundles/org.openhab.binding.nanoleaf/src/main/java/org/openhab/binding/nanoleaf/internal/OpenAPIUtils.java @@ -20,6 +20,7 @@ import java.nio.charset.StandardCharsets; import java.util.Iterator; import java.util.concurrent.ExecutionException; +import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -57,7 +58,7 @@ public static Request requestBuilder(HttpClient httpClient, NanoleafControllerCo LOGGER.trace("RequestBuilder: Sending Request {}:{} {} ", requestURI.getHost(), requestURI.getPort(), requestURI.getPath()); - return httpClient.newRequest(requestURI).method(method); + return httpClient.newRequest(requestURI).method(method).timeout(10, TimeUnit.SECONDS); } public static URI getUri(NanoleafControllerConfig controllerConfig, String apiOperation, @Nullable String query) diff --git a/bundles/org.openhab.binding.nanoleaf/src/main/java/org/openhab/binding/nanoleaf/internal/command/NanoleafCommandExtension.java b/bundles/org.openhab.binding.nanoleaf/src/main/java/org/openhab/binding/nanoleaf/internal/command/NanoleafCommandExtension.java new file mode 100644 index 0000000000000..d47e100729d19 --- /dev/null +++ b/bundles/org.openhab.binding.nanoleaf/src/main/java/org/openhab/binding/nanoleaf/internal/command/NanoleafCommandExtension.java @@ -0,0 +1,103 @@ +/** + * Copyright (c) 2010-2021 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.nanoleaf.internal.command; + +import java.util.Arrays; +import java.util.List; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.openhab.binding.nanoleaf.internal.NanoleafBindingConstants; +import org.openhab.binding.nanoleaf.internal.handler.NanoleafControllerHandler; +import org.openhab.core.io.console.Console; +import org.openhab.core.io.console.extensions.AbstractConsoleCommandExtension; +import org.openhab.core.io.console.extensions.ConsoleCommandExtension; +import org.openhab.core.thing.Thing; +import org.openhab.core.thing.ThingRegistry; +import org.openhab.core.thing.ThingUID; +import org.openhab.core.thing.binding.ThingHandler; +import org.osgi.service.component.annotations.Activate; +import org.osgi.service.component.annotations.Component; +import org.osgi.service.component.annotations.Reference; + +/** + * Console commands for interacting with Nanoleaf integration + * + * @author Kai Kreuzer - Initial contribution + */ +@NonNullByDefault +@Component(service = ConsoleCommandExtension.class) +public class NanoleafCommandExtension extends AbstractConsoleCommandExtension { + + private static final String CMD_LAYOUT = "layout"; + private final ThingRegistry thingRegistry; + + @Activate + public NanoleafCommandExtension(@Reference ThingRegistry thingRegistry) { + super("nanoleaf", "Interact with the Nanoleaf integration."); + this.thingRegistry = thingRegistry; + } + + @Override + public void execute(String[] args, Console console) { + if (args.length > 0) { + String subCommand = args[0]; + switch (subCommand) { + case CMD_LAYOUT: + if (args.length == 1) { + thingRegistry.getAll().forEach(thing -> { + if (thing.getUID().getBindingId().equals(NanoleafBindingConstants.BINDING_ID)) { + ThingHandler handler = thing.getHandler(); + if (handler instanceof NanoleafControllerHandler) { + NanoleafControllerHandler nanoleafControllerHandler = (NanoleafControllerHandler) handler; + String layout = nanoleafControllerHandler.getLayout(); + console.println("Layout of Nanoleaf controller '" + thing.getUID().getAsString() + + "' with label '" + thing.getLabel() + "':" + System.lineSeparator()); + console.println(layout); + console.println(System.lineSeparator()); + } + } + }); + } else if (args.length == 2) { + String uid = args[1]; + Thing thing = thingRegistry.get(new ThingUID(uid)); + if (thing != null) { + ThingHandler handler = thing.getHandler(); + if (handler instanceof NanoleafControllerHandler) { + NanoleafControllerHandler nanoleafControllerHandler = (NanoleafControllerHandler) handler; + String layout = nanoleafControllerHandler.getLayout(); + console.println(layout); + } else { + console.println("Thing with UID '" + uid.toString() + + "' is not an initialized Nanoleaf controller."); + } + } else { + console.println("Thing with UID '" + uid.toString() + "' does not exist."); + } + } else { + printUsage(console); + } + break; + + default: + console.println("Unknown command '" + subCommand + "'"); + printUsage(console); + break; + } + } + } + + @Override + public List getUsages() { + return Arrays.asList(buildCommandUsage(CMD_LAYOUT + " ", "Prints the panel layout on the console.")); + } +} diff --git a/bundles/org.openhab.binding.nanoleaf/src/main/java/org/openhab/binding/nanoleaf/internal/commanddescription/NanoleafCommandDescriptionProvider.java b/bundles/org.openhab.binding.nanoleaf/src/main/java/org/openhab/binding/nanoleaf/internal/commanddescription/NanoleafCommandDescriptionProvider.java new file mode 100644 index 0000000000000..3d59ba3f53035 --- /dev/null +++ b/bundles/org.openhab.binding.nanoleaf/src/main/java/org/openhab/binding/nanoleaf/internal/commanddescription/NanoleafCommandDescriptionProvider.java @@ -0,0 +1,80 @@ +/** + * Copyright (c) 2010-2021 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.nanoleaf.internal.commanddescription; + +import java.util.List; +import java.util.stream.Collectors; + +import org.eclipse.jdt.annotation.NonNull; +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; +import org.openhab.binding.nanoleaf.internal.NanoleafBindingConstants; +import org.openhab.binding.nanoleaf.internal.NanoleafControllerListener; +import org.openhab.binding.nanoleaf.internal.handler.NanoleafControllerHandler; +import org.openhab.binding.nanoleaf.internal.model.ControllerInfo; +import org.openhab.core.thing.ChannelUID; +import org.openhab.core.thing.ThingUID; +import org.openhab.core.thing.binding.BaseDynamicCommandDescriptionProvider; +import org.openhab.core.thing.binding.ThingHandler; +import org.openhab.core.thing.binding.ThingHandlerService; +import org.openhab.core.thing.type.DynamicCommandDescriptionProvider; +import org.openhab.core.types.CommandOption; +import org.osgi.service.component.annotations.Component; + +/** + * This class provides the available effects as dynamic options as they are read from the Nanoleaf controller. + * + * @author Kai Kreuzer - Initial contribution + * + */ +@NonNullByDefault +@Component(service = { DynamicCommandDescriptionProvider.class }) +public class NanoleafCommandDescriptionProvider extends BaseDynamicCommandDescriptionProvider + implements NanoleafControllerListener, ThingHandlerService { + + private @Nullable ChannelUID effectChannelUID; + + private @Nullable NanoleafControllerHandler bridgeHandler; + + @Override + public void setThingHandler(ThingHandler handler) { + this.bridgeHandler = (NanoleafControllerHandler) handler; + bridgeHandler.registerControllerListener(this); + effectChannelUID = new ChannelUID(handler.getThing().getUID(), NanoleafBindingConstants.CHANNEL_EFFECT); + } + + @Override + public @Nullable ThingHandler getThingHandler() { + return bridgeHandler; + } + + @Override + public void deactivate() { + if (bridgeHandler != null) { + bridgeHandler.unregisterControllerListener(this); + } + super.deactivate(); + } + + @Override + public void onControllerInfoFetched(@NonNull ThingUID bridge, @NonNull ControllerInfo controllerInfo) { + List<@NonNull String> effects = controllerInfo.getEffects().getEffectsList(); + ChannelUID uid = effectChannelUID; + if (effects != null && uid != null && uid.getThingUID().equals(bridge)) { + List<@NonNull CommandOption> commandOptions = effects.stream() // + .map(effect -> new CommandOption(effect, effect)) // + .collect(Collectors.toList()); + setCommandOptions(uid, commandOptions); + } + } +} diff --git a/bundles/org.openhab.binding.nanoleaf/src/main/java/org/openhab/binding/nanoleaf/internal/discovery/NanoleafMDNSDiscoveryParticipant.java b/bundles/org.openhab.binding.nanoleaf/src/main/java/org/openhab/binding/nanoleaf/internal/discovery/NanoleafMDNSDiscoveryParticipant.java index 751f90712e98a..aa18f063f8e49 100644 --- a/bundles/org.openhab.binding.nanoleaf/src/main/java/org/openhab/binding/nanoleaf/internal/discovery/NanoleafMDNSDiscoveryParticipant.java +++ b/bundles/org.openhab.binding.nanoleaf/src/main/java/org/openhab/binding/nanoleaf/internal/discovery/NanoleafMDNSDiscoveryParticipant.java @@ -79,10 +79,10 @@ public String getServiceType() { logger.trace("Discovered nanoleaf host: {} port: {} firmWare: {} modelId: {} qualifiedName: {}", host, port, firmwareVersion, modelId, qualifiedName); - logger.debug("Adding Nanoleaf controller {} with FW version {} found at {} {} to inbox", qualifiedName, + logger.debug("Adding Nanoleaf controller {} with FW version {} found at {}:{} to inbox", qualifiedName, firmwareVersion, host, port); if (!OpenAPIUtils.checkRequiredFirmware(service.getPropertyString("md"), firmwareVersion)) { - logger.warn("Nanoleaf controller firmware is too old. Must be {} or higher", + logger.debug("Nanoleaf controller firmware is too old. Must be {} or higher", MODEL_ID_LIGHTPANELS.equals(modelId) ? API_MIN_FW_VER_LIGHTPANELS : API_MIN_FW_VER_CANVAS); } diff --git a/bundles/org.openhab.binding.nanoleaf/src/main/java/org/openhab/binding/nanoleaf/internal/discovery/NanoleafPanelsDiscoveryService.java b/bundles/org.openhab.binding.nanoleaf/src/main/java/org/openhab/binding/nanoleaf/internal/discovery/NanoleafPanelsDiscoveryService.java index 391d36f63b000..73e4404718b9d 100644 --- a/bundles/org.openhab.binding.nanoleaf/src/main/java/org/openhab/binding/nanoleaf/internal/discovery/NanoleafPanelsDiscoveryService.java +++ b/bundles/org.openhab.binding.nanoleaf/src/main/java/org/openhab/binding/nanoleaf/internal/discovery/NanoleafPanelsDiscoveryService.java @@ -33,6 +33,8 @@ import org.openhab.core.config.discovery.DiscoveryResult; import org.openhab.core.config.discovery.DiscoveryResultBuilder; 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; @@ -41,36 +43,37 @@ * panels connected to the controller. * * @author Martin Raepple - Initial contribution + * @author Kai Kreuzer - Made it a ThingHandlerService */ @NonNullByDefault -public class NanoleafPanelsDiscoveryService extends AbstractDiscoveryService implements NanoleafControllerListener { +public class NanoleafPanelsDiscoveryService extends AbstractDiscoveryService + implements NanoleafControllerListener, ThingHandlerService { private static final int SEARCH_TIMEOUT_SECONDS = 60; private final Logger logger = LoggerFactory.getLogger(NanoleafPanelsDiscoveryService.class); - private final NanoleafControllerHandler bridgeHandler; + private @Nullable NanoleafControllerHandler bridgeHandler; + private @Nullable ControllerInfo controllerInfo; /** - * Constructs a new {@link NanoleafPanelsDiscoveryService} attached to the given bridge handler. - * - * @param nanoleafControllerHandler The bridge handler this discovery service is attached to + * Constructs a new {@link NanoleafPanelsDiscoveryService}. */ - public NanoleafPanelsDiscoveryService(NanoleafControllerHandler nanoleafControllerHandler) { + public NanoleafPanelsDiscoveryService() { super(NanoleafHandlerFactory.SUPPORTED_THING_TYPES_UIDS, SEARCH_TIMEOUT_SECONDS, false); - this.bridgeHandler = nanoleafControllerHandler; } @Override - protected void startScan() { - logger.debug("Starting Nanoleaf panel discovery"); - bridgeHandler.registerControllerListener(this); + public void deactivate() { + if (bridgeHandler != null) { + bridgeHandler.unregisterControllerListener(this); + } + super.deactivate(); } @Override - protected synchronized void stopScan() { - logger.debug("Stopping Nanoleaf panel discovery"); - super.stopScan(); - bridgeHandler.unregisterControllerListener(this); + protected void startScan() { + logger.debug("Starting Nanoleaf panel discovery"); + createResultsFromControllerInfo(); } /** @@ -81,38 +84,60 @@ protected synchronized void stopScan() { */ @Override public void onControllerInfoFetched(ThingUID bridge, ControllerInfo controllerInfo) { - logger.debug("Discover panels connected to controller with id {}", bridge.getAsString()); - final PanelLayout panelLayout = controllerInfo.getPanelLayout(); - @Nullable - Layout layout = panelLayout.getLayout(); + this.controllerInfo = controllerInfo; + } - if (layout != null && layout.getNumPanels() > 0) { + private void createResultsFromControllerInfo() { + ThingUID bridgeUID; + if (bridgeHandler != null) { + bridgeUID = bridgeHandler.getThing().getUID(); + } else { + return; + } + if (controllerInfo != null) { + final PanelLayout panelLayout = controllerInfo.getPanelLayout(); @Nullable - final List positionData = layout.getPositionData(); - if (positionData != null) { - Iterator iterator = positionData.iterator(); - while (iterator.hasNext()) { - @Nullable - PositionDatum panel = iterator.next(); - ThingUID newPanelThingUID = new ThingUID(NanoleafBindingConstants.THING_TYPE_LIGHT_PANEL, bridge, - Integer.toString(panel.getPanelId())); - - final Map properties = new HashMap<>(1); - properties.put(CONFIG_PANEL_ID, panel.getPanelId()); - - DiscoveryResult newPanel = DiscoveryResultBuilder.create(newPanelThingUID).withBridge(bridge) - .withProperties(properties).withLabel("Light Panel " + panel.getPanelId()) - .withRepresentationProperty(CONFIG_PANEL_ID).build(); - - logger.debug("Adding panel with id {} to inbox", panel.getPanelId()); - thingDiscovered(newPanel); + Layout layout = panelLayout.getLayout(); + + if (layout != null && layout.getNumPanels() > 0) { + @Nullable + final List positionData = layout.getPositionData(); + if (positionData != null) { + Iterator iterator = positionData.iterator(); + while (iterator.hasNext()) { + @Nullable + PositionDatum panel = iterator.next(); + ThingUID newPanelThingUID = new ThingUID(NanoleafBindingConstants.THING_TYPE_LIGHT_PANEL, + bridgeUID, Integer.toString(panel.getPanelId())); + + final Map properties = new HashMap<>(1); + properties.put(CONFIG_PANEL_ID, panel.getPanelId()); + + DiscoveryResult newPanel = DiscoveryResultBuilder.create(newPanelThingUID).withBridge(bridgeUID) + .withProperties(properties).withLabel("Light Panel " + panel.getPanelId()) + .withRepresentationProperty(CONFIG_PANEL_ID).build(); + + logger.debug("Adding panel with id {} to inbox", panel.getPanelId()); + thingDiscovered(newPanel); + } + } else { + logger.debug("Couldn't add panels to inbox as layout position data was null"); } + } else { - logger.debug("Couldn't add panels to inbox as layout position data was null"); + logger.debug("No panels found or connected to controller"); } - - } else { - logger.info("No panels found or connected to controller"); } } + + @Override + public void setThingHandler(ThingHandler handler) { + this.bridgeHandler = (NanoleafControllerHandler) handler; + this.bridgeHandler.registerControllerListener(this); + } + + @Override + public @Nullable ThingHandler getThingHandler() { + return bridgeHandler; + } } diff --git a/bundles/org.openhab.binding.nanoleaf/src/main/java/org/openhab/binding/nanoleaf/internal/handler/NanoleafControllerHandler.java b/bundles/org.openhab.binding.nanoleaf/src/main/java/org/openhab/binding/nanoleaf/internal/handler/NanoleafControllerHandler.java index ce5d0f0677a32..c36ef3bcf1f18 100644 --- a/bundles/org.openhab.binding.nanoleaf/src/main/java/org/openhab/binding/nanoleaf/internal/handler/NanoleafControllerHandler.java +++ b/bundles/org.openhab.binding.nanoleaf/src/main/java/org/openhab/binding/nanoleaf/internal/handler/NanoleafControllerHandler.java @@ -17,7 +17,11 @@ import java.net.URI; import java.nio.ByteBuffer; import java.nio.charset.StandardCharsets; -import java.util.*; +import java.util.Collection; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.Scanner; import java.util.concurrent.CopyOnWriteArrayList; import java.util.concurrent.ExecutionException; import java.util.concurrent.ScheduledFuture; @@ -36,10 +40,11 @@ import org.eclipse.jetty.http.HttpStatus; import org.openhab.binding.nanoleaf.internal.NanoleafControllerListener; import org.openhab.binding.nanoleaf.internal.NanoleafException; -import org.openhab.binding.nanoleaf.internal.NanoleafInterruptedException; import org.openhab.binding.nanoleaf.internal.NanoleafUnauthorizedException; import org.openhab.binding.nanoleaf.internal.OpenAPIUtils; +import org.openhab.binding.nanoleaf.internal.commanddescription.NanoleafCommandDescriptionProvider; import org.openhab.binding.nanoleaf.internal.config.NanoleafControllerConfig; +import org.openhab.binding.nanoleaf.internal.discovery.NanoleafPanelsDiscoveryService; import org.openhab.binding.nanoleaf.internal.model.AuthToken; import org.openhab.binding.nanoleaf.internal.model.BooleanState; import org.openhab.binding.nanoleaf.internal.model.Brightness; @@ -67,6 +72,7 @@ import org.openhab.core.thing.ThingStatus; import org.openhab.core.thing.ThingStatusDetail; import org.openhab.core.thing.binding.BaseBridgeHandler; +import org.openhab.core.thing.binding.ThingHandlerService; import org.openhab.core.types.Command; import org.openhab.core.types.RefreshType; import org.slf4j.Logger; @@ -81,15 +87,13 @@ * * @author Martin Raepple - Initial contribution * @author Stefan Höhn - Canvas Touch Support + * @author Kai Kreuzer - refactoring, bug fixing and code clean up */ @NonNullByDefault public class NanoleafControllerHandler extends BaseBridgeHandler { // Pairing interval in seconds - private static final int PAIRING_INTERVAL = 25; - - // Panel discovery interval in seconds - private static final int PANEL_DISCOVERY_INTERVAL = 30; + private static final int PAIRING_INTERVAL = 10; private final Logger logger = LoggerFactory.getLogger(NanoleafControllerHandler.class); private HttpClient httpClient; @@ -98,7 +102,6 @@ public class NanoleafControllerHandler extends BaseBridgeHandler { // Pairing, update and panel discovery jobs and touch event job private @NonNullByDefault({}) ScheduledFuture pairingJob; private @NonNullByDefault({}) ScheduledFuture updateJob; - private @NonNullByDefault({}) ScheduledFuture panelDiscoveryJob; private @NonNullByDefault({}) ScheduledFuture touchJob; // JSON parser for API responses @@ -120,7 +123,7 @@ public NanoleafControllerHandler(Bridge bridge, HttpClient httpClient) { @Override public void initialize() { logger.debug("Initializing the controller (bridge)"); - updateStatus(ThingStatus.UNKNOWN, ThingStatusDetail.BRIDGE_UNINITIALIZED); + updateStatus(ThingStatus.UNKNOWN); NanoleafControllerConfig config = getConfigAs(NanoleafControllerConfig.class); setAddress(config.address); setPort(config.port); @@ -157,13 +160,9 @@ public void initialize() { "@text/error.nanoleaf.controller.noToken"); startPairingJob(); stopUpdateJob(); - stopPanelDiscoveryJob(); } else { - logger.debug("Controller is online. Stop pairing job, start update & panel discovery jobs"); - updateStatus(ThingStatus.ONLINE); stopPairingJob(); startUpdateJob(); - startPanelDiscoveryJob(); startTouchJob(); } } catch (IllegalArgumentException iae) { @@ -186,11 +185,9 @@ public void handleCommand(ChannelUID channelUID, Command command) { updateFromControllerInfo(); } else { switch (channelUID.getId()) { - case CHANNEL_POWER: case CHANNEL_COLOR: case CHANNEL_COLOR_TEMPERATURE: case CHANNEL_COLOR_TEMPERATURE_ABS: - case CHANNEL_PANEL_LAYOUT: sendStateCommand(channelUID.getId(), command); break; case CHANNEL_EFFECT: @@ -218,25 +215,28 @@ public void handleCommand(ChannelUID channelUID, Command command) { @Override public void handleRemoval() { - // delete token for openHAB - ContentResponse deleteTokenResponse; - try { - Request deleteTokenRequest = OpenAPIUtils.requestBuilder(httpClient, getControllerConfig(), API_DELETE_USER, - HttpMethod.DELETE); - deleteTokenResponse = OpenAPIUtils.sendOpenAPIRequest(deleteTokenRequest); - if (deleteTokenResponse.getStatus() != HttpStatus.NO_CONTENT_204) { - logger.warn("Failed to delete token for openHAB. Response code is {}", deleteTokenResponse.getStatus()); - return; + scheduler.execute(() -> { + // delete token for openHAB + ContentResponse deleteTokenResponse; + try { + Request deleteTokenRequest = OpenAPIUtils.requestBuilder(httpClient, getControllerConfig(), + API_DELETE_USER, HttpMethod.DELETE); + deleteTokenResponse = OpenAPIUtils.sendOpenAPIRequest(deleteTokenRequest); + if (deleteTokenResponse.getStatus() != HttpStatus.NO_CONTENT_204) { + logger.warn("Failed to delete token for openHAB. Response code is {}", + deleteTokenResponse.getStatus()); + return; + } + logger.debug("Successfully deleted token for openHAB from controller"); + } catch (NanoleafUnauthorizedException e) { + logger.warn("Attempt to delete token for openHAB failed. Token unauthorized."); + } catch (NanoleafException ne) { + logger.warn("Attempt to delete token for openHAB failed : {}", ne.getMessage()); } - logger.debug("Successfully deleted token for openHAB from controller"); - } catch (NanoleafUnauthorizedException e) { - logger.warn("Attempt to delete token for openHAB failed. Token unauthorized."); - } catch (NanoleafException ne) { - logger.warn("Attempt to delete token for openHAB failed : {}", ne.getMessage()); - } - stopAllJobs(); - super.handleRemoval(); - logger.debug("Nanoleaf controller removed"); + stopAllJobs(); + super.handleRemoval(); + logger.debug("Nanoleaf controller removed"); + }); } @Override @@ -246,34 +246,37 @@ public void dispose() { logger.debug("Disposing handler for Nanoleaf controller {}", getThing().getUID()); } + @Override + public Collection> getServices() { + return List.of(NanoleafPanelsDiscoveryService.class, NanoleafCommandDescriptionProvider.class); + } + public boolean registerControllerListener(NanoleafControllerListener controllerListener) { logger.debug("Register new listener for controller {}", getThing().getUID()); - boolean result = controllerListeners.add(controllerListener); - if (result) { - startPanelDiscoveryJob(); - } - return result; + return controllerListeners.add(controllerListener); } public boolean unregisterControllerListener(NanoleafControllerListener controllerListener) { logger.debug("Unregister listener for controller {}", getThing().getUID()); - boolean result = controllerListeners.remove(controllerListener); - if (result) { - stopPanelDiscoveryJob(); - } - return result; + return controllerListeners.remove(controllerListener); } public NanoleafControllerConfig getControllerConfig() { NanoleafControllerConfig config = new NanoleafControllerConfig(); config.address = Objects.requireNonNullElse(getAddress(), ""); config.port = getPort(); - config.refreshInterval = getRefreshIntervall(); + config.refreshInterval = getRefreshInterval(); config.authToken = getAuthToken(); config.deviceType = Objects.requireNonNullElse(getDeviceType(), ""); return config; } + public String getLayout() { + Layout layout = controllerInfo.getPanelLayout().getLayout(); + String layoutView = (layout != null) ? layout.getLayoutView() : ""; + return layoutView; + } + public synchronized void startPairingJob() { if (pairingJob == null || pairingJob.isCancelled()) { logger.debug("Start pairing job, interval={} sec", PAIRING_INTERVAL); @@ -293,8 +296,8 @@ private synchronized void startUpdateJob() { String localAuthToken = getAuthToken(); if (localAuthToken != null && !localAuthToken.isEmpty()) { if (updateJob == null || updateJob.isCancelled()) { - logger.debug("Start controller status job, repeat every {} sec", getRefreshIntervall()); - updateJob = scheduler.scheduleWithFixedDelay(this::runUpdate, 0, getRefreshIntervall(), + logger.debug("Start controller status job, repeat every {} sec", getRefreshInterval()); + updateJob = scheduler.scheduleWithFixedDelay(this::runUpdate, 0, getRefreshInterval(), TimeUnit.SECONDS); } } else { @@ -311,24 +314,6 @@ private synchronized void stopUpdateJob() { } } - public synchronized void startPanelDiscoveryJob() { - logger.debug("Starting panel discovery job. Has Controller-Listeners: {} panelDiscoveryJob: {}", - !controllerListeners.isEmpty(), panelDiscoveryJob); - if (!controllerListeners.isEmpty() && (panelDiscoveryJob == null || panelDiscoveryJob.isCancelled())) { - logger.debug("Start panel discovery job, interval={} sec", PANEL_DISCOVERY_INTERVAL); - panelDiscoveryJob = scheduler.scheduleWithFixedDelay(this::runPanelDiscovery, 0, PANEL_DISCOVERY_INTERVAL, - TimeUnit.SECONDS); - } - } - - private synchronized void stopPanelDiscoveryJob() { - if (controllerListeners.isEmpty() && panelDiscoveryJob != null && !panelDiscoveryJob.isCancelled()) { - logger.debug("Stop panel discovery job"); - panelDiscoveryJob.cancel(true); - this.panelDiscoveryJob = null; - } - } - private synchronized void startTouchJob() { NanoleafControllerConfig config = getConfigAs(NanoleafControllerConfig.class); if (!config.deviceType.equals(DEVICE_TYPE_TOUCHSUPPORT)) { @@ -367,11 +352,7 @@ private void runUpdate() { try { updateFromControllerInfo(); startTouchJob(); // if device type has changed, start touch detection. - // controller might have been offline, e.g. for firmware update. In this case, return to online state - if (ThingStatus.OFFLINE.equals(getThing().getStatus())) { - logger.debug("Controller {} is back online", thing.getUID()); - updateStatus(ThingStatus.ONLINE); - } + updateStatus(ThingStatus.ONLINE); } catch (NanoleafUnauthorizedException nae) { logger.warn("Status update unauthorized: {}", nae.getMessage()); updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, @@ -403,7 +384,8 @@ private void runPairing() { return; } ContentResponse authTokenResponse = OpenAPIUtils - .requestBuilder(httpClient, getControllerConfig(), API_ADD_USER, HttpMethod.POST).send(); + .requestBuilder(httpClient, getControllerConfig(), API_ADD_USER, HttpMethod.POST) + .timeout(20, TimeUnit.SECONDS).send(); if (logger.isTraceEnabled()) { logger.trace("Auth token response: {}", authTokenResponse.getContentAsString()); } @@ -429,7 +411,6 @@ private void runPairing() { stopPairingJob(); startUpdateJob(); - startPanelDiscoveryJob(); startTouchJob(); } else { logger.debug("No auth token found in response: {}", authTokenResponse.getContentAsString()); @@ -446,7 +427,7 @@ private void runPairing() { updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, "@text/error.nanoleaf.controller.noTokenReceived"); } catch (InterruptedException | ExecutionException | TimeoutException e) { - logger.warn("Cannot send authorization request to controller: ", e); + logger.debug("Cannot send authorization request to controller: ", e); updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, "@text/error.nanoleaf.controller.authRequest"); } catch (RuntimeException e) { @@ -459,34 +440,6 @@ private void runPairing() { } } - private void runPanelDiscovery() { - logger.debug("Run panel discovery job"); - // Trigger a new discovery of connected panels - for (NanoleafControllerListener controllerListener : controllerListeners) { - try { - controllerListener.onControllerInfoFetched(getThing().getUID(), receiveControllerInfo()); - } catch (NanoleafUnauthorizedException nue) { - logger.warn("Panel discovery unauthorized: {}", nue.getMessage()); - updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, - "@text/error.nanoleaf.controller.invalidToken"); - String localAuthToken = getAuthToken(); - if (localAuthToken == null || localAuthToken.isEmpty()) { - updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_PENDING, - "@text/error.nanoleaf.controller.noToken"); - } - } catch (NanoleafInterruptedException nie) { - logger.info("Panel discovery has been stopped."); - } catch (NanoleafException ne) { - logger.warn("Failed to discover panels: ", ne); - updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, - "@text/error.nanoleaf.controller.communication"); - } catch (RuntimeException e) { - logger.warn("Panel discovery job failed", e); - updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.NONE, "@text/error.nanoleaf.controller.runtime"); - } - } - } - /** * This is based on the touch event detection described in https://forum.nanoleaf.me/docs/openapi#_842h3097vbgq */ @@ -579,14 +532,9 @@ private void handleTouchEvents(TouchEvents touchEvents) { private void updateFromControllerInfo() throws NanoleafException { logger.debug("Update channels for controller {}", thing.getUID()); this.controllerInfo = receiveControllerInfo(); - if (controllerInfo == null) { - logger.debug("No Controller Info has been provided"); - return; - } final State state = controllerInfo.getState(); OnOffType powerState = state.getOnOff(); - updateState(CHANNEL_POWER, powerState); @Nullable Ct colorTemperature = state.getColorTemperature(); @@ -662,6 +610,10 @@ private void updateFromControllerInfo() throws NanoleafException { panelHandler.updatePanelColorChannel(); } }); + + for (NanoleafControllerListener controllerListener : controllerListeners) { + controllerListener.onControllerInfoFetched(getThing().getUID(), controllerInfo); + } } private ControllerInfo receiveControllerInfo() throws NanoleafException, NanoleafUnauthorizedException { @@ -674,17 +626,6 @@ private ControllerInfo receiveControllerInfo() throws NanoleafException, Nanolea private void sendStateCommand(String channel, Command command) throws NanoleafException { State stateObject = new State(); switch (channel) { - case CHANNEL_POWER: - if (command instanceof OnOffType) { - // On/Off command - turns controller on/off - BooleanState state = new On(); - state.setValue(OnOffType.ON.equals(command)); - stateObject.setState(state); - } else { - logger.warn("Unhandled command type: {}", command.getClass().getName()); - return; - } - break; case CHANNEL_COLOR: if (command instanceof OnOffType) { // On/Off command - turns controller on/off @@ -780,13 +721,6 @@ private void sendStateCommand(String channel, Command command) throws NanoleafEx return; } break; - case CHANNEL_PANEL_LAYOUT: - @Nullable - Layout layout = controllerInfo.getPanelLayout().getLayout(); - String layoutView = (layout != null) ? layout.getLayoutView() : ""; - logger.info("Panel layout and ids for controller {} \n{}", thing.getUID(), layoutView); - updateState(CHANNEL_PANEL_LAYOUT, OnOffType.OFF); - break; default: logger.warn("Unhandled command type: {}", command.getClass().getName()); return; @@ -844,7 +778,7 @@ private void setPort(int port) { this.port = port; } - private int getRefreshIntervall() { + private int getRefreshInterval() { return refreshIntervall; } @@ -871,7 +805,6 @@ private void setDeviceType(String deviceType) { private void stopAllJobs() { stopPairingJob(); stopUpdateJob(); - stopPanelDiscoveryJob(); stopTouchJob(); } } diff --git a/bundles/org.openhab.binding.nanoleaf/src/main/java/org/openhab/binding/nanoleaf/internal/handler/NanoleafPanelHandler.java b/bundles/org.openhab.binding.nanoleaf/src/main/java/org/openhab/binding/nanoleaf/internal/handler/NanoleafPanelHandler.java index 628f11960693d..f25c54822ecd8 100644 --- a/bundles/org.openhab.binding.nanoleaf/src/main/java/org/openhab/binding/nanoleaf/internal/handler/NanoleafPanelHandler.java +++ b/bundles/org.openhab.binding.nanoleaf/src/main/java/org/openhab/binding/nanoleaf/internal/handler/NanoleafPanelHandler.java @@ -15,11 +15,11 @@ import static org.openhab.binding.nanoleaf.internal.NanoleafBindingConstants.*; import java.math.BigDecimal; +import java.math.RoundingMode; import java.util.Arrays; import java.util.HashMap; import java.util.Map; import java.util.concurrent.ScheduledFuture; -import java.util.concurrent.TimeUnit; import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.Nullable; @@ -28,13 +28,21 @@ import org.eclipse.jetty.client.api.Request; import org.eclipse.jetty.client.util.StringContentProvider; import org.eclipse.jetty.http.HttpMethod; -import org.openhab.binding.nanoleaf.internal.*; +import org.openhab.binding.nanoleaf.internal.NanoleafBadRequestException; +import org.openhab.binding.nanoleaf.internal.NanoleafException; +import org.openhab.binding.nanoleaf.internal.NanoleafNotFoundException; +import org.openhab.binding.nanoleaf.internal.NanoleafUnauthorizedException; +import org.openhab.binding.nanoleaf.internal.OpenAPIUtils; import org.openhab.binding.nanoleaf.internal.config.NanoleafControllerConfig; import org.openhab.binding.nanoleaf.internal.model.Effects; import org.openhab.binding.nanoleaf.internal.model.Write; -import org.openhab.core.library.types.*; +import org.openhab.core.library.types.HSBType; +import org.openhab.core.library.types.IncreaseDecreaseType; +import org.openhab.core.library.types.OnOffType; +import org.openhab.core.library.types.PercentType; import org.openhab.core.thing.Bridge; import org.openhab.core.thing.ChannelUID; +import org.openhab.core.thing.CommonTriggerEvents; import org.openhab.core.thing.Thing; import org.openhab.core.thing.ThingStatus; import org.openhab.core.thing.ThingStatusDetail; @@ -165,8 +173,9 @@ private void initializePanel(ThingStatusInfo panelStatus) { private void sendRenderedEffectCommand(Command command) throws NanoleafException { logger.debug("Command Type: {}", command.getClass()); HSBType currentPanelColor = getPanelColor(); - if (currentPanelColor != null) + if (currentPanelColor != null) { logger.debug("currentPanelColor: {}", currentPanelColor.toString()); + } HSBType newPanelColor = new HSBType(); if (command instanceof HSBType) { @@ -205,11 +214,11 @@ private void sendRenderedEffectCommand(Command command) throws NanoleafException // transform to RGB PercentType[] rgbPercent = newPanelColor.toRGB(); logger.trace("Setting new rgbpercent {} {} {}", rgbPercent[0], rgbPercent[1], rgbPercent[2]); - int red = rgbPercent[0].toBigDecimal().divide(BigDecimal.valueOf(100), 2, BigDecimal.ROUND_HALF_UP) + int red = rgbPercent[0].toBigDecimal().divide(BigDecimal.valueOf(100), 2, RoundingMode.HALF_UP) .multiply(new BigDecimal(255)).intValue(); - int green = rgbPercent[1].toBigDecimal().divide(BigDecimal.valueOf(100), 2, BigDecimal.ROUND_HALF_UP) + int green = rgbPercent[1].toBigDecimal().divide(BigDecimal.valueOf(100), 2, RoundingMode.HALF_UP) .multiply(new BigDecimal(255)).intValue(); - int blue = rgbPercent[2].toBigDecimal().divide(BigDecimal.valueOf(100), 2, BigDecimal.ROUND_HALF_UP) + int blue = rgbPercent[2].toBigDecimal().divide(BigDecimal.valueOf(100), 2, RoundingMode.HALF_UP) .multiply(new BigDecimal(255)).intValue(); logger.trace("Setting new rgb {} {} {}", red, green, blue); Bridge bridge = getBridge(); @@ -253,8 +262,9 @@ public void updatePanelColorChannel() { @Nullable HSBType panelColor = getPanelColor(); logger.trace("updatePanelColorChannel: panelColor: {}", panelColor); - if (panelColor != null) + if (panelColor != null) { updateState(CHANNEL_PANEL_COLOR, panelColor); + } } /** @@ -265,28 +275,14 @@ public void updatePanelColorChannel() { public void updatePanelGesture(int gesture) { switch (gesture) { case 0: - updateState(CHANNEL_PANEL_SINGLE_TAP, OnOffType.ON); - singleTapJob = scheduler.schedule(this::resetSingleTap, 1, TimeUnit.SECONDS); - logger.debug("Asserting single tap of panel {} to ON", getPanelID()); + triggerChannel(CHANNEL_PANEL_TAP, CommonTriggerEvents.SHORT_PRESSED); break; case 1: - updateState(CHANNEL_PANEL_DOUBLE_TAP, OnOffType.ON); - doubleTapJob = scheduler.schedule(this::resetDoubleTap, 1, TimeUnit.SECONDS); - logger.debug("Asserting double tap of panel {} to ON", getPanelID()); + triggerChannel(CHANNEL_PANEL_TAP, CommonTriggerEvents.DOUBLE_PRESSED); break; } } - private void resetSingleTap() { - updateState(CHANNEL_PANEL_SINGLE_TAP, OnOffType.OFF); - logger.debug("Resetting single tap of panel {} to OFF", getPanelID()); - } - - private void resetDoubleTap() { - updateState(CHANNEL_PANEL_DOUBLE_TAP, OnOffType.OFF); - logger.debug("Resetting double tap of panel {} to OFF", getPanelID()); - } - public String getPanelID() { String panelID = getThing().getConfiguration().get(CONFIG_PANEL_ID).toString(); return panelID; @@ -327,7 +323,7 @@ public String getPanelID() { } catch (NanoleafException nue) { updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, "@text/error.nanoleaf.panel.communication"); - logger.warn("Panel data could not be retrieved: {}", nue.getMessage()); + logger.debug("Panel data could not be retrieved: {}", nue.getMessage()); } return panelInfo.get(panelID); diff --git a/bundles/org.openhab.binding.nanoleaf/src/main/java/org/openhab/binding/nanoleaf/internal/model/Layout.java b/bundles/org.openhab.binding.nanoleaf/src/main/java/org/openhab/binding/nanoleaf/internal/model/Layout.java index dd8a3230163a8..2c3db6be1e568 100644 --- a/bundles/org.openhab.binding.nanoleaf/src/main/java/org/openhab/binding/nanoleaf/internal/model/Layout.java +++ b/bundles/org.openhab.binding.nanoleaf/src/main/java/org/openhab/binding/nanoleaf/internal/model/Layout.java @@ -60,7 +60,7 @@ public void setPositionData(List positionData) { * Returns an text representation for a canvas layout. * * Note only canvas supported currently due to its easy geometry - * + * * @return a String containing the layout */ public String getLayoutView() { @@ -97,6 +97,11 @@ public String getLayoutView() { int shiftWidth = getSideLength() / 2; + if (shiftWidth == 0) { + // seems we do not have squares here + return "Cannot render layout. Please note that layout views are only supported for square panels."; + } + int lineY = maxy; Map map; @@ -108,8 +113,9 @@ public String getLayoutView() { @Nullable PositionDatum panel = positionData.get(index); - if (panel != null && panel.getPosY() == lineY) + if (panel != null && panel.getPosY() == lineY) { map.put(panel.getPosX(), panel); + } } } lineY -= shiftWidth; @@ -118,14 +124,16 @@ public String getLayoutView() { @Nullable PositionDatum panel = map.get(x); view += String.format("%5s ", panel.getPanelId()); - } else + } else { view += " "; + } } - view += "\n"; + view += System.lineSeparator(); } return view; - } else + } else { return ""; + } } } diff --git a/bundles/org.openhab.binding.nanoleaf/src/main/resources/OH-INF/config/config.xml b/bundles/org.openhab.binding.nanoleaf/src/main/resources/OH-INF/config/config.xml index 22348de623dc6..76ce1758656a2 100644 --- a/bundles/org.openhab.binding.nanoleaf/src/main/resources/OH-INF/config/config.xml +++ b/bundles/org.openhab.binding.nanoleaf/src/main/resources/OH-INF/config/config.xml @@ -25,7 +25,7 @@ @text/thing-type.config.nanoleaf.controller.refreshInterval.description 60 - + @text/thing-type.config.nanoleaf.controller.deviceType.description lightPanels diff --git a/bundles/org.openhab.binding.nanoleaf/src/main/resources/OH-INF/i18n/nanoleaf.properties b/bundles/org.openhab.binding.nanoleaf/src/main/resources/OH-INF/i18n/nanoleaf.properties index 39aefb6623b89..caf3424be8f77 100644 --- a/bundles/org.openhab.binding.nanoleaf/src/main/resources/OH-INF/i18n/nanoleaf.properties +++ b/bundles/org.openhab.binding.nanoleaf/src/main/resources/OH-INF/i18n/nanoleaf.properties @@ -1,5 +1,5 @@ binding.nanoleaf.name = Nanoleaf Binding -binding.nanoleaf.description = Integrates the Nanoleaf Light Panels (v010221) +binding.nanoleaf.description = Integrates the Nanoleaf light panels # thing types thing-type.nanoleaf.controller.name = Nanoleaf Controller @@ -22,14 +22,6 @@ thing-type.config.nanoleaf.lightpanel.id.label = ID Of The Panel thing-type.config.nanoleaf.lightpanel.id.description = The ID of the panel assigned by the controller # channel types -channel-type.nanoleaf.power.label = Power -channel-type.nanoleaf.power.description = Power state of the controller -channel-type.nanoleaf.color.label = Color -channel-type.nanoleaf.color.description = Color setting for an individual or all panels -channel-type.nanoleaf.colorTemperature.label = Color Temperature -channel-type.nanoleaf.colorTemperature.description = Color temperature in percent -channel-type.nanoleaf.colorTemperatureAbs.label = Color Temperature (K) -channel-type.nanoleaf.colorTemperatureAbs.description = Color temperature in Kelvin (K) channel-type.nanoleaf.colorMode.label = Color Mode channel-type.nanoleaf.colorMode.description = Current color mode: Effect, hue/saturation or color temperature channel-type.nanoleaf.effect.label = Effect @@ -40,21 +32,17 @@ channel-type.nanoleaf.rhythmActive.label = Rhythm Active channel-type.nanoleaf.rhythmActive.description = Activity state of the rhythm module channel-type.nanoleaf.rhythmMode.label = Rhythm Mode channel-type.nanoleaf.rhythmMode.description = Sound source for the rhythm module (microphone or aux cable) -channel-type.nanoleaf.panelLayout.label = Panel Layout -channel-type.nanoleaf.panelLayout.description = Creates a panel layout upon request channel-type.nanoleaf.panelColor.label = Panel Color channel-type.nanoleaf.panelColor.description = Color of the individual panel -channel-type.nanoleaf.singleTap.label = SingleTap -channel-type.nanoleaf.singleTap.description = Single tap on the panel -channel-type.nanoleaf.doubleTap.label = DoubleTap -channel-type.nanoleaf.doubleTap.description = Double tap on the panel +channel-type.nanoleaf.tap.label = Button +channel-type.nanoleaf.tap.description = Button events of the panel # error messages error.nanoleaf.controller.noIp = IP/host address and/or port are not configured for the controller. error.nanoleaf.controller.incompatibleFirmware = Incompatible controller firmware. Remove the device, update the firmware, and add it again. error.nanoleaf.controller.noToken = No authorization token found. To start pairing, press the on-off button of the controller for 5-7 seconds until the LED starts flashing in a pattern. error.nanoleaf.controller.invalidToken = Invalid token. Replace with valid token or start pairing again by removing the invalid token from the configuration. -error.nanoleaf.controller.communication = Communication failed. Please check configuration. +error.nanoleaf.controller.communication = Communication failed. Please check your network and configuration. error.nanoleaf.controller.pairingFailed = Pairing failed. Press the on-off button for 5-7 seconds until the LED starts flashing in a pattern. error.nanoleaf.controller.invalidData = Pairing failed. Received invalid data error.nanoleaf.controller.noTokenReceived = Pairing failed. No authorization token received from controller. diff --git a/bundles/org.openhab.binding.nanoleaf/src/main/resources/OH-INF/i18n/nanoleaf_de.properties b/bundles/org.openhab.binding.nanoleaf/src/main/resources/OH-INF/i18n/nanoleaf_de.properties index b21023e07cdd4..d94531575837c 100644 --- a/bundles/org.openhab.binding.nanoleaf/src/main/resources/OH-INF/i18n/nanoleaf_de.properties +++ b/bundles/org.openhab.binding.nanoleaf/src/main/resources/OH-INF/i18n/nanoleaf_de.properties @@ -22,14 +22,6 @@ thing-type.config.nanoleaf.lightpanel.id.label = Paneel ID thing-type.config.nanoleaf.lightpanel.id.description = Vom Controller vergebene ID eines Paneels # channel types -channel-type.nanoleaf.power.label = Power -channel-type.nanoleaf.power.description = Ermöglicht das An- und Ausschalten des Nanoleaf Light Panels -channel-type.nanoleaf.color.label = Farbe -channel-type.nanoleaf.color.description = Farbe aller oder eines einzelnen Paneels -channel-type.nanoleaf.colorTemperature.label = Farbtemperatur -channel-type.nanoleaf.colorTemperature.description = Farbtemperatur in Prozent -channel-type.nanoleaf.colorTemperatureAbs.label = Farbtemperatur (K) -channel-type.nanoleaf.colorTemperatureAbs.description = Farbtemperatur in Kelvin (K) channel-type.nanoleaf.colorMode.label = Farbmodus channel-type.nanoleaf.colorMode.description = Effekt, Hue/Sat oder Farbtemperatur für alle Paneele. channel-type.nanoleaf.effect.label = Effekt @@ -40,21 +32,17 @@ channel-type.nanoleaf.rhythmActive.label = Rhythm Aktiv channel-type.nanoleaf.rhythmActive.description = Zeigt an ob das Mikrofon des Rhythm Modules ativ ist. channel-type.nanoleaf.rhythmMode.label = Rhythm Modus channel-type.nanoleaf.rhythmMode.description = Erlaubt den Wechsel zwischen eingebautem Mikrofon und AUX-Kabel. -channel-type.nanoleaf.panelLayout.label = PanelLayout -channel-type.nanoleaf.panelLayout.description = Erzeugt auf Anfrage ein Panel-Layout channel-type.nanoleaf.panelColor.label = Paneelfarbe channel-type.nanoleaf.panelColor.description = Farbe des einzelnen Paneels -channel-type.nanoleaf.singleTap.label = Einzel-Tap -channel-type.nanoleaf.singleTap.description = Panel wurde einmal angetippt -channel-type.nanoleaf.doubleTap.label = Doppel-Tap -channel-type.nanoleaf.doubleTap.description = Panel wurde zweimal hintereinander angetippt +channel-type.nanoleaf.tap.label = Taster +channel-type.nanoleaf.tap.description = Tastevents des Panels # error messages error.nanoleaf.controller.noIp = IP/Host-Adresse und/oder Port sind für den Controller nicht konfiguriert. error.nanoleaf.controller.incompatibleFirmware = Inkompatible Controller-Firmware. Firmware aktualisieren und das Gerät neu hinzufügen. error.nanoleaf.controller.noToken = Kein Authentifizierungstoken gefunden. Um das Pairing zu starten, den An/Aus-Knopf am Controller für 5-7 Sekunden lang gedrückt halten, bis die LED anfängt zu blinken. error.nanoleaf.controller.invalidToken = Ungültiges Authentifizierungstoken. Token ändern oder das Pairing neu starten durch Löschen des ungültigen Tokens. -error.nanoleaf.controller.communication = Kommunikationsfehler. Konfiguration des Controllers prüfen. +error.nanoleaf.controller.communication = Kommunikationsfehler. Netzwerk und Konfiguration des Controllers prüfen. error.nanoleaf.controller.pairingFailed = Pairing fehlgeschlagen. Um das Pairing zu starten, den An/Aus-Knopf am Controller für 5-7 Sekunden lang gedrückt halten, bis die LED anfängt zu blinken. error.nanoleaf.controller.invalidData = Pairing fehlgeschlagen. Ungültige Daten vom Controller empfangen. error.nanoleaf.controller.noTokenReceived = Pairing fehlgeschlagen. Kein Authentifizierungstoken empfangen. diff --git a/bundles/org.openhab.binding.nanoleaf/src/main/resources/OH-INF/thing/lightpanels.xml b/bundles/org.openhab.binding.nanoleaf/src/main/resources/OH-INF/thing/lightpanels.xml index a8a7f33d80cc9..5aa026613d7e1 100644 --- a/bundles/org.openhab.binding.nanoleaf/src/main/resources/OH-INF/thing/lightpanels.xml +++ b/bundles/org.openhab.binding.nanoleaf/src/main/resources/OH-INF/thing/lightpanels.xml @@ -9,16 +9,14 @@ @text/thing-type.nanoleaf.controller.description - - - - + + + - @@ -40,9 +38,8 @@ @text/thing-type.nanoleaf.lightpanel.description - - - + + id @@ -50,35 +47,13 @@ - - Switch - - @text/channel-type.nanoleaf.power.description - Switch - - - - - - - - - - Dimmer - - @text/channel-type.nanoleaf.colorTemperature.description - - - - - Number - - @text/channel-type.nanoleaf.colorTemperatureAbs.description - ColorLight - + + String + + @text/channel-type.nanoleaf.effect.description - + String @text/channel-type.nanoleaf.colorMode.description @@ -91,62 +66,30 @@ - - Color - - @text/Color of the Light Panels - - - - String - - @text/channel-type.nanoleaf.effect.description - - - + Switch @text/channel-type.nanoleaf.rhythmState.description - + Switch @text/channel-type.nanoleaf.rhythmActive.description - + Number @text/channel-type.nanoleaf.rhythmMode.description - + - - Switch - - @text/channel-type.nanoleaf.singleTap.description - - - - - Switch - - @text/channel-type.nanoleaf.doubleTap.description - - - - - Switch - - @text/channel-type.nanoleaf.panelLayout.description - - diff --git a/bundles/org.openhab.binding.neato/src/main/java/org/openhab/binding/neato/internal/handler/NeatoHandler.java b/bundles/org.openhab.binding.neato/src/main/java/org/openhab/binding/neato/internal/handler/NeatoHandler.java index ba355b733065a..8cd011da807cd 100644 --- a/bundles/org.openhab.binding.neato/src/main/java/org/openhab/binding/neato/internal/handler/NeatoHandler.java +++ b/bundles/org.openhab.binding.neato/src/main/java/org/openhab/binding/neato/internal/handler/NeatoHandler.java @@ -17,7 +17,7 @@ import java.util.concurrent.ScheduledFuture; import java.util.concurrent.TimeUnit; -import org.apache.commons.lang.ObjectUtils; +import org.apache.commons.lang3.ObjectUtils; import org.eclipse.jdt.annotation.NonNull; import org.openhab.binding.neato.internal.CouldNotFindRobotException; import org.openhab.binding.neato.internal.NeatoBindingConstants; diff --git a/bundles/org.openhab.binding.nest/src/main/java/org/openhab/binding/nest/internal/handler/NestRedirectUrlSupplier.java b/bundles/org.openhab.binding.nest/src/main/java/org/openhab/binding/nest/internal/handler/NestRedirectUrlSupplier.java index 3d027ced5f32e..90d08da70d14b 100644 --- a/bundles/org.openhab.binding.nest/src/main/java/org/openhab/binding/nest/internal/handler/NestRedirectUrlSupplier.java +++ b/bundles/org.openhab.binding.nest/src/main/java/org/openhab/binding/nest/internal/handler/NestRedirectUrlSupplier.java @@ -71,7 +71,7 @@ public void resetCache() { * @see https://developers.nest.com/documentation/cloud/how-to-handle-redirects */ private String resolveRedirectUrl() throws FailedResolvingNestUrlException { - HttpClient httpClient = new HttpClient(new SslContextFactory()); + HttpClient httpClient = new HttpClient(new SslContextFactory.Client()); httpClient.setFollowRedirects(false); Request request = httpClient.newRequest(NestBindingConstants.NEST_URL).method(HttpMethod.GET).timeout(30, diff --git a/bundles/org.openhab.binding.netatmo/pom.xml b/bundles/org.openhab.binding.netatmo/pom.xml index 3048a3f8c0cdd..d9ba59a62b736 100644 --- a/bundles/org.openhab.binding.netatmo/pom.xml +++ b/bundles/org.openhab.binding.netatmo/pom.xml @@ -79,6 +79,12 @@ 1.8 compile + + com.google.code.gson + gson + 2.8.5 + compile + diff --git a/bundles/org.openhab.binding.netatmo/src/main/java/org/openhab/binding/netatmo/internal/ChannelTypeUtils.java b/bundles/org.openhab.binding.netatmo/src/main/java/org/openhab/binding/netatmo/internal/ChannelTypeUtils.java index 16cbaeda2d8d0..909187a1895c2 100644 --- a/bundles/org.openhab.binding.netatmo/src/main/java/org/openhab/binding/netatmo/internal/ChannelTypeUtils.java +++ b/bundles/org.openhab.binding.netatmo/src/main/java/org/openhab/binding/netatmo/internal/ChannelTypeUtils.java @@ -13,6 +13,7 @@ package org.openhab.binding.netatmo.internal; import java.math.BigDecimal; +import java.math.RoundingMode; import java.time.Instant; import java.time.ZoneId; import java.time.ZonedDateTime; @@ -83,7 +84,7 @@ public static State toDecimalType(double value) { } public static State toDecimalType(@Nullable BigDecimal decimal) { - return decimal == null ? UnDefType.NULL : new DecimalType(decimal.setScale(2, BigDecimal.ROUND_HALF_UP)); + return decimal == null ? UnDefType.NULL : new DecimalType(decimal.setScale(2, RoundingMode.HALF_UP)); } public static State toDecimalType(@Nullable String textualDecimal) { diff --git a/bundles/org.openhab.binding.network/src/main/java/org/openhab/binding/network/internal/utils/NetworkUtils.java b/bundles/org.openhab.binding.network/src/main/java/org/openhab/binding/network/internal/utils/NetworkUtils.java index 6dc3bfadcb871..444c32fc8fd6e 100644 --- a/bundles/org.openhab.binding.network/src/main/java/org/openhab/binding/network/internal/utils/NetworkUtils.java +++ b/bundles/org.openhab.binding.network/src/main/java/org/openhab/binding/network/internal/utils/NetworkUtils.java @@ -36,8 +36,7 @@ import java.util.Set; import java.util.stream.Collectors; -import org.apache.commons.lang.StringUtils; -import org.apache.commons.lang.SystemUtils; +import org.apache.commons.lang3.SystemUtils; import org.apache.commons.net.util.SubnetUtils; import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.Nullable; @@ -203,7 +202,7 @@ public IpPingMethodEnum determinePingMethod() { */ public ArpPingUtilEnum determineNativeARPpingMethod(String arpToolPath) { String result = ExecUtil.executeCommandLineAndWaitResponse(Duration.ofMillis(100), arpToolPath, "--help"); - if (StringUtils.isBlank(result)) { + if (result == null || result.isBlank()) { return ArpPingUtilEnum.UNKNOWN_TOOL; } else if (result.contains("Thomas Habets")) { if (result.matches("(?s)(.*)w sec Specify a timeout(.*)")) { diff --git a/bundles/org.openhab.binding.nibeheatpump/src/main/java/org/openhab/binding/nibeheatpump/internal/protocol/NibeHeatPumpProtocol.java b/bundles/org.openhab.binding.nibeheatpump/src/main/java/org/openhab/binding/nibeheatpump/internal/protocol/NibeHeatPumpProtocol.java index 8c81168194626..0ef1c81010172 100644 --- a/bundles/org.openhab.binding.nibeheatpump/src/main/java/org/openhab/binding/nibeheatpump/internal/protocol/NibeHeatPumpProtocol.java +++ b/bundles/org.openhab.binding.nibeheatpump/src/main/java/org/openhab/binding/nibeheatpump/internal/protocol/NibeHeatPumpProtocol.java @@ -12,7 +12,7 @@ */ package org.openhab.binding.nibeheatpump.internal.protocol; -import org.apache.commons.lang.ArrayUtils; +import org.apache.commons.lang3.ArrayUtils; import org.openhab.binding.nibeheatpump.internal.NibeHeatPumpException; /** diff --git a/bundles/org.openhab.binding.nibeuplink/src/main/java/org/openhab/binding/nibeuplink/internal/config/NibeUplinkConfiguration.java b/bundles/org.openhab.binding.nibeuplink/src/main/java/org/openhab/binding/nibeuplink/internal/config/NibeUplinkConfiguration.java index d444245a255ba..2c8e2f253ff6a 100644 --- a/bundles/org.openhab.binding.nibeuplink/src/main/java/org/openhab/binding/nibeuplink/internal/config/NibeUplinkConfiguration.java +++ b/bundles/org.openhab.binding.nibeuplink/src/main/java/org/openhab/binding/nibeuplink/internal/config/NibeUplinkConfiguration.java @@ -12,7 +12,7 @@ */ package org.openhab.binding.nibeuplink.internal.config; -import org.apache.commons.lang.builder.ToStringBuilder; +import org.apache.commons.lang3.builder.ToStringBuilder; import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.Nullable; diff --git a/bundles/org.openhab.binding.nikohomecontrol/src/main/java/org/openhab/binding/nikohomecontrol/internal/discovery/NikoHomeControlBridgeDiscoveryService.java b/bundles/org.openhab.binding.nikohomecontrol/src/main/java/org/openhab/binding/nikohomecontrol/internal/discovery/NikoHomeControlBridgeDiscoveryService.java index 50b0e1b73fedc..ffe67643dbbae 100644 --- a/bundles/org.openhab.binding.nikohomecontrol/src/main/java/org/openhab/binding/nikohomecontrol/internal/discovery/NikoHomeControlBridgeDiscoveryService.java +++ b/bundles/org.openhab.binding.nikohomecontrol/src/main/java/org/openhab/binding/nikohomecontrol/internal/discovery/NikoHomeControlBridgeDiscoveryService.java @@ -55,7 +55,7 @@ public class NikoHomeControlBridgeDiscoveryService extends AbstractDiscoveryServ public NikoHomeControlBridgeDiscoveryService() { super(NikoHomeControlBindingConstants.BRIDGE_THING_TYPES_UIDS, TIMEOUT); - logger.debug("Niko Home Control: bridge discovery service started"); + logger.debug("bridge discovery service started"); } /** @@ -65,10 +65,10 @@ private void discoverBridge() { try { String broadcastAddr = networkAddressService.getConfiguredBroadcastAddress(); if (broadcastAddr == null) { - logger.warn("Niko Home Control: discovery not possible, no broadcast address found"); + logger.warn("discovery not possible, no broadcast address found"); return; } - logger.debug("Niko Home Control: discovery broadcast on {}", broadcastAddr); + logger.debug("discovery broadcast on {}", broadcastAddr); NikoHomeControlDiscover nhcDiscover = new NikoHomeControlDiscover(broadcastAddr); if (nhcDiscover.isNhcII()) { addNhcIIBridge(nhcDiscover.getAddr(), nhcDiscover.getNhcBridgeId()); @@ -76,12 +76,12 @@ private void discoverBridge() { addNhcIBridge(nhcDiscover.getAddr(), nhcDiscover.getNhcBridgeId()); } } catch (IOException e) { - logger.debug("Niko Home Control: no bridge found."); + logger.debug("no bridge found."); } } private void addNhcIBridge(InetAddress addr, String bridgeId) { - logger.debug("Niko Home Control: NHC I bridge found at {}", addr); + logger.debug("NHC I bridge found at {}", addr); String bridgeName = "Niko Home Control Bridge"; ThingUID uid = new ThingUID(BINDING_ID, "bridge", bridgeId); @@ -92,7 +92,7 @@ private void addNhcIBridge(InetAddress addr, String bridgeId) { } private void addNhcIIBridge(InetAddress addr, String bridgeId) { - logger.debug("Niko Home Control: NHC II bridge found at {}", addr); + logger.debug("NHC II bridge found at {}", addr); String bridgeName = "Niko Home Control II Bridge"; ThingUID uid = new ThingUID(BINDING_ID, "bridge2", bridgeId); @@ -115,7 +115,7 @@ protected synchronized void stopScan() { @Override protected void startBackgroundDiscovery() { - logger.debug("Niko Home Control: Start background bridge discovery"); + logger.debug("Start background bridge discovery"); ScheduledFuture job = nhcDiscoveryJob; if (job == null || job.isCancelled()) { nhcDiscoveryJob = scheduler.scheduleWithFixedDelay(this::discoverBridge, 0, REFRESH_INTERVAL, @@ -125,7 +125,7 @@ protected void startBackgroundDiscovery() { @Override protected void stopBackgroundDiscovery() { - logger.debug("Niko Home Control: Stop bridge background discovery"); + logger.debug("Stop bridge background discovery"); ScheduledFuture job = nhcDiscoveryJob; if (job != null && !job.isCancelled()) { job.cancel(true); diff --git a/bundles/org.openhab.binding.nikohomecontrol/src/main/java/org/openhab/binding/nikohomecontrol/internal/discovery/NikoHomeControlDiscoveryService.java b/bundles/org.openhab.binding.nikohomecontrol/src/main/java/org/openhab/binding/nikohomecontrol/internal/discovery/NikoHomeControlDiscoveryService.java index 838b49f2d4920..63e847068dc5d 100644 --- a/bundles/org.openhab.binding.nikohomecontrol/src/main/java/org/openhab/binding/nikohomecontrol/internal/discovery/NikoHomeControlDiscoveryService.java +++ b/bundles/org.openhab.binding.nikohomecontrol/src/main/java/org/openhab/binding/nikohomecontrol/internal/discovery/NikoHomeControlDiscoveryService.java @@ -48,7 +48,7 @@ public class NikoHomeControlDiscoveryService extends AbstractDiscoveryService { public NikoHomeControlDiscoveryService(NikoHomeControlBridgeHandler handler) { super(SUPPORTED_THING_TYPES_UIDS, TIMEOUT, false); - logger.debug("Niko Home Control: discovery service {}", handler); + logger.debug("discovery service {}", handler); bridgeUID = handler.getThing().getUID(); this.handler = handler; } @@ -70,10 +70,10 @@ public void discoverDevices() { NikoHomeControlCommunication nhcComm = handler.getCommunication(); if ((nhcComm == null) || !nhcComm.communicationActive()) { - logger.warn("Niko Home Control: not connected."); + logger.warn("not connected"); return; } - logger.debug("Niko Home Control: getting devices on {}", handler.getThing().getUID().getId()); + logger.debug("getting devices on {}", handler.getThing().getUID().getId()); Map actions = nhcComm.getActions(); @@ -99,8 +99,7 @@ public void discoverDevices() { thingName, thingLocation); break; default: - logger.debug("Niko Home Control: unrecognized action type {} for {} {}", nhcAction.getType(), - actionId, thingName); + logger.debug("unrecognized action type {} for {} {}", nhcAction.getType(), actionId, thingName); } }); diff --git a/bundles/org.openhab.binding.nikohomecontrol/src/main/java/org/openhab/binding/nikohomecontrol/internal/handler/NhcJwtToken2.java b/bundles/org.openhab.binding.nikohomecontrol/src/main/java/org/openhab/binding/nikohomecontrol/internal/handler/NhcJwtToken2.java index abab7e2036730..38ebff218e410 100644 --- a/bundles/org.openhab.binding.nikohomecontrol/src/main/java/org/openhab/binding/nikohomecontrol/internal/handler/NhcJwtToken2.java +++ b/bundles/org.openhab.binding.nikohomecontrol/src/main/java/org/openhab/binding/nikohomecontrol/internal/handler/NhcJwtToken2.java @@ -14,17 +14,20 @@ import java.util.List; +import org.eclipse.jdt.annotation.NonNullByDefault; + /** * {@link NhcJwtToken2} represents the Niko Home Control II hobby API token payload. * * @author Mark Herwege - Initial Contribution */ +@NonNullByDefault class NhcJwtToken2 { - String sub; - String iat; - String exp; - String aud; - String iss; - String jti; - List role; + String sub = ""; + String iat = ""; + String exp = ""; + String aud = ""; + String iss = ""; + String jti = ""; + List role = List.of(); } diff --git a/bundles/org.openhab.binding.nikohomecontrol/src/main/java/org/openhab/binding/nikohomecontrol/internal/handler/NikoHomeControlActionBlindConfig.java b/bundles/org.openhab.binding.nikohomecontrol/src/main/java/org/openhab/binding/nikohomecontrol/internal/handler/NikoHomeControlActionBlindConfig.java new file mode 100644 index 0000000000000..6423c31b030fd --- /dev/null +++ b/bundles/org.openhab.binding.nikohomecontrol/src/main/java/org/openhab/binding/nikohomecontrol/internal/handler/NikoHomeControlActionBlindConfig.java @@ -0,0 +1,25 @@ +/** + * Copyright (c) 2010-2021 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.nikohomecontrol.internal.handler; + +import org.eclipse.jdt.annotation.NonNullByDefault; + +/** + * {@link NikoHomeControlActionBlindConfig} is the config class for Niko Home Control Blind Actions. + * + * @author Mark Herwege - Initial Contribution + */ +@NonNullByDefault +public class NikoHomeControlActionBlindConfig extends NikoHomeControlActionConfig { + public boolean invert; +} diff --git a/bundles/org.openhab.binding.nikohomecontrol/src/main/java/org/openhab/binding/nikohomecontrol/internal/handler/NikoHomeControlActionConfig.java b/bundles/org.openhab.binding.nikohomecontrol/src/main/java/org/openhab/binding/nikohomecontrol/internal/handler/NikoHomeControlActionConfig.java index 2cd963c169055..39c0a3a74d344 100644 --- a/bundles/org.openhab.binding.nikohomecontrol/src/main/java/org/openhab/binding/nikohomecontrol/internal/handler/NikoHomeControlActionConfig.java +++ b/bundles/org.openhab.binding.nikohomecontrol/src/main/java/org/openhab/binding/nikohomecontrol/internal/handler/NikoHomeControlActionConfig.java @@ -12,11 +12,14 @@ */ package org.openhab.binding.nikohomecontrol.internal.handler; +import org.eclipse.jdt.annotation.NonNullByDefault; + /** * {@link NikoHomeControlActionConfig} is the general config class for Niko Home Control Actions. * * @author Mark Herwege - Initial Contribution */ +@NonNullByDefault public class NikoHomeControlActionConfig { - public String actionId; + public String actionId = ""; } diff --git a/bundles/org.openhab.binding.nikohomecontrol/src/main/java/org/openhab/binding/nikohomecontrol/internal/handler/NikoHomeControlActionDimmerConfig.java b/bundles/org.openhab.binding.nikohomecontrol/src/main/java/org/openhab/binding/nikohomecontrol/internal/handler/NikoHomeControlActionDimmerConfig.java index 2a5d52f32633c..712c09b42c30c 100644 --- a/bundles/org.openhab.binding.nikohomecontrol/src/main/java/org/openhab/binding/nikohomecontrol/internal/handler/NikoHomeControlActionDimmerConfig.java +++ b/bundles/org.openhab.binding.nikohomecontrol/src/main/java/org/openhab/binding/nikohomecontrol/internal/handler/NikoHomeControlActionDimmerConfig.java @@ -12,11 +12,14 @@ */ package org.openhab.binding.nikohomecontrol.internal.handler; +import org.eclipse.jdt.annotation.NonNullByDefault; + /** * {@link NikoHomeControlActionDimmerConfig} is the config class for Niko Home Control Dimmer Actions. * * @author Mark Herwege - Initial Contribution */ +@NonNullByDefault public class NikoHomeControlActionDimmerConfig extends NikoHomeControlActionConfig { - public int step; + public int step = 10; } diff --git a/bundles/org.openhab.binding.nikohomecontrol/src/main/java/org/openhab/binding/nikohomecontrol/internal/handler/NikoHomeControlActionHandler.java b/bundles/org.openhab.binding.nikohomecontrol/src/main/java/org/openhab/binding/nikohomecontrol/internal/handler/NikoHomeControlActionHandler.java index 4b39852d2ba5d..3cd76e9da2d16 100644 --- a/bundles/org.openhab.binding.nikohomecontrol/src/main/java/org/openhab/binding/nikohomecontrol/internal/handler/NikoHomeControlActionHandler.java +++ b/bundles/org.openhab.binding.nikohomecontrol/src/main/java/org/openhab/binding/nikohomecontrol/internal/handler/NikoHomeControlActionHandler.java @@ -52,10 +52,11 @@ public class NikoHomeControlActionHandler extends BaseThingHandler implements Nh private final Logger logger = LoggerFactory.getLogger(NikoHomeControlActionHandler.class); - private volatile @NonNullByDefault({}) NhcAction nhcAction; + private volatile @Nullable NhcAction nhcAction; private String actionId = ""; private int stepValue; + private boolean invert; public NikoHomeControlActionHandler(Thing thing) { super(thing); @@ -66,8 +67,7 @@ public void handleCommand(ChannelUID channelUID, Command command) { NikoHomeControlCommunication nhcComm = getCommunication(); if (nhcComm == null) { updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, - "Niko Home Control: bridge communication not initialized when trying to execute action command " - + actionId); + "Bridge communication not initialized when trying to execute action command " + actionId); return; } @@ -84,7 +84,13 @@ public void handleCommand(ChannelUID channelUID, Command command) { } private void handleCommandSelection(ChannelUID channelUID, Command command) { - logger.debug("Niko Home Control: handle command {} for {}", command, channelUID); + NhcAction nhcAction = this.nhcAction; + if (nhcAction == null) { + logger.debug("action with ID {} not initialized", actionId); + return; + } + + logger.debug("handle command {} for {}", command, channelUID); if (command == REFRESH) { actionEvent(nhcAction.getState()); @@ -109,11 +115,17 @@ private void handleCommandSelection(ChannelUID channelUID, Command command) { default: updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, - "Niko Home Control: channel unknown " + channelUID.getId()); + "Channel unknown " + channelUID.getId()); } } private void handleSwitchCommand(Command command) { + NhcAction nhcAction = this.nhcAction; + if (nhcAction == null) { + logger.debug("action with ID {} not initialized", actionId); + return; + } + if (command instanceof OnOffType) { OnOffType s = (OnOffType) command; if (OnOffType.OFF.equals(s)) { @@ -125,6 +137,12 @@ private void handleSwitchCommand(Command command) { } private void handleBrightnessCommand(Command command) { + NhcAction nhcAction = this.nhcAction; + if (nhcAction == null) { + logger.debug("action with ID {} not initialized", actionId); + return; + } + if (command instanceof OnOffType) { OnOffType s = (OnOffType) command; if (OnOffType.OFF.equals(s)) { @@ -162,18 +180,24 @@ private void handleBrightnessCommand(Command command) { } private void handleRollershutterCommand(Command command) { + NhcAction nhcAction = this.nhcAction; + if (nhcAction == null) { + logger.debug("action with ID {} not initialized", actionId); + return; + } + if (command instanceof UpDownType) { UpDownType s = (UpDownType) command; if (UpDownType.UP.equals(s)) { - nhcAction.execute(NHCUP); + nhcAction.execute(!invert ? NHCUP : NHCDOWN); } else { - nhcAction.execute(NHCDOWN); + nhcAction.execute(!invert ? NHCDOWN : NHCUP); } } else if (command instanceof StopMoveType) { nhcAction.execute(NHCSTOP); } else if (command instanceof PercentType) { PercentType p = (PercentType) command; - nhcAction.execute(Integer.toString(100 - p.intValue())); + nhcAction.execute(!invert ? Integer.toString(100 - p.intValue()) : Integer.toString(p.intValue())); } } @@ -183,6 +207,9 @@ public void initialize() { if (thing.getThingTypeUID().equals(THING_TYPE_DIMMABLE_LIGHT)) { config = getConfig().as(NikoHomeControlActionDimmerConfig.class); stepValue = ((NikoHomeControlActionDimmerConfig) config).step; + } else if (thing.getThingTypeUID().equals(THING_TYPE_BLIND)) { + config = getConfig().as(NikoHomeControlActionBlindConfig.class); + invert = ((NikoHomeControlActionBlindConfig) config).invert; } else { config = getConfig().as(NikoHomeControlActionConfig.class); } @@ -190,6 +217,8 @@ public void initialize() { NikoHomeControlCommunication nhcComm = getCommunication(); if (nhcComm == null) { + updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, + "Connection with controller not started yet, could not initialize action " + actionId); return; } @@ -197,15 +226,14 @@ public void initialize() { scheduler.submit(() -> { if (!nhcComm.communicationActive()) { updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, - "Niko Home Control: no connection with Niko Home Control, could not initialize action " - + actionId); + "No connection with controller, could not initialize action " + actionId); return; } - nhcAction = nhcComm.getActions().get(actionId); + NhcAction nhcAction = nhcComm.getActions().get(actionId); if (nhcAction == null) { updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, - "Niko Home Control: actionId does not match an action in the controller " + actionId); + "Action " + actionId + " does not match an action in the controller"); return; } @@ -220,7 +248,9 @@ public void initialize() { actionEvent(nhcAction.getState()); - logger.debug("Niko Home Control: action initialized {}", actionId); + this.nhcAction = nhcAction; + + logger.debug("action initialized {}", actionId); Bridge bridge = getBridge(); if ((bridge != null) && (bridge.getStatus() == ThingStatus.ONLINE)) { @@ -232,6 +262,12 @@ public void initialize() { } private void updateProperties() { + NhcAction nhcAction = this.nhcAction; + if (nhcAction == null) { + logger.debug("action with ID {} not initialized", actionId); + return; + } + Map properties = new HashMap<>(); properties.put("type", String.valueOf(nhcAction.getType())); if (getThing().getThingTypeUID() == THING_TYPE_BLIND) { @@ -250,6 +286,12 @@ private void updateProperties() { @Override public void actionEvent(int actionState) { + NhcAction nhcAction = this.nhcAction; + if (nhcAction == null) { + logger.debug("action with ID {} not initialized", actionId); + return; + } + ActionType actionType = nhcAction.getType(); switch (actionType) { @@ -265,19 +307,28 @@ public void actionEvent(int actionState) { updateStatus(ThingStatus.ONLINE); break; case ROLLERSHUTTER: - updateState(CHANNEL_ROLLERSHUTTER, new PercentType(actionState)); + updateState(CHANNEL_ROLLERSHUTTER, + !invert ? new PercentType(100 - actionState) : new PercentType(actionState)); updateStatus(ThingStatus.ONLINE); break; default: updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, - "Niko Home Control: unknown action type " + actionType); + "Unknown action type " + actionType); + } + } + + @Override + public void actionInitialized() { + Bridge bridge = getBridge(); + if ((bridge != null) && (bridge.getStatus() == ThingStatus.ONLINE)) { + updateStatus(ThingStatus.ONLINE); } } @Override public void actionRemoved() { updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, - "Niko Home Control: action has been removed from the controller " + actionId); + "Action " + actionId + " has been removed from the controller"); } private void restartCommunication(NikoHomeControlCommunication nhcComm) { @@ -286,8 +337,7 @@ private void restartCommunication(NikoHomeControlCommunication nhcComm) { nhcComm.restartCommunication(); // If still not active, take thing offline and return. if (!nhcComm.communicationActive()) { - updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, - "Niko Home Control: communication socket error"); + updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, "Communication error"); return; } // Also put the bridge back online @@ -301,7 +351,7 @@ private void restartCommunication(NikoHomeControlCommunication nhcComm) { NikoHomeControlBridgeHandler nhcBridgeHandler = getBridgeHandler(); if (nhcBridgeHandler == null) { updateStatus(ThingStatus.UNINITIALIZED, ThingStatusDetail.BRIDGE_UNINITIALIZED, - "Niko Home Control: no bridge initialized for action " + actionId); + "No bridge initialized for action " + actionId); return null; } NikoHomeControlCommunication nhcComm = nhcBridgeHandler.getCommunication(); @@ -312,7 +362,7 @@ private void restartCommunication(NikoHomeControlCommunication nhcComm) { Bridge nhcBridge = getBridge(); if (nhcBridge == null) { updateStatus(ThingStatus.UNINITIALIZED, ThingStatusDetail.BRIDGE_UNINITIALIZED, - "Niko Home Control: no bridge initialized for action " + actionId); + "No bridge initialized for action " + actionId); return null; } NikoHomeControlBridgeHandler nhcBridgeHandler = (NikoHomeControlBridgeHandler) nhcBridge.getHandler(); diff --git a/bundles/org.openhab.binding.nikohomecontrol/src/main/java/org/openhab/binding/nikohomecontrol/internal/handler/NikoHomeControlBridgeConfig.java b/bundles/org.openhab.binding.nikohomecontrol/src/main/java/org/openhab/binding/nikohomecontrol/internal/handler/NikoHomeControlBridgeConfig.java index 614cd0ac4ec41..979ad3d8d421d 100644 --- a/bundles/org.openhab.binding.nikohomecontrol/src/main/java/org/openhab/binding/nikohomecontrol/internal/handler/NikoHomeControlBridgeConfig.java +++ b/bundles/org.openhab.binding.nikohomecontrol/src/main/java/org/openhab/binding/nikohomecontrol/internal/handler/NikoHomeControlBridgeConfig.java @@ -12,13 +12,16 @@ */ package org.openhab.binding.nikohomecontrol.internal.handler; +import org.eclipse.jdt.annotation.NonNullByDefault; + /** * {@link NikoHomeControlBridgeConfig} is the general config class for Niko Home Control Bridges. * * @author Mark Herwege - Initial Contribution */ +@NonNullByDefault public class NikoHomeControlBridgeConfig { - public String addr; + public String addr = ""; public int port; public int refresh; } diff --git a/bundles/org.openhab.binding.nikohomecontrol/src/main/java/org/openhab/binding/nikohomecontrol/internal/handler/NikoHomeControlBridgeConfig2.java b/bundles/org.openhab.binding.nikohomecontrol/src/main/java/org/openhab/binding/nikohomecontrol/internal/handler/NikoHomeControlBridgeConfig2.java index 5d1d3eb17c193..384d869ef4181 100644 --- a/bundles/org.openhab.binding.nikohomecontrol/src/main/java/org/openhab/binding/nikohomecontrol/internal/handler/NikoHomeControlBridgeConfig2.java +++ b/bundles/org.openhab.binding.nikohomecontrol/src/main/java/org/openhab/binding/nikohomecontrol/internal/handler/NikoHomeControlBridgeConfig2.java @@ -12,12 +12,15 @@ */ package org.openhab.binding.nikohomecontrol.internal.handler; +import org.eclipse.jdt.annotation.NonNullByDefault; + /** * {@link NikoHomeControlBridgeConfig2} is the extended config class for Niko Home Control II Bridges. * * @author Mark Herwege - Initial Contribution */ +@NonNullByDefault public class NikoHomeControlBridgeConfig2 extends NikoHomeControlBridgeConfig { - public String profile; - public String password; + public String profile = ""; + public String password = ""; } diff --git a/bundles/org.openhab.binding.nikohomecontrol/src/main/java/org/openhab/binding/nikohomecontrol/internal/handler/NikoHomeControlBridgeHandler.java b/bundles/org.openhab.binding.nikohomecontrol/src/main/java/org/openhab/binding/nikohomecontrol/internal/handler/NikoHomeControlBridgeHandler.java index f120e93d7eb6f..b7e4472ff5071 100644 --- a/bundles/org.openhab.binding.nikohomecontrol/src/main/java/org/openhab/binding/nikohomecontrol/internal/handler/NikoHomeControlBridgeHandler.java +++ b/bundles/org.openhab.binding.nikohomecontrol/src/main/java/org/openhab/binding/nikohomecontrol/internal/handler/NikoHomeControlBridgeHandler.java @@ -95,7 +95,7 @@ protected void startCommunication() { if (discovery != null) { discovery.discoverDevices(); } else { - logger.debug("Niko Home Control: cannot discover devices, discovery service not started"); + logger.debug("cannot discover devices, discovery service not started"); } }); } @@ -117,9 +117,9 @@ private void setupRefreshTimer(int refreshInterval) { } // This timer will restart the bridge connection periodically - logger.debug("Niko Home Control: restart bridge connection every {} min", refreshInterval); + logger.debug("restart bridge connection every {} min", refreshInterval); refreshTimer = scheduler.scheduleWithFixedDelay(() -> { - logger.debug("Niko Home Control: restart communication at scheduled time"); + logger.debug("restart communication at scheduled time"); NikoHomeControlCommunication comm = nhcComm; if (comm != null) { @@ -141,7 +141,7 @@ private void setupRefreshTimer(int refreshInterval) { */ protected void bridgeOffline() { updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.OFFLINE.COMMUNICATION_ERROR, - "Niko Home Control: error starting bridge connection"); + "Error with bridge connection"); } /** @@ -153,8 +153,8 @@ public void bridgeOnline() { } @Override - public void controllerOffline() { - bridgeOffline(); + public void controllerOffline(String message) { + updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.OFFLINE.COMMUNICATION_ERROR, message); } @Override @@ -231,14 +231,14 @@ public void setNhcDiscovery(@Nullable NikoHomeControlDiscoveryService nhcDiscove @Override public void alarmEvent(String alarmText) { - logger.debug("Niko Home Control: triggering alarm channel with {}", alarmText); + logger.debug("triggering alarm channel with {}", alarmText); triggerChannel(CHANNEL_ALARM, alarmText); updateStatus(ThingStatus.ONLINE); } @Override public void noticeEvent(String alarmText) { - logger.debug("Niko Home Control: triggering notice channel with {}", alarmText); + logger.debug("triggering notice channel with {}", alarmText); triggerChannel(CHANNEL_NOTICE, alarmText); updateStatus(ThingStatus.ONLINE); } @@ -263,7 +263,7 @@ public void updatePropertiesEvent() { try { addr = InetAddress.getByName(config.addr); } catch (UnknownHostException e) { - logger.debug("Niko Home Control: Cannot resolve hostname {} to IP adress", config.addr); + logger.debug("Cannot resolve hostname {} to IP adress", config.addr); } return addr; } diff --git a/bundles/org.openhab.binding.nikohomecontrol/src/main/java/org/openhab/binding/nikohomecontrol/internal/handler/NikoHomeControlBridgeHandler1.java b/bundles/org.openhab.binding.nikohomecontrol/src/main/java/org/openhab/binding/nikohomecontrol/internal/handler/NikoHomeControlBridgeHandler1.java index 5f6408db7f317..e0938e1a2cdb1 100644 --- a/bundles/org.openhab.binding.nikohomecontrol/src/main/java/org/openhab/binding/nikohomecontrol/internal/handler/NikoHomeControlBridgeHandler1.java +++ b/bundles/org.openhab.binding.nikohomecontrol/src/main/java/org/openhab/binding/nikohomecontrol/internal/handler/NikoHomeControlBridgeHandler1.java @@ -41,20 +41,20 @@ public NikoHomeControlBridgeHandler1(Bridge nikoHomeControlBridge) { @Override public void initialize() { - logger.debug("Niko Home Control: initializing bridge handler"); + logger.debug("initializing bridge handler"); setConfig(); InetAddress addr = getAddr(); int port = getPort(); - logger.debug("Niko Home Control: bridge handler host {}, port {}", addr, port); + logger.debug("bridge handler host {}, port {}", addr, port); if (addr != null) { nhcComm = new NikoHomeControlCommunication1(this, scheduler); startCommunication(); } else { updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.OFFLINE.COMMUNICATION_ERROR, - "Niko Home Control: cannot resolve bridge IP with hostname " + config.addr); + "Cannot resolve bridge IP with hostname " + config.addr); } } diff --git a/bundles/org.openhab.binding.nikohomecontrol/src/main/java/org/openhab/binding/nikohomecontrol/internal/handler/NikoHomeControlBridgeHandler2.java b/bundles/org.openhab.binding.nikohomecontrol/src/main/java/org/openhab/binding/nikohomecontrol/internal/handler/NikoHomeControlBridgeHandler2.java index 668d7f33a789d..e6687926e96a2 100644 --- a/bundles/org.openhab.binding.nikohomecontrol/src/main/java/org/openhab/binding/nikohomecontrol/internal/handler/NikoHomeControlBridgeHandler2.java +++ b/bundles/org.openhab.binding.nikohomecontrol/src/main/java/org/openhab/binding/nikohomecontrol/internal/handler/NikoHomeControlBridgeHandler2.java @@ -57,7 +57,7 @@ public NikoHomeControlBridgeHandler2(Bridge nikoHomeControlBridge, NetworkAddres @Override public void initialize() { - logger.debug("Niko Home Control: initializing NHC II bridge handler"); + logger.debug("initializing NHC II bridge handler"); setConfig(); @@ -69,15 +69,14 @@ public void initialize() { // advanced configuration, skipping token validation. // This behavior would allow the same logic to be used (with profile UUID) as before token validation // was introduced. - updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.OFFLINE.CONFIGURATION_ERROR, - "Niko Home Control: token is empty"); + updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.OFFLINE.CONFIGURATION_ERROR, "Token is empty"); return; } } else { Date now = new Date(); if (expiryDate.before(now)) { updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.OFFLINE.CONFIGURATION_ERROR, - "Niko Home Control: hobby api token has expired"); + "Hobby api token has expired"); return; } } @@ -91,7 +90,7 @@ public void initialize() { } catch (CertificateException e) { // this should not happen unless there is a programming error updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.OFFLINE.COMMUNICATION_ERROR, - "Niko Home Control: not able to set SSL context"); + "Not able to set SSL context"); return; } } @@ -167,9 +166,8 @@ public String getProfile() { @Override public String getToken() { String token = ((NikoHomeControlBridgeConfig2) config).password; - if ((token == null) || token.isEmpty()) { - logger.debug("Niko Home Control: no JWT token set."); - return ""; + if (token.isEmpty()) { + logger.debug("no JWT token set."); } return token; } @@ -192,10 +190,10 @@ public String getToken() { try { jwtToken = gson.fromJson(tokenPayload, NhcJwtToken2.class); } catch (JsonSyntaxException e) { - logger.debug("Niko Home Control: unexpected token payload {}", tokenPayload); + logger.debug("unexpected token payload {}", tokenPayload); } catch (NoSuchElementException ignore) { // Ignore if exp not present in response, this should not happen in token payload response - logger.trace("Niko Home Control: no expiry date found in payload {}", tokenPayload); + logger.trace("no expiry date found in payload {}", tokenPayload); } } @@ -206,20 +204,20 @@ public String getToken() { long epoch = Long.parseLong(expiryEpoch) * 1000; // convert to milliseconds expiryDate = new Date(epoch); } catch (NumberFormatException e) { - logger.debug("Niko Home Control: token expiry not valid {}", jwtToken.exp); + logger.debug("token expiry not valid {}", jwtToken.exp); return null; } Date now = new Date(); if (expiryDate.before(now)) { - logger.warn("Niko Home Control: hobby API token expired, was valid until {}", + logger.warn("hobby API token expired, was valid until {}", DateFormat.getDateInstance().format(expiryDate)); } else { Calendar c = Calendar.getInstance(); c.setTime(expiryDate); c.add(Calendar.DATE, -14); if (c.getTime().before(now)) { - logger.info("Niko Home Control: hobby API token will expire in less than 14 days, valid until {}", + logger.info("hobby API token will expire in less than 14 days, valid until {}", DateFormat.getDateInstance().format(expiryDate)); } } diff --git a/bundles/org.openhab.binding.nikohomecontrol/src/main/java/org/openhab/binding/nikohomecontrol/internal/handler/NikoHomeControlEnergyMeterConfig.java b/bundles/org.openhab.binding.nikohomecontrol/src/main/java/org/openhab/binding/nikohomecontrol/internal/handler/NikoHomeControlEnergyMeterConfig.java index dd61f04278771..2af74b26ca2a9 100644 --- a/bundles/org.openhab.binding.nikohomecontrol/src/main/java/org/openhab/binding/nikohomecontrol/internal/handler/NikoHomeControlEnergyMeterConfig.java +++ b/bundles/org.openhab.binding.nikohomecontrol/src/main/java/org/openhab/binding/nikohomecontrol/internal/handler/NikoHomeControlEnergyMeterConfig.java @@ -12,11 +12,14 @@ */ package org.openhab.binding.nikohomecontrol.internal.handler; +import org.eclipse.jdt.annotation.NonNullByDefault; + /** * {@link NikoHomeControlEnergyMeterConfig} is the config class for Niko Home Control Thermostats. * * @author Mark Herwege - Initial Contribution */ +@NonNullByDefault public class NikoHomeControlEnergyMeterConfig { - public String energyMeterId; + public String energyMeterId = ""; } diff --git a/bundles/org.openhab.binding.nikohomecontrol/src/main/java/org/openhab/binding/nikohomecontrol/internal/handler/NikoHomeControlEnergyMeterHandler.java b/bundles/org.openhab.binding.nikohomecontrol/src/main/java/org/openhab/binding/nikohomecontrol/internal/handler/NikoHomeControlEnergyMeterHandler.java index 7f22acc7907fa..637f065f5af01 100644 --- a/bundles/org.openhab.binding.nikohomecontrol/src/main/java/org/openhab/binding/nikohomecontrol/internal/handler/NikoHomeControlEnergyMeterHandler.java +++ b/bundles/org.openhab.binding.nikohomecontrol/src/main/java/org/openhab/binding/nikohomecontrol/internal/handler/NikoHomeControlEnergyMeterHandler.java @@ -48,7 +48,7 @@ public class NikoHomeControlEnergyMeterHandler extends BaseThingHandler implemen private final Logger logger = LoggerFactory.getLogger(NikoHomeControlEnergyMeterHandler.class); - private volatile @NonNullByDefault({}) NhcEnergyMeter nhcEnergyMeter; + private volatile @Nullable NhcEnergyMeter nhcEnergyMeter; private String energyMeterId = ""; @@ -58,6 +58,12 @@ public NikoHomeControlEnergyMeterHandler(Thing thing) { @Override public void handleCommand(ChannelUID channelUID, Command command) { + NhcEnergyMeter nhcEnergyMeter = this.nhcEnergyMeter; + if (nhcEnergyMeter == null) { + logger.debug("energy meter with ID {} not initialized", energyMeterId); + return; + } + if (command == REFRESH) { energyMeterEvent(nhcEnergyMeter.getPower()); } @@ -71,6 +77,8 @@ public void initialize() { NikoHomeControlCommunication nhcComm = getCommunication(); if (nhcComm == null) { + updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, + "Connection with controller not started yet, could not initialize energy meter " + energyMeterId); return; } @@ -79,16 +87,14 @@ public void initialize() { scheduler.submit(() -> { if (!nhcComm.communicationActive()) { updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, - "Niko Home Control: no connection with Niko Home Control, could not initialize energy meter " - + energyMeterId); + "No connection with controller, could not initialize energy meter " + energyMeterId); return; } - nhcEnergyMeter = nhcComm.getEnergyMeters().get(energyMeterId); + NhcEnergyMeter nhcEnergyMeter = nhcComm.getEnergyMeters().get(energyMeterId); if (nhcEnergyMeter == null) { updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, - "Niko Home Control: energyMeterId does not match a energy meter in the controller " - + energyMeterId); + "Energy meter " + energyMeterId + " does not match a energy meter in the controller"); return; } @@ -102,7 +108,9 @@ public void initialize() { nhcComm.startEnergyMeter(energyMeterId); } - logger.debug("Niko Home Control: energy meter intialized {}", energyMeterId); + this.nhcEnergyMeter = nhcEnergyMeter; + + logger.debug("energy meter intialized {}", energyMeterId); Bridge bridge = getBridge(); if ((bridge != null) && (bridge.getStatus() == ThingStatus.ONLINE)) { @@ -144,10 +152,18 @@ public void energyMeterEvent(@Nullable Integer power) { updateStatus(ThingStatus.ONLINE); } + @Override + public void energyMeterInitialized() { + Bridge bridge = getBridge(); + if ((bridge != null) && (bridge.getStatus() == ThingStatus.ONLINE)) { + updateStatus(ThingStatus.ONLINE); + } + } + @Override public void energyMeterRemoved() { updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, - "Niko Home Control: energy meter has been removed from the controller " + energyMeterId); + "Energy meter " + energyMeterId + " has been removed from the controller"); } @Override @@ -157,8 +173,7 @@ public void channelLinked(ChannelUID channelUID) { NikoHomeControlCommunication nhcComm = getCommunication(); if (nhcComm == null) { updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, - "Niko Home Control: bridge communication not initialized when trying to start energy meter " - + energyMeterId); + "Bridge communication not initialized when trying to start energy meter " + energyMeterId); return; } @@ -180,8 +195,7 @@ public void channelUnlinked(ChannelUID channelUID) { NikoHomeControlCommunication nhcComm = getCommunication(); if (nhcComm == null) { updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, - "Niko Home Control: bridge communication not initialized when trying to stop energy meter " - + energyMeterId); + "Bridge communication not initialized when trying to stop energy meter " + energyMeterId); return; } @@ -206,8 +220,7 @@ private void restartCommunication(NikoHomeControlCommunication nhcComm) { nhcComm.restartCommunication(); // If still not active, take thing offline and return. if (!nhcComm.communicationActive()) { - updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, - "Niko Home Control: communication socket error"); + updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, "Communication error"); return; } // Also put the bridge back online @@ -221,7 +234,7 @@ private void restartCommunication(NikoHomeControlCommunication nhcComm) { NikoHomeControlBridgeHandler nhcBridgeHandler = getBridgeHandler(); if (nhcBridgeHandler == null) { updateStatus(ThingStatus.UNINITIALIZED, ThingStatusDetail.BRIDGE_UNINITIALIZED, - "Niko Home Control: no bridge initialized for energy meter " + energyMeterId); + "No bridge initialized for energy meter " + energyMeterId); return null; } NikoHomeControlCommunication nhcComm = nhcBridgeHandler.getCommunication(); @@ -232,7 +245,7 @@ private void restartCommunication(NikoHomeControlCommunication nhcComm) { Bridge nhcBridge = getBridge(); if (nhcBridge == null) { updateStatus(ThingStatus.UNINITIALIZED, ThingStatusDetail.BRIDGE_UNINITIALIZED, - "Niko Home Control: no bridge initialized for energy meter " + energyMeterId); + "No bridge initialized for energy meter " + energyMeterId); return null; } NikoHomeControlBridgeHandler nhcBridgeHandler = (NikoHomeControlBridgeHandler) nhcBridge.getHandler(); diff --git a/bundles/org.openhab.binding.nikohomecontrol/src/main/java/org/openhab/binding/nikohomecontrol/internal/handler/NikoHomeControlThermostatConfig.java b/bundles/org.openhab.binding.nikohomecontrol/src/main/java/org/openhab/binding/nikohomecontrol/internal/handler/NikoHomeControlThermostatConfig.java index d6cf3a453b92f..725a8e0ed9136 100644 --- a/bundles/org.openhab.binding.nikohomecontrol/src/main/java/org/openhab/binding/nikohomecontrol/internal/handler/NikoHomeControlThermostatConfig.java +++ b/bundles/org.openhab.binding.nikohomecontrol/src/main/java/org/openhab/binding/nikohomecontrol/internal/handler/NikoHomeControlThermostatConfig.java @@ -12,12 +12,15 @@ */ package org.openhab.binding.nikohomecontrol.internal.handler; +import org.eclipse.jdt.annotation.NonNullByDefault; + /** * {@link NikoHomeControlThermostatConfig} is the config class for Niko Home Control Thermostats. * * @author Mark Herwege - Initial Contribution */ +@NonNullByDefault public class NikoHomeControlThermostatConfig { - public String thermostatId; - public int overruleTime; + public String thermostatId = ""; + public int overruleTime = 60; } diff --git a/bundles/org.openhab.binding.nikohomecontrol/src/main/java/org/openhab/binding/nikohomecontrol/internal/handler/NikoHomeControlThermostatHandler.java b/bundles/org.openhab.binding.nikohomecontrol/src/main/java/org/openhab/binding/nikohomecontrol/internal/handler/NikoHomeControlThermostatHandler.java index 12da67d0e763b..d852f07ade143 100644 --- a/bundles/org.openhab.binding.nikohomecontrol/src/main/java/org/openhab/binding/nikohomecontrol/internal/handler/NikoHomeControlThermostatHandler.java +++ b/bundles/org.openhab.binding.nikohomecontrol/src/main/java/org/openhab/binding/nikohomecontrol/internal/handler/NikoHomeControlThermostatHandler.java @@ -52,7 +52,7 @@ public class NikoHomeControlThermostatHandler extends BaseThingHandler implement private final Logger logger = LoggerFactory.getLogger(NikoHomeControlThermostatHandler.class); - private volatile @NonNullByDefault({}) NhcThermostat nhcThermostat; + private volatile @Nullable NhcThermostat nhcThermostat; private String thermostatId = ""; private int overruleTime; @@ -69,7 +69,7 @@ public void handleCommand(ChannelUID channelUID, Command command) { NikoHomeControlCommunication nhcComm = getCommunication(); if (nhcComm == null) { updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, - "Niko Home Control: bridge communication not initialized when trying to execute thermostat command " + "Bridge communication not initialized when trying to execute thermostat command on " + thermostatId); return; } @@ -87,7 +87,13 @@ public void handleCommand(ChannelUID channelUID, Command command) { } private void handleCommandSelection(ChannelUID channelUID, Command command) { - logger.debug("Niko Home Control: handle command {} for {}", command, channelUID); + NhcThermostat nhcThermostat = this.nhcThermostat; + if (nhcThermostat == null) { + logger.debug("thermostat with ID {} not initialized", thermostatId); + return; + } + + logger.debug("handle command {} for {}", command, channelUID); if (REFRESH.equals(command)) { thermostatEvent(nhcThermostat.getMeasured(), nhcThermostat.getSetpoint(), nhcThermostat.getMode(), @@ -140,7 +146,7 @@ private void handleCommandSelection(ChannelUID channelUID, Command command) { default: updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, - "Niko Home Control: channel unknown " + channelUID.getId()); + "Channel unknown " + channelUID.getId()); } } @@ -153,6 +159,8 @@ public void initialize() { NikoHomeControlCommunication nhcComm = getCommunication(); if (nhcComm == null) { + updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, + "Connection with controller not started yet, could not initialize thermostat " + thermostatId); return; } @@ -161,16 +169,14 @@ public void initialize() { scheduler.submit(() -> { if (!nhcComm.communicationActive()) { updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, - "Niko Home Control: no connection with Niko Home Control, could not initialize thermostat " - + thermostatId); + "No connection with controller, could not initialize thermostat " + thermostatId); return; } - nhcThermostat = nhcComm.getThermostats().get(thermostatId); + NhcThermostat nhcThermostat = nhcComm.getThermostats().get(thermostatId); if (nhcThermostat == null) { updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, - "Niko Home Control: thermostatId does not match a thermostat in the controller " - + thermostatId); + "Thermostat " + thermostatId + " does not match a thermostat in the controller"); return; } @@ -186,7 +192,9 @@ public void initialize() { thermostatEvent(nhcThermostat.getMeasured(), nhcThermostat.getSetpoint(), nhcThermostat.getMode(), nhcThermostat.getOverrule(), nhcThermostat.getDemand()); - logger.debug("Niko Home Control: thermostat intialized {}", thermostatId); + this.nhcThermostat = nhcThermostat; + + logger.debug("thermostat intialized {}", thermostatId); Bridge bridge = getBridge(); if ((bridge != null) && (bridge.getStatus() == ThingStatus.ONLINE)) { @@ -211,6 +219,12 @@ private void updateProperties() { @Override public void thermostatEvent(int measured, int setpoint, int mode, int overrule, int demand) { + NhcThermostat nhcThermostat = this.nhcThermostat; + if (nhcThermostat == null) { + logger.debug("thermostat with ID {} not initialized", thermostatId); + return; + } + updateState(CHANNEL_MEASURED, new QuantityType<>(nhcThermostat.getMeasured() / 10.0, CELSIUS)); int overruletime = nhcThermostat.getRemainingOverruletime(); @@ -263,10 +277,18 @@ private void cancelRefreshTimer() { refreshTimer = null; } + @Override + public void thermostatInitialized() { + Bridge bridge = getBridge(); + if ((bridge != null) && (bridge.getStatus() == ThingStatus.ONLINE)) { + updateStatus(ThingStatus.ONLINE); + } + } + @Override public void thermostatRemoved() { updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, - "Niko Home Control: thermostat has been removed from the controller " + thermostatId); + "Thermostat " + thermostatId + " has been removed from the controller"); } private void restartCommunication(NikoHomeControlCommunication nhcComm) { @@ -275,8 +297,7 @@ private void restartCommunication(NikoHomeControlCommunication nhcComm) { nhcComm.restartCommunication(); // If still not active, take thing offline and return. if (!nhcComm.communicationActive()) { - updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, - "Niko Home Control: communication socket error"); + updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, "Communication error"); return; } // Also put the bridge back online @@ -290,7 +311,7 @@ private void restartCommunication(NikoHomeControlCommunication nhcComm) { NikoHomeControlBridgeHandler nhcBridgeHandler = getBridgeHandler(); if (nhcBridgeHandler == null) { updateStatus(ThingStatus.UNINITIALIZED, ThingStatusDetail.BRIDGE_UNINITIALIZED, - "Niko Home Control: no bridge initialized for thermostat " + thermostatId); + "No bridge initialized for thermostat " + thermostatId); return null; } NikoHomeControlCommunication nhcComm = nhcBridgeHandler.getCommunication(); @@ -301,7 +322,7 @@ private void restartCommunication(NikoHomeControlCommunication nhcComm) { Bridge nhcBridge = getBridge(); if (nhcBridge == null) { updateStatus(ThingStatus.UNINITIALIZED, ThingStatusDetail.BRIDGE_UNINITIALIZED, - "Niko Home Control: no bridge initialized for thermostat " + thermostatId); + "No bridge initialized for thermostat " + thermostatId); return null; } NikoHomeControlBridgeHandler nhcBridgeHandler = (NikoHomeControlBridgeHandler) nhcBridge.getHandler(); diff --git a/bundles/org.openhab.binding.nikohomecontrol/src/main/java/org/openhab/binding/nikohomecontrol/internal/protocol/NhcAction.java b/bundles/org.openhab.binding.nikohomecontrol/src/main/java/org/openhab/binding/nikohomecontrol/internal/protocol/NhcAction.java index 8425d7588fe49..dbe5d5a3471f8 100644 --- a/bundles/org.openhab.binding.nikohomecontrol/src/main/java/org/openhab/binding/nikohomecontrol/internal/protocol/NhcAction.java +++ b/bundles/org.openhab.binding.nikohomecontrol/src/main/java/org/openhab/binding/nikohomecontrol/internal/protocol/NhcAction.java @@ -158,7 +158,7 @@ protected void updateState() { protected void updateState(int state) { NhcActionEvent eventHandler = this.eventHandler; if (eventHandler != null) { - logger.debug("Niko Home Control: update channel state for {} with {}", id, state); + logger.debug("update channel state for {} with {}", id, state); eventHandler.actionEvent(state); } } @@ -167,7 +167,7 @@ protected void updateState(int state) { * Method called when action is removed from the Niko Home Control Controller. */ public void actionRemoved() { - logger.warn("Niko Home Control: action removed {}, {}", id, name); + logger.debug("action removed {}, {}", id, name); NhcActionEvent eventHandler = this.eventHandler; if (eventHandler != null) { eventHandler.actionRemoved(); diff --git a/bundles/org.openhab.binding.nikohomecontrol/src/main/java/org/openhab/binding/nikohomecontrol/internal/protocol/NhcActionEvent.java b/bundles/org.openhab.binding.nikohomecontrol/src/main/java/org/openhab/binding/nikohomecontrol/internal/protocol/NhcActionEvent.java index 4f8495c8e3234..a58b1f422e16a 100644 --- a/bundles/org.openhab.binding.nikohomecontrol/src/main/java/org/openhab/binding/nikohomecontrol/internal/protocol/NhcActionEvent.java +++ b/bundles/org.openhab.binding.nikohomecontrol/src/main/java/org/openhab/binding/nikohomecontrol/internal/protocol/NhcActionEvent.java @@ -32,6 +32,12 @@ public interface NhcActionEvent { */ public void actionEvent(int state); + /** + * Called to indicate the action has been initialized. + * + */ + public void actionInitialized(); + /** * Called to indicate the action has been removed from the Niko Home Control controller. * diff --git a/bundles/org.openhab.binding.nikohomecontrol/src/main/java/org/openhab/binding/nikohomecontrol/internal/protocol/NhcControllerEvent.java b/bundles/org.openhab.binding.nikohomecontrol/src/main/java/org/openhab/binding/nikohomecontrol/internal/protocol/NhcControllerEvent.java index a3eea885a1c0d..ead01aeddaf45 100644 --- a/bundles/org.openhab.binding.nikohomecontrol/src/main/java/org/openhab/binding/nikohomecontrol/internal/protocol/NhcControllerEvent.java +++ b/bundles/org.openhab.binding.nikohomecontrol/src/main/java/org/openhab/binding/nikohomecontrol/internal/protocol/NhcControllerEvent.java @@ -67,8 +67,9 @@ public default String getToken() { /** * Called to indicate the connection with the Niko Home Control Controller is offline. * + * @param message */ - public void controllerOffline(); + public void controllerOffline(String message); /** * Called to indicate the connection with the Niko Home Control Controller is online. diff --git a/bundles/org.openhab.binding.nikohomecontrol/src/main/java/org/openhab/binding/nikohomecontrol/internal/protocol/NhcEnergyMeter.java b/bundles/org.openhab.binding.nikohomecontrol/src/main/java/org/openhab/binding/nikohomecontrol/internal/protocol/NhcEnergyMeter.java index c37daa8741636..c6dad086b0896 100644 --- a/bundles/org.openhab.binding.nikohomecontrol/src/main/java/org/openhab/binding/nikohomecontrol/internal/protocol/NhcEnergyMeter.java +++ b/bundles/org.openhab.binding.nikohomecontrol/src/main/java/org/openhab/binding/nikohomecontrol/internal/protocol/NhcEnergyMeter.java @@ -56,7 +56,7 @@ protected NhcEnergyMeter(String id, String name, NikoHomeControlCommunication nh public void updateState(int power) { NhcEnergyMeterEvent handler = eventHandler; if (handler != null) { - logger.debug("Niko Home Control: update channel for {}", id); + logger.debug("update channel for {}", id); handler.energyMeterEvent(power); } } @@ -65,7 +65,7 @@ public void updateState(int power) { * Method called when energyMeters meter is removed from the Niko Home Control Controller. */ public void energyMeterRemoved() { - logger.warn("Niko Home Control: action removed {}, {}", id, name); + logger.debug("action removed {}, {}", id, name); NhcEnergyMeterEvent eventHandler = this.eventHandler; if (eventHandler != null) { eventHandler.energyMeterRemoved(); @@ -117,7 +117,7 @@ public void setPower(@Nullable Integer power) { this.power = power; NhcEnergyMeterEvent handler = eventHandler; if (handler != null) { - logger.debug("Niko Home Control: update power channel for {} with {}", id, power); + logger.debug("update power channel for {} with {}", id, power); handler.energyMeterEvent(power); } } diff --git a/bundles/org.openhab.binding.nikohomecontrol/src/main/java/org/openhab/binding/nikohomecontrol/internal/protocol/NhcEnergyMeterEvent.java b/bundles/org.openhab.binding.nikohomecontrol/src/main/java/org/openhab/binding/nikohomecontrol/internal/protocol/NhcEnergyMeterEvent.java index 8beae434f1a9c..f7dee8b807273 100644 --- a/bundles/org.openhab.binding.nikohomecontrol/src/main/java/org/openhab/binding/nikohomecontrol/internal/protocol/NhcEnergyMeterEvent.java +++ b/bundles/org.openhab.binding.nikohomecontrol/src/main/java/org/openhab/binding/nikohomecontrol/internal/protocol/NhcEnergyMeterEvent.java @@ -28,14 +28,20 @@ public interface NhcEnergyMeterEvent { /** - * This method is called when an energyMeters meter event is received from the Niko Home Control controller. + * This method is called when an energyMeter event is received from the Niko Home Control controller. * * @param power current power consumption/production in W (positive for consumption), null for an empty reading */ public void energyMeterEvent(@Nullable Integer power); /** - * Called to indicate the energyMeters meter has been removed from the Niko Home Control controller. + * Called to indicate the energyMeter has been initialized. + * + */ + public void energyMeterInitialized(); + + /** + * Called to indicate the energyMeter has been removed from the Niko Home Control controller. * */ public void energyMeterRemoved(); diff --git a/bundles/org.openhab.binding.nikohomecontrol/src/main/java/org/openhab/binding/nikohomecontrol/internal/protocol/NhcThermostat.java b/bundles/org.openhab.binding.nikohomecontrol/src/main/java/org/openhab/binding/nikohomecontrol/internal/protocol/NhcThermostat.java index c25124f8895ea..0d4d5a866f6ac 100644 --- a/bundles/org.openhab.binding.nikohomecontrol/src/main/java/org/openhab/binding/nikohomecontrol/internal/protocol/NhcThermostat.java +++ b/bundles/org.openhab.binding.nikohomecontrol/src/main/java/org/openhab/binding/nikohomecontrol/internal/protocol/NhcThermostat.java @@ -115,7 +115,7 @@ public void updateState(int mode) { * Method called when thermostat is removed from the Niko Home Control Controller. */ public void thermostatRemoved() { - logger.warn("Niko Home Control: action removed {}, {}", id, name); + logger.debug("action removed {}, {}", id, name); NhcThermostatEvent eventHandler = this.eventHandler; if (eventHandler != null) { eventHandler.thermostatRemoved(); @@ -125,7 +125,7 @@ public void thermostatRemoved() { private void updateChannels() { NhcThermostatEvent handler = eventHandler; if (handler != null) { - logger.debug("Niko Home Control: update channels for {}", id); + logger.debug("update channels for {}", id); handler.thermostatEvent(measured, setpoint, mode, overrule, demand); } } diff --git a/bundles/org.openhab.binding.nikohomecontrol/src/main/java/org/openhab/binding/nikohomecontrol/internal/protocol/NhcThermostatEvent.java b/bundles/org.openhab.binding.nikohomecontrol/src/main/java/org/openhab/binding/nikohomecontrol/internal/protocol/NhcThermostatEvent.java index 6eb1fa5e69460..7bdf1c803d1cc 100644 --- a/bundles/org.openhab.binding.nikohomecontrol/src/main/java/org/openhab/binding/nikohomecontrol/internal/protocol/NhcThermostatEvent.java +++ b/bundles/org.openhab.binding.nikohomecontrol/src/main/java/org/openhab/binding/nikohomecontrol/internal/protocol/NhcThermostatEvent.java @@ -37,6 +37,12 @@ public interface NhcThermostatEvent { */ public void thermostatEvent(int measured, int setpoint, int mode, int overrule, int demand); + /** + * Called to indicate the thermostat has been initialized. + * + */ + public void thermostatInitialized(); + /** * Called to indicate the thermostat has been removed from the Niko Home Control controller. * diff --git a/bundles/org.openhab.binding.nikohomecontrol/src/main/java/org/openhab/binding/nikohomecontrol/internal/protocol/NikoHomeControlCommunication.java b/bundles/org.openhab.binding.nikohomecontrol/src/main/java/org/openhab/binding/nikohomecontrol/internal/protocol/NikoHomeControlCommunication.java index e2a4f5a221f35..853e9c9cb4f17 100644 --- a/bundles/org.openhab.binding.nikohomecontrol/src/main/java/org/openhab/binding/nikohomecontrol/internal/protocol/NikoHomeControlCommunication.java +++ b/bundles/org.openhab.binding.nikohomecontrol/src/main/java/org/openhab/binding/nikohomecontrol/internal/protocol/NikoHomeControlCommunication.java @@ -65,7 +65,7 @@ protected NikoHomeControlCommunication(NhcControllerEvent handler) { public synchronized void restartCommunication() { stopCommunication(); - logger.debug("Niko Home Control: restart communication from thread {}", Thread.currentThread().getId()); + logger.debug("restart communication from thread {}", Thread.currentThread().getId()); startCommunication(); } diff --git a/bundles/org.openhab.binding.nikohomecontrol/src/main/java/org/openhab/binding/nikohomecontrol/internal/protocol/NikoHomeControlDiscover.java b/bundles/org.openhab.binding.nikohomecontrol/src/main/java/org/openhab/binding/nikohomecontrol/internal/protocol/NikoHomeControlDiscover.java index 29de7bae862a4..708e399929f54 100644 --- a/bundles/org.openhab.binding.nikohomecontrol/src/main/java/org/openhab/binding/nikohomecontrol/internal/protocol/NikoHomeControlDiscover.java +++ b/bundles/org.openhab.binding.nikohomecontrol/src/main/java/org/openhab/binding/nikohomecontrol/internal/protocol/NikoHomeControlDiscover.java @@ -70,7 +70,7 @@ public NikoHomeControlDiscover(String broadcast) throws IOException { datagramSocket.send(discoveryPacket); while (true) { datagramSocket.receive(packet); - logger.trace("Niko Home Control: bridge discovery response {}", + logger.trace("bridge discovery response {}", HexUtils.bytesToHex(Arrays.copyOf(packet.getData(), packet.getLength()))); if (isNhc(packet)) { break; @@ -79,7 +79,7 @@ public NikoHomeControlDiscover(String broadcast) throws IOException { addr = packet.getAddress(); setNhcBridgeId(packet); setIsNhcII(packet); - logger.debug("Niko Home Control: IP address is {}, unique ID is {}", addr, nhcBridgeId); + logger.debug("IP address is {}, unique ID is {}", addr, nhcBridgeId); } } diff --git a/bundles/org.openhab.binding.nikohomecontrol/src/main/java/org/openhab/binding/nikohomecontrol/internal/protocol/nhc1/NhcAction1.java b/bundles/org.openhab.binding.nikohomecontrol/src/main/java/org/openhab/binding/nikohomecontrol/internal/protocol/nhc1/NhcAction1.java index a88c23c47f1b8..784f5d79fc654 100644 --- a/bundles/org.openhab.binding.nikohomecontrol/src/main/java/org/openhab/binding/nikohomecontrol/internal/protocol/nhc1/NhcAction1.java +++ b/bundles/org.openhab.binding.nikohomecontrol/src/main/java/org/openhab/binding/nikohomecontrol/internal/protocol/nhc1/NhcAction1.java @@ -74,7 +74,7 @@ public void setState(int newState) { if (getType() == ActionType.ROLLERSHUTTER) { if (filterEvent) { filterEvent = false; - logger.debug("Niko Home Control: filtered event {} for {}", newState, id); + logger.debug("filtered event {} for {}", newState, id); return; } @@ -88,7 +88,7 @@ public void setState(int newState) { } } if (waitForEvent) { - logger.debug("Niko Home Control: received requested rollershutter {} position event {}", id, newState); + logger.debug("received requested rollershutter {} position event {}", id, newState); executeRollershutterTask(); } else { state = newState; @@ -103,7 +103,7 @@ public void setState(int newState) { */ @Override public void execute(String command) { - logger.debug("Niko Home Control: execute action {} of type {} for {}", command, type, id); + logger.debug("execute action {} of type {} for {}", command, type, id); String value = ""; switch (getType()) { @@ -162,7 +162,7 @@ private void executeRollershutter(String command) { } else if (command.equals(NHCSTOP)) { executeRollershutterStop(); } else { - int newValue = 100 - Integer.parseInt(command); + int newValue = Integer.parseInt(command); if (logger.isTraceEnabled()) { logger.trace("handleRollerShutterCommand: rollershutter {} percent command, current {}, new {}", id, currentValue, newValue); @@ -174,9 +174,9 @@ private void executeRollershutter(String command) { scheduleRollershutterStop(currentValue, newValue); } if (newValue < currentValue) { - executeRollershutterDown(); - } else if (newValue > currentValue) { executeRollershutterUp(); + } else if (newValue > currentValue) { + executeRollershutterDown(); } } }; diff --git a/bundles/org.openhab.binding.nikohomecontrol/src/main/java/org/openhab/binding/nikohomecontrol/internal/protocol/nhc1/NhcMessageBase1.java b/bundles/org.openhab.binding.nikohomecontrol/src/main/java/org/openhab/binding/nikohomecontrol/internal/protocol/nhc1/NhcMessageBase1.java index a80ae058a060a..8ae35740f30e7 100644 --- a/bundles/org.openhab.binding.nikohomecontrol/src/main/java/org/openhab/binding/nikohomecontrol/internal/protocol/nhc1/NhcMessageBase1.java +++ b/bundles/org.openhab.binding.nikohomecontrol/src/main/java/org/openhab/binding/nikohomecontrol/internal/protocol/nhc1/NhcMessageBase1.java @@ -12,6 +12,8 @@ */ package org.openhab.binding.nikohomecontrol.internal.protocol.nhc1; +import org.eclipse.jdt.annotation.NonNullByDefault; + /** * Class {@link NhcMessageBase1} used as base class for output from gson for cmd or event feedback from Niko Home * Control. This class only contains the common base fields required for the deserializer @@ -21,10 +23,11 @@ * * @author Mark Herwege - Initial Contribution */ +@NonNullByDefault abstract class NhcMessageBase1 { - private String cmd; - private String event; + private String cmd = ""; + private String event = ""; String getCmd() { return cmd; diff --git a/bundles/org.openhab.binding.nikohomecontrol/src/main/java/org/openhab/binding/nikohomecontrol/internal/protocol/nhc1/NhcMessageCmd1.java b/bundles/org.openhab.binding.nikohomecontrol/src/main/java/org/openhab/binding/nikohomecontrol/internal/protocol/nhc1/NhcMessageCmd1.java index d9700913bd9c4..c0bac1dff6d7f 100644 --- a/bundles/org.openhab.binding.nikohomecontrol/src/main/java/org/openhab/binding/nikohomecontrol/internal/protocol/nhc1/NhcMessageCmd1.java +++ b/bundles/org.openhab.binding.nikohomecontrol/src/main/java/org/openhab/binding/nikohomecontrol/internal/protocol/nhc1/NhcMessageCmd1.java @@ -12,6 +12,8 @@ */ package org.openhab.binding.nikohomecontrol.internal.protocol.nhc1; +import org.eclipse.jdt.annotation.NonNullByDefault; + /** * Class {@link NhcMessageCmd1} used as input to gson to send commands to Niko Home Control. Extends * {@link NhcMessageBase1}. @@ -21,6 +23,7 @@ * @author Mark Herwege - Initial Contribution */ @SuppressWarnings("unused") +@NonNullByDefault class NhcMessageCmd1 extends NhcMessageBase1 { private int id; @@ -29,7 +32,7 @@ class NhcMessageCmd1 extends NhcMessageBase1 { private int value3; private int mode; private int overrule; - private String overruletime; + private String overruletime = ""; NhcMessageCmd1(String cmd) { super.setCmd(cmd); diff --git a/bundles/org.openhab.binding.nikohomecontrol/src/main/java/org/openhab/binding/nikohomecontrol/internal/protocol/nhc1/NhcMessageListMap1.java b/bundles/org.openhab.binding.nikohomecontrol/src/main/java/org/openhab/binding/nikohomecontrol/internal/protocol/nhc1/NhcMessageListMap1.java index 016405dcc3142..a8f9418c15472 100644 --- a/bundles/org.openhab.binding.nikohomecontrol/src/main/java/org/openhab/binding/nikohomecontrol/internal/protocol/nhc1/NhcMessageListMap1.java +++ b/bundles/org.openhab.binding.nikohomecontrol/src/main/java/org/openhab/binding/nikohomecontrol/internal/protocol/nhc1/NhcMessageListMap1.java @@ -16,6 +16,8 @@ import java.util.List; import java.util.Map; +import org.eclipse.jdt.annotation.NonNullByDefault; + /** * Class {@link NhcMessageListMap1} used as output from gson for cmd or event feedback from Niko Home Control where the * data part is enclosed by [] and contains a list of json strings. Extends {@link NhcMessageBase1}. @@ -25,6 +27,7 @@ * * @author Mark Herwege - Initial Contribution */ +@NonNullByDefault class NhcMessageListMap1 extends NhcMessageBase1 { private List> data = new ArrayList<>(); diff --git a/bundles/org.openhab.binding.nikohomecontrol/src/main/java/org/openhab/binding/nikohomecontrol/internal/protocol/nhc1/NhcMessageMap1.java b/bundles/org.openhab.binding.nikohomecontrol/src/main/java/org/openhab/binding/nikohomecontrol/internal/protocol/nhc1/NhcMessageMap1.java index 12cfb12d5f4c9..a2ed5497f5ff4 100644 --- a/bundles/org.openhab.binding.nikohomecontrol/src/main/java/org/openhab/binding/nikohomecontrol/internal/protocol/nhc1/NhcMessageMap1.java +++ b/bundles/org.openhab.binding.nikohomecontrol/src/main/java/org/openhab/binding/nikohomecontrol/internal/protocol/nhc1/NhcMessageMap1.java @@ -15,6 +15,8 @@ import java.util.HashMap; import java.util.Map; +import org.eclipse.jdt.annotation.NonNullByDefault; + /** * Class {@link NhcMessageMap1} used as output from gson for cmd or event feedback from Niko Home Control where the * data part is a simple json string. Extends {@link NhcMessageBase1}. @@ -23,6 +25,7 @@ * * @author Mark Herwege - Initial Contribution */ +@NonNullByDefault class NhcMessageMap1 extends NhcMessageBase1 { private Map data = new HashMap<>(); diff --git a/bundles/org.openhab.binding.nikohomecontrol/src/main/java/org/openhab/binding/nikohomecontrol/internal/protocol/nhc1/NhcThermostat1.java b/bundles/org.openhab.binding.nikohomecontrol/src/main/java/org/openhab/binding/nikohomecontrol/internal/protocol/nhc1/NhcThermostat1.java index c9c85339a5f23..697ad9c34b79b 100644 --- a/bundles/org.openhab.binding.nikohomecontrol/src/main/java/org/openhab/binding/nikohomecontrol/internal/protocol/nhc1/NhcThermostat1.java +++ b/bundles/org.openhab.binding.nikohomecontrol/src/main/java/org/openhab/binding/nikohomecontrol/internal/protocol/nhc1/NhcThermostat1.java @@ -42,7 +42,7 @@ public class NhcThermostat1 extends NhcThermostat { */ @Override public void executeMode(int mode) { - logger.debug("Niko Home Control: execute thermostat mode {} for {}", mode, id); + logger.debug("execute thermostat mode {} for {}", mode, id); nhcComm.executeThermostat(id, Integer.toString(mode)); } @@ -55,8 +55,7 @@ public void executeMode(int mode) { */ @Override public void executeOverrule(int overrule, int overruletime) { - logger.debug("Niko Home Control: execute thermostat overrule {} during {} min for {}", overrule, overruletime, - id); + logger.debug("execute thermostat overrule {} during {} min for {}", overrule, overruletime, id); nhcComm.executeThermostat(id, overrule, overruletime); } diff --git a/bundles/org.openhab.binding.nikohomecontrol/src/main/java/org/openhab/binding/nikohomecontrol/internal/protocol/nhc1/NikoHomeControlCommunication1.java b/bundles/org.openhab.binding.nikohomecontrol/src/main/java/org/openhab/binding/nikohomecontrol/internal/protocol/nhc1/NikoHomeControlCommunication1.java index 0eb1adb653aa0..a2c8b7bd40908 100644 --- a/bundles/org.openhab.binding.nikohomecontrol/src/main/java/org/openhab/binding/nikohomecontrol/internal/protocol/nhc1/NikoHomeControlCommunication1.java +++ b/bundles/org.openhab.binding.nikohomecontrol/src/main/java/org/openhab/binding/nikohomecontrol/internal/protocol/nhc1/NikoHomeControlCommunication1.java @@ -20,6 +20,7 @@ import java.net.Socket; import java.util.List; import java.util.Map; +import java.util.Optional; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ScheduledExecutorService; import java.util.function.Consumer; @@ -94,7 +95,7 @@ public synchronized void startCommunication() { Thread.sleep(1000); } if (nhcEventsRunning) { - logger.debug("Niko Home Control: starting but previous connection still active after 5000ms"); + logger.debug("starting but previous connection still active after 5000ms"); throw new IOException(); } @@ -105,7 +106,7 @@ public synchronized void startCommunication() { nhcSocket = socket; nhcOut = new PrintWriter(socket.getOutputStream(), true); nhcIn = new BufferedReader(new InputStreamReader(socket.getInputStream())); - logger.debug("Niko Home Control: connected via local port {}", socket.getLocalPort()); + logger.debug("connected via local port {}", socket.getLocalPort()); // initialize all info in local fields initialize(); @@ -115,9 +116,8 @@ public synchronized void startCommunication() { (new Thread(this::runNhcEvents)).start(); } catch (IOException | InterruptedException e) { - logger.warn("Niko Home Control: error initializing communication"); stopCommunication(); - handler.controllerOffline(); + handler.controllerOffline("Error initializing communication"); } } @@ -139,7 +139,7 @@ public synchronized void stopCommunication() { } nhcSocket = null; - logger.debug("Niko Home Control: communication stopped"); + logger.debug("communication stopped"); } @Override @@ -158,7 +158,7 @@ public boolean communicationActive() { private void runNhcEvents() { String nhcMessage; - logger.debug("Niko Home Control: listening for events"); + logger.debug("listening for events"); listenerStopped = false; nhcEventsRunning = true; @@ -170,7 +170,7 @@ private void runNhcEvents() { if (!listenerStopped) { nhcEventsRunning = false; // this is a socket error, not a communication stop triggered from outside this runnable - logger.warn("Niko Home Control: IO error in listener"); + logger.debug("IO error in listener"); // the IO has stopped working, so we need to close cleanly and try to restart restartCommunication(); return; @@ -181,7 +181,7 @@ private void runNhcEvents() { nhcEventsRunning = false; // this is a stop from outside the runnable, so just log it and stop - logger.debug("Niko Home Control: event listener thread stopped"); + logger.debug("event listener thread stopped"); } /** @@ -218,17 +218,16 @@ private void sendAndReadMessage(String command) throws IOException { @SuppressWarnings("null") private synchronized void sendMessage(Object nhcMessage) { String json = gsonOut.toJson(nhcMessage); - logger.debug("Niko Home Control: send json {}", json); + logger.debug("send json {}", json); nhcOut.println(json); if (nhcOut.checkError()) { - logger.warn("Niko Home Control: error sending message, trying to restart communication"); + logger.debug("error sending message, trying to restart communication"); restartCommunication(); // retry sending after restart - logger.debug("Niko Home Control: resend json {}", json); + logger.debug("resend json {}", json); nhcOut.println(json); if (nhcOut.checkError()) { - logger.warn("Niko Home Control: error resending message"); - handler.controllerOffline(); + handler.controllerOffline("Error resending message"); } } } @@ -239,11 +238,14 @@ private synchronized void sendMessage(Object nhcMessage) { * @param nhcMessage message read from Niko Home Control. */ private void readMessage(@Nullable String nhcMessage) { - logger.debug("Niko Home Control: received json {}", nhcMessage); + logger.debug("received json {}", nhcMessage); try { NhcMessageBase1 nhcMessageGson = gsonIn.fromJson(nhcMessage, NhcMessageBase1.class); + if (nhcMessageGson == null) { + return; + } String cmd = nhcMessageGson.getCmd(); String event = nhcMessageGson.getEvent(); @@ -268,10 +270,10 @@ private void readMessage(@Nullable String nhcMessage) { } else if ("getalarms".equals(event)) { eventGetAlarms(((NhcMessageMap1) nhcMessageGson).getData()); } else { - logger.debug("Niko Home Control: not acted on json {}", nhcMessage); + logger.debug("not acted on json {}", nhcMessage); } } catch (JsonParseException e) { - logger.debug("Niko Home Control: not acted on unsupported json {}", nhcMessage); + logger.debug("not acted on unsupported json {}", nhcMessage); } } @@ -283,7 +285,7 @@ private void setIfPresent(Map data, String key, Consumer } private synchronized void cmdSystemInfo(Map data) { - logger.debug("Niko Home Control: systeminfo"); + logger.debug("systeminfo"); setIfPresent(data, "swversion", systemInfo::setSwVersion); setIfPresent(data, "api", systemInfo::setApi); @@ -311,17 +313,17 @@ private void cmdStartEvents(Map data) { if (errorCodeString != null) { int errorCode = Integer.parseInt(errorCodeString); if (errorCode == 0) { - logger.debug("Niko Home Control: start events success"); + logger.debug("start events success"); } else { - logger.warn("Niko Home Control: error code {} returned on start events", errorCode); + logger.debug("error code {} returned on start events", errorCode); } } else { - logger.warn("Niko Home Control: could not determine error code returned on start events"); + logger.debug("could not determine error code returned on start events"); } } private void cmdListLocations(List> data) { - logger.debug("Niko Home Control: list locations"); + logger.debug("list locations"); locations.clear(); @@ -338,7 +340,7 @@ private void cmdListLocations(List> data) { } private void cmdListActions(List> data) { - logger.debug("Niko Home Control: list actions"); + logger.debug("list actions"); for (Map action : data) { String id = action.get("id"); @@ -360,7 +362,7 @@ private void cmdListActions(List> data) { logger.debug("name not found in action {}", action); continue; } - String type = action.get("type"); + String type = Optional.ofNullable(action.get("type")).orElse(""); ActionType actionType = ActionType.GENERIC; switch (type) { case "0": @@ -377,7 +379,7 @@ private void cmdListActions(List> data) { actionType = ActionType.ROLLERSHUTTER; break; default: - logger.debug("Niko Home Control: unknown action type {} for action {}", type, id); + logger.debug("unknown action type {} for action {}", type, id); continue; } String locationId = action.get("location"); @@ -395,14 +397,18 @@ private void cmdListActions(List> data) { // Action object already exists, so only update state. // If we would re-instantiate action, we would lose pointer back from action to thing handler that was // set in thing handler initialize(). - actions.get(id).setState(state); + NhcAction nhcAction = actions.get(id); + if (nhcAction != null) { + nhcAction.setState(state); + } } } } private int parseIntOrThrow(@Nullable String str) throws IllegalArgumentException { - if (str == null) + if (str == null) { throw new IllegalArgumentException("String is null"); + } try { return Integer.parseInt(str); } catch (NumberFormatException e) { @@ -411,7 +417,7 @@ private int parseIntOrThrow(@Nullable String str) throws IllegalArgumentExceptio } private void cmdListThermostat(List> data) { - logger.debug("Niko Home Control: list thermostats"); + logger.debug("list thermostats"); for (Map thermostat : data) { try { @@ -442,8 +448,11 @@ private void cmdListThermostat(List> data) { String name = thermostat.get("name"); String locationId = thermostat.get("location"); String location = ""; - if (!locationId.isEmpty()) { - location = locations.get(locationId).getName(); + if (!((locationId == null) || locationId.isEmpty())) { + NhcLocation1 nhcLocation = locations.get(locationId); + if (nhcLocation != null) { + location = nhcLocation.getName(); + } } if (name != null) { return new NhcThermostat1(i, name, location, this); @@ -463,12 +472,12 @@ private void cmdExecuteActions(Map data) { try { int errorCode = parseIntOrThrow(data.get("error")); if (errorCode == 0) { - logger.debug("Niko Home Control: execute action success"); + logger.debug("execute action success"); } else { - logger.warn("Niko Home Control: error code {} returned on command execution", errorCode); + logger.debug("error code {} returned on command execution", errorCode); } } catch (IllegalArgumentException e) { - logger.warn("Niko Home Control: no error code returned on command execution"); + logger.debug("no error code returned on command execution"); } } @@ -476,12 +485,12 @@ private void cmdExecuteThermostat(Map data) { try { int errorCode = parseIntOrThrow(data.get("error")); if (errorCode == 0) { - logger.debug("Niko Home Control: execute thermostats success"); + logger.debug("execute thermostats success"); } else { - logger.warn("Niko Home Control: error code {} returned on command execution", errorCode); + logger.debug("error code {} returned on command execution", errorCode); } } catch (IllegalArgumentException e) { - logger.warn("Niko Home Control: no error code returned on command execution"); + logger.debug("no error code returned on command execution"); } } @@ -489,13 +498,13 @@ private void eventListActions(List> data) { for (Map action : data) { String id = action.get("id"); if (id == null || !actions.containsKey(id)) { - logger.warn("Niko Home Control: action in controller not known {}", id); + logger.warn("action in controller not known {}", id); return; } String stateString = action.get("value1"); if (stateString != null) { int state = Integer.parseInt(stateString); - logger.debug("Niko Home Control: event execute action {} with state {}", id, state); + logger.debug("event execute action {} with state {}", id, state); NhcAction action1 = actions.get(id); if (action1 != null) { action1.setState(state); @@ -509,7 +518,7 @@ private void eventListThermostat(List> data) { try { String id = thermostat.get("id"); if (!thermostats.containsKey(id)) { - logger.warn("Niko Home Control: thermostat in controller not known {}", id); + logger.warn("thermostat in controller not known {}", id); return; } @@ -549,15 +558,15 @@ private void eventGetAlarms(Map data) { } switch (data.getOrDefault("type", "")) { case "0": - logger.debug("Niko Home Control: alarm - {}", alarmText); + logger.debug("alarm - {}", alarmText); handler.alarmEvent(alarmText); break; case "1": - logger.debug("Niko Home Control: notice - {}", alarmText); + logger.debug("notice - {}", alarmText); handler.noticeEvent(alarmText); break; default: - logger.debug("Niko Home Control: unexpected message type {}", data.get("type")); + logger.debug("unexpected message type {}", data.get("type")); } } diff --git a/bundles/org.openhab.binding.nikohomecontrol/src/main/java/org/openhab/binding/nikohomecontrol/internal/protocol/nhc1/NikoHomeControlMessageDeserializer1.java b/bundles/org.openhab.binding.nikohomecontrol/src/main/java/org/openhab/binding/nikohomecontrol/internal/protocol/nhc1/NikoHomeControlMessageDeserializer1.java index 3429512631bc0..df5b4fd9816b2 100644 --- a/bundles/org.openhab.binding.nikohomecontrol/src/main/java/org/openhab/binding/nikohomecontrol/internal/protocol/nhc1/NikoHomeControlMessageDeserializer1.java +++ b/bundles/org.openhab.binding.nikohomecontrol/src/main/java/org/openhab/binding/nikohomecontrol/internal/protocol/nhc1/NikoHomeControlMessageDeserializer1.java @@ -19,6 +19,9 @@ import java.util.Map; import java.util.Map.Entry; +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; + import com.google.gson.JsonArray; import com.google.gson.JsonDeserializationContext; import com.google.gson.JsonDeserializer; @@ -33,16 +36,17 @@ * @author Mark Herwege - Initial Contribution * */ +@NonNullByDefault class NikoHomeControlMessageDeserializer1 implements JsonDeserializer { @Override - public NhcMessageBase1 deserialize(final JsonElement json, final Type typeOfT, + public @Nullable NhcMessageBase1 deserialize(final JsonElement json, final Type typeOfT, final JsonDeserializationContext context) throws JsonParseException { final JsonObject jsonObject = json.getAsJsonObject(); try { - String cmd = null; - String event = null; + String cmd = ""; + String event = ""; if (jsonObject.has("cmd")) { cmd = jsonObject.get("cmd").getAsString(); } diff --git a/bundles/org.openhab.binding.nikohomecontrol/src/main/java/org/openhab/binding/nikohomecontrol/internal/protocol/nhc2/NhcAction2.java b/bundles/org.openhab.binding.nikohomecontrol/src/main/java/org/openhab/binding/nikohomecontrol/internal/protocol/nhc2/NhcAction2.java index 3b66d90f45b27..1155ac5b6f843 100644 --- a/bundles/org.openhab.binding.nikohomecontrol/src/main/java/org/openhab/binding/nikohomecontrol/internal/protocol/nhc2/NhcAction2.java +++ b/bundles/org.openhab.binding.nikohomecontrol/src/main/java/org/openhab/binding/nikohomecontrol/internal/protocol/nhc2/NhcAction2.java @@ -115,7 +115,7 @@ public void setState(int state) { */ @Override public void execute(String command) { - logger.debug("Niko Home Control: execute action {} of type {} for {}", command, type, id); + logger.debug("execute action {} of type {} for {}", command, type, id); nhcComm.executeAction(id, command); } diff --git a/bundles/org.openhab.binding.nikohomecontrol/src/main/java/org/openhab/binding/nikohomecontrol/internal/protocol/nhc2/NhcMqttConnection2.java b/bundles/org.openhab.binding.nikohomecontrol/src/main/java/org/openhab/binding/nikohomecontrol/internal/protocol/nhc2/NhcMqttConnection2.java index 86ff112c0adf7..6ad1a686db3c1 100644 --- a/bundles/org.openhab.binding.nikohomecontrol/src/main/java/org/openhab/binding/nikohomecontrol/internal/protocol/nhc2/NhcMqttConnection2.java +++ b/bundles/org.openhab.binding.nikohomecontrol/src/main/java/org/openhab/binding/nikohomecontrol/internal/protocol/nhc2/NhcMqttConnection2.java @@ -100,7 +100,7 @@ private TrustManager[] getTrustManagers() throws CertificateException { tmFactory.init(keyStore); return tmFactory.getTrustManagers(); } catch (CertificateException | KeyStoreException | NoSuchAlgorithmException | IOException e) { - logger.warn("Niko Home Control: error with SSL context creation: {} ", e.getMessage()); + logger.debug("error with SSL context creation: {} ", e.getMessage()); throw new CertificateException("SSL context creation exception", e); } finally { ResourceBundle.clearCache(); @@ -121,14 +121,14 @@ synchronized void startConnection(String cocoAddress, int port, String profile, if (future != null) { try { future.get(5000, TimeUnit.MILLISECONDS); - logger.debug("Niko Home Control: finished stopping connection"); + logger.debug("finished stopping connection"); } catch (InterruptedException | ExecutionException | TimeoutException ignore) { - logger.debug("Niko Home Control: error stopping connection"); + logger.debug("error stopping connection"); } stoppedFuture = null; } - logger.debug("Niko Home Control: starting connection..."); + logger.debug("starting connection..."); this.cocoAddress = cocoAddress; this.port = port; this.profile = profile; @@ -142,17 +142,17 @@ synchronized void startConnection(String cocoAddress, int port, String profile, subscribedFuture = connection.subscribe("#", messageSubscriber); } } else { - logger.debug("Niko Home Control: error connecting"); + logger.debug("error connecting"); throw new MqttException("Connection execution exception"); } } catch (InterruptedException e) { - logger.debug("Niko Home Control: connection interrupted exception"); + logger.debug("connection interrupted exception"); throw new MqttException("Connection interrupted exception"); } catch (ExecutionException e) { - logger.debug("Niko Home Control: connection execution exception", e.getCause()); + logger.debug("connection execution exception", e.getCause()); throw new MqttException("Connection execution exception"); } catch (TimeoutException e) { - logger.debug("Niko Home Control: connection timeout exception"); + logger.debug("connection timeout exception"); throw new MqttException("Connection timeout exception"); } } @@ -169,7 +169,7 @@ private MqttBrokerConnection createMqttConnection() throws MqttException { * Stop the MQTT connection. */ void stopConnection() { - logger.debug("Niko Home Control: stopping connection..."); + logger.debug("stopping connection..."); MqttBrokerConnection connection = mqttConnection; if (connection != null) { connection.removeConnectionObserver(connectionObserver); @@ -203,7 +203,7 @@ private boolean isConnected() { try { if ((future != null) && future.get(5000, TimeUnit.MILLISECONDS)) { MqttConnectionState state = connection.connectionState(); - logger.debug("Niko Home Control: connection state {} for {}", state, connection.getClientId()); + logger.debug("connection state {} for {}", state, connection.getClientId()); return state == MqttConnectionState.CONNECTED; } } catch (InterruptedException | ExecutionException | TimeoutException e) { @@ -223,25 +223,25 @@ private boolean isConnected() { void connectionPublish(String topic, String payload) throws MqttException { MqttBrokerConnection connection = mqttConnection; if (connection == null) { - logger.debug("Niko Home Control: cannot publish, no connection"); + logger.debug("cannot publish, no connection"); throw new MqttException("No connection exception"); } if (isConnected()) { - logger.debug("Niko Home Control: publish {}, {}", topic, payload); + logger.debug("publish {}, {}", topic, payload); connection.publish(topic, payload.getBytes(), connection.getQos(), false); } else { - logger.debug("Niko Home Control: cannot publish, not subscribed to connection messages"); + logger.debug("cannot publish, not subscribed to connection messages"); } } @Override public void onSuccess(String topic) { - logger.debug("Niko Home Control: publish succeeded {}", topic); + logger.debug("publish succeeded {}", topic); } @Override public void onFailure(String topic, Throwable error) { - logger.debug("Niko Home Control: publish failed {}, {}", topic, error.getMessage(), error); + logger.debug("publish failed {}, {}", topic, error.getMessage(), error); } } diff --git a/bundles/org.openhab.binding.nikohomecontrol/src/main/java/org/openhab/binding/nikohomecontrol/internal/protocol/nhc2/NhcThermostat2.java b/bundles/org.openhab.binding.nikohomecontrol/src/main/java/org/openhab/binding/nikohomecontrol/internal/protocol/nhc2/NhcThermostat2.java index 4f05e4421359d..06b0936727759 100644 --- a/bundles/org.openhab.binding.nikohomecontrol/src/main/java/org/openhab/binding/nikohomecontrol/internal/protocol/nhc2/NhcThermostat2.java +++ b/bundles/org.openhab.binding.nikohomecontrol/src/main/java/org/openhab/binding/nikohomecontrol/internal/protocol/nhc2/NhcThermostat2.java @@ -45,7 +45,7 @@ protected NhcThermostat2(String id, String name, String model, String technology @Override public void executeMode(int mode) { - logger.debug("Niko Home Control: execute thermostat mode {} for {}", mode, id); + logger.debug("execute thermostat mode {} for {}", mode, id); String program = THERMOSTATMODES[mode]; nhcComm.executeThermostat(id, program); @@ -53,8 +53,7 @@ public void executeMode(int mode) { @Override public void executeOverrule(int overrule, int overruletime) { - logger.debug("Niko Home Control: execute thermostat overrule {} during {} min for {}", overrule, overruletime, - id); + logger.debug("execute thermostat overrule {} during {} min for {}", overrule, overruletime, id); nhcComm.executeThermostat(id, overrule, overruletime); } diff --git a/bundles/org.openhab.binding.nikohomecontrol/src/main/java/org/openhab/binding/nikohomecontrol/internal/protocol/nhc2/NikoHomeControlCommunication2.java b/bundles/org.openhab.binding.nikohomecontrol/src/main/java/org/openhab/binding/nikohomecontrol/internal/protocol/nhc2/NikoHomeControlCommunication2.java index 916ae9c20ecaa..866e18283ddb2 100644 --- a/bundles/org.openhab.binding.nikohomecontrol/src/main/java/org/openhab/binding/nikohomecontrol/internal/protocol/nhc2/NikoHomeControlCommunication2.java +++ b/bundles/org.openhab.binding.nikohomecontrol/src/main/java/org/openhab/binding/nikohomecontrol/internal/protocol/nhc2/NikoHomeControlCommunication2.java @@ -33,7 +33,11 @@ import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.Nullable; -import org.openhab.binding.nikohomecontrol.internal.protocol.*; +import org.openhab.binding.nikohomecontrol.internal.protocol.NhcAction; +import org.openhab.binding.nikohomecontrol.internal.protocol.NhcControllerEvent; +import org.openhab.binding.nikohomecontrol.internal.protocol.NhcEnergyMeter; +import org.openhab.binding.nikohomecontrol.internal.protocol.NhcThermostat; +import org.openhab.binding.nikohomecontrol.internal.protocol.NikoHomeControlCommunication; import org.openhab.binding.nikohomecontrol.internal.protocol.NikoHomeControlConstants.ActionType; import org.openhab.binding.nikohomecontrol.internal.protocol.nhc2.NhcDevice2.NhcProperty; import org.openhab.binding.nikohomecontrol.internal.protocol.nhc2.NhcMessage2.NhcMessageParam; @@ -104,19 +108,19 @@ public synchronized void startCommunication() { InetAddress addr = handler.getAddr(); if (addr == null) { - logger.warn("Niko Home Control: IP address cannot be empty"); + logger.warn("IP address cannot be empty"); stopCommunication(); return; } String addrString = addr.getHostAddress(); int port = handler.getPort(); - logger.debug("Niko Home Control: initializing for mqtt connection to CoCo on {}:{}", addrString, port); + logger.debug("initializing for mqtt connection to CoCo on {}:{}", addrString, port); profile = handler.getProfile(); String token = handler.getToken(); if (token.isEmpty()) { - logger.warn("Niko Home Control: JWT token cannot be empty"); + logger.warn("JWT token cannot be empty"); stopCommunication(); return; } @@ -125,7 +129,7 @@ public synchronized void startCommunication() { mqttConnection.startConnection(addrString, port, profile, token); initialize(); } catch (MqttException e) { - logger.warn("Niko Home Control: error in mqtt communication"); + logger.debug("error in mqtt communication"); stopCommunication(); } } @@ -150,7 +154,7 @@ public boolean communicationActive() { // Wait until we received all devices info to confirm we are active. return started.get(5000, TimeUnit.MILLISECONDS); } catch (InterruptedException | ExecutionException | TimeoutException e) { - logger.debug("Niko Home Control: exception waiting for connection start"); + logger.debug("exception waiting for connection start"); return false; } } @@ -176,10 +180,10 @@ private void initialize() throws MqttException { mqttConnection.connectionPublish(profile + "/notification/cmd", gson.toJson(message)); } - private void connectionLost() { - logger.debug("Niko Home Control: connection lost"); + private void connectionLost(String message) { + logger.debug("connection lost"); stopCommunication(); - handler.controllerOffline(); + handler.controllerOffline(message); } private void systemEvt(String response) { @@ -189,13 +193,13 @@ private void systemEvt(String response) { List systemInfo = null; try { NhcMessage2 message = gson.fromJson(response, messageType); - List messageParams = message.params; + List messageParams = (message != null) ? message.params : null; if (messageParams != null) { timeInfo = messageParams.stream().filter(p -> (p.timeInfo != null)).findFirst().get().timeInfo; systemInfo = messageParams.stream().filter(p -> (p.systemInfo != null)).findFirst().get().systemInfo; } } catch (JsonSyntaxException e) { - logger.debug("Niko Home Control: unexpected json {}", response); + logger.debug("unexpected json {}", response); } catch (NoSuchElementException ignore) { // Ignore if timeInfo not present in response, this should not happen in a timeInfo response } @@ -214,12 +218,12 @@ private void systeminfoPublishRsp(String response) { List systemInfo = null; try { NhcMessage2 message = gson.fromJson(response, messageType); - List messageParams = message.params; + List messageParams = (message != null) ? message.params : null; if (messageParams != null) { systemInfo = messageParams.stream().filter(p -> (p.systemInfo != null)).findFirst().get().systemInfo; } } catch (JsonSyntaxException e) { - logger.debug("Niko Home Control: unexpected json {}", response); + logger.debug("unexpected json {}", response); } catch (NoSuchElementException ignore) { // Ignore if systemInfo not present in response, this should not happen in a systemInfo response } @@ -234,12 +238,12 @@ private void servicesListRsp(String response) { List serviceList = null; try { NhcMessage2 message = gson.fromJson(response, messageType); - List messageParams = message.params; + List messageParams = (message != null) ? message.params : null; if (messageParams != null) { serviceList = messageParams.stream().filter(p -> (p.services != null)).findFirst().get().services; } } catch (JsonSyntaxException e) { - logger.debug("Niko Home Control: unexpected json {}", response); + logger.debug("unexpected json {}", response); } catch (NoSuchElementException ignore) { // Ignore if services not present in response, this should not happen in a services response } @@ -255,12 +259,12 @@ private void devicesListRsp(String response) { List deviceList = null; try { NhcMessage2 message = gson.fromJson(response, messageType); - List messageParams = message.params; + List messageParams = (message != null) ? message.params : null; if (messageParams != null) { deviceList = messageParams.stream().filter(p -> (p.devices != null)).findFirst().get().devices; } } catch (JsonSyntaxException e) { - logger.debug("Niko Home Control: unexpected json {}", response); + logger.debug("unexpected json {}", response); } catch (NoSuchElementException ignore) { // Ignore if devices not present in response, this should not happen in a devices response } @@ -274,7 +278,7 @@ private void devicesListRsp(String response) { } // Once a devices list response is received, we know the communication is fully started. - logger.debug("Niko Home Control: Communication start complete."); + logger.debug("Communication start complete."); handler.controllerOnline(); CompletableFuture future = communicationStarted; if (future != null) { @@ -289,13 +293,13 @@ private void devicesEvt(String response) { String method = null; try { NhcMessage2 message = gson.fromJson(response, messageType); - method = message.method; - List messageParams = message.params; + method = (message != null) ? message.method : null; + List messageParams = (message != null) ? message.params : null; if (messageParams != null) { deviceList = messageParams.stream().filter(p -> (p.devices != null)).findFirst().get().devices; } } catch (JsonSyntaxException e) { - logger.debug("Niko Home Control: unexpected json {}", response); + logger.debug("unexpected json {}", response); } catch (NoSuchElementException ignore) { // Ignore if devices not present in response, this should not happen in a devices event } @@ -308,9 +312,6 @@ private void devicesEvt(String response) { return; } else if ("devices.added".equals(method)) { deviceList.forEach(this::addDevice); - } else if ("devices.changed".equals(method)) { - deviceList.forEach(this::removeDevice); - deviceList.forEach(this::addDevice); } deviceList.forEach(this::updateState); @@ -322,17 +323,17 @@ private void notificationEvt(String response) { List notificationList = null; try { NhcMessage2 message = gson.fromJson(response, messageType); - List messageParams = message.params; + List messageParams = (message != null) ? message.params : null; if (messageParams != null) { notificationList = messageParams.stream().filter(p -> (p.notifications != null)).findFirst() .get().notifications; } } catch (JsonSyntaxException e) { - logger.debug("Niko Home Control: unexpected json {}", response); + logger.debug("unexpected json {}", response); } catch (NoSuchElementException ignore) { // Ignore if notifications not present in response, this should not happen in a notifications event } - logger.debug("Niko Home Control: notifications {}", notificationList); + logger.debug("notifications {}", notificationList); if (notificationList == null) { return; } @@ -348,7 +349,7 @@ private void notificationEvt(String response) { handler.noticeEvent(alarmText); break; default: - logger.debug("Niko Home Control: unexpected message type {}", notification.type); + logger.debug("unexpected message type {}", notification.type); } } } @@ -363,7 +364,7 @@ private void addDevice(NhcDevice2 device) { if ("action".equals(device.type)) { if (!actions.containsKey(device.uuid)) { - logger.debug("Niko Home Control: adding action device {}, {}", device.uuid, device.name); + logger.debug("adding action device {}, {}", device.uuid, device.name); ActionType actionType; switch (device.model) { @@ -394,8 +395,7 @@ private void addDevice(NhcDevice2 device) { break; default: actionType = ActionType.GENERIC; - logger.debug("Niko Home Control: device type {} not recognised, default to GENERIC action", - device.type); + logger.debug("device type {} not recognised, default to GENERIC action", device.type); } NhcAction2 nhcAction = new NhcAction2(device.uuid, device.name, device.model, device.technology, @@ -404,7 +404,7 @@ private void addDevice(NhcDevice2 device) { } } else if ("thermostat".equals(device.type)) { if (!thermostats.containsKey(device.uuid)) { - logger.debug("Niko Home Control: adding thermostat device {}, {}", device.uuid, device.name); + logger.debug("adding thermostat device {}, {}", device.uuid, device.name); NhcThermostat2 nhcThermostat = new NhcThermostat2(device.uuid, device.name, device.model, device.technology, location, this); @@ -412,26 +412,28 @@ private void addDevice(NhcDevice2 device) { } } else if ("centralmeter".equals(device.type)) { if (!energyMeters.containsKey(device.uuid)) { - logger.debug("Niko Home Control: adding centralmeter device {}, {}", device.uuid, device.name); + logger.debug("adding centralmeter device {}, {}", device.uuid, device.name); NhcEnergyMeter2 nhcEnergyMeter = new NhcEnergyMeter2(device.uuid, device.name, device.model, device.technology, this, scheduler); energyMeters.put(device.uuid, nhcEnergyMeter); } } else { - logger.debug("Niko Home Control: device type {} not supported for {}, {}", device.type, device.uuid, - device.name); + logger.debug("device type {} not supported for {}, {}", device.type, device.uuid, device.name); } } private void removeDevice(NhcDevice2 device) { - if (actions.containsKey(device.uuid)) { - actions.get(device.uuid).actionRemoved(); + NhcAction action = actions.get(device.uuid); + NhcThermostat thermostat = thermostats.get(device.uuid); + NhcEnergyMeter energyMeter = energyMeters.get(device.uuid); + if (action != null) { + action.actionRemoved(); actions.remove(device.uuid); - } else if (thermostats.containsKey(device.uuid)) { - thermostats.get(device.uuid).thermostatRemoved(); + } else if (thermostat != null) { + thermostat.thermostatRemoved(); thermostats.remove(device.uuid); - } else if (energyMeters.containsKey(device.uuid)) { - energyMeters.get(device.uuid).energyMeterRemoved(); + } else if (energyMeter != null) { + energyMeter.energyMeterRemoved(); energyMeters.remove(device.uuid); } } @@ -481,10 +483,10 @@ private void updateLightState(NhcAction2 action, List devicePropert if (booleanState != null) { if (NHCON.equals(booleanState)) { action.setBooleanState(true); - logger.debug("Niko Home Control: setting action {} internally to ON", action.getId()); + logger.debug("setting action {} internally to ON", action.getId()); } else if (NHCOFF.equals(booleanState)) { action.setBooleanState(false); - logger.debug("Niko Home Control: setting action {} internally to OFF", action.getId()); + logger.debug("setting action {} internally to OFF", action.getId()); } } @@ -492,8 +494,7 @@ private void updateLightState(NhcAction2 action, List devicePropert String brightness = dimmerProperty.get().brightness; if (brightness != null) { action.setState(Integer.parseInt(brightness)); - logger.debug("Niko Home Control: setting action {} internally to {}", action.getId(), - dimmerProperty.get().brightness); + logger.debug("setting action {} internally to {}", action.getId(), dimmerProperty.get().brightness); } } } @@ -502,9 +503,9 @@ private void updateRollershutterState(NhcAction2 action, List devic deviceProperties.stream().map(p -> p.position).filter(Objects::nonNull).findFirst().ifPresent(position -> { try { action.setState(Integer.parseInt(position)); - logger.debug("Niko Home Control: setting action {} internally to {}", action.getId(), position); + logger.debug("setting action {} internally to {}", action.getId(), position); } catch (NumberFormatException e) { - logger.trace("Niko Home Control: received empty rollershutter {} position info", action.getId()); + logger.trace("received empty rollershutter {} position info", action.getId()); } }); } @@ -577,12 +578,10 @@ private void updateEnergyMeterState(NhcEnergyMeter2 energyMeter, List { try { energyMeter.setPower(Integer.parseInt(electricalPower)); - logger.trace("Niko Home Control: setting energy meter {} power to {}", energyMeter.getId(), - electricalPower); + logger.trace("setting energy meter {} power to {}", energyMeter.getId(), electricalPower); } catch (NumberFormatException e) { energyMeter.setPower(null); - logger.trace("Niko Home Control: received empty energy meter {} power reading", - energyMeter.getId()); + logger.trace("received empty energy meter {} power reading", energyMeter.getId()); } }); } @@ -607,6 +606,9 @@ public void executeAction(String actionId, String value) { device.properties = deviceProperties; NhcAction2 action = (NhcAction2) actions.get(actionId); + if (action == null) { + return; + } switch (action.getType()) { case GENERIC: @@ -639,7 +641,7 @@ public void executeAction(String actionId, String value) { } else if (NHCDOWN.equals(value)) { property.position = "0"; } else { - int position = 100 - Integer.parseInt(value); + int position = Integer.parseInt(value); property.position = String.valueOf(position); } break; @@ -745,12 +747,18 @@ public void startEnergyMeter(String energyMeterId) { String topic = profile + "/control/devices/cmd"; String gsonMessage = gson.toJson(message); - ((NhcEnergyMeter2) energyMeters.get(energyMeterId)).startEnergyMeter(topic, gsonMessage); + NhcEnergyMeter2 energyMeter = (NhcEnergyMeter2) energyMeters.get(energyMeterId); + if (energyMeter != null) { + energyMeter.startEnergyMeter(topic, gsonMessage); + } } @Override public void stopEnergyMeter(String energyMeterId) { - ((NhcEnergyMeter2) energyMeters.get(energyMeterId)).stopEnergyMeter(); + NhcEnergyMeter2 energyMeter = (NhcEnergyMeter2) energyMeters.get(energyMeterId); + if (energyMeter != null) { + energyMeter.stopEnergyMeter(); + } } /** @@ -768,19 +776,26 @@ private void sendDeviceMessage(String topic, String gsonMessage) { mqttConnection.connectionPublish(topic, gsonMessage); } catch (MqttException e) { - logger.warn("Niko Home Control: sending command failed, trying to restart communication"); + String message = e.getMessage(); + message = (message != null) ? message : "Communication error"; + + logger.debug("sending command failed, trying to restart communication"); restartCommunication(); // retry sending after restart try { if (communicationActive()) { mqttConnection.connectionPublish(topic, gsonMessage); } else { - logger.warn("Niko Home Control: failed to restart communication"); - connectionLost(); + logger.debug("failed to restart communication"); } } catch (MqttException e1) { - logger.warn("Niko Home Control: error resending device command"); - connectionLost(); + message = e1.getMessage(); + message = (message != null) ? message : "Communication error"; + + logger.debug("error resending device command"); + } + if (!communicationActive()) { + connectionLost(message); } } } @@ -791,24 +806,24 @@ public void processMessage(String topic, byte[] payload) { if ((profile + "/system/evt").equals(topic)) { systemEvt(message); } else if ((profile + "/system/rsp").equals(topic)) { - logger.debug("Niko Home Control: received topic {}, payload {}", topic, message); + logger.debug("received topic {}, payload {}", topic, message); systeminfoPublishRsp(message); } else if ((profile + "/notification/evt").equals(topic)) { - logger.debug("Niko Home Control: received topic {}, payload {}", topic, message); + logger.debug("received topic {}, payload {}", topic, message); notificationEvt(message); } else if ((profile + "/control/devices/evt").equals(topic)) { - logger.trace("Niko Home Control: received topic {}, payload {}", topic, message); + logger.trace("received topic {}, payload {}", topic, message); devicesEvt(message); } else if ((profile + "/control/devices/rsp").equals(topic)) { - logger.debug("Niko Home Control: received topic {}, payload {}", topic, message); + logger.debug("received topic {}, payload {}", topic, message); devicesListRsp(message); } else if ((profile + "/authentication/rsp").equals(topic)) { - logger.debug("Niko Home Control: received topic {}, payload {}", topic, message); + logger.debug("received topic {}, payload {}", topic, message); servicesListRsp(message); } else if ((profile + "/control/devices.error").equals(topic)) { - logger.warn("Niko Home Control: received error {}", message); + logger.warn("received error {}", message); } else { - logger.trace("Niko Home Control: not acted on received message topic {}, payload {}", topic, message); + logger.trace("not acted on received message topic {}, payload {}", topic, message); } } @@ -845,10 +860,14 @@ public String getServices() { public void connectionStateChanged(MqttConnectionState state, @Nullable Throwable error) { if (error != null) { logger.debug("Connection state: {}", state, error); - restartCommunication(); + String message = error.getMessage(); + message = (message != null) ? message : "Error communicating with the controller"; + if (!MqttConnectionState.CONNECTING.equals(state)) { + // This is a connection loss, try to restart + restartCommunication(); + } if (!communicationActive()) { - logger.warn("Niko Home Control: failed to restart communication"); - connectionLost(); + connectionLost(message); } } else { logger.trace("Connection state: {}", state); diff --git a/bundles/org.openhab.binding.nikohomecontrol/src/main/resources/OH-INF/thing/thing-types.xml b/bundles/org.openhab.binding.nikohomecontrol/src/main/resources/OH-INF/thing/thing-types.xml index c2b637da355d9..6382d8656b414 100644 --- a/bundles/org.openhab.binding.nikohomecontrol/src/main/resources/OH-INF/thing/thing-types.xml +++ b/bundles/org.openhab.binding.nikohomecontrol/src/main/resources/OH-INF/thing/thing-types.xml @@ -127,7 +127,7 @@ Niko Home Control action ID false - + Step value used for increase/decrease of dimmer brightness, default 10% 10 @@ -151,6 +151,12 @@ Niko Home Control action ID false + + + Invert rollershutter direction + false + true + diff --git a/bundles/org.openhab.binding.novafinedust/src/main/java/org/openhab/binding/novafinedust/internal/SDS011Handler.java b/bundles/org.openhab.binding.novafinedust/src/main/java/org/openhab/binding/novafinedust/internal/SDS011Handler.java index 87cec1ad18bc2..608663ebd84f8 100644 --- a/bundles/org.openhab.binding.novafinedust/src/main/java/org/openhab/binding/novafinedust/internal/SDS011Handler.java +++ b/bundles/org.openhab.binding.novafinedust/src/main/java/org/openhab/binding/novafinedust/internal/SDS011Handler.java @@ -16,6 +16,7 @@ import java.time.Duration; import java.time.ZonedDateTime; import java.util.TooManyListenersException; +import java.util.concurrent.Future; import java.util.concurrent.ScheduledFuture; import java.util.concurrent.TimeUnit; @@ -62,6 +63,7 @@ public class SDS011Handler extends BaseThingHandler { private @Nullable ScheduledFuture dataReadJob; private @Nullable ScheduledFuture connectionMonitor; + private @Nullable Future initJob; private @Nullable ScheduledFuture retryInitJob; private ZonedDateTime lastCommunication = ZonedDateTime.now(); @@ -115,10 +117,10 @@ public void initialize() { if (config.reporting) { timeBetweenDataShouldArrive = Duration.ofMinutes(config.reportingInterval); - scheduler.submit(() -> initializeCommunicator(WorkMode.REPORTING, timeBetweenDataShouldArrive)); + initJob = scheduler.submit(() -> initializeCommunicator(WorkMode.REPORTING, timeBetweenDataShouldArrive)); } else { timeBetweenDataShouldArrive = Duration.ofSeconds(config.pollingInterval); - scheduler.submit(() -> initializeCommunicator(WorkMode.POLLING, timeBetweenDataShouldArrive)); + initJob = scheduler.submit(() -> initializeCommunicator(WorkMode.POLLING, timeBetweenDataShouldArrive)); } } @@ -130,66 +132,45 @@ private void initializeCommunicator(WorkMode mode, Duration interval) { return; } - boolean initSuccessful = false; - int retryInit = 3; - int retryCount = 0; - // sometimes the device is a little difficult and needs multiple configuration attempts - while (!initSuccessful && retryCount < retryInit) { - logger.trace("Trying to initialize device attempt={}", retryCount); - initSuccessful = doInit(localCommunicator, mode, interval); - retryCount++; - } - - if (initSuccessful) { - lastCommunication = ZonedDateTime.now(); - updateStatus(ThingStatus.ONLINE); + logger.trace("Trying to initialize device"); + doInit(localCommunicator, mode, interval); - if (mode == WorkMode.POLLING) { - dataReadJob = scheduler.scheduleWithFixedDelay(() -> { - try { - localCommunicator.requestSensorData(); - } catch (IOException e) { - updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.OFFLINE.COMMUNICATION_ERROR, - "Cannot query data from device"); - } - }, 2, config.pollingInterval, TimeUnit.SECONDS); - } else { - // start a job that reads the port until data arrives - int reportingReadStartDelay = 10; - int startReadBeforeDataArrives = 5; - long readReportedDataInterval = (config.reportingInterval * 60) - reportingReadStartDelay - - startReadBeforeDataArrives; - logger.trace("Scheduling job to receive reported values"); - dataReadJob = scheduler.scheduleWithFixedDelay(() -> { - try { - localCommunicator.readSensorData(); - } catch (IOException e) { - updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.OFFLINE.COMMUNICATION_ERROR, - "Cannot query data from device, because: " + e.getMessage()); - } - }, reportingReadStartDelay, readReportedDataInterval, TimeUnit.SECONDS); - } + lastCommunication = ZonedDateTime.now(); - Duration connectionMonitorStartDelay = timeBetweenDataShouldArrive - .plus(CONNECTION_MONITOR_START_DELAY_OFFSET); - connectionMonitor = scheduler.scheduleWithFixedDelay(this::verifyIfStillConnected, - connectionMonitorStartDelay.getSeconds(), timeBetweenDataShouldArrive.getSeconds(), - TimeUnit.SECONDS); + if (mode == WorkMode.POLLING) { + dataReadJob = scheduler.scheduleWithFixedDelay(() -> { + try { + localCommunicator.requestSensorData(); + } catch (IOException e) { + updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.OFFLINE.COMMUNICATION_ERROR, + "Cannot query data from device"); + } + }, 2, config.pollingInterval, TimeUnit.SECONDS); } else { - updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.OFFLINE.COMMUNICATION_ERROR, - "Commands and replies from the device don't seem to match"); - logger.debug( - "Could not configure sensor -> setting Thing to OFFLINE, disposing the handler and reschedule initialize in {} seconds", - RETRY_INIT_DELAY); - doDispose(false); - retryInitJob = scheduler.schedule(this::initialize, RETRY_INIT_DELAY.getSeconds(), TimeUnit.SECONDS); + // start a job that reads the port until data arrives + int reportingReadStartDelay = 10; + int startReadBeforeDataArrives = 5; + long readReportedDataInterval = (config.reportingInterval * 60) - reportingReadStartDelay + - startReadBeforeDataArrives; + logger.trace("Scheduling job to receive reported values"); + dataReadJob = scheduler.scheduleWithFixedDelay(() -> { + try { + localCommunicator.readSensorData(); + } catch (IOException e) { + updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.OFFLINE.COMMUNICATION_ERROR, + "Cannot query data from device, because: " + e.getMessage()); + } + }, reportingReadStartDelay, readReportedDataInterval, TimeUnit.SECONDS); } + + Duration connectionMonitorStartDelay = timeBetweenDataShouldArrive.plus(CONNECTION_MONITOR_START_DELAY_OFFSET); + connectionMonitor = scheduler.scheduleWithFixedDelay(this::verifyIfStillConnected, + connectionMonitorStartDelay.getSeconds(), timeBetweenDataShouldArrive.getSeconds(), TimeUnit.SECONDS); } - private boolean doInit(SDS011Communicator localCommunicator, WorkMode mode, Duration interval) { - boolean initSuccessful = false; + private void doInit(SDS011Communicator localCommunicator, WorkMode mode, Duration interval) { try { - initSuccessful = localCommunicator.initialize(mode, interval); + localCommunicator.initialize(mode, interval); } catch (final IOException ex) { updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.OFFLINE.COMMUNICATION_ERROR, "I/O error!"); } catch (PortInUseException e) { @@ -201,7 +182,6 @@ private boolean doInit(SDS011Communicator localCommunicator, WorkMode mode, Dura updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.OFFLINE.COMMUNICATION_ERROR, "Cannot set serial port parameters"); } - return initSuccessful; } private boolean validateConfiguration() { @@ -243,6 +223,12 @@ private void doDispose(boolean sendDeviceToSleep) { this.connectionMonitor = null; } + Future localInitJob = this.initJob; + if (localInitJob != null) { + localInitJob.cancel(true); + this.initJob = null; + } + ScheduledFuture localRetryOpenPortJob = this.retryInitJob; if (localRetryOpenPortJob != null) { localRetryOpenPortJob.cancel(true); diff --git a/bundles/org.openhab.binding.novafinedust/src/main/java/org/openhab/binding/novafinedust/internal/sds011protocol/SDS011Communicator.java b/bundles/org.openhab.binding.novafinedust/src/main/java/org/openhab/binding/novafinedust/internal/sds011protocol/SDS011Communicator.java index 3e3c11e32980d..8ea0299026d98 100644 --- a/bundles/org.openhab.binding.novafinedust/src/main/java/org/openhab/binding/novafinedust/internal/sds011protocol/SDS011Communicator.java +++ b/bundles/org.openhab.binding.novafinedust/src/main/java/org/openhab/binding/novafinedust/internal/sds011protocol/SDS011Communicator.java @@ -29,12 +29,8 @@ import org.openhab.binding.novafinedust.internal.SDS011Handler; import org.openhab.binding.novafinedust.internal.sds011protocol.messages.CommandMessage; import org.openhab.binding.novafinedust.internal.sds011protocol.messages.Constants; -import org.openhab.binding.novafinedust.internal.sds011protocol.messages.ModeReply; -import org.openhab.binding.novafinedust.internal.sds011protocol.messages.SensorFirmwareReply; import org.openhab.binding.novafinedust.internal.sds011protocol.messages.SensorMeasuredDataReply; import org.openhab.binding.novafinedust.internal.sds011protocol.messages.SensorReply; -import org.openhab.binding.novafinedust.internal.sds011protocol.messages.SleepReply; -import org.openhab.binding.novafinedust.internal.sds011protocol.messages.WorkingPeriodReply; import org.openhab.core.io.transport.serial.PortInUseException; import org.openhab.core.io.transport.serial.SerialPort; import org.openhab.core.io.transport.serial.SerialPortIdentifier; @@ -52,7 +48,7 @@ @NonNullByDefault public class SDS011Communicator { - private static final int MAX_SENDOR_REPORTINGS_UNTIL_EXPECTED_REPLY = 20; + private static final int MAX_READ_UNTIL_SENSOR_DATA = 6; // at least 6 because we send 5 configuration commands private final Logger logger = LoggerFactory.getLogger(SDS011Communicator.class); @@ -82,9 +78,8 @@ public SDS011Communicator(SDS011Handler thingHandler, SerialPortIdentifier portI * @throws IOException * @throws UnsupportedCommOperationException */ - public boolean initialize(WorkMode mode, Duration interval) + public void initialize(WorkMode mode, Duration interval) throws PortInUseException, TooManyListenersException, IOException, UnsupportedCommOperationException { - boolean initSuccessful = true; logger.trace("Initializing with mode={}, interval={}", mode, interval); @@ -102,30 +97,28 @@ public boolean initialize(WorkMode mode, Duration interval) logger.trace("Input and Outputstream opened for the port"); // wake up the device - initSuccessful &= sendSleep(false); - logger.trace("Wake up call done, initSuccessful={}", initSuccessful); - initSuccessful &= getFirmware(); - logger.trace("Firmware requested, initSuccessful={}", initSuccessful); + sendSleep(false); + logger.trace("Wake up call done"); + getFirmware(); + logger.trace("Firmware requested"); if (mode == WorkMode.POLLING) { - initSuccessful &= setMode(WorkMode.POLLING); - logger.trace("Polling mode set, initSuccessful={}", initSuccessful); - initSuccessful &= setWorkingPeriod((byte) 0); - logger.trace("Working period for polling set, initSuccessful={}", initSuccessful); + setMode(WorkMode.POLLING); + logger.trace("Polling mode set"); + setWorkingPeriod((byte) 0); + logger.trace("Working period for polling set"); } else { // reporting - initSuccessful &= setWorkingPeriod((byte) interval.toMinutes()); - logger.trace("Working period for reporting set, initSuccessful={}", initSuccessful); - initSuccessful &= setMode(WorkMode.REPORTING); - logger.trace("Reporting mode set, initSuccessful={}", initSuccessful); + setWorkingPeriod((byte) interval.toMinutes()); + logger.trace("Working period for reporting set"); + setMode(WorkMode.REPORTING); + logger.trace("Reporting mode set"); } this.serialPort = localSerialPort; - - return initSuccessful; } - private @Nullable SensorReply sendCommand(CommandMessage message) throws IOException { + private void sendCommand(CommandMessage message) throws IOException { byte[] commandData = message.getBytes(); if (logger.isDebugEnabled()) { logger.debug("Will send command: {} ({})", HexUtils.bytesToHex(commandData), Arrays.toString(commandData)); @@ -139,23 +132,12 @@ public boolean initialize(WorkMode mode, Duration interval) } try { - // Give the sensor some time to handle the command + // Give the sensor some time to handle the command before doing something else with it Thread.sleep(500); } catch (InterruptedException e) { - logger.warn("Problem while waiting for reading a reply to our command."); + logger.warn("Interrupted while waiting after sending command={}", message); Thread.currentThread().interrupt(); } - SensorReply reply = readReply(); - // in case there is still another reporting active, we want to discard the sensor data and read the reply to our - // command again, this might happen more often in case the sensor has buffered some data - for (int i = 0; i < MAX_SENDOR_REPORTINGS_UNTIL_EXPECTED_REPLY; i++) { - if (reply instanceof SensorMeasuredDataReply) { - reply = readReply(); - } else { - break; - } - } - return reply; } private void write(byte[] commandData) throws IOException { @@ -166,21 +148,13 @@ private void write(byte[] commandData) throws IOException { } } - private boolean setWorkingPeriod(byte period) throws IOException { + private void setWorkingPeriod(byte period) throws IOException { CommandMessage m = new CommandMessage(Command.WORKING_PERIOD, new byte[] { Constants.SET_ACTION, period }); logger.debug("Sending work period: {}", period); - SensorReply reply = sendCommand(m); - logger.debug("Got reply to setWorkingPeriod command: {}", reply); - if (reply instanceof WorkingPeriodReply) { - WorkingPeriodReply wpReply = (WorkingPeriodReply) reply; - if (wpReply.getPeriod() == period && wpReply.getActionType() == Constants.SET_ACTION) { - return true; - } - } - return false; + sendCommand(m); } - private boolean setMode(WorkMode workMode) throws IOException { + private void setMode(WorkMode workMode) throws IOException { byte haveToRequestData = 0; if (workMode == WorkMode.POLLING) { haveToRequestData = 1; @@ -188,18 +162,10 @@ private boolean setMode(WorkMode workMode) throws IOException { CommandMessage m = new CommandMessage(Command.MODE, new byte[] { Constants.SET_ACTION, haveToRequestData }); logger.debug("Sending mode: {}", workMode); - SensorReply reply = sendCommand(m); - logger.debug("Got reply to setMode command: {}", reply); - if (reply instanceof ModeReply) { - ModeReply mr = (ModeReply) reply; - if (mr.getActionType() == Constants.SET_ACTION && mr.getMode() == workMode) { - return true; - } - } - return false; + sendCommand(m); } - private boolean sendSleep(boolean doSleep) throws IOException { + private void sendSleep(boolean doSleep) throws IOException { byte payload = (byte) 1; if (doSleep) { payload = (byte) 0; @@ -207,38 +173,21 @@ private boolean sendSleep(boolean doSleep) throws IOException { CommandMessage m = new CommandMessage(Command.SLEEP, new byte[] { Constants.SET_ACTION, payload }); logger.debug("Sending doSleep: {}", doSleep); - SensorReply reply = sendCommand(m); - logger.debug("Got reply to sendSleep command: {}", reply); + sendCommand(m); + // as it turns out, the protocol doesn't work as described: sometimes the device just wakes up without replying + // to us. Hence we should not wait for a reply, but just force to wake it up to then send out our configuration + // commands if (!doSleep) { // sometimes the sensor does not wakeup on the first attempt, thus we try again - for (int i = 0; reply == null && i < 3; i++) { - reply = sendCommand(m); - logger.debug("Got reply to sendSleep command after retry#{}: {}", i + 1, reply); - } + sendCommand(m); } - - if (reply instanceof SleepReply) { - SleepReply sr = (SleepReply) reply; - if (sr.getActionType() == Constants.SET_ACTION && sr.getSleep() == payload) { - return true; - } - } - return false; } - private boolean getFirmware() throws IOException { + private void getFirmware() throws IOException { CommandMessage m = new CommandMessage(Command.FIRMWARE, new byte[] {}); logger.debug("Sending get firmware request"); - SensorReply reply = sendCommand(m); - logger.debug("Got reply to getFirmware command: {}", reply); - - if (reply instanceof SensorFirmwareReply) { - SensorFirmwareReply fwReply = (SensorFirmwareReply) reply; - thingHandler.setFirmware(fwReply.getFirmware()); - return true; - } - return false; + sendCommand(m); } /** @@ -256,7 +205,7 @@ public void requestSensorData() throws IOException { try { Thread.sleep(200); // give the device some time to handle the command } catch (InterruptedException e) { - logger.warn("Interrupted while waiting before reading a reply to our rquest data command."); + logger.warn("Interrupted while waiting before reading a reply to our request data command."); Thread.currentThread().interrupt(); } readSensorData(); @@ -271,7 +220,7 @@ public void requestSensorData() throws IOException { if (localInpuStream != null) { logger.trace("Reading for reply until first byte is found"); while ((b = localInpuStream.read()) != Constants.MESSAGE_START_AS_INT) { - logger.debug("Trying to find first reply byte now..."); + // logger.trace("Trying to find first reply byte now..."); } readBuffer[0] = (byte) b; int remainingBytesRead = localInpuStream.read(readBuffer, 1, Constants.REPLY_LENGTH - 1); @@ -286,16 +235,26 @@ public void requestSensorData() throws IOException { public void readSensorData() throws IOException { logger.trace("readSensorData() called"); + + boolean foundSensorData = doRead(); + for (int i = 0; !foundSensorData && i < MAX_READ_UNTIL_SENSOR_DATA; i++) { + foundSensorData = doRead(); + } + } + + private boolean doRead() throws IOException { SensorReply reply = readReply(); - logger.trace("readSensorData(): Read reply={}", reply); + logger.trace("doRead(): Read reply={}", reply); if (reply instanceof SensorMeasuredDataReply) { SensorMeasuredDataReply sensorData = (SensorMeasuredDataReply) reply; logger.trace("We received sensor data"); if (sensorData.isValidData()) { logger.trace("Sensor data is valid => updating channels"); thingHandler.updateChannels(sensorData); + return true; } } + return false; } /** diff --git a/bundles/org.openhab.binding.nuki/README.md b/bundles/org.openhab.binding.nuki/README.md index 8fbb20bd94c68..19bd16cf68448 100644 --- a/bundles/org.openhab.binding.nuki/README.md +++ b/bundles/org.openhab.binding.nuki/README.md @@ -93,7 +93,7 @@ sitemap nuki label="Nuki Smart Lock" { Switch item=Frontdoor_Lock } Frame label="Channel State used for lock actions" { - Switch item=Frontdoor_State mappings=[2="Unlock", 7="Unlatch", 1002="LnGo", 1007="LnGoU", 4="Lock"] + Switch item=Frontdoor_LockState mappings=[2="Unlock", 7="Unlatch", 1002="LnGo", 1007="LnGoU", 4="Lock"] } Frame label="Channel State" { Text item=Frontdoor_LockState label="Lock State [MAP(nukilockstates.map):%s]" diff --git a/bundles/org.openhab.binding.oceanic/src/main/java/org/openhab/binding/oceanic/internal/handler/NetworkOceanicThingHandler.java b/bundles/org.openhab.binding.oceanic/src/main/java/org/openhab/binding/oceanic/internal/handler/NetworkOceanicThingHandler.java index f968b1618b013..8b1f9b39700f1 100644 --- a/bundles/org.openhab.binding.oceanic/src/main/java/org/openhab/binding/oceanic/internal/handler/NetworkOceanicThingHandler.java +++ b/bundles/org.openhab.binding.oceanic/src/main/java/org/openhab/binding/oceanic/internal/handler/NetworkOceanicThingHandler.java @@ -21,7 +21,7 @@ import java.util.concurrent.ScheduledFuture; import java.util.concurrent.TimeUnit; -import org.apache.commons.lang.StringUtils; +import org.apache.commons.lang3.StringUtils; import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.Nullable; import org.openhab.binding.oceanic.internal.NetworkOceanicBindingConfiguration; diff --git a/bundles/org.openhab.binding.oceanic/src/main/java/org/openhab/binding/oceanic/internal/handler/SerialOceanicThingHandler.java b/bundles/org.openhab.binding.oceanic/src/main/java/org/openhab/binding/oceanic/internal/handler/SerialOceanicThingHandler.java index bb87024df73f3..f9d54b8035826 100644 --- a/bundles/org.openhab.binding.oceanic/src/main/java/org/openhab/binding/oceanic/internal/handler/SerialOceanicThingHandler.java +++ b/bundles/org.openhab.binding.oceanic/src/main/java/org/openhab/binding/oceanic/internal/handler/SerialOceanicThingHandler.java @@ -19,7 +19,7 @@ import java.util.Arrays; import java.util.Enumeration; -import org.apache.commons.lang.StringUtils; +import org.apache.commons.lang3.StringUtils; import org.openhab.binding.oceanic.internal.SerialOceanicBindingConfiguration; import org.openhab.binding.oceanic.internal.Throttler; import org.openhab.core.thing.Thing; diff --git a/bundles/org.openhab.binding.omnikinverter/src/main/java/org/openhab/binding/omnikinverter/internal/OmnikInverter.java b/bundles/org.openhab.binding.omnikinverter/src/main/java/org/openhab/binding/omnikinverter/internal/OmnikInverter.java index fc3bc89caa2ba..9ef1d649965cd 100644 --- a/bundles/org.openhab.binding.omnikinverter/src/main/java/org/openhab/binding/omnikinverter/internal/OmnikInverter.java +++ b/bundles/org.openhab.binding.omnikinverter/src/main/java/org/openhab/binding/omnikinverter/internal/OmnikInverter.java @@ -16,7 +16,7 @@ import java.net.Socket; import java.nio.ByteBuffer; -import org.apache.commons.lang.ArrayUtils; +import org.apache.commons.lang3.ArrayUtils; import org.eclipse.jdt.annotation.NonNullByDefault; /** diff --git a/bundles/org.openhab.binding.omnilink/pom.xml b/bundles/org.openhab.binding.omnilink/pom.xml index ee0e2767fb4ae..7b68c65c9417f 100644 --- a/bundles/org.openhab.binding.omnilink/pom.xml +++ b/bundles/org.openhab.binding.omnilink/pom.xml @@ -18,7 +18,7 @@ com.github.digitaldan jomnilink - 1.4.0 + 1.4.2 compile diff --git a/bundles/org.openhab.binding.omnilink/src/main/java/org/openhab/binding/omnilink/internal/SystemType.java b/bundles/org.openhab.binding.omnilink/src/main/java/org/openhab/binding/omnilink/internal/SystemType.java index 5ac9cf644dfd1..a17159463b54b 100644 --- a/bundles/org.openhab.binding.omnilink/src/main/java/org/openhab/binding/omnilink/internal/SystemType.java +++ b/bundles/org.openhab.binding.omnilink/src/main/java/org/openhab/binding/omnilink/internal/SystemType.java @@ -13,6 +13,7 @@ package org.openhab.binding.omnilink.internal; import java.util.Arrays; +import java.util.Optional; import java.util.Set; import org.eclipse.jdt.annotation.NonNullByDefault; @@ -34,8 +35,7 @@ public enum SystemType { this.modelNumbers = Set.of(modelNumbers); } - public static SystemType getType(int modelNumber) { - return Arrays.stream(values()).filter(s -> s.modelNumbers.contains(modelNumber)).findFirst() - .orElseThrow(IllegalArgumentException::new); + public static Optional getType(int modelNumber) { + return Arrays.stream(values()).filter(s -> s.modelNumbers.contains(modelNumber)).findFirst(); } } diff --git a/bundles/org.openhab.binding.omnilink/src/main/java/org/openhab/binding/omnilink/internal/handler/TemperatureFormat.java b/bundles/org.openhab.binding.omnilink/src/main/java/org/openhab/binding/omnilink/internal/TemperatureFormat.java similarity index 92% rename from bundles/org.openhab.binding.omnilink/src/main/java/org/openhab/binding/omnilink/internal/handler/TemperatureFormat.java rename to bundles/org.openhab.binding.omnilink/src/main/java/org/openhab/binding/omnilink/internal/TemperatureFormat.java index 8379e0dfb2ef6..58f02afefec98 100644 --- a/bundles/org.openhab.binding.omnilink/src/main/java/org/openhab/binding/omnilink/internal/handler/TemperatureFormat.java +++ b/bundles/org.openhab.binding.omnilink/src/main/java/org/openhab/binding/omnilink/internal/TemperatureFormat.java @@ -10,7 +10,7 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.binding.omnilink.internal.handler; +package org.openhab.binding.omnilink.internal; import org.eclipse.jdt.annotation.NonNullByDefault; @@ -33,7 +33,7 @@ public float omniToFormat(int omniNumber) { } @Override - public int formatToOmni(int celsius) { + public int formatToOmni(float celsius) { return MessageUtils.CToOmni(celsius); } }, @@ -44,7 +44,7 @@ public float omniToFormat(int omniNumber) { } @Override - public int formatToOmni(int fahrenheit) { + public int formatToOmni(float fahrenheit) { return MessageUtils.FtoOmni(fahrenheit); } }; @@ -69,7 +69,7 @@ private TemperatureFormat(int formatNumber) { * @param format Number in the current format. * @return Omni formatted number. */ - public abstract int formatToOmni(int format); + public abstract int formatToOmni(float format); /** * Get the number which identifies this format as defined by the omniprotocol. diff --git a/bundles/org.openhab.binding.omnilink/src/main/java/org/openhab/binding/omnilink/internal/discovery/ObjectPropertyRequest.java b/bundles/org.openhab.binding.omnilink/src/main/java/org/openhab/binding/omnilink/internal/discovery/ObjectPropertyRequest.java index 7cf1dd8a2a65f..f44c8e12b6eba 100644 --- a/bundles/org.openhab.binding.omnilink/src/main/java/org/openhab/binding/omnilink/internal/discovery/ObjectPropertyRequest.java +++ b/bundles/org.openhab.binding.omnilink/src/main/java/org/openhab/binding/omnilink/internal/discovery/ObjectPropertyRequest.java @@ -17,7 +17,7 @@ import java.util.List; import org.eclipse.jdt.annotation.NonNullByDefault; -import org.openhab.binding.omnilink.internal.handler.BridgeOfflineException; +import org.openhab.binding.omnilink.internal.exceptions.BridgeOfflineException; import org.openhab.binding.omnilink.internal.handler.OmnilinkBridgeHandler; import org.slf4j.Logger; import org.slf4j.LoggerFactory; diff --git a/bundles/org.openhab.binding.omnilink/src/main/java/org/openhab/binding/omnilink/internal/discovery/OmnilinkDiscoveryService.java b/bundles/org.openhab.binding.omnilink/src/main/java/org/openhab/binding/omnilink/internal/discovery/OmnilinkDiscoveryService.java index 86ceadf7c6986..e796ad66112c4 100644 --- a/bundles/org.openhab.binding.omnilink/src/main/java/org/openhab/binding/omnilink/internal/discovery/OmnilinkDiscoveryService.java +++ b/bundles/org.openhab.binding.omnilink/src/main/java/org/openhab/binding/omnilink/internal/discovery/OmnilinkDiscoveryService.java @@ -21,11 +21,12 @@ import java.util.LinkedList; import java.util.List; import java.util.Map; +import java.util.Optional; import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.Nullable; import org.openhab.binding.omnilink.internal.SystemType; -import org.openhab.binding.omnilink.internal.handler.BridgeOfflineException; +import org.openhab.binding.omnilink.internal.exceptions.BridgeOfflineException; import org.openhab.binding.omnilink.internal.handler.OmnilinkBridgeHandler; import org.openhab.core.config.discovery.AbstractDiscoveryService; import org.openhab.core.config.discovery.DiscoveryResult; @@ -62,7 +63,7 @@ public class OmnilinkDiscoveryService extends AbstractDiscoveryService private final Logger logger = LoggerFactory.getLogger(OmnilinkDiscoveryService.class); private static final int DISCOVER_TIMEOUT_SECONDS = 30; private @Nullable OmnilinkBridgeHandler bridgeHandler; - private @Nullable SystemType systemType; + private Optional systemType = Optional.empty(); private @Nullable List areas; /** @@ -389,7 +390,6 @@ private void discoverThermostats() { int thingNumber = areaProperties.getNumber(); String thingName = areaProperties.getName(); String thingID = Integer.toString(thingNumber); - ThingUID thingUID = null; /* * It seems that for simple OmniLink Controller configurations there @@ -405,27 +405,24 @@ private void discoverThermostats() { Map properties = Map.of(THING_PROPERTIES_NAME, thingName); - final SystemType systemType = this.systemType; - if (systemType != null) { - switch (systemType) { + final String name = thingName; + systemType.ifPresentOrElse(t -> { + ThingUID thingUID = null; + switch (t) { case LUMINA: thingUID = new ThingUID(THING_TYPE_LUMINA_AREA, bridgeUID, thingID); break; - case OMNI: - thingUID = new ThingUID(THING_TYPE_OMNI_AREA, bridgeUID, thingID); - break; default: - throw new IllegalStateException("Unknown System Type"); + thingUID = new ThingUID(THING_TYPE_OMNI_AREA, bridgeUID, thingID); } - } - - if (thingUID != null) { DiscoveryResult discoveryResult = DiscoveryResultBuilder.create(thingUID).withProperties(properties) .withProperty(THING_PROPERTIES_NUMBER, thingID) - .withRepresentationProperty(THING_PROPERTIES_NUMBER).withBridge(bridgeUID) - .withLabel(thingName).build(); + .withRepresentationProperty(THING_PROPERTIES_NUMBER).withBridge(bridgeUID).withLabel(name) + .build(); thingDiscovered(discoveryResult); - } + }, () -> { + logger.warn("Unknown System Type"); + }); areas.add(areaProperties); } diff --git a/bundles/org.openhab.binding.omnilink/src/main/java/org/openhab/binding/omnilink/internal/handler/BridgeOfflineException.java b/bundles/org.openhab.binding.omnilink/src/main/java/org/openhab/binding/omnilink/internal/exceptions/BridgeOfflineException.java similarity index 85% rename from bundles/org.openhab.binding.omnilink/src/main/java/org/openhab/binding/omnilink/internal/handler/BridgeOfflineException.java rename to bundles/org.openhab.binding.omnilink/src/main/java/org/openhab/binding/omnilink/internal/exceptions/BridgeOfflineException.java index 891d4c4a6994b..7b35721280d4c 100644 --- a/bundles/org.openhab.binding.omnilink/src/main/java/org/openhab/binding/omnilink/internal/handler/BridgeOfflineException.java +++ b/bundles/org.openhab.binding.omnilink/src/main/java/org/openhab/binding/omnilink/internal/exceptions/BridgeOfflineException.java @@ -10,7 +10,7 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.binding.omnilink.internal.handler; +package org.openhab.binding.omnilink.internal.exceptions; import org.eclipse.jdt.annotation.NonNullByDefault; @@ -21,9 +21,8 @@ * @author Craig Hamilton - Initial contribution */ @NonNullByDefault +@SuppressWarnings("serial") public class BridgeOfflineException extends Exception { - private static final long serialVersionUID = -9081729691518514097L; - public BridgeOfflineException(Exception e) { super(e); } diff --git a/bundles/org.openhab.binding.omnilink/src/main/java/org/openhab/binding/omnilink/internal/handler/AbstractAreaHandler.java b/bundles/org.openhab.binding.omnilink/src/main/java/org/openhab/binding/omnilink/internal/handler/AbstractAreaHandler.java index f848860a3169b..1201d30b9b16c 100644 --- a/bundles/org.openhab.binding.omnilink/src/main/java/org/openhab/binding/omnilink/internal/handler/AbstractAreaHandler.java +++ b/bundles/org.openhab.binding.omnilink/src/main/java/org/openhab/binding/omnilink/internal/handler/AbstractAreaHandler.java @@ -22,6 +22,7 @@ import org.eclipse.jdt.annotation.NonNullByDefault; import org.openhab.binding.omnilink.internal.AreaAlarm; +import org.openhab.binding.omnilink.internal.exceptions.BridgeOfflineException; import org.openhab.core.library.types.DecimalType; import org.openhab.core.library.types.OnOffType; import org.openhab.core.library.types.StringType; diff --git a/bundles/org.openhab.binding.omnilink/src/main/java/org/openhab/binding/omnilink/internal/handler/AbstractOmnilinkHandler.java b/bundles/org.openhab.binding.omnilink/src/main/java/org/openhab/binding/omnilink/internal/handler/AbstractOmnilinkHandler.java index f17f716a1cd32..587b253dc6b38 100644 --- a/bundles/org.openhab.binding.omnilink/src/main/java/org/openhab/binding/omnilink/internal/handler/AbstractOmnilinkHandler.java +++ b/bundles/org.openhab.binding.omnilink/src/main/java/org/openhab/binding/omnilink/internal/handler/AbstractOmnilinkHandler.java @@ -22,6 +22,7 @@ import org.eclipse.jdt.annotation.Nullable; import org.openhab.binding.omnilink.internal.discovery.ObjectPropertyRequest; import org.openhab.binding.omnilink.internal.discovery.ObjectPropertyRequests; +import org.openhab.binding.omnilink.internal.exceptions.BridgeOfflineException; import org.openhab.core.thing.Bridge; import org.openhab.core.thing.Thing; import org.openhab.core.thing.binding.BaseThingHandler; diff --git a/bundles/org.openhab.binding.omnilink/src/main/java/org/openhab/binding/omnilink/internal/handler/AudioSourceHandler.java b/bundles/org.openhab.binding.omnilink/src/main/java/org/openhab/binding/omnilink/internal/handler/AudioSourceHandler.java index 9846edf7d12da..e5501cc60be10 100644 --- a/bundles/org.openhab.binding.omnilink/src/main/java/org/openhab/binding/omnilink/internal/handler/AudioSourceHandler.java +++ b/bundles/org.openhab.binding.omnilink/src/main/java/org/openhab/binding/omnilink/internal/handler/AudioSourceHandler.java @@ -22,6 +22,7 @@ import org.eclipse.jdt.annotation.Nullable; import org.openhab.binding.omnilink.internal.discovery.ObjectPropertyRequest; import org.openhab.binding.omnilink.internal.discovery.ObjectPropertyRequests; +import org.openhab.binding.omnilink.internal.exceptions.BridgeOfflineException; import org.openhab.core.library.types.OnOffType; import org.openhab.core.library.types.StringType; import org.openhab.core.thing.ChannelUID; diff --git a/bundles/org.openhab.binding.omnilink/src/main/java/org/openhab/binding/omnilink/internal/handler/AudioZoneHandler.java b/bundles/org.openhab.binding.omnilink/src/main/java/org/openhab/binding/omnilink/internal/handler/AudioZoneHandler.java index c3e4f4d11346f..6a51b68f34f27 100644 --- a/bundles/org.openhab.binding.omnilink/src/main/java/org/openhab/binding/omnilink/internal/handler/AudioZoneHandler.java +++ b/bundles/org.openhab.binding.omnilink/src/main/java/org/openhab/binding/omnilink/internal/handler/AudioZoneHandler.java @@ -22,6 +22,7 @@ import org.openhab.binding.omnilink.internal.AudioPlayer; import org.openhab.binding.omnilink.internal.discovery.ObjectPropertyRequest; import org.openhab.binding.omnilink.internal.discovery.ObjectPropertyRequests; +import org.openhab.binding.omnilink.internal.exceptions.BridgeOfflineException; import org.openhab.core.library.types.DecimalType; import org.openhab.core.library.types.NextPreviousType; import org.openhab.core.library.types.OnOffType; @@ -37,6 +38,7 @@ import org.slf4j.LoggerFactory; import com.digitaldan.jomnilinkII.Message; +import com.digitaldan.jomnilinkII.MessageTypes.CommandMessage; import com.digitaldan.jomnilinkII.MessageTypes.ObjectStatus; import com.digitaldan.jomnilinkII.MessageTypes.properties.AudioZoneProperties; import com.digitaldan.jomnilinkII.MessageTypes.statuses.ExtendedAudioZoneStatus; @@ -97,7 +99,7 @@ public void handleCommand(ChannelUID channelUID, Command command) { switch (channelUID.getId()) { case CHANNEL_AUDIO_ZONE_POWER: if (command instanceof OnOffType) { - sendOmnilinkCommand(OmniLinkCmd.CMD_AUDIO_ZONE_SET_ON_MUTE.getNumber(), + sendOmnilinkCommand(CommandMessage.CMD_AUDIO_ZONE_SET_ON_AND_MUTE, OnOffType.OFF.equals(command) ? 0 : 1, thingID); } else { logger.debug("Invalid command: {}, must be OnOffType", command); @@ -105,7 +107,7 @@ public void handleCommand(ChannelUID channelUID, Command command) { break; case CHANNEL_AUDIO_ZONE_MUTE: if (command instanceof OnOffType) { - sendOmnilinkCommand(OmniLinkCmd.CMD_AUDIO_ZONE_SET_ON_MUTE.getNumber(), + sendOmnilinkCommand(CommandMessage.CMD_AUDIO_ZONE_SET_ON_AND_MUTE, OnOffType.OFF.equals(command) ? 2 : 3, thingID); } else { logger.debug("Invalid command: {}, must be OnOffType", command); @@ -113,16 +115,16 @@ public void handleCommand(ChannelUID channelUID, Command command) { break; case CHANNEL_AUDIO_ZONE_VOLUME: if (command instanceof PercentType) { - sendOmnilinkCommand(OmniLinkCmd.CMD_AUDIO_ZONE_SET_VOLUME.getNumber(), - ((PercentType) command).intValue(), thingID); + sendOmnilinkCommand(CommandMessage.CMD_AUDIO_ZONE_SET_VOLUME, ((PercentType) command).intValue(), + thingID); } else { logger.debug("Invalid command: {}, must be PercentType", command); } break; case CHANNEL_AUDIO_ZONE_SOURCE: if (command instanceof DecimalType) { - sendOmnilinkCommand(OmniLinkCmd.CMD_AUDIO_ZONE_SET_SOURCE.getNumber(), - ((DecimalType) command).intValue(), thingID); + sendOmnilinkCommand(CommandMessage.CMD_AUDIO_ZONE_SET_SOURCE, ((DecimalType) command).intValue(), + thingID); } else { logger.debug("Invalid command: {}, must be DecimalType", command); } @@ -149,7 +151,7 @@ private void handlePlayPauseCommand(ChannelUID channelUID, PlayPauseType command Optional audioPlayer = bridgeHandler.getAudioPlayer(); if (audioPlayer.isPresent()) { AudioPlayer player = audioPlayer.get(); - sendOmnilinkCommand(OmniLinkCmd.CMD_AUDIO_ZONE_SET_SOURCE.getNumber(), + sendOmnilinkCommand(CommandMessage.CMD_AUDIO_ZONE_SET_SOURCE, PlayPauseType.PLAY.equals(command) ? player.getPlayCommand() : player.getPauseCommand(), thingID); } else { @@ -168,7 +170,7 @@ private void handleNextPreviousCommand(ChannelUID channelUID, NextPreviousType c Optional audioPlayer = bridgeHandler.getAudioPlayer(); if (audioPlayer.isPresent()) { AudioPlayer player = audioPlayer.get(); - sendOmnilinkCommand(OmniLinkCmd.CMD_AUDIO_ZONE_SET_SOURCE.getNumber(), + sendOmnilinkCommand(CommandMessage.CMD_AUDIO_ZONE_SET_SOURCE, NextPreviousType.NEXT.equals(command) ? player.getNextCommand() : player.getPreviousCommand(), thingID); } else { diff --git a/bundles/org.openhab.binding.omnilink/src/main/java/org/openhab/binding/omnilink/internal/handler/ButtonHandler.java b/bundles/org.openhab.binding.omnilink/src/main/java/org/openhab/binding/omnilink/internal/handler/ButtonHandler.java index a288fa00a88f4..64e47498bd31c 100644 --- a/bundles/org.openhab.binding.omnilink/src/main/java/org/openhab/binding/omnilink/internal/handler/ButtonHandler.java +++ b/bundles/org.openhab.binding.omnilink/src/main/java/org/openhab/binding/omnilink/internal/handler/ButtonHandler.java @@ -31,6 +31,7 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import com.digitaldan.jomnilinkII.MessageTypes.CommandMessage; import com.digitaldan.jomnilinkII.MessageTypes.properties.AreaProperties; import com.digitaldan.jomnilinkII.MessageTypes.properties.ButtonProperties; @@ -97,7 +98,7 @@ public void handleCommand(ChannelUID channelUID, Command command) { switch (channelUID.getId()) { case CHANNEL_BUTTON_PRESS: if (command instanceof OnOffType) { - sendOmnilinkCommand(OmniLinkCmd.CMD_BUTTON.getNumber(), 0, thingID); + sendOmnilinkCommand(CommandMessage.CMD_BUTTON, 0, thingID); updateChannels(); } else { logger.debug("Invalid command: {}, must be OnOffType", command); diff --git a/bundles/org.openhab.binding.omnilink/src/main/java/org/openhab/binding/omnilink/internal/handler/ConsoleHandler.java b/bundles/org.openhab.binding.omnilink/src/main/java/org/openhab/binding/omnilink/internal/handler/ConsoleHandler.java index 70340edb48bac..0afb46af4173f 100644 --- a/bundles/org.openhab.binding.omnilink/src/main/java/org/openhab/binding/omnilink/internal/handler/ConsoleHandler.java +++ b/bundles/org.openhab.binding.omnilink/src/main/java/org/openhab/binding/omnilink/internal/handler/ConsoleHandler.java @@ -27,6 +27,8 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import com.digitaldan.jomnilinkII.MessageTypes.CommandMessage; + /** * The {@link ConsoleHandler} defines some methods that are used to * interface with an OmniLink Console. This by extension also defines the @@ -67,7 +69,7 @@ public void handleCommand(ChannelUID channelUID, Command command) { switch (channelUID.getId()) { case CHANNEL_CONSOLE_ENABLE_DISABLE_BEEPER: if (command instanceof StringType) { - sendOmnilinkCommand(OmniLinkCmd.CMD_CONSOLE_ENABLE_DISABLE_BEEPER.getNumber(), + sendOmnilinkCommand(CommandMessage.CMD_CONSOLE_ENABLE_DISABLE_BEEPER, ((StringType) command).equals(StringType.valueOf("OFF")) ? 0 : 1, thingID); } else { logger.debug("Invalid command: {}, must be StringType", command); @@ -75,8 +77,7 @@ public void handleCommand(ChannelUID channelUID, Command command) { break; case CHANNEL_CONSOLE_BEEP: if (command instanceof DecimalType) { - sendOmnilinkCommand(OmniLinkCmd.CMD_CONSOLE_BEEP.getNumber(), ((DecimalType) command).intValue(), - thingID); + sendOmnilinkCommand(CommandMessage.CMD_CONSOLE_BEEP, ((DecimalType) command).intValue(), thingID); } else { logger.debug("Invalid command: {}, must be DecimalType", command); } diff --git a/bundles/org.openhab.binding.omnilink/src/main/java/org/openhab/binding/omnilink/internal/handler/HumiditySensorHandler.java b/bundles/org.openhab.binding.omnilink/src/main/java/org/openhab/binding/omnilink/internal/handler/HumiditySensorHandler.java index bda8fe7df6112..4e865439ead0b 100644 --- a/bundles/org.openhab.binding.omnilink/src/main/java/org/openhab/binding/omnilink/internal/handler/HumiditySensorHandler.java +++ b/bundles/org.openhab.binding.omnilink/src/main/java/org/openhab/binding/omnilink/internal/handler/HumiditySensorHandler.java @@ -22,8 +22,10 @@ import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.Nullable; +import org.openhab.binding.omnilink.internal.TemperatureFormat; import org.openhab.binding.omnilink.internal.discovery.ObjectPropertyRequest; import org.openhab.binding.omnilink.internal.discovery.ObjectPropertyRequests; +import org.openhab.binding.omnilink.internal.exceptions.BridgeOfflineException; import org.openhab.core.library.types.QuantityType; import org.openhab.core.library.unit.Units; import org.openhab.core.thing.ChannelUID; @@ -36,6 +38,7 @@ import org.slf4j.LoggerFactory; import com.digitaldan.jomnilinkII.Message; +import com.digitaldan.jomnilinkII.MessageTypes.CommandMessage; import com.digitaldan.jomnilinkII.MessageTypes.ObjectStatus; import com.digitaldan.jomnilinkII.MessageTypes.properties.AreaProperties; import com.digitaldan.jomnilinkII.MessageTypes.properties.AuxSensorProperties; @@ -112,13 +115,13 @@ public void handleCommand(ChannelUID channelUID, Command command) { switch (channelUID.getId()) { case CHANNEL_AUX_LOW_SETPOINT: - sendOmnilinkCommand(OmniLinkCmd.CMD_THERMO_SET_HEAT_LOW_POINT.getNumber(), - TemperatureFormat.FAHRENHEIT.formatToOmni(((QuantityType) command).intValue()), + sendOmnilinkCommand(CommandMessage.CMD_THERMO_SET_HEAT_POINT, + TemperatureFormat.FAHRENHEIT.formatToOmni(((QuantityType) command).floatValue()), thingID); break; case CHANNEL_AUX_HIGH_SETPOINT: - sendOmnilinkCommand(OmniLinkCmd.CMD_THERMO_SET_COOL_HIGH_POINT.getNumber(), - TemperatureFormat.FAHRENHEIT.formatToOmni(((QuantityType) command).intValue()), + sendOmnilinkCommand(CommandMessage.CMD_THERMO_SET_COOL_POINT, + TemperatureFormat.FAHRENHEIT.formatToOmni(((QuantityType) command).floatValue()), thingID); break; default: diff --git a/bundles/org.openhab.binding.omnilink/src/main/java/org/openhab/binding/omnilink/internal/handler/LockHandler.java b/bundles/org.openhab.binding.omnilink/src/main/java/org/openhab/binding/omnilink/internal/handler/LockHandler.java index 7c7ceed68e529..6c2bfa8f60b93 100644 --- a/bundles/org.openhab.binding.omnilink/src/main/java/org/openhab/binding/omnilink/internal/handler/LockHandler.java +++ b/bundles/org.openhab.binding.omnilink/src/main/java/org/openhab/binding/omnilink/internal/handler/LockHandler.java @@ -21,6 +21,7 @@ import org.eclipse.jdt.annotation.Nullable; import org.openhab.binding.omnilink.internal.discovery.ObjectPropertyRequest; import org.openhab.binding.omnilink.internal.discovery.ObjectPropertyRequests; +import org.openhab.binding.omnilink.internal.exceptions.BridgeOfflineException; import org.openhab.core.library.types.OnOffType; import org.openhab.core.thing.ChannelUID; import org.openhab.core.thing.Thing; @@ -32,6 +33,7 @@ import org.slf4j.LoggerFactory; import com.digitaldan.jomnilinkII.Message; +import com.digitaldan.jomnilinkII.MessageTypes.CommandMessage; import com.digitaldan.jomnilinkII.MessageTypes.ObjectStatus; import com.digitaldan.jomnilinkII.MessageTypes.properties.AccessControlReaderProperties; import com.digitaldan.jomnilinkII.MessageTypes.statuses.ExtendedAccessControlReaderLockStatus; @@ -92,8 +94,8 @@ public void handleCommand(ChannelUID channelUID, Command command) { switch (channelUID.getId()) { case CHANNEL_LOCK_SWITCH: if (command instanceof OnOffType) { - sendOmnilinkCommand(OnOffType.OFF.equals(command) ? OmniLinkCmd.CMD_UNLOCK_DOOR.getNumber() - : OmniLinkCmd.CMD_LOCK_DOOR.getNumber(), 0, thingID); + sendOmnilinkCommand(OnOffType.OFF.equals(command) ? CommandMessage.CMD_UNLOCK_DOOR + : CommandMessage.CMD_LOCK_DOOR, 0, thingID); } else { logger.debug("Invalid command {}, must be OnOffType", command); } diff --git a/bundles/org.openhab.binding.omnilink/src/main/java/org/openhab/binding/omnilink/internal/handler/LuminaAreaHandler.java b/bundles/org.openhab.binding.omnilink/src/main/java/org/openhab/binding/omnilink/internal/handler/LuminaAreaHandler.java index e560e0939e435..b7a626ddc5c2e 100644 --- a/bundles/org.openhab.binding.omnilink/src/main/java/org/openhab/binding/omnilink/internal/handler/LuminaAreaHandler.java +++ b/bundles/org.openhab.binding.omnilink/src/main/java/org/openhab/binding/omnilink/internal/handler/LuminaAreaHandler.java @@ -23,6 +23,8 @@ import org.openhab.core.thing.ChannelUID; import org.openhab.core.thing.Thing; +import com.digitaldan.jomnilinkII.MessageTypes.CommandMessage; + /** * The {@link LuminaAreaHandler} defines some methods that are used to * interface with an OmniLink Lumina Area. This by extension also defines the @@ -44,17 +46,17 @@ public LuminaAreaHandler(Thing thing) { protected int getMode(ChannelUID channelUID) { switch (channelUID.getId()) { case CHANNEL_AREA_SECURITY_MODE_HOME: - return OmniLinkCmd.CMD_SECURITY_LUMINA_HOME_MODE.getNumber(); + return CommandMessage.CMD_SECURITY_LUMINA_HOME_MODE; case CHANNEL_AREA_SECURITY_MODE_SLEEP: - return OmniLinkCmd.CMD_SECURITY_LUMINA_SLEEP_MODE.getNumber(); + return CommandMessage.CMD_SECURITY_LUMINA_SLEEP_MODE; case CHANNEL_AREA_SECURITY_MODE_AWAY: - return OmniLinkCmd.CMD_SECURITY_LUMINA_AWAY_MODE.getNumber(); + return CommandMessage.CMD_SECURITY_LUMINA_AWAY_MODE; case CHANNEL_AREA_SECURITY_MODE_VACATION: - return OmniLinkCmd.CMD_SECURITY_LUMINA_VACATION_MODE.getNumber(); + return CommandMessage.CMD_SECURITY_LUMINA_VACATION_MODE; case CHANNEL_AREA_SECURITY_MODE_PARTY: - return OmniLinkCmd.CMD_SECURITY_LUMINA_PARTY_MODE.getNumber(); + return CommandMessage.CMD_SECURITY_LUMINA_PARTY_MODE; case CHANNEL_AREA_SECURITY_MODE_SPECIAL: - return OmniLinkCmd.CMD_SECURITY_LUMINA_SPECIAL_MODE.getNumber(); + return CommandMessage.CMD_SECURITY_LUMINA_SPECIAL_MODE; default: throw new IllegalStateException("Unknown channel for area thing " + channelUID); } diff --git a/bundles/org.openhab.binding.omnilink/src/main/java/org/openhab/binding/omnilink/internal/handler/OmniAreaHandler.java b/bundles/org.openhab.binding.omnilink/src/main/java/org/openhab/binding/omnilink/internal/handler/OmniAreaHandler.java index 7424d8d981e20..e16a198b17874 100644 --- a/bundles/org.openhab.binding.omnilink/src/main/java/org/openhab/binding/omnilink/internal/handler/OmniAreaHandler.java +++ b/bundles/org.openhab.binding.omnilink/src/main/java/org/openhab/binding/omnilink/internal/handler/OmniAreaHandler.java @@ -23,6 +23,8 @@ import org.openhab.core.thing.ChannelUID; import org.openhab.core.thing.Thing; +import com.digitaldan.jomnilinkII.MessageTypes.CommandMessage; + /** * The {@link OmniAreaHandler} defines some methods that are used to * interface with an OmniLink OmniPro Area. This by extension also defines the @@ -45,19 +47,19 @@ public OmniAreaHandler(Thing thing) { protected int getMode(ChannelUID channelUID) { switch (channelUID.getId()) { case CHANNEL_AREA_SECURITY_MODE_DISARM: - return OmniLinkCmd.CMD_SECURITY_OMNI_DISARM.getNumber(); + return CommandMessage.CMD_SECURITY_OMNI_DISARM; case CHANNEL_AREA_SECURITY_MODE_DAY: - return OmniLinkCmd.CMD_SECURITY_OMNI_DAY_MODE.getNumber(); + return CommandMessage.CMD_SECURITY_OMNI_DAY_MODE; case CHANNEL_AREA_SECURITY_MODE_NIGHT: - return OmniLinkCmd.CMD_SECURITY_OMNI_NIGHT_MODE.getNumber(); + return CommandMessage.CMD_SECURITY_OMNI_NIGHT_MODE; case CHANNEL_AREA_SECURITY_MODE_AWAY: - return OmniLinkCmd.CMD_SECURITY_OMNI_AWAY_MODE.getNumber(); + return CommandMessage.CMD_SECURITY_OMNI_AWAY_MODE; case CHANNEL_AREA_SECURITY_MODE_VACATION: - return OmniLinkCmd.CMD_SECURITY_OMNI_VACATION_MODE.getNumber(); + return CommandMessage.CMD_SECURITY_OMNI_VACATION_MODE; case CHANNEL_AREA_SECURITY_MODE_DAY_INSTANT: - return OmniLinkCmd.CMD_SECURITY_OMNI_DAY_INSTANCE_MODE.getNumber(); + return CommandMessage.CMD_SECURITY_OMNI_DAY_INSTANT_MODE; case CHANNEL_AREA_SECURITY_MODE_NIGHT_DELAYED: - return OmniLinkCmd.CMD_SECURITY_OMNI_NIGHT_DELAYED_MODE.getNumber(); + return CommandMessage.CMD_SECURITY_OMNI_NIGHT_DELAYED_MODE; default: throw new IllegalStateException("Unknown channel for area thing " + channelUID); } diff --git a/bundles/org.openhab.binding.omnilink/src/main/java/org/openhab/binding/omnilink/internal/handler/OmniLinkCmd.java b/bundles/org.openhab.binding.omnilink/src/main/java/org/openhab/binding/omnilink/internal/handler/OmniLinkCmd.java deleted file mode 100644 index 20e3f7cba1fe0..0000000000000 --- a/bundles/org.openhab.binding.omnilink/src/main/java/org/openhab/binding/omnilink/internal/handler/OmniLinkCmd.java +++ /dev/null @@ -1,199 +0,0 @@ -/** - * Copyright (c) 2010-2021 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.omnilink.internal.handler; - -import org.eclipse.jdt.annotation.NonNullByDefault; -import org.eclipse.jdt.annotation.Nullable; - -/** - * OmniLink commands - * - * @author Dan Cunningham - Initial contribution - * @author Ethan Dye - openHAB3 rewrite - * @since 1.5.0 - */ -@NonNullByDefault -public enum OmniLinkCmd { - CMD_UNIT_OFF(0), - CMD_UNIT_ON(1), - CMD_UNIT_AREA_ALL_OFF(2), - CMD_UNIT_AREA_ALL_ON(3), - CMD_UNIT_PERCENT(9), - CMD_UNIT_LO9_LEVEL_HIGH7(101), - CMD_UNIT_DECREMENT_COUNTER(10), - CMD_UNIT_INCREMENT_COUNTER(11), - CMD_UNIT_SET_COUNTER(12), - CMD_UNIT_LO9_RAMP_HIGH7(13), - CMD_UNIT_LIGHTOLIER(14), - CMD_UNIT_UPB_REQ_STATUS(15), - CMD_UNIT_UNIT_DIM_STEP_1(17), - CMD_UNIT_UNIT_DIM_STEP_2(18), - CMD_UNIT_UNIT_DIM_STEP_3(19), - CMD_UNIT_UNIT_DIM_STEP_4(20), - CMD_UNIT_UNIT_DIM_STEP_5(21), - CMD_UNIT_UNIT_DIM_STEP_6(22), - CMD_UNIT_UNIT_DIM_STEP_7(23), - CMD_UNIT_UNIT_DIM_STEP_8(24), - CMD_UNIT_UNIT_DIM_STEP_9(25), - CMD_UNIT_UNIT_BRIGHTEN_STEP_1(33), - CMD_UNIT_UNIT_BRIGHTEN_STEP_2(34), - CMD_UNIT_UNIT_BRIGHTEN_STEP_3(35), - CMD_UNIT_UNIT_BRIGHTEN_STEP_4(36), - CMD_UNIT_UNIT_BRIGHTEN_STEP_5(37), - CMD_UNIT_UNIT_BRIGHTEN_STEP_6(38), - CMD_UNIT_UNIT_BRIGHTEN_STEP_7(39), - CMD_UNIT_UNIT_BRIGHTEN_STEP_8(40), - CMD_UNIT_UNIT_BRIGHTEN_STEP_9(41), - CMD_UNIT_UPB_LO9_BLINK_HIGH7(26), - CMD_UNIT_UPB_STOP_BLINK(27), - CMD_UNIT_UPB_LINK_OFF(28), - CMD_UNIT_UPB_LINK_ON(29), - CMD_UNIT_UPB_LINK_SET(30), - CMD_UNIT_CENTRALITE_SCENE_OFF(42), - CMD_UNIT_CENTRALITE_SCENE_ON(43), - CMD_UNIT_UPB_LED_OFF(44), - CMD_UNIT_UPB_LED_ON(45), - CMD_UNIT_RADIORA_PHANTOM_BUTTON_OFF(46), - CMD_UNIT_RADIORA_PHANTM_BUTTON_ON(46), - CMD_UNIT_LEVITON_SCENE_OFF(60), - CMD_UNIT_LEVITON_SCENE_ON(61), - CMD_UNIT_LEVITON_SCENE_SET(62), - - CMD_SECURITY_OMNI_DISARM(48), - CMD_SECURITY_OMNI_DAY_MODE(49), - CMD_SECURITY_OMNI_NIGHT_MODE(50), - CMD_SECURITY_OMNI_AWAY_MODE(51), - CMD_SECURITY_OMNI_VACATION_MODE(52), - CMD_SECURITY_OMNI_DAY_INSTANCE_MODE(53), - CMD_SECURITY_OMNI_NIGHT_DELAYED_MODE(54), - CMD_SECURITY_BYPASS_ZONE(4), - CMD_SECURITY_RESTORE_ZONE(5), - CMD_SECURITY_RESTORE_ALL_ZONES(6), - CMD_SECURITY_LUMINA_HOME_MODE(49), - CMD_SECURITY_LUMINA_SLEEP_MODE(50), - CMD_SECURITY_LUMINA_AWAY_MODE(51), - CMD_SECURITY_LUMINA_VACATION_MODE(52), - CMD_SECURITY_LUMINA_PARTY_MODE(53), - CMD_SECURITY_LUMINA_SPECIAL_MODE(54), - - CMD_BUTTON(7), - - CMD_ENERGY_SAVER_OFF(64), - CMD_ENERGY_SAVER_ON(65), - - CMD_THERMO_SET_HEAT_LOW_POINT(66), - CMD_THERMO_SET_COOL_HIGH_POINT(67), - CMD_THERMO_SET_SYSTEM_MODE(68), - CMD_THERMO_SET_FAN_MODE(69), - CMD_THERMO_SET_HOLD_MODE(70), - CMD_THERMO_RAISE_LOWER_HEAT(71), - CMD_THERMO_RAISE_LOWER_COOL(72), - CMD_THERMO_SET_HUMDIFY_POINT(73), - CMD_THERMO_SET_DEHUMIDIFY_POINT(74), - - CMD_MESSAGE_SHOW_MESSAGE_WITH_BEEP_AND_LED(80), - CMD_MESSAGE_SHOW_MESSAGE_WITH_BEEP_OR_LED(86), - CMD_MESSAGE_LOG_MESSAGE(81), - CMD_MESSAGE_CLEAR_MESSAGE(82), - CMD_MESSAGE_SAY_MESSAGE(83), - CMD_MESSAGE_PHONE_AND_SAY_MESSAGE(84), - CMD_MESSAGE_SEND_MESSAGE_TO_SERIAL_PORT(85), - - CMD_CONSOLE_ENABLE_DISABLE_BEEPER(102), - CMD_CONSOLE_BEEP(103), - - CMD_LOCK_DOOR(105), - CMD_UNLOCK_DOOR(106), - - CMD_AUDIO_ZONE_SET_ON_MUTE(112), - CMD_AUDIO_ZONE_SET_VOLUME(113), - CMD_AUDIO_ZONE_SET_SOURCE(114), - CMD_AUDIO_ZONE_SELECT_KEY(115), - - SENSOR_UNIT_POWER(1001), - SENSOR_UNIT_LEVEL(1002), - SENSOR_UNIT_DISPLAY(1003), - SENSOR_THERMO_HEAT_POINTC(2001), - SENSOR_THERMO_HEAT_POINTF(2002), - SENSOR_THERMO_COOL_POINTC(2003), - SENSOR_THERMO_COOL_POINTF(2004), - SENSOR_THERMO_SYSTEM_MODE(2005), - SENSOR_THERMO_FAN_MODE(2006), - SENSOR_THERMO_HOLD_MODE(2007), - SENSOR_THERMO_TEMPC(2006), - SENSOR_THERMO_TEMPF(2007), - SENSOR_ZONE_STATUS_CURRENT(3001), - SENSOR_ZONE_STATUS_LATCHED(3002), - SENSOR_ZONE_STATUS_ARMING(3003), - SENSOR_AREA_STATUS_MODE(4001), - SENSOR_AREA_STATUS_ALARM(4002), - SENSOR_AREA_STATUS_EXIT_DELAY(4003), - SENSOR_AREA_STATUS_ENTRY_DELAY(4003), - SENSOR_AREA_EXIT_TIMER(4004), - SENSOR_AREA_ENTRY_TIMER(4005), - SENSOR_AUX_STATUS(5001), - SENSOR_AUX_CURRENTC(5002), - SENSOR_AUX_CURRENTF(5003), - SENSOR_AUX_LOWC(5004), - SENSOR_AUX_LOWF(5005), - SENSOR_AUX_HIGHC(5006), - SENSOR_AUX_HIGHF(5007), - SENSOR_AUDIOZONE_POWER(6001), - SENSOR_AUDIOZONE_SOURCE(6002), - SENSOR_AUDIOZONE_VOLUME(6003), - SENSOR_AUDIOZONE_MUTE(6004), - SENSOR_AUDIOZONE_TEXT(6005), - SENSOR_AUDIOZONE_TEXT_FIELD1(6006), - SENSOR_AUDIOZONE_TEXT_FIELD2(6007), - SENSOR_AUDIOZONE_TEXT_FIELD3(6008), - SENSOR_AUDIOSOURCE_TEXT(7001), - SENSOR_AUDIOSOURCE_TEXT_FIELD1(7002), - SENSOR_AUDIOSOURCE_TEXT_FIELD2(7003), - SENSOR_AUDIOSOURCE_TEXT_FIELD3(7004); - - private int number; - - OmniLinkCmd(int number) { - this.number = number; - } - - public int getNumber() { - return number; - } - - public static @Nullable OmniLinkCmd getCommand(String name) { - for (OmniLinkCmd command : OmniLinkCmd.values()) { - if (name.equals(command.toString())) { - return command; - } - } - return null; - } - - public static @Nullable OmniLinkCmd getCommand(int ordinal) { - OmniLinkCmd[] values = OmniLinkCmd.values(); - if (ordinal < values.length) { - return values[ordinal]; - } else { - return null; - } - } - - public static String toList() { - StringBuilder sb = new StringBuilder(); - for (OmniLinkCmd command : OmniLinkCmd.values()) { - sb.append(command.toString()).append(","); - } - return sb.toString(); - } -} diff --git a/bundles/org.openhab.binding.omnilink/src/main/java/org/openhab/binding/omnilink/internal/handler/OmnilinkBridgeHandler.java b/bundles/org.openhab.binding.omnilink/src/main/java/org/openhab/binding/omnilink/internal/handler/OmnilinkBridgeHandler.java index 52232bab26e03..a0991bb9a5621 100644 --- a/bundles/org.openhab.binding.omnilink/src/main/java/org/openhab/binding/omnilink/internal/handler/OmnilinkBridgeHandler.java +++ b/bundles/org.openhab.binding.omnilink/src/main/java/org/openhab/binding/omnilink/internal/handler/OmnilinkBridgeHandler.java @@ -28,8 +28,10 @@ import org.eclipse.jdt.annotation.Nullable; import org.openhab.binding.omnilink.internal.AudioPlayer; import org.openhab.binding.omnilink.internal.SystemType; +import org.openhab.binding.omnilink.internal.TemperatureFormat; import org.openhab.binding.omnilink.internal.config.OmnilinkBridgeConfig; import org.openhab.binding.omnilink.internal.discovery.OmnilinkDiscoveryService; +import org.openhab.binding.omnilink.internal.exceptions.BridgeOfflineException; import org.openhab.core.library.types.DateTimeType; import org.openhab.core.library.types.DecimalType; import org.openhab.core.library.types.StringType; @@ -50,6 +52,7 @@ import com.digitaldan.jomnilinkII.Connection; import com.digitaldan.jomnilinkII.DisconnectListener; import com.digitaldan.jomnilinkII.Message; +import com.digitaldan.jomnilinkII.MessageTypes.CommandMessage; import com.digitaldan.jomnilinkII.MessageTypes.EventLogData; import com.digitaldan.jomnilinkII.MessageTypes.ObjectStatus; import com.digitaldan.jomnilinkII.MessageTypes.SecurityCodeValidation; @@ -92,7 +95,7 @@ public class OmnilinkBridgeHandler extends BaseBridgeHandler implements Notifica private @Nullable ScheduledFuture eventPollingJob; private final int autoReconnectPeriod = 60; private Optional audioPlayer = Optional.empty(); - private @Nullable SystemType systemType = null; + private Optional systemType = Optional.empty(); private final Gson gson = new Gson(); private int eventLogNumber = 0; @@ -194,7 +197,7 @@ public void handleCommand(ChannelUID channelUID, Command command) { case CHANNEL_CONSOLE_ENABLE_DISABLE_BEEPER: if (command instanceof StringType) { try { - sendOmnilinkCommand(OmniLinkCmd.CMD_CONSOLE_ENABLE_DISABLE_BEEPER.getNumber(), + sendOmnilinkCommand(CommandMessage.CMD_CONSOLE_ENABLE_DISABLE_BEEPER, ((StringType) command).equals(StringType.valueOf("OFF")) ? 0 : 1, 0); updateState(CHANNEL_CONSOLE_ENABLE_DISABLE_BEEPER, UnDefType.UNDEF); } catch (NumberFormatException | OmniInvalidResponseException | OmniUnknownMessageTypeException @@ -208,8 +211,7 @@ public void handleCommand(ChannelUID channelUID, Command command) { case CHANNEL_CONSOLE_BEEP: if (command instanceof DecimalType) { try { - sendOmnilinkCommand(OmniLinkCmd.CMD_CONSOLE_BEEP.getNumber(), - ((DecimalType) command).intValue(), 0); + sendOmnilinkCommand(CommandMessage.CMD_CONSOLE_BEEP, ((DecimalType) command).intValue(), 0); updateState(CHANNEL_CONSOLE_BEEP, UnDefType.UNDEF); } catch (NumberFormatException | OmniInvalidResponseException | OmniUnknownMessageTypeException | BridgeOfflineException e) { @@ -306,28 +308,23 @@ public void objectStatusNotification(@Nullable ObjectStatus objectStatus) { theThing.map(Thing::getHandler) .ifPresent(theHandler -> ((ZoneHandler) theHandler).handleStatus(zoneStatus)); } else if (status instanceof ExtendedAreaStatus) { - final SystemType systemType = this.systemType; ExtendedAreaStatus areaStatus = (ExtendedAreaStatus) status; int areaNumber = areaStatus.getNumber(); - if (systemType != null) { - logger.debug("Received status update for Area: {}, status: {}", areaNumber, areaStatus); - Optional theThing; - switch (systemType) { - case OMNI: - theThing = getChildThing(THING_TYPE_OMNI_AREA, areaNumber); - break; + logger.debug("Received status update for Area: {}, status: {}", areaNumber, areaStatus); + systemType.ifPresent(t -> { + Optional theThing = Optional.empty(); + switch (t) { case LUMINA: theThing = getChildThing(THING_TYPE_LUMINA_AREA, areaNumber); break; - default: - theThing = Optional.empty(); + case OMNI: + theThing = getChildThing(THING_TYPE_OMNI_AREA, areaNumber); + break; } theThing.map(Thing::getHandler) .ifPresent(theHandler -> ((AbstractAreaHandler) theHandler).handleStatus(areaStatus)); - } else { - logger.debug("Received null System Type!"); - } + }); } else if (status instanceof ExtendedAccessControlReaderLockStatus) { ExtendedAccessControlReaderLockStatus lockStatus = (ExtendedAccessControlReaderLockStatus) status; int lockNumber = lockStatus.getNumber(); diff --git a/bundles/org.openhab.binding.omnilink/src/main/java/org/openhab/binding/omnilink/internal/handler/TempSensorHandler.java b/bundles/org.openhab.binding.omnilink/src/main/java/org/openhab/binding/omnilink/internal/handler/TempSensorHandler.java index 850e2ad99c4c3..969527e91ac64 100644 --- a/bundles/org.openhab.binding.omnilink/src/main/java/org/openhab/binding/omnilink/internal/handler/TempSensorHandler.java +++ b/bundles/org.openhab.binding.omnilink/src/main/java/org/openhab/binding/omnilink/internal/handler/TempSensorHandler.java @@ -22,8 +22,10 @@ import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.Nullable; +import org.openhab.binding.omnilink.internal.TemperatureFormat; import org.openhab.binding.omnilink.internal.discovery.ObjectPropertyRequest; import org.openhab.binding.omnilink.internal.discovery.ObjectPropertyRequests; +import org.openhab.binding.omnilink.internal.exceptions.BridgeOfflineException; import org.openhab.core.library.types.QuantityType; import org.openhab.core.library.unit.ImperialUnits; import org.openhab.core.library.unit.SIUnits; @@ -37,6 +39,7 @@ import org.slf4j.LoggerFactory; import com.digitaldan.jomnilinkII.Message; +import com.digitaldan.jomnilinkII.MessageTypes.CommandMessage; import com.digitaldan.jomnilinkII.MessageTypes.ObjectStatus; import com.digitaldan.jomnilinkII.MessageTypes.properties.AreaProperties; import com.digitaldan.jomnilinkII.MessageTypes.properties.AuxSensorProperties; @@ -125,13 +128,13 @@ public void handleCommand(ChannelUID channelUID, Command command) { switch (channelUID.getId()) { case CHANNEL_AUX_LOW_SETPOINT: - sendOmnilinkCommand(OmniLinkCmd.CMD_THERMO_SET_HEAT_LOW_POINT.getNumber(), - temperatureFormat.get().formatToOmni(((QuantityType) command).intValue()), + sendOmnilinkCommand(CommandMessage.CMD_THERMO_SET_HEAT_POINT, + temperatureFormat.get().formatToOmni(((QuantityType) command).floatValue()), thingID); break; case CHANNEL_AUX_HIGH_SETPOINT: - sendOmnilinkCommand(OmniLinkCmd.CMD_THERMO_SET_COOL_HIGH_POINT.getNumber(), - temperatureFormat.get().formatToOmni(((QuantityType) command).intValue()), + sendOmnilinkCommand(CommandMessage.CMD_THERMO_SET_COOL_POINT, + temperatureFormat.get().formatToOmni(((QuantityType) command).floatValue()), thingID); break; default: diff --git a/bundles/org.openhab.binding.omnilink/src/main/java/org/openhab/binding/omnilink/internal/handler/ThermostatHandler.java b/bundles/org.openhab.binding.omnilink/src/main/java/org/openhab/binding/omnilink/internal/handler/ThermostatHandler.java index 37fb57f2f7a6d..df84bc9e0e1c1 100644 --- a/bundles/org.openhab.binding.omnilink/src/main/java/org/openhab/binding/omnilink/internal/handler/ThermostatHandler.java +++ b/bundles/org.openhab.binding.omnilink/src/main/java/org/openhab/binding/omnilink/internal/handler/ThermostatHandler.java @@ -24,8 +24,10 @@ import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.Nullable; +import org.openhab.binding.omnilink.internal.TemperatureFormat; import org.openhab.binding.omnilink.internal.discovery.ObjectPropertyRequest; import org.openhab.binding.omnilink.internal.discovery.ObjectPropertyRequests; +import org.openhab.binding.omnilink.internal.exceptions.BridgeOfflineException; import org.openhab.core.library.types.DecimalType; import org.openhab.core.library.types.OpenClosedType; import org.openhab.core.library.types.QuantityType; @@ -42,6 +44,7 @@ import org.slf4j.LoggerFactory; import com.digitaldan.jomnilinkII.Message; +import com.digitaldan.jomnilinkII.MessageTypes.CommandMessage; import com.digitaldan.jomnilinkII.MessageTypes.ObjectStatus; import com.digitaldan.jomnilinkII.MessageTypes.properties.AreaProperties; import com.digitaldan.jomnilinkII.MessageTypes.properties.ThermostatProperties; @@ -144,35 +147,35 @@ public void handleCommand(ChannelUID channelUID, Command command) { switch (channelUID.getId()) { case CHANNEL_THERMO_SYSTEM_MODE: - sendOmnilinkCommand(OmniLinkCmd.CMD_THERMO_SET_SYSTEM_MODE.getNumber(), - ((DecimalType) command).intValue(), thingID); + sendOmnilinkCommand(CommandMessage.CMD_THERMO_SET_SYSTEM_MODE, ((DecimalType) command).intValue(), + thingID); break; case CHANNEL_THERMO_FAN_MODE: - sendOmnilinkCommand(OmniLinkCmd.CMD_THERMO_SET_FAN_MODE.getNumber(), ((DecimalType) command).intValue(), + sendOmnilinkCommand(CommandMessage.CMD_THERMO_SET_FAN_MODE, ((DecimalType) command).intValue(), thingID); break; case CHANNEL_THERMO_HOLD_STATUS: - sendOmnilinkCommand(OmniLinkCmd.CMD_THERMO_SET_HOLD_MODE.getNumber(), - ((DecimalType) command).intValue(), thingID); + sendOmnilinkCommand(CommandMessage.CMD_THERMO_SET_HOLD_MODE, ((DecimalType) command).intValue(), + thingID); break; case CHANNEL_THERMO_HEAT_SETPOINT: - sendOmnilinkCommand(OmniLinkCmd.CMD_THERMO_SET_HEAT_LOW_POINT.getNumber(), - temperatureFormat.get().formatToOmni(((QuantityType) command).intValue()), + sendOmnilinkCommand(CommandMessage.CMD_THERMO_SET_HEAT_POINT, + temperatureFormat.get().formatToOmni(((QuantityType) command).floatValue()), thingID); break; case CHANNEL_THERMO_COOL_SETPOINT: - sendOmnilinkCommand(OmniLinkCmd.CMD_THERMO_SET_COOL_HIGH_POINT.getNumber(), - temperatureFormat.get().formatToOmni(((QuantityType) command).intValue()), + sendOmnilinkCommand(CommandMessage.CMD_THERMO_SET_COOL_POINT, + temperatureFormat.get().formatToOmni(((QuantityType) command).floatValue()), thingID); break; case CHANNEL_THERMO_HUMIDIFY_SETPOINT: - sendOmnilinkCommand(OmniLinkCmd.CMD_THERMO_SET_HUMDIFY_POINT.getNumber(), - TemperatureFormat.FAHRENHEIT.formatToOmni(((QuantityType) command).intValue()), + sendOmnilinkCommand(CommandMessage.CMD_THERMO_SET_HUMDIFY_POINT, + TemperatureFormat.FAHRENHEIT.formatToOmni(((QuantityType) command).floatValue()), thingID); break; case CHANNEL_THERMO_DEHUMIDIFY_SETPOINT: - sendOmnilinkCommand(OmniLinkCmd.CMD_THERMO_SET_DEHUMIDIFY_POINT.getNumber(), - TemperatureFormat.FAHRENHEIT.formatToOmni(((QuantityType) command).intValue()), + sendOmnilinkCommand(CommandMessage.CMD_THERMO_SET_DEHUMIDIFY_POINT, + TemperatureFormat.FAHRENHEIT.formatToOmni(((QuantityType) command).floatValue()), thingID); break; default: diff --git a/bundles/org.openhab.binding.omnilink/src/main/java/org/openhab/binding/omnilink/internal/handler/UnitHandler.java b/bundles/org.openhab.binding.omnilink/src/main/java/org/openhab/binding/omnilink/internal/handler/UnitHandler.java index 95951b748c769..379fc3c109d02 100644 --- a/bundles/org.openhab.binding.omnilink/src/main/java/org/openhab/binding/omnilink/internal/handler/UnitHandler.java +++ b/bundles/org.openhab.binding.omnilink/src/main/java/org/openhab/binding/omnilink/internal/handler/UnitHandler.java @@ -22,6 +22,7 @@ import org.eclipse.jdt.annotation.Nullable; import org.openhab.binding.omnilink.internal.discovery.ObjectPropertyRequest; import org.openhab.binding.omnilink.internal.discovery.ObjectPropertyRequests; +import org.openhab.binding.omnilink.internal.exceptions.BridgeOfflineException; import org.openhab.core.library.types.DecimalType; import org.openhab.core.library.types.OnOffType; import org.openhab.core.library.types.PercentType; @@ -35,6 +36,7 @@ import org.slf4j.LoggerFactory; import com.digitaldan.jomnilinkII.Message; +import com.digitaldan.jomnilinkII.MessageTypes.CommandMessage; import com.digitaldan.jomnilinkII.MessageTypes.ObjectStatus; import com.digitaldan.jomnilinkII.MessageTypes.properties.AreaProperties; import com.digitaldan.jomnilinkII.MessageTypes.properties.UnitProperties; @@ -144,15 +146,14 @@ private void handleUnitDuration(ChannelUID channelUID, DecimalType command) { return; } - sendOmnilinkCommand( - channelID.startsWith("on") ? OmniLinkCmd.CMD_UNIT_ON.getNumber() : OmniLinkCmd.CMD_UNIT_OFF.getNumber(), + sendOmnilinkCommand(channelID.startsWith("on") ? CommandMessage.CMD_UNIT_ON : CommandMessage.CMD_UNIT_OFF, duration, thingID); } protected void handleOnOff(ChannelUID channelUID, OnOffType command) { logger.debug("handleOnOff called for channel: {}, command: {}", channelUID, command); - sendOmnilinkCommand(OnOffType.ON.equals(command) ? OmniLinkCmd.CMD_UNIT_ON.getNumber() - : OmniLinkCmd.CMD_UNIT_OFF.getNumber(), 0, thingID); + sendOmnilinkCommand(OnOffType.ON.equals(command) ? CommandMessage.CMD_UNIT_ON : CommandMessage.CMD_UNIT_OFF, 0, + thingID); } @Override diff --git a/bundles/org.openhab.binding.omnilink/src/main/java/org/openhab/binding/omnilink/internal/handler/ZoneHandler.java b/bundles/org.openhab.binding.omnilink/src/main/java/org/openhab/binding/omnilink/internal/handler/ZoneHandler.java index ce8b216e15909..8d07e9228716d 100644 --- a/bundles/org.openhab.binding.omnilink/src/main/java/org/openhab/binding/omnilink/internal/handler/ZoneHandler.java +++ b/bundles/org.openhab.binding.omnilink/src/main/java/org/openhab/binding/omnilink/internal/handler/ZoneHandler.java @@ -23,6 +23,7 @@ import org.eclipse.jdt.annotation.Nullable; import org.openhab.binding.omnilink.internal.discovery.ObjectPropertyRequest; import org.openhab.binding.omnilink.internal.discovery.ObjectPropertyRequests; +import org.openhab.binding.omnilink.internal.exceptions.BridgeOfflineException; import org.openhab.core.library.types.DecimalType; import org.openhab.core.library.types.OpenClosedType; import org.openhab.core.library.types.StringType; @@ -38,6 +39,7 @@ import org.slf4j.LoggerFactory; import com.digitaldan.jomnilinkII.Message; +import com.digitaldan.jomnilinkII.MessageTypes.CommandMessage; import com.digitaldan.jomnilinkII.MessageTypes.ObjectStatus; import com.digitaldan.jomnilinkII.MessageTypes.SecurityCodeValidation; import com.digitaldan.jomnilinkII.MessageTypes.properties.AreaProperties; @@ -115,10 +117,10 @@ public void handleCommand(ChannelUID channelUID, Command command) { switch (channelUID.getId()) { case CHANNEL_ZONE_BYPASS: - mode = OmniLinkCmd.CMD_SECURITY_BYPASS_ZONE.getNumber(); + mode = CommandMessage.CMD_SECURITY_BYPASS_ZONE; break; case CHANNEL_ZONE_RESTORE: - mode = OmniLinkCmd.CMD_SECURITY_RESTORE_ZONE.getNumber(); + mode = CommandMessage.CMD_SECURITY_RESTORE_ZONE; break; default: mode = -1; diff --git a/bundles/org.openhab.binding.omnilink/src/main/java/org/openhab/binding/omnilink/internal/handler/units/DimmableUnitHandler.java b/bundles/org.openhab.binding.omnilink/src/main/java/org/openhab/binding/omnilink/internal/handler/units/DimmableUnitHandler.java index 632197f0852fd..c4a6ecdbc6cc7 100644 --- a/bundles/org.openhab.binding.omnilink/src/main/java/org/openhab/binding/omnilink/internal/handler/units/DimmableUnitHandler.java +++ b/bundles/org.openhab.binding.omnilink/src/main/java/org/openhab/binding/omnilink/internal/handler/units/DimmableUnitHandler.java @@ -16,7 +16,6 @@ import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.Nullable; -import org.openhab.binding.omnilink.internal.handler.OmniLinkCmd; import org.openhab.binding.omnilink.internal.handler.UnitHandler; import org.openhab.core.library.types.IncreaseDecreaseType; import org.openhab.core.library.types.OnOffType; @@ -27,6 +26,8 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import com.digitaldan.jomnilinkII.MessageTypes.CommandMessage; + /** * The {@link DimmableUnitHandler} defines some methods that are used to * interface with an OmniLink Dimmable Unit. This by extension also defines the @@ -79,15 +80,13 @@ private void handlePercent(ChannelUID channelUID, PercentType command) { } else if (lightLevel == 100) { super.handleOnOff(channelUID, OnOffType.ON); } else { - sendOmnilinkCommand(OmniLinkCmd.CMD_UNIT_PERCENT.getNumber(), lightLevel, thingID); + sendOmnilinkCommand(CommandMessage.CMD_UNIT_PERCENT, lightLevel, thingID); } } private void handleIncreaseDecrease(ChannelUID channelUID, IncreaseDecreaseType command) { logger.debug("handleIncreaseDecrease called for channel: {}, command: {}", channelUID, command); - sendOmnilinkCommand( - IncreaseDecreaseType.INCREASE.equals(command) ? OmniLinkCmd.CMD_UNIT_UNIT_BRIGHTEN_STEP_1.getNumber() - : OmniLinkCmd.CMD_UNIT_UNIT_DIM_STEP_1.getNumber(), - 0, thingID); + sendOmnilinkCommand(IncreaseDecreaseType.INCREASE.equals(command) ? CommandMessage.CMD_UNIT_UPB_BRIGHTEN_STEP_1 + : CommandMessage.CMD_UNIT_UPB_DIM_STEP_1, 0, thingID); } } diff --git a/bundles/org.openhab.binding.omnilink/src/main/java/org/openhab/binding/omnilink/internal/handler/units/FlagHandler.java b/bundles/org.openhab.binding.omnilink/src/main/java/org/openhab/binding/omnilink/internal/handler/units/FlagHandler.java index 67e5083a99091..87b8199035027 100644 --- a/bundles/org.openhab.binding.omnilink/src/main/java/org/openhab/binding/omnilink/internal/handler/units/FlagHandler.java +++ b/bundles/org.openhab.binding.omnilink/src/main/java/org/openhab/binding/omnilink/internal/handler/units/FlagHandler.java @@ -16,7 +16,6 @@ import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.Nullable; -import org.openhab.binding.omnilink.internal.handler.OmniLinkCmd; import org.openhab.binding.omnilink.internal.handler.UnitHandler; import org.openhab.core.library.types.DecimalType; import org.openhab.core.library.types.OnOffType; @@ -29,6 +28,7 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import com.digitaldan.jomnilinkII.MessageTypes.CommandMessage; import com.digitaldan.jomnilinkII.MessageTypes.statuses.ExtendedUnitStatus; /** @@ -62,8 +62,8 @@ public void handleCommand(ChannelUID channelUID, Command command) { switch (channelUID.getId()) { case CHANNEL_FLAG_VALUE: if (command instanceof DecimalType) { - sendOmnilinkCommand(OmniLinkCmd.CMD_UNIT_SET_COUNTER.getNumber(), - ((DecimalType) command).intValue(), thingID); + sendOmnilinkCommand(CommandMessage.CMD_UNIT_SET_COUNTER, ((DecimalType) command).intValue(), + thingID); } else { logger.debug("Invalid command: {}, must be DecimalType", command); } diff --git a/bundles/org.openhab.binding.omnilink/src/main/java/org/openhab/binding/omnilink/internal/handler/units/UpbRoomHandler.java b/bundles/org.openhab.binding.omnilink/src/main/java/org/openhab/binding/omnilink/internal/handler/units/UpbRoomHandler.java index 047b1cf209c58..e92458527fa88 100644 --- a/bundles/org.openhab.binding.omnilink/src/main/java/org/openhab/binding/omnilink/internal/handler/units/UpbRoomHandler.java +++ b/bundles/org.openhab.binding.omnilink/src/main/java/org/openhab/binding/omnilink/internal/handler/units/UpbRoomHandler.java @@ -16,7 +16,6 @@ import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.Nullable; -import org.openhab.binding.omnilink.internal.handler.OmniLinkCmd; import org.openhab.binding.omnilink.internal.handler.UnitHandler; import org.openhab.core.library.types.DecimalType; import org.openhab.core.library.types.OnOffType; @@ -29,6 +28,7 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import com.digitaldan.jomnilinkII.MessageTypes.CommandMessage; import com.digitaldan.jomnilinkII.MessageTypes.statuses.ExtendedUnitStatus; /** @@ -113,8 +113,8 @@ private void handleRoomScene(ChannelUID channelUID, OnOffType command) { } int roomNum = (thingID + 7) / 8; int param2 = ((roomNum * 6) - 3) + linkNum; - sendOmnilinkCommand(OnOffType.ON.equals(command) ? OmniLinkCmd.CMD_UNIT_UPB_LINK_ON.getNumber() - : OmniLinkCmd.CMD_UNIT_UPB_LINK_OFF.getNumber(), 0, param2); + sendOmnilinkCommand(OnOffType.ON.equals(command) ? CommandMessage.CMD_UNIT_UPB_LINK_ON + : CommandMessage.CMD_UNIT_UPB_LINK_OFF, 0, param2); } private void handleRoomState(ChannelUID channelUID, DecimalType command) { @@ -125,18 +125,18 @@ private void handleRoomState(ChannelUID channelUID, DecimalType command) { switch (cmdValue) { case 0: - cmd = OmniLinkCmd.CMD_UNIT_OFF.getNumber(); + cmd = CommandMessage.CMD_UNIT_OFF; param2 = thingID; break; case 1: - cmd = OmniLinkCmd.CMD_UNIT_ON.getNumber(); + cmd = CommandMessage.CMD_UNIT_ON; param2 = thingID; break; case 2: case 3: case 4: case 5: - cmd = OmniLinkCmd.CMD_UNIT_UPB_LINK_ON.getNumber(); + cmd = CommandMessage.CMD_UNIT_UPB_LINK_ON; /* * A little magic with the link #'s: 0 and 1 are off and on, respectively. * So A ends up being 2, but OmniLink Protocol expects an offset of 0. That diff --git a/bundles/org.openhab.binding.omnilink/src/main/java/org/openhab/binding/omnilink/internal/handler/units/dimmable/UpbUnitHandler.java b/bundles/org.openhab.binding.omnilink/src/main/java/org/openhab/binding/omnilink/internal/handler/units/dimmable/UpbUnitHandler.java index 2fda1ced90bcb..c128b12cec3ca 100644 --- a/bundles/org.openhab.binding.omnilink/src/main/java/org/openhab/binding/omnilink/internal/handler/units/dimmable/UpbUnitHandler.java +++ b/bundles/org.openhab.binding.omnilink/src/main/java/org/openhab/binding/omnilink/internal/handler/units/dimmable/UpbUnitHandler.java @@ -16,7 +16,6 @@ import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.Nullable; -import org.openhab.binding.omnilink.internal.handler.OmniLinkCmd; import org.openhab.binding.omnilink.internal.handler.units.DimmableUnitHandler; import org.openhab.core.library.types.StringType; import org.openhab.core.thing.ChannelUID; @@ -27,6 +26,8 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import com.digitaldan.jomnilinkII.MessageTypes.CommandMessage; + /** * The {@link UpbUnitHandler} defines some methods that are used to * interface with an OmniLink UPB Unit. This by extension also defines the @@ -57,7 +58,7 @@ public void handleCommand(ChannelUID channelUID, Command command) { switch (channelUID.getId()) { case CHANNEL_UPB_STATUS: if (command instanceof StringType) { - sendOmnilinkCommand(OmniLinkCmd.CMD_UNIT_UPB_REQ_STATUS.getNumber(), 0, thingID); + sendOmnilinkCommand(CommandMessage.CMD_UNIT_UPB_REQ_STATUS, 0, thingID); updateState(CHANNEL_UPB_STATUS, UnDefType.UNDEF); } else { logger.debug("Invalid command: {}, must be StringType", command); diff --git a/bundles/org.openhab.binding.onebusaway/src/main/java/org/openhab/binding/onebusaway/internal/config/ApiConfiguration.java b/bundles/org.openhab.binding.onebusaway/src/main/java/org/openhab/binding/onebusaway/internal/config/ApiConfiguration.java index 0425bac2a543b..5309241591068 100644 --- a/bundles/org.openhab.binding.onebusaway/src/main/java/org/openhab/binding/onebusaway/internal/config/ApiConfiguration.java +++ b/bundles/org.openhab.binding.onebusaway/src/main/java/org/openhab/binding/onebusaway/internal/config/ApiConfiguration.java @@ -14,7 +14,7 @@ import static org.openhab.binding.onebusaway.internal.OneBusAwayBindingConstants.*; -import org.apache.commons.lang.builder.ToStringBuilder; +import org.apache.commons.lang3.builder.ToStringBuilder; /** * The {@link ApiConfiguration} defines the model for a API bridge configuration. diff --git a/bundles/org.openhab.binding.onebusaway/src/main/java/org/openhab/binding/onebusaway/internal/config/ChannelConfig.java b/bundles/org.openhab.binding.onebusaway/src/main/java/org/openhab/binding/onebusaway/internal/config/ChannelConfig.java index e5e5491086d50..d1158c6da96ef 100644 --- a/bundles/org.openhab.binding.onebusaway/src/main/java/org/openhab/binding/onebusaway/internal/config/ChannelConfig.java +++ b/bundles/org.openhab.binding.onebusaway/src/main/java/org/openhab/binding/onebusaway/internal/config/ChannelConfig.java @@ -14,7 +14,7 @@ import static org.openhab.binding.onebusaway.internal.OneBusAwayBindingConstants.CHANNEL_CONFIG_OFFSET; -import org.apache.commons.lang.builder.ToStringBuilder; +import org.apache.commons.lang3.builder.ToStringBuilder; /** * The {@link ChannelConfig} defines the model for a channel configuration. diff --git a/bundles/org.openhab.binding.onebusaway/src/main/java/org/openhab/binding/onebusaway/internal/config/RouteConfiguration.java b/bundles/org.openhab.binding.onebusaway/src/main/java/org/openhab/binding/onebusaway/internal/config/RouteConfiguration.java index 965a5585492db..a4be8d16ba3b3 100644 --- a/bundles/org.openhab.binding.onebusaway/src/main/java/org/openhab/binding/onebusaway/internal/config/RouteConfiguration.java +++ b/bundles/org.openhab.binding.onebusaway/src/main/java/org/openhab/binding/onebusaway/internal/config/RouteConfiguration.java @@ -14,7 +14,7 @@ import static org.openhab.binding.onebusaway.internal.OneBusAwayBindingConstants.ROUTE_CONFIG_ROUTE_ID; -import org.apache.commons.lang.builder.ToStringBuilder; +import org.apache.commons.lang3.builder.ToStringBuilder; /** * The {@link RouteConfiguration} defines the model for a route stop configuration. diff --git a/bundles/org.openhab.binding.onebusaway/src/main/java/org/openhab/binding/onebusaway/internal/config/StopConfiguration.java b/bundles/org.openhab.binding.onebusaway/src/main/java/org/openhab/binding/onebusaway/internal/config/StopConfiguration.java index b799b823f8e90..488c00d005c20 100644 --- a/bundles/org.openhab.binding.onebusaway/src/main/java/org/openhab/binding/onebusaway/internal/config/StopConfiguration.java +++ b/bundles/org.openhab.binding.onebusaway/src/main/java/org/openhab/binding/onebusaway/internal/config/StopConfiguration.java @@ -14,7 +14,7 @@ import static org.openhab.binding.onebusaway.internal.OneBusAwayBindingConstants.*; -import org.apache.commons.lang.builder.ToStringBuilder; +import org.apache.commons.lang3.builder.ToStringBuilder; /** * The {@link StopConfiguration} defines the model for a stop bridge configuration. diff --git a/bundles/org.openhab.binding.onewire/src/main/resources/OH-INF/config/config.xml b/bundles/org.openhab.binding.onewire/src/main/resources/OH-INF/config/config.xml index 994537637ab67..4b7b222725bad 100644 --- a/bundles/org.openhab.binding.onewire/src/main/resources/OH-INF/config/config.xml +++ b/bundles/org.openhab.binding.onewire/src/main/resources/OH-INF/config/config.xml @@ -4,33 +4,29 @@ xmlns:config-description="https://openhab.org/schemas/config-description/v1.0.0" xsi:schemaLocation="https://openhab.org/schemas/config-description/v1.0.0 https://openhab.org/schemas/config-description-1.0.0.xsd"> - + Sensor ID in format: xx.xxxxxxxxxxxx or a full path including hubs/branches - true - + Time in seconds after which the thing is refreshed 300 s - false - + Sensor ID in format: xx.xxxxxxxxxxxx or a full path including hubs/branches - true - + Time in seconds after which the thing is refreshed 300 s - false - + Overrides detected sensor type @@ -41,7 +37,6 @@ true - false true diff --git a/bundles/org.openhab.binding.onewire/src/main/resources/OH-INF/thing/bae09x.xml b/bundles/org.openhab.binding.onewire/src/main/resources/OH-INF/thing/bae09x.xml index 38973babbbab5..79e2d72a2c58b 100644 --- a/bundles/org.openhab.binding.onewire/src/main/resources/OH-INF/thing/bae09x.xml +++ b/bundles/org.openhab.binding.onewire/src/main/resources/OH-INF/thing/bae09x.xml @@ -13,10 +13,9 @@ 1 - + Sensor ID in format: xx.xxxxxxxxxxxx) - true diff --git a/bundles/org.openhab.binding.onewire/src/main/resources/OH-INF/thing/bridge.xml b/bundles/org.openhab.binding.onewire/src/main/resources/OH-INF/thing/bridge.xml index 0c0f85075e227..b0f5501b146b5 100644 --- a/bundles/org.openhab.binding.onewire/src/main/resources/OH-INF/thing/bridge.xml +++ b/bundles/org.openhab.binding.onewire/src/main/resources/OH-INF/thing/bridge.xml @@ -7,17 +7,15 @@ An owserver instance - + network_address Network address of the host running the owserver - true - + Listening port of the owserver 4304 - false @@ -27,17 +25,15 @@ Allows direct access to the OWFS - + full path to the OWFS-node (e.g. statistics/errors/CRC8_errors) - true - + Time in seconds after which the channel is refreshed 300 s - false @@ -47,17 +43,15 @@ Allows direct access to the OWFS - + full path to the OWFS-node (e.g. statistics/errors/CRC8_errors) - true - + Time in seconds after which the channel is refreshed 300 s - false diff --git a/bundles/org.openhab.binding.onewire/src/main/resources/OH-INF/thing/common.xml b/bundles/org.openhab.binding.onewire/src/main/resources/OH-INF/thing/common.xml index 482c37f3eb520..762237d3599ac 100644 --- a/bundles/org.openhab.binding.onewire/src/main/resources/OH-INF/thing/common.xml +++ b/bundles/org.openhab.binding.onewire/src/main/resources/OH-INF/thing/common.xml @@ -23,11 +23,10 @@ temperature value of this sensor - + filters all 85°C readings (POR-value), may suppress valid readings if enabled false - false @@ -37,13 +36,12 @@ temperature value of this sensor - + filters all 85°C readings (POR-value), may suppress valid readings if enabled false - false - + @@ -53,7 +51,6 @@ 10 true - false @@ -77,7 +74,7 @@ relative humidity (0-100%) - + true @@ -87,7 +84,6 @@ /humidity - false diff --git a/bundles/org.openhab.binding.onewire/src/main/resources/OH-INF/thing/multisensor.xml b/bundles/org.openhab.binding.onewire/src/main/resources/OH-INF/thing/multisensor.xml index 3af91defdcf0e..7d65617e271ad 100644 --- a/bundles/org.openhab.binding.onewire/src/main/resources/OH-INF/thing/multisensor.xml +++ b/bundles/org.openhab.binding.onewire/src/main/resources/OH-INF/thing/multisensor.xml @@ -18,19 +18,17 @@ 1-wire multisensor (DS2438-based) - + Sensor ID of the DS2438 sensor in format: xx.xxxxxxxxxxxx or a full path including hubs/branches - true - + Time in seconds after which the thing is refreshed 300 s - false - + @@ -38,7 +36,6 @@ DS2438 true - false @@ -49,26 +46,23 @@ 1-wire multisensor (DS2438-based) - + Sensor ID of the DS2438 sensor in format: xx.xxxxxxxxxxxx or a full path including hubs/branches - true - + Time in seconds after which the thing is refreshed 300 s - false - + Time in seconds after which the digital I/Os are refreshed 10 s - false - + @@ -76,7 +70,6 @@ DS2438 true - false @@ -90,17 +83,15 @@ 1 - + Sensor ID in format: xx.xxxxxxxxxxxx) - true - + Time in seconds after which the thing is refreshed 300 s - false diff --git a/bundles/org.openhab.binding.onewiregpio/src/main/java/org/openhab/binding/onewiregpio/internal/handler/OneWireGPIOHandler.java b/bundles/org.openhab.binding.onewiregpio/src/main/java/org/openhab/binding/onewiregpio/internal/handler/OneWireGPIOHandler.java index bbc8548c9a8bb..7ee519735d57c 100644 --- a/bundles/org.openhab.binding.onewiregpio/src/main/java/org/openhab/binding/onewiregpio/internal/handler/OneWireGPIOHandler.java +++ b/bundles/org.openhab.binding.onewiregpio/src/main/java/org/openhab/binding/onewiregpio/internal/handler/OneWireGPIOHandler.java @@ -26,7 +26,6 @@ import java.util.concurrent.TimeUnit; import java.util.stream.Stream; -import org.apache.commons.lang.StringUtils; import org.openhab.binding.onewiregpio.internal.OneWireGPIOBindingConstants; import org.openhab.binding.onewiregpio.internal.OneWireGpioConfiguration; import org.openhab.core.library.types.QuantityType; @@ -94,7 +93,7 @@ public void initialize() { * When invalid parameter is found, default value is assigned. */ private boolean checkConfiguration() { - if (StringUtils.isEmpty(gpioBusFile)) { + if (gpioBusFile == null || gpioBusFile.isEmpty()) { logger.debug("GPIO_BUS_FILE not set. Please check configuration, and set proper path to w1_slave file."); updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, "The path to the w1_slave sensor data file is missing."); diff --git a/bundles/org.openhab.binding.onkyo/src/main/java/org/openhab/binding/onkyo/internal/discovery/OnkyoUpnpDiscoveryParticipant.java b/bundles/org.openhab.binding.onkyo/src/main/java/org/openhab/binding/onkyo/internal/discovery/OnkyoUpnpDiscoveryParticipant.java index 280b930351085..a77136443604b 100644 --- a/bundles/org.openhab.binding.onkyo/src/main/java/org/openhab/binding/onkyo/internal/discovery/OnkyoUpnpDiscoveryParticipant.java +++ b/bundles/org.openhab.binding.onkyo/src/main/java/org/openhab/binding/onkyo/internal/discovery/OnkyoUpnpDiscoveryParticipant.java @@ -20,7 +20,7 @@ import java.util.Map; import java.util.Set; -import org.apache.commons.lang.StringUtils; +import org.apache.commons.lang3.StringUtils; import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.Nullable; import org.jupnp.model.meta.RemoteDevice; @@ -64,7 +64,7 @@ public OnkyoUpnpDiscoveryParticipant() { protected void activate(ComponentContext componentContext) { if (componentContext.getProperties() != null) { String autoDiscoveryPropertyValue = (String) componentContext.getProperties().get("enableAutoDiscovery"); - if (StringUtils.isNotEmpty(autoDiscoveryPropertyValue)) { + if (autoDiscoveryPropertyValue != null && !autoDiscoveryPropertyValue.isEmpty()) { isAutoDiscoveryEnabled = Boolean.valueOf(autoDiscoveryPropertyValue); } } @@ -81,8 +81,8 @@ public Set getSupportedThingTypeUIDs() { DiscoveryResult result = null; ThingUID thingUid = getThingUID(device); if (thingUid != null) { - String label = StringUtils.isEmpty(device.getDetails().getFriendlyName()) ? device.getDisplayString() - : device.getDetails().getFriendlyName(); + String friendlyName = device.getDetails().getFriendlyName(); + String label = friendlyName == null || friendlyName.isEmpty() ? device.getDisplayString() : friendlyName; Map properties = new HashMap<>(2, 1); properties.put(HOST_PARAMETER, device.getIdentity().getDescriptorURL().getHost()); properties.put(UDN_PARAMETER, device.getIdentity().getUdn().getIdentifierString()); @@ -97,13 +97,12 @@ public Set getSupportedThingTypeUIDs() { public @Nullable ThingUID getThingUID(RemoteDevice device) { ThingUID result = null; if (isAutoDiscoveryEnabled) { - if (StringUtils.containsIgnoreCase(device.getDetails().getManufacturerDetails().getManufacturer(), - MANUFACTURER)) { - logger.debug("Manufacturer matched: search: {}, device value: {}.", MANUFACTURER, - device.getDetails().getManufacturerDetails().getManufacturer()); - if (StringUtils.containsIgnoreCase(device.getType().getType(), UPNP_DEVICE_TYPE)) { - logger.debug("Device type matched: search: {}, device value: {}.", UPNP_DEVICE_TYPE, - device.getType().getType()); + String manufacturer = device.getDetails().getManufacturerDetails().getManufacturer(); + if (manufacturer != null && manufacturer.toLowerCase().contains(MANUFACTURER.toLowerCase())) { + logger.debug("Manufacturer matched: search: {}, device value: {}.", MANUFACTURER, manufacturer); + String type = device.getType().getType(); + if (type != null && type.toLowerCase().contains(UPNP_DEVICE_TYPE.toLowerCase())) { + logger.debug("Device type matched: search: {}, device value: {}.", UPNP_DEVICE_TYPE, type); String deviceModel = device.getDetails().getModelDetails() != null ? device.getDetails().getModelDetails().getModelName() @@ -143,7 +142,7 @@ private ThingTypeUID findThingType(@Nullable String deviceModel) { * @return */ private boolean isSupportedDeviceModel(final @Nullable String deviceModel) { - return StringUtils.isNotBlank(deviceModel) && Arrays.stream(OnkyoModel.values()) + return deviceModel != null && !deviceModel.isBlank() && Arrays.stream(OnkyoModel.values()) .anyMatch(model -> StringUtils.startsWithIgnoreCase(deviceModel, model.getId())); } } diff --git a/bundles/org.openhab.binding.opengarage/src/main/java/org/openhab/binding/opengarage/internal/api/ControllerVariables.java b/bundles/org.openhab.binding.opengarage/src/main/java/org/openhab/binding/opengarage/internal/api/ControllerVariables.java index e048a7e5a1f04..14c7d1e9f3109 100644 --- a/bundles/org.openhab.binding.opengarage/src/main/java/org/openhab/binding/opengarage/internal/api/ControllerVariables.java +++ b/bundles/org.openhab.binding.opengarage/src/main/java/org/openhab/binding/opengarage/internal/api/ControllerVariables.java @@ -43,7 +43,7 @@ private ControllerVariables() { public static ControllerVariables parse(String response) { LOGGER.debug("Parsing string: \"{}\"", response); /* parse json string */ - JsonObject jsonObject = new JsonParser().parse(response).getAsJsonObject(); + JsonObject jsonObject = JsonParser.parseString(response).getAsJsonObject(); ControllerVariables info = new ControllerVariables(); info.dist = jsonObject.get("dist").getAsInt(); info.door = jsonObject.get("door").getAsInt(); diff --git a/bundles/org.openhab.binding.opensprinkler/src/main/java/org/openhab/binding/opensprinkler/internal/util/Parse.java b/bundles/org.openhab.binding.opensprinkler/src/main/java/org/openhab/binding/opensprinkler/internal/util/Parse.java index dccc437a5487a..6fffab23fe03e 100644 --- a/bundles/org.openhab.binding.opensprinkler/src/main/java/org/openhab/binding/opensprinkler/internal/util/Parse.java +++ b/bundles/org.openhab.binding.opensprinkler/src/main/java/org/openhab/binding/opensprinkler/internal/util/Parse.java @@ -27,8 +27,6 @@ * @author Chris Graham - Initial contribution */ public class Parse { - private static JsonParser jsonParser = new JsonParser(); - /** * Parses an integer from a JSON string given its key name. * @@ -37,7 +35,7 @@ public class Parse { * @return int value of the objects data. */ public static int jsonInt(String jsonData, String keyName) { - JsonElement jelement = jsonParser.parse(jsonData); + JsonElement jelement = JsonParser.parseString(jsonData); JsonObject jobject = jelement.getAsJsonObject(); return jobject.get(keyName).getAsInt(); } @@ -50,7 +48,7 @@ public static int jsonInt(String jsonData, String keyName) { * @return String value of the objects data. */ public static String jsonString(String jsonData, String keyName) { - JsonElement jelement = jsonParser.parse(jsonData); + JsonElement jelement = JsonParser.parseString(jsonData); JsonObject jobject = jelement.getAsJsonObject(); return jobject.get(keyName).getAsString(); } @@ -64,7 +62,7 @@ public static String jsonString(String jsonData, String keyName) { * @return int value of the objects data. */ public static int jsonIntAtArrayIndex(String jsonData, String keyName, int index) { - JsonElement jelement = jsonParser.parse(jsonData); + JsonElement jelement = JsonParser.parseString(jsonData); JsonObject jobject = jelement.getAsJsonObject(); JsonArray jarray = jobject.get(keyName).getAsJsonArray(); return jarray.get(index).getAsInt(); @@ -79,7 +77,7 @@ public static int jsonIntAtArrayIndex(String jsonData, String keyName, int index * @return String value of the objects data. */ public static String jsonStringAtArrayIndex(String jsonData, String keyName, int index) { - JsonElement jelement = jsonParser.parse(jsonData); + JsonElement jelement = JsonParser.parseString(jsonData); JsonObject jobject = jelement.getAsJsonObject(); JsonArray jarray = jobject.get(keyName).getAsJsonArray(); return jarray.get(index).getAsString(); @@ -95,7 +93,7 @@ public static String jsonStringAtArrayIndex(String jsonData, String keyName, int public static List jsonIntArray(String jsonData, String keyName) { List returnList = new ArrayList<>(); - JsonElement jelement = jsonParser.parse(jsonData); + JsonElement jelement = JsonParser.parseString(jsonData); JsonObject jobject = jelement.getAsJsonObject(); JsonArray jarray = jobject.get(keyName).getAsJsonArray(); @@ -116,7 +114,7 @@ public static List jsonIntArray(String jsonData, String keyName) { public static List jsonStringArray(String jsonData, String keyName) { List returnList = new ArrayList<>(); - JsonElement jelement = jsonParser.parse(jsonData); + JsonElement jelement = JsonParser.parseString(jsonData); JsonObject jobject = jelement.getAsJsonObject(); JsonArray jarray = jobject.get(keyName).getAsJsonArray(); diff --git a/bundles/org.openhab.binding.openweathermap/README.md b/bundles/org.openhab.binding.openweathermap/README.md index 59688934a34f1..69510989c5e8d 100644 --- a/bundles/org.openhab.binding.openweathermap/README.md +++ b/bundles/org.openhab.binding.openweathermap/README.md @@ -29,6 +29,11 @@ If the request fails, all daily forecast channel groups will be removed from the ### Current UV Index And Forecast +::: tip Note +The product will retire on 1st April 2021, please find UV data in the One Call API. +One Call API includes current, hourly forecast for 7 days and 5 days historical UV data. +::: + The third thing `uvindex` supports the [current UV Index](https://openweathermap.org/api/uvi#current) and [forecasted UV Index](https://openweathermap.org/api/uvi#forecast) for a specific location. It requires coordinates of the location of your interest. You can add as much `uvindex` things for different locations to your setup as you like to observe. @@ -48,7 +53,7 @@ For every day in history you have to create a different thing. ## Discovery -If a system location is set, a "Local Weather And Forecast" (`weather-and-forecast`) thing and "Local UV Index" (`uvindex`) thing will be automatically discovered for this location. +If a system location is set, a "Local Weather And Forecast" (`weather-and-forecast`) thing will be automatically discovered for this location. Once the system location will be changed, the background discovery updates the configuration of both things accordingly. ## Thing Configuration @@ -133,7 +138,6 @@ These channels are not supported in the One Call API | current | visibility | Number:Length | Current visibility. | | current | uvindex | Number | Current UV Index. Only available in the One Call API | - **Attention**: Rain item is showing "1h" in the case when data are received from weather stations directly. The fact is that some METAR stations do not have precipitation indicators or do not measure precipitation conditions due to some other technical reasons. In this case, we use model data. @@ -181,31 +185,30 @@ See above for a description of the available channels. ### Daily Forecast -| Channel Group ID | Channel ID | Item Type | Description | -|------------------------------------------------------------------|----------------------|----------------------|----------------------------------------------------------------------------| -| forecastToday, forecastTomorrow, forecastDay2, ... forecastDay16 | time-stamp | DateTime | Date of data forecasted. | -| forecastToday, forecastTomorrow, forecastDay2, ... forecastDay16 | condition | String | Forecast weather condition. | -| forecastToday, forecastTomorrow, forecastDay2, ... forecastDay16 | condition-id | String | Id of the forecasted weather condition. **Advanced** | -| forecastToday, forecastTomorrow, forecastDay2, ... forecastDay16 | icon | Image | Icon representing the forecasted weather condition. | -| forecastToday, forecastTomorrow, forecastDay2, ... forecastDay16 | icon-id | String | Id of the icon representing the forecasted weather condition. **Advanced** | -| forecastToday, forecastTomorrow, forecastDay2, ... forecastDay16 | apparent-temperature | Number:Temperature | Forecasted apparent temperature. Not available in the One Call API | -| forecastToday, forecastTomorrow, forecastDay2, ... forecastDay16 | min-temperature | Number:Temperature | Minimum forecasted temperature of a day. | -| forecastToday, forecastTomorrow, forecastDay2, ... forecastDay16 | max-temperature | Number:Temperature | Maximum forecasted temperature of a day. | -| forecastToday, forecastTomorrow, forecastDay2, ... forecastDay16 | pressure | Number:Pressure | Forecasted barometric pressure. | -| forecastToday, forecastTomorrow, forecastDay2, ... forecastDay16 | humidity | Number:Dimensionless | Forecasted atmospheric humidity. | -| forecastToday, forecastTomorrow, forecastDay2, ... forecastDay16 | wind-speed | Number:Speed | Forecasted wind speed. | -| forecastToday, forecastTomorrow, forecastDay2, ... forecastDay16 | wind-direction | Number:Angle | Forecasted wind direction. | -| forecastToday, forecastTomorrow, forecastDay2, ... forecastDay16 | gust-speed | Number:Speed | Forecasted gust speed. **Advanced** | -| forecastToday, forecastTomorrow, forecastDay2, ... forecastDay16 | cloudiness | Number:Dimensionless | Forecasted cloudiness. | -| forecastToday, forecastTomorrow, forecastDay2, ... forecastDay16 | rain | Number:Length | Expected rain volume of a day. | -| forecastToday, forecastTomorrow, forecastDay2, ... forecastDay16 | snow | Number:Length | Expected snow volume of a day. | -| forecastToday, forecastTomorrow, forecastDay2, ... forecastDay7 | dew-point | Number:Temperature | Expected dew-point. Only available in the One Call API | -| forecastToday, forecastTomorrow, forecastDay2, ... forecastDay7 | uvindex | Number | Forecasted Midday UV Index. Only available in the One Call API | -| forecastToday, forecastTomorrow, forecastDay2, ... forecastDay7 | precip-probability | Number:Dimensionless | Precipitation probability. Only available in the One Call API | -| forecastToday, forecastTomorrow, forecastDay2, ... forecastDay7 | morning-temperature | Number:Temperature | Expected morning temperature. Only available in the One Call API | -| forecastToday, forecastTomorrow, forecastDay2, ... forecastDay7 | day-temperature | Number:Temperature | Expected day-temperature. Only available in the One Call API | -| forecastToday, forecastTomorrow, forecastDay2, ... forecastDay7 | evening-temperature | Number:Temperature | Expected evening-temperature. Only available in the One Call API | -| forecastToday, forecastTomorrow, forecastDay2, ... forecastDay7 | night-temperature | Number:Temperature | Expected night-temperature. Only available in the One Call API | +| Channel Group ID | Channel ID | Item Type | Description | +|------------------------------------------------------------------|----------------------|----------------------|-----------------------------------------------------------------------------------| +| forecastToday, forecastTomorrow, forecastDay2, ... forecastDay16 | time-stamp | DateTime | Date of data forecasted. | +| forecastToday, forecastTomorrow, forecastDay2, ... forecastDay16 | condition | String | Forecast weather condition. | +| forecastToday, forecastTomorrow, forecastDay2, ... forecastDay16 | condition-id | String | Id of the forecasted weather condition. **Advanced** | +| forecastToday, forecastTomorrow, forecastDay2, ... forecastDay16 | icon | Image | Icon representing the forecasted weather condition. | +| forecastToday, forecastTomorrow, forecastDay2, ... forecastDay16 | icon-id | String | Id of the icon representing the forecasted weather condition. **Advanced** | +| forecastToday, forecastTomorrow, forecastDay2, ... forecastDay16 | apparent-temperature | Number:Temperature | Forecasted apparent temperature. Not available in the One Call API | +| forecastToday, forecastTomorrow, forecastDay2, ... forecastDay16 | min-temperature | Number:Temperature | Minimum forecasted temperature of a day. | +| forecastToday, forecastTomorrow, forecastDay2, ... forecastDay16 | max-temperature | Number:Temperature | Maximum forecasted temperature of a day. | +| forecastToday, forecastTomorrow, forecastDay2, ... forecastDay16 | pressure | Number:Pressure | Forecasted barometric pressure. | +| forecastToday, forecastTomorrow, forecastDay2, ... forecastDay16 | humidity | Number:Dimensionless | Forecasted atmospheric humidity. | +| forecastToday, forecastTomorrow, forecastDay2, ... forecastDay16 | wind-speed | Number:Speed | Forecasted wind speed. | +| forecastToday, forecastTomorrow, forecastDay2, ... forecastDay16 | wind-direction | Number:Angle | Forecasted wind direction. | +| forecastToday, forecastTomorrow, forecastDay2, ... forecastDay16 | gust-speed | Number:Speed | Forecasted gust speed. **Advanced** | +| forecastToday, forecastTomorrow, forecastDay2, ... forecastDay16 | cloudiness | Number:Dimensionless | Forecasted cloudiness. | +| forecastToday, forecastTomorrow, forecastDay2, ... forecastDay16 | rain | Number:Length | Expected rain volume of a day. | +| forecastToday, forecastTomorrow, forecastDay2, ... forecastDay16 | snow | Number:Length | Expected snow volume of a day. | +| forecastToday, forecastTomorrow, forecastDay2, ... forecastDay7 | dew-point | Number:Temperature | Expected dew-point. Only available in the One Call API | +| forecastToday, forecastTomorrow, forecastDay2, ... forecastDay7 | uvindex | Number | Forecasted Midday UV Index. Only available in the One Call API | +| forecastToday, forecastTomorrow, forecastDay2, ... forecastDay7 | morning-temperature | Number:Temperature | Expected morning temperature. Only available in the One Call API | +| forecastToday, forecastTomorrow, forecastDay2, ... forecastDay7 | day-temperature | Number:Temperature | Expected day-temperature. Only available in the One Call API | +| forecastToday, forecastTomorrow, forecastDay2, ... forecastDay7 | evening-temperature | Number:Temperature | Expected evening-temperature. Only available in the One Call API | +| forecastToday, forecastTomorrow, forecastDay2, ... forecastDay7 | night-temperature | Number:Temperature | Expected night-temperature. Only available in the One Call API | | forecastToday, forecastTomorrow, forecastDay2, ... forecastDay7 | apparent-morning | Number:Temperature | Expected apparent temperature in the morning. Only available in the One Call API | | forecastToday, forecastTomorrow, forecastDay2, ... forecastDay7 | apparent-day | Number:Temperature | Expected apparent temperature in the day. Only available in the One Call API | | forecastToday, forecastTomorrow, forecastDay2, ... forecastDay7 | apparent-evening | Number:Temperature | Expected apparent temperature in the evening. Only available in the One Call API | @@ -230,11 +233,10 @@ demo.things Bridge openweathermap:weather-api:api "OpenWeatherMap Account" [apikey="AAA", refreshInterval=30, language="de"] { Thing weather-and-forecast local "Local Weather And Forecast" [location="XXX,YYY", forecastHours=0, forecastDays=7] Thing weather-and-forecast miami "Weather And Forecast In Miami" [location="25.782403,-80.264563", forecastHours=24, forecastDays=0] - Thing uvindex local "Local UV Index" [location="XXX,YYY", forecastDays=7] } ``` -#### One Call API version +#### One Call API Version ```java Bridge openweathermap:weather-api:api "OpenWeatherMap Account" [apikey="Add your API key", refreshInterval=60, language="de"] { @@ -306,16 +308,9 @@ String miamiHourlyForecast06Condition "Condition in Miami for hours 3 to 6 [%s]" Image miamiHourlyForecast06ConditionIcon "Icon" { channel="openweathermap:weather-and-forecast:api:miami:forecastHours06#icon" } Number:Temperature miamiHourlyForecast06Temperature "Temperature in Miami for hours 3 to 6 [%.1f %unit%]" { channel="openweathermap:weather-and-forecast:api:miami:forecastHours06#temperature" } ... - -DateTime localCurrentUVIndexTimestamp "Timestamp of last measurement [%1$tY-%1$tm-%1$td]"