]> git.basschouten.com Git - openhab-addons.git/commitdiff
[nanoleaf] CODEOWNERs, add Shapes Support, beta-firmware support (#10029)
authorstefan-hoehn <stefan.hoehn@aoe.com>
Wed, 3 Feb 2021 20:26:13 +0000 (21:26 +0100)
committerGitHub <noreply@github.com>
Wed, 3 Feb 2021 20:26:13 +0000 (21:26 +0100)
Signed-off-by: Stefan Höhn <stefan@andreaundstefanhoehn.de>
CODEOWNERS
bundles/org.openhab.binding.nanoleaf/README.md
bundles/org.openhab.binding.nanoleaf/src/main/java/org/openhab/binding/nanoleaf/internal/NanoleafBindingConstants.java
bundles/org.openhab.binding.nanoleaf/src/main/java/org/openhab/binding/nanoleaf/internal/OpenAPIUtils.java
bundles/org.openhab.binding.nanoleaf/src/main/java/org/openhab/binding/nanoleaf/internal/discovery/NanoleafMDNSDiscoveryParticipant.java
bundles/org.openhab.binding.nanoleaf/src/main/java/org/openhab/binding/nanoleaf/internal/handler/NanoleafControllerHandler.java
bundles/org.openhab.binding.nanoleaf/src/main/java/org/openhab/binding/nanoleaf/internal/handler/NanoleafPanelHandler.java
bundles/org.openhab.binding.nanoleaf/src/main/resources/OH-INF/i18n/nanoleaf.properties
bundles/org.openhab.binding.nanoleaf/src/main/resources/OH-INF/i18n/nanoleaf_de.properties
bundles/org.openhab.binding.nanoleaf/src/test/java/org/openhab/binding/nanoleaf/internal/OpenAPUUtilsTest.java [new file with mode: 0644]

index 1f67b3c3411fcf4dcf82dc567cb8e058ac560811..4a69d5e57644e3854c9d54cd6e02412cb1eb352b 100644 (file)
 /bundles/org.openhab.binding.mqtt.homeassistant/ @davidgraeff
 /bundles/org.openhab.binding.mqtt.homie/ @davidgraeff
 /bundles/org.openhab.binding.mystrom/ @pail23
-/bundles/org.openhab.binding.nanoleaf/ @raepple
+/bundles/org.openhab.binding.nanoleaf/ @raepple @stefan-hoehn
 /bundles/org.openhab.binding.neato/ @jjlauterbach
 /bundles/org.openhab.binding.neeo/ @tmrobert8
 /bundles/org.openhab.binding.neohub/ @andrewfg
index 7c743513c2aecc28411f6cd0a6cddf853c12780d..ec2435cb035046bb77c4ae6b6ddda517c58f9e1b 100644 (file)
@@ -1,6 +1,6 @@
 # Nanoleaf Binding
 
-This binding integrates the [Nanoleaf Light Panels](https://nanoleaf.me/en/consumer-led-lighting/products/smarter-series/nanoleaf-light-panels-smarter-kit/). 
+This binding integrates the [Nanoleaf Light Panels](https://nanoleaf.me/en/consumer-led-lighting/products/smarter-series/nanoleaf-light-panels-smarter-kit/).
 
 ![Image](doc/Nanoleaf.jpg)
 
@@ -11,8 +11,10 @@ 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.
+
 Currently Nanoleaf's "Light Panels" and "Canvas" devices are supported.
-Note that only the canvas type does support the touch functionality.
+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.
 
@@ -21,7 +23,18 @@ With the controller thing you can control channels which affect all panels, e.g.
 
 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 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.
+
+
+| 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 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.
@@ -49,7 +62,7 @@ Tip: if you press (2) just before adding the item from the inbox it usually catc
 
 **Adding the invidual light panels as a thing**
 
-After you have added the controller as a thing and it has been successfully paired as described as above, the individual panels connected to it can be discovered by **starting another scan** for the Nanoleaf binding. 
+After you have added the controller as a thing and it has been successfully paired as described as above, the individual panels connected to it can be discovered by **starting another scan** for the Nanoleaf binding.
 All connected panels will be added as separate things to the inbox.
 
 Troubleshooting: In seldom cases (in particular together with updating the binding) things or items do not work as expected, are offline or may not be detected.
@@ -62,7 +75,7 @@ In this case:
 
 **Knowing which panel has which id**
 
-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)
 
 - Set up a switch item with the channel panelLayout on the controller (see NanoRetrieveLayout below) and set the switch to true
@@ -73,14 +86,14 @@ Compare the following output with the right picture at the beginning of the arti
 ```                                     
             31413                    9162       13276     
 
-55836       56093       48111       38724       17870        5164       64279 
+55836       56093       48111       38724       17870        5164       64279
 
                         58086        8134                   39755             
 
                                     41451                                     
-                               
+
 ```
-           
+
 Disclaimer: this works best with square devices and not necessarily well with triangles due to the more geometrically flexible layout.
 
 ## Thing Configuration
@@ -131,7 +144,7 @@ A lightpanel thing has the following channels:
 
 **color and panelColor**
 
-The color and panelColor channels support full color control with hue, saturation and brightness values. 
+The color and panelColor 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.
 
@@ -140,7 +153,7 @@ What might not be obvious and even maybe confusing is the fact that brightness a
 **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 
+- 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)
@@ -158,7 +171,7 @@ If a panel is tapped the switch is set to ON and automatically reset to OFF afte
 
 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. 
+- 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.
 
@@ -179,16 +192,16 @@ 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
 
-* the [address="..." ]  of the controller 
+* the [address="..." ]  of the controller
 * and the [id=123] of the lightpanel
 
 in the bracket to identify the uniqueness of the discovered device. Therefore it is recommended to the give the controller a fixed ip address.
 
 Note: To generate the `authToken`:
-    
+
 * On the Nanoleaf controller, hold the on-off button for 5-7 seconds until the LED starts flashing.
 * Send a POST request to the authorization endpoint within 30 seconds of activating pairing, like this:
-    
+
 `http://<address>:16021/api/v1/new`
 
 e.g. via command line `curl --location --request POST 'http://<address>:16021/api/v1/new'`
@@ -232,10 +245,10 @@ sitemap nanoleaf label="Nanoleaf"
 {
     Frame label="Controller" {
             Switch item=NanoleafPower
-            Slider item=NanoleafBrightness 
+            Slider item=NanoleafBrightness
             Colorpicker item=NanoleafColor           
             Text item=NanoleafHue
-            Text item=NanoleafSaturation 
+            Text item=NanoleafSaturation
             Slider item=NanoleafColorTemp     
             Setpoint item=NanoleafColorTempAbs step=100 minValue=1200 maxValue=6500            
             Text item=NanoleafColorMode
@@ -245,20 +258,20 @@ sitemap nanoleaf label="Nanoleaf"
             Selection item=NanoleafRhythmSource mappings=[0="Microphone", 1="Aux"]
             Switch item=NanoRetrieveLayout
     }
-    
+
     Frame label="Panels" {
         Colorpicker item=Panel1Color
         Slider item=Panel1Brightness
         Colorpicker item=Panel2Color
     }
-    
+
     Frame label="Scenes" {
         Switch item=NanoleafRainbowScene
     }
 }
 ```
 
-Note: The mappings to effects in the selection item are specific for each Nanoleaf installation and should be adapted accordingly. 
+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
@@ -281,7 +294,7 @@ then
 
     var hue = 0
     var direction = 1
-    
+
     while(NanoleafRainbowScene.state == ON) {        
         Thread::sleep(pause)        
         hue = hue + (5 * direction)
index 987c2a4ebd98cf9be6587e8e547728e3b2850e7b..57933199d4641cf07c1569477d4397d063f5c652 100644 (file)
@@ -12,6 +12,9 @@
  */
 package org.openhab.binding.nanoleaf.internal;
 
+import java.util.Arrays;
+import java.util.List;
+
 import org.eclipse.jdt.annotation.NonNullByDefault;
 import org.openhab.core.thing.ThingTypeUID;
 
@@ -71,9 +74,12 @@ public class NanoleafBindingConstants {
     public static final String API_MIN_FW_VER_LIGHTPANELS = "1.5.0";
     public static final String API_MIN_FW_VER_CANVAS = "1.1.0";
     public static final String MODEL_ID_LIGHTPANELS = "NL22";
-    public static final String MODEL_ID_CANVAS = "NL29";
+
+    public static final List<String> MODELS_WITH_TOUCHSUPPORT = Arrays.asList("NL29", "NL42");
     public static final String DEVICE_TYPE_LIGHTPANELS = "lightPanels";
-    public static final String DEVICE_TYPE_CANVAS = "canvas";
+    public static final String DEVICE_TYPE_TOUCHSUPPORT = "canvas"; // we need to keep this enum for backward
+                                                                    // compatibility even though not only canvas type
+                                                                    // support touch
 
     // mDNS discovery service type
     // see http://forum.nanoleaf.me/docs/openapi#_gf9l5guxt8r0
index c3c7f646da5bbf26a17fa622ce1718ddccba3f30..1875d474f7afe924357344f9a01ecd0d3c1a01ad 100644 (file)
@@ -49,6 +49,7 @@ public class OpenAPIUtils {
 
     // Regular expression for firmware version
     private static final Pattern FIRMWARE_VERSION_PATTERN = Pattern.compile("(\\d+)\\.(\\d+)\\.(\\d+)");
+    private static final Pattern FIRMWARE_VERSION_PATTERN_BETA = Pattern.compile("(\\d+)\\.(\\d+)\\.(\\d+)-(\\d+)");
 
     public static Request requestBuilder(HttpClient httpClient, NanoleafControllerConfig controllerConfig,
             String apiOperation, HttpMethod method) throws NanoleafException {
@@ -162,11 +163,21 @@ public class OpenAPIUtils {
         return true;
     }
 
-    private static int[] getFirmwareVersionNumbers(String firmwareVersion) throws IllegalArgumentException {
+    public static int[] getFirmwareVersionNumbers(String firmwareVersion) throws IllegalArgumentException {
+        LOGGER.debug("firmwareVersion: {}", firmwareVersion);
         Matcher m = FIRMWARE_VERSION_PATTERN.matcher(firmwareVersion);
-        if (!m.matches()) {
-            throw new IllegalArgumentException("Malformed controller firmware version");
+
+        if (m.matches()) {
+            return new int[] { Integer.parseInt(m.group(1)), Integer.parseInt(m.group(2)),
+                    Integer.parseInt(m.group(3)) };
+        } else {
+            m = FIRMWARE_VERSION_PATTERN_BETA.matcher(firmwareVersion);
+            if (m.matches()) {
+                return new int[] { Integer.parseInt(m.group(1)), Integer.parseInt(m.group(2)),
+                        Integer.parseInt(m.group(3)), Integer.parseInt(m.group(4)) };
+            } else {
+                throw new IllegalArgumentException("Malformed controller firmware version " + firmwareVersion);
+            }
         }
-        return new int[] { Integer.parseInt(m.group(1)), Integer.parseInt(m.group(2)), Integer.parseInt(m.group(3)) };
     }
 }
index 004e0311ce76005f2dcaeb5fc236dd2ef1e0e8ee..751f90712e98a1e714b9b06b4c49f400ccab87a9 100644 (file)
@@ -75,7 +75,7 @@ public class NanoleafMDNSDiscoveryParticipant implements MDNSDiscoveryParticipan
         properties.put(Thing.PROPERTY_MODEL_ID, modelId);
         properties.put(Thing.PROPERTY_VENDOR, "Nanoleaf");
         String qualifiedName = service.getQualifiedName();
-        logger.debug("AVR found: {}", qualifiedName);
+        logger.debug("Device found: {}", qualifiedName);
 
         logger.trace("Discovered nanoleaf host: {} port: {} firmWare: {} modelId: {} qualifiedName: {}", host, port,
                 firmwareVersion, modelId, qualifiedName);
index c5f17a71584047469f8ed435130d685df9ac6116..ce5d0f0677a328d776b5fe6ac1148985a7e284ec 100644 (file)
@@ -17,10 +17,7 @@ import static org.openhab.binding.nanoleaf.internal.NanoleafBindingConstants.*;
 import java.net.URI;
 import java.nio.ByteBuffer;
 import java.nio.charset.StandardCharsets;
-import java.util.List;
-import java.util.Map;
-import java.util.Objects;
-import java.util.Scanner;
+import java.util.*;
 import java.util.concurrent.CopyOnWriteArrayList;
 import java.util.concurrent.ExecutionException;
 import java.util.concurrent.ScheduledFuture;
@@ -132,8 +129,8 @@ public class NanoleafControllerHandler extends BaseBridgeHandler {
 
         Map<String, String> properties = getThing().getProperties();
         String propertyModelId = properties.get(Thing.PROPERTY_MODEL_ID);
-        if (MODEL_ID_CANVAS.equals(propertyModelId)) {
-            config.deviceType = DEVICE_TYPE_CANVAS;
+        if (hasTouchSupport(propertyModelId)) {
+            config.deviceType = DEVICE_TYPE_TOUCHSUPPORT;
         } else {
             config.deviceType = DEVICE_TYPE_LIGHTPANELS;
         }
@@ -334,9 +331,9 @@ public class NanoleafControllerHandler extends BaseBridgeHandler {
 
     private synchronized void startTouchJob() {
         NanoleafControllerConfig config = getConfigAs(NanoleafControllerConfig.class);
-        if (!config.deviceType.equals(DEVICE_TYPE_CANVAS)) {
+        if (!config.deviceType.equals(DEVICE_TYPE_TOUCHSUPPORT)) {
             logger.debug("NOT starting TouchJob for Panel {} because it has wrong device type '{}' vs required '{}'",
-                    this.getThing().getUID(), config.deviceType, DEVICE_TYPE_CANVAS);
+                    this.getThing().getUID(), config.deviceType, DEVICE_TYPE_TOUCHSUPPORT);
             return;
         } else {
             logger.debug("Starting TouchJob for Panel {}", this.getThing().getUID());
@@ -353,6 +350,10 @@ public class NanoleafControllerHandler extends BaseBridgeHandler {
         }
     }
 
+    private boolean hasTouchSupport(@Nullable String deviceType) {
+        return (MODELS_WITH_TOUCHSUPPORT.contains(deviceType));
+    }
+
     private synchronized void stopTouchJob() {
         if (touchJob != null && !touchJob.isCancelled()) {
             logger.debug("Stop touch job");
@@ -636,9 +637,9 @@ public class NanoleafControllerHandler extends BaseBridgeHandler {
 
         Configuration config = editConfiguration();
 
-        if (MODEL_ID_CANVAS.equals(controllerInfo.getModel())) {
-            config.put(NanoleafControllerConfig.DEVICE_TYPE, DEVICE_TYPE_CANVAS);
-            logger.debug("Set to device type {}", DEVICE_TYPE_CANVAS);
+        if (hasTouchSupport(controllerInfo.getModel())) {
+            config.put(NanoleafControllerConfig.DEVICE_TYPE, DEVICE_TYPE_TOUCHSUPPORT);
+            logger.debug("Set to device type {}", DEVICE_TYPE_TOUCHSUPPORT);
         } else {
             config.put(NanoleafControllerConfig.DEVICE_TYPE, DEVICE_TYPE_LIGHTPANELS);
             logger.debug("Set to device type {}", DEVICE_TYPE_LIGHTPANELS);
index 1d928cbb08022b35ec908b42198284697d972174..628f11960693d0edb6eb858f63092ed0f579b868 100644 (file)
@@ -318,7 +318,7 @@ public class NanoleafPanelHandler extends BaseThingHandler {
                 }
             }
         } catch (NanoleafNotFoundException nfe) {
-            logger.warn("Panel data could not be retrieved as no data was returned (static type missing?) : {}",
+            logger.debug("Panel data could not be retrieved as no data was returned (static type missing?) : {}",
                     nfe.getMessage());
         } catch (NanoleafBadRequestException nfe) {
             logger.debug(
index 4f7f992a145d1051e1eb12b012bf0643d5e3c0b4..39aefb6623b891ae7413e6db4c052b0de0c11807 100644 (file)
@@ -1,5 +1,5 @@
 binding.nanoleaf.name = Nanoleaf Binding
-binding.nanoleaf.description = Integrates the Nanoleaf Light Panels (v150320)
+binding.nanoleaf.description = Integrates the Nanoleaf Light Panels (v010221)
 
 # thing types
 thing-type.nanoleaf.controller.name = Nanoleaf Controller
index aeebf1f8297f28265c5ee8cd4019f96132a076d3..b21023e07cdd41e8cba73240deb146a0ffe960be 100644 (file)
@@ -1,5 +1,5 @@
 binding.nanoleaf.name = Nanoleaf Binding
-binding.nanoleaf.description = Binding für die Integration des Nanoleaf Light Panels (v150320)
+binding.nanoleaf.description = Binding für die Integration des Nanoleaf Light Panels (Ein mit dem Controller verbundenes Paneel)
 
 # thing types
 thing-type.nanoleaf.controller.name = Nanoleaf Controller
diff --git a/bundles/org.openhab.binding.nanoleaf/src/test/java/org/openhab/binding/nanoleaf/internal/OpenAPUUtilsTest.java b/bundles/org.openhab.binding.nanoleaf/src/test/java/org/openhab/binding/nanoleaf/internal/OpenAPUUtilsTest.java
new file mode 100644 (file)
index 0000000..6a11bd6
--- /dev/null
@@ -0,0 +1,42 @@
+/**
+ * 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;
+
+import static org.hamcrest.CoreMatchers.is;
+import static org.hamcrest.MatcherAssert.assertThat;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.junit.jupiter.api.Test;
+
+/**
+ * Test Firmware check
+ *
+ * @author Stefan Höhn - Initial contribution
+ */
+
+@NonNullByDefault
+public class OpenAPUUtilsTest {
+
+    @Test
+    public void testStateOn() {
+        int[] versions = OpenAPIUtils.getFirmwareVersionNumbers("5.1.2");
+        assertThat(versions[0], is(5));
+        assertThat(versions[1], is(1));
+        assertThat(versions[2], is(2));
+        int[] versions2 = OpenAPIUtils.getFirmwareVersionNumbers("5.1.2-4");
+        assertThat(versions2[0], is(5));
+        assertThat(versions2[1], is(1));
+        assertThat(versions2[2], is(2));
+        assertThat(versions2[3], is(4));
+    }
+}