From f3209ec2741a660f44dcc088a9651ec8b7645184 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B8rgen=20Austvik?= Date: Sat, 15 Oct 2022 13:48:15 +0200 Subject: [PATCH 01/13] Visualize Nanoleaf layout MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Jørgen Austvik --- .../internal/NanoleafBindingConstants.java | 5 + .../handler/NanoleafControllerHandler.java | 15 ++ .../internal/layout/NanoleafLayout.java | 130 ++++++++++++++++++ .../nanoleaf/internal/layout/Point2D.java | 55 ++++++++ .../nanoleaf/internal/layout/ShapeType.java | 79 +++++++++++ .../resources/OH-INF/i18n/nanoleaf.properties | 2 + .../resources/OH-INF/thing/lightpanels.xml | 7 + 7 files changed, 293 insertions(+) create mode 100644 bundles/org.openhab.binding.nanoleaf/src/main/java/org/openhab/binding/nanoleaf/internal/layout/NanoleafLayout.java create mode 100644 bundles/org.openhab.binding.nanoleaf/src/main/java/org/openhab/binding/nanoleaf/internal/layout/Point2D.java create mode 100644 bundles/org.openhab.binding.nanoleaf/src/main/java/org/openhab/binding/nanoleaf/internal/layout/ShapeType.java 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 fef86f56181ca..17900c9a957af 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 @@ -58,6 +58,7 @@ public class NanoleafBindingConstants { public static final String CHANNEL_SWIPE_EVENT_DOWN = "DOWN"; public static final String CHANNEL_SWIPE_EVENT_LEFT = "LEFT"; public static final String CHANNEL_SWIPE_EVENT_RIGHT = "RIGHT"; + public static final String CHANNEL_LAYOUT = "layout"; // List of light panel channels public static final String CHANNEL_PANEL_COLOR = "color"; @@ -93,4 +94,8 @@ public class NanoleafBindingConstants { // Color channels increase/decrease brightness step size public static final int BRIGHTNESS_STEP_SIZE = 5; + + // Layout rendering + public static final int LAYOUT_LIGHT_RADIUS = 8; + public static final int LAYOUT_BORDER_WIDTH = 30; } 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 43308905867df..3f3aab4b65f2a 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 @@ -14,6 +14,7 @@ import static org.openhab.binding.nanoleaf.internal.NanoleafBindingConstants.*; +import java.io.IOException; import java.net.URI; import java.nio.charset.StandardCharsets; import java.util.Collection; @@ -43,6 +44,7 @@ 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.layout.NanoleafLayout; import org.openhab.binding.nanoleaf.internal.model.AuthToken; import org.openhab.binding.nanoleaf.internal.model.BooleanState; import org.openhab.binding.nanoleaf.internal.model.Brightness; @@ -65,6 +67,7 @@ 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.RawType; import org.openhab.core.library.types.StringType; import org.openhab.core.thing.Bridge; import org.openhab.core.thing.ChannelUID; @@ -664,6 +667,7 @@ private void updateFromControllerInfo() throws NanoleafException { updateProperties(); updateConfiguration(); + updateLayout(controllerInfo.getPanelLayout()); for (NanoleafControllerListener controllerListener : controllerListeners) { controllerListener.onControllerInfoFetched(getThing().getUID(), controllerInfo); @@ -705,6 +709,17 @@ private void updateProperties() { } } + private void updateLayout(PanelLayout panelLayout) { + try { + byte[] bytes = NanoleafLayout.render(panelLayout); + if (bytes.length > 0) { + updateState(CHANNEL_LAYOUT, new RawType(bytes, "image/png")); + } + } catch (IOException ioex) { + logger.info("Failed to create layout image", ioex); + } + } + private ControllerInfo receiveControllerInfo() throws NanoleafException, NanoleafUnauthorizedException { ContentResponse controllerlInfoJSON = OpenAPIUtils.sendOpenAPIRequest(OpenAPIUtils.requestBuilder(httpClient, getControllerConfig(), API_GET_CONTROLLER_INFO, HttpMethod.GET)); diff --git a/bundles/org.openhab.binding.nanoleaf/src/main/java/org/openhab/binding/nanoleaf/internal/layout/NanoleafLayout.java b/bundles/org.openhab.binding.nanoleaf/src/main/java/org/openhab/binding/nanoleaf/internal/layout/NanoleafLayout.java new file mode 100644 index 0000000000000..3838baf62cc2e --- /dev/null +++ b/bundles/org.openhab.binding.nanoleaf/src/main/java/org/openhab/binding/nanoleaf/internal/layout/NanoleafLayout.java @@ -0,0 +1,130 @@ +/** + * Copyright (c) 2010-2022 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.layout; + +import java.awt.Graphics2D; +import java.awt.image.BufferedImage; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.util.Collection; +import java.util.List; + +import javax.imageio.ImageIO; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.openhab.binding.nanoleaf.internal.NanoleafBindingConstants; +import org.openhab.binding.nanoleaf.internal.model.GlobalOrientation; +import org.openhab.binding.nanoleaf.internal.model.Layout; +import org.openhab.binding.nanoleaf.internal.model.PanelLayout; +import org.openhab.binding.nanoleaf.internal.model.PositionDatum; + +/** + * Renders the Nonoleaf layout to an image. + * + * @author Jørgen Austvik - Initial contribution + */ +@NonNullByDefault +public class NanoleafLayout { + + public static byte[] render(PanelLayout panelLayout) throws IOException { + double rotationRadians = 0; + GlobalOrientation globalOrientation = panelLayout.getGlobalOrientation(); + if (globalOrientation != null) { + rotationRadians = calculateRotation(globalOrientation); + } + + Layout layout = panelLayout.getLayout(); + if (layout == null) { + return new byte[] {}; + } + + List panels = layout.getPositionData(); + if (panels == null) { + return new byte[] {}; + } + + Point2D size[] = findSize(panels, rotationRadians); + Point2D min = size[0]; + Point2D max = size[1]; + Point2D prev = null; + Point2D first = null; + + int sideCounter = 0; + + BufferedImage image = new BufferedImage( + (max.getX() - min.getX()) + 2 * NanoleafBindingConstants.LAYOUT_BORDER_WIDTH, + (max.getY() - min.getY()) + 2 * NanoleafBindingConstants.LAYOUT_BORDER_WIDTH, + BufferedImage.TYPE_INT_RGB); + Graphics2D g2 = image.createGraphics(); + for (PositionDatum panel : panels) { + final int expectedSides = ShapeType.valueOf(panel.getShapeType()).getNumSides(); + var rotated = new Point2D(panel.getPosX(), panel.getPosY()).rotate(rotationRadians); + + Point2D current = new Point2D(NanoleafBindingConstants.LAYOUT_BORDER_WIDTH + rotated.getX() - min.getX(), + NanoleafBindingConstants.LAYOUT_BORDER_WIDTH - rotated.getY() - min.getY()); + + g2.fillOval((int) current.getX() - NanoleafBindingConstants.LAYOUT_LIGHT_RADIUS / 2, + (int) current.getY() - NanoleafBindingConstants.LAYOUT_LIGHT_RADIUS / 2, + NanoleafBindingConstants.LAYOUT_LIGHT_RADIUS, NanoleafBindingConstants.LAYOUT_LIGHT_RADIUS); + g2.drawString(Integer.toString(panel.getPanelId()), (int) current.getX(), (int) current.getY()); + + if (sideCounter == 0) { + first = current; + } + + if (sideCounter > 0 && sideCounter != expectedSides && prev != null) { + g2.drawLine((int) prev.getX(), (int) prev.getY(), (int) current.getX(), (int) current.getY()); + } + + sideCounter++; + + if (sideCounter == expectedSides && first != null) { + g2.drawLine((int) current.getX(), (int) current.getY(), (int) first.getX(), (int) first.getY()); + sideCounter = 0; + } + + prev = current; + } + + ByteArrayOutputStream out = new ByteArrayOutputStream(); + ImageIO.write(image, "png", out); + return out.toByteArray(); + } + + private static double calculateRotation(GlobalOrientation globalOrientation) { + Integer maxObj = globalOrientation.getMax(); + int maxValue = maxObj == null ? 360 : (int) maxObj; + int value = globalOrientation.getValue(); // 0 - 360 measured counter clockwise. + return ((double) (maxValue - value)) * (Math.PI / 180); + } + + private static Point2D[] findSize(Collection panels, double rotationRadians) { + + int maxX = 0; + int maxY = 0; + int minX = 0; + int minY = 0; + + for (PositionDatum panel : panels) { + var rotated = new Point2D(panel.getPosX(), panel.getPosY()).rotate(rotationRadians); + + maxX = Math.max((int) rotated.getX(), maxX); + maxY = Math.max((int) rotated.getY(), maxY); + minX = Math.min((int) rotated.getX(), minX); + minY = Math.min((int) rotated.getY(), minY); + } + + return new Point2D[] { new Point2D(minX, minY), new Point2D(maxX, maxY) }; + } +} diff --git a/bundles/org.openhab.binding.nanoleaf/src/main/java/org/openhab/binding/nanoleaf/internal/layout/Point2D.java b/bundles/org.openhab.binding.nanoleaf/src/main/java/org/openhab/binding/nanoleaf/internal/layout/Point2D.java new file mode 100644 index 0000000000000..7dadd51ed6dc2 --- /dev/null +++ b/bundles/org.openhab.binding.nanoleaf/src/main/java/org/openhab/binding/nanoleaf/internal/layout/Point2D.java @@ -0,0 +1,55 @@ +/** + * Copyright (c) 2010-2022 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.layout; + +import org.eclipse.jdt.annotation.NonNullByDefault; + +/** + * Simple pair class. + * + * @author Jørgen Austvik - Initial contribution + */ +@NonNullByDefault +public class Point2D { + private final int x; + private final int y; + + public Point2D(int x, int y) { + this.x = x; + this.y = y; + } + + public int getX() { + return x; + } + + public int getY() { + return y; + } + + /** + * Rotates the point a given amount of radians. + * + * @param radians The amount to rotate the point + * @return A new point which is rotated + */ + public Point2D rotate(double radians) { + double sinAngle = Math.sin(radians); + double cosAngle = Math.cos(radians); + + int newX = (int) (cosAngle * x - sinAngle * y); + int newY = (int) (sinAngle * x + cosAngle * y); + return new Point2D(newX, newY); + } +} diff --git a/bundles/org.openhab.binding.nanoleaf/src/main/java/org/openhab/binding/nanoleaf/internal/layout/ShapeType.java b/bundles/org.openhab.binding.nanoleaf/src/main/java/org/openhab/binding/nanoleaf/internal/layout/ShapeType.java new file mode 100644 index 0000000000000..73ea07ff926ea --- /dev/null +++ b/bundles/org.openhab.binding.nanoleaf/src/main/java/org/openhab/binding/nanoleaf/internal/layout/ShapeType.java @@ -0,0 +1,79 @@ +/** + * Copyright (c) 2010-2022 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.layout; + +import org.eclipse.jdt.annotation.NonNullByDefault; + +/** + * Information about the different Nanoleaf shapes. + * + * @author Jørgen Austvik - Initial contribution + */ +@NonNullByDefault +public enum ShapeType { + TRIANGLE("Triangle", 0, 150, 3), + RHYTHM("Rhythm", 1, 0, 1), + SQUUARE("Square", 2, 100, 4), + CONTROL_SQUARE_MASTER("Control Square Master", 3, 100, 0), + CONTROL_SQUARE_PASSIVE("Control Square Passive", 4, 100, 0), + SHAPES_HEXAGON("Hexagon (Shapes)", 7, 67, 6), + SHAPES_TRIANGLE("Triangle (Shapes)", 8, 134, 3), + SHAPES_MINI_TRIANGLE("Mini Triangle (Shapes)", 9, 67, 3), + SHAPES_CONTROLLER("Controller (Shapes)", 12, 0, 0), + ELEMENTS_HEXAGON("Elements Hexagon", 14, 134, 6), + ELEMENTS_HEXAGON_CORNER("Elements Hexagon - Corner", 15, 33.5 / 58, 6), + LINES_CONNECTOR("Lines Connector", 16, 11, 1), + LIGHT_LINES("Light Lines", 17, 154, 1), + LINES_LINES_SINGLE("Light Lines - Single Sone", 18, 77, 1), + CONTROLLER_CAP("Controller Cap", 19, 11, 0), + POWER_CONNECTOR("Power Connector", 20, 11, 0); + + private final String name; + private final int id; + private final double sideLength; + private final int numSides; + + ShapeType(String name, int id, double sideLenght, int numSides) { + this.name = name; + this.id = id; + this.sideLength = sideLenght; + this.numSides = numSides; + } + + public String getName() { + return name; + } + + public int getId() { + return id; + } + + public double getSideLength() { + return sideLength; + } + + public int getNumSides() { + return numSides; + } + + public static ShapeType valueOf(int id) { + for (ShapeType shapeType : values()) { + if (shapeType.getId() == id) { + return shapeType; + } + } + + throw new IllegalArgumentException("Unknown shape type with id " + id); + } +} 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 01abe9afac3ed..b4189d5aaf3b9 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 @@ -38,6 +38,8 @@ channel-type.nanoleaf.tap.label = Button channel-type.nanoleaf.tap.description = Button events of the panel channel-type.nanoleaf.swipe.label = Swipe channel-type.nanoleaf.swipe.description = Swipe over the panels +channel-type.nanoleaf.layout.label = Layout +channel-type.nanoleaf.layout.description = Layout of the shapes # error messages error.nanoleaf.controller.noIp = IP/host address and/or port are not configured for the controller. 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 53d2488705866..afc9142ad9e30 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 @@ -18,6 +18,7 @@ + @@ -107,4 +108,10 @@ + + Image + + @text/channel-type.nanoleaf.layout.description + + From a06588c4c946acd47ae1f5a17edd96ceed2554ee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B8rgen=20Austvik?= Date: Sat, 15 Oct 2022 14:19:10 +0200 Subject: [PATCH 02/13] Small style changes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Jørgen Austvik --- .../org.openhab.binding.nanoleaf/README.md | 4 ++-- .../nanoleaf/internal/OpenAPIUtils.java | 6 +----- .../handler/NanoleafControllerHandler.java | 2 +- .../internal/layout/NanoleafLayout.java | 20 +++++++++---------- 4 files changed, 13 insertions(+), 19 deletions(-) diff --git a/bundles/org.openhab.binding.nanoleaf/README.md b/bundles/org.openhab.binding.nanoleaf/README.md index 77858fdbc13f7..af7105626cdd6 100644 --- a/bundles/org.openhab.binding.nanoleaf/README.md +++ b/bundles/org.openhab.binding.nanoleaf/README.md @@ -70,8 +70,6 @@ In this case: ### 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. - For canvas that use square panels, you can request the layout through a [console command](https://www.openhab.org/docs/administration/console.html): then issue the following command: @@ -94,6 +92,8 @@ Compare the following output with the right picture at the beginning of the arti 41451 ``` + +For other canvases, use the Layout channel on the controller to get a picture of the layout with the thing IDs. ## Thing Configuration 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 55def4c0bd844..08a3e5c046709 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 @@ -154,11 +154,7 @@ public static boolean checkRequiredFirmware(@Nullable String modelId, @Nullable for (int i = 0; i < currentVer.length; ++i) { if (currentVer[i] != requiredVer[i]) { - if (currentVer[i] > requiredVer[i]) { - return true; - } - - return false; + return (currentVer[i] > requiredVer[i]); } } 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 3f3aab4b65f2a..3d46975a5be0d 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 @@ -716,7 +716,7 @@ private void updateLayout(PanelLayout panelLayout) { updateState(CHANNEL_LAYOUT, new RawType(bytes, "image/png")); } } catch (IOException ioex) { - logger.info("Failed to create layout image", ioex); + logger.warn("Failed to create layout image", ioex); } } diff --git a/bundles/org.openhab.binding.nanoleaf/src/main/java/org/openhab/binding/nanoleaf/internal/layout/NanoleafLayout.java b/bundles/org.openhab.binding.nanoleaf/src/main/java/org/openhab/binding/nanoleaf/internal/layout/NanoleafLayout.java index 3838baf62cc2e..e8f783eccc5a6 100644 --- a/bundles/org.openhab.binding.nanoleaf/src/main/java/org/openhab/binding/nanoleaf/internal/layout/NanoleafLayout.java +++ b/bundles/org.openhab.binding.nanoleaf/src/main/java/org/openhab/binding/nanoleaf/internal/layout/NanoleafLayout.java @@ -74,23 +74,23 @@ public static byte[] render(PanelLayout panelLayout) throws IOException { Point2D current = new Point2D(NanoleafBindingConstants.LAYOUT_BORDER_WIDTH + rotated.getX() - min.getX(), NanoleafBindingConstants.LAYOUT_BORDER_WIDTH - rotated.getY() - min.getY()); - g2.fillOval((int) current.getX() - NanoleafBindingConstants.LAYOUT_LIGHT_RADIUS / 2, - (int) current.getY() - NanoleafBindingConstants.LAYOUT_LIGHT_RADIUS / 2, + g2.fillOval(current.getX() - NanoleafBindingConstants.LAYOUT_LIGHT_RADIUS / 2, + current.getY() - NanoleafBindingConstants.LAYOUT_LIGHT_RADIUS / 2, NanoleafBindingConstants.LAYOUT_LIGHT_RADIUS, NanoleafBindingConstants.LAYOUT_LIGHT_RADIUS); - g2.drawString(Integer.toString(panel.getPanelId()), (int) current.getX(), (int) current.getY()); + g2.drawString(Integer.toString(panel.getPanelId()), current.getX(), current.getY()); if (sideCounter == 0) { first = current; } if (sideCounter > 0 && sideCounter != expectedSides && prev != null) { - g2.drawLine((int) prev.getX(), (int) prev.getY(), (int) current.getX(), (int) current.getY()); + g2.drawLine(prev.getX(), prev.getY(), current.getX(), current.getY()); } sideCounter++; if (sideCounter == expectedSides && first != null) { - g2.drawLine((int) current.getX(), (int) current.getY(), (int) first.getX(), (int) first.getY()); + g2.drawLine(current.getX(), current.getY(), first.getX(), first.getY()); sideCounter = 0; } @@ -110,7 +110,6 @@ private static double calculateRotation(GlobalOrientation globalOrientation) { } private static Point2D[] findSize(Collection panels, double rotationRadians) { - int maxX = 0; int maxY = 0; int minX = 0; @@ -118,11 +117,10 @@ private static Point2D[] findSize(Collection panels, double rotat for (PositionDatum panel : panels) { var rotated = new Point2D(panel.getPosX(), panel.getPosY()).rotate(rotationRadians); - - maxX = Math.max((int) rotated.getX(), maxX); - maxY = Math.max((int) rotated.getY(), maxY); - minX = Math.min((int) rotated.getX(), minX); - minY = Math.min((int) rotated.getY(), minY); + maxX = Math.max(rotated.getX(), maxX); + maxY = Math.max(rotated.getY(), maxY); + minX = Math.min(rotated.getX(), minX); + minY = Math.min(rotated.getY(), minY); } return new Point2D[] { new Point2D(minX, minY), new Point2D(maxX, maxY) }; From 9502de5d8137ae8e4ab0c179e7d0bd1bdbb15ede Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B8rgen=20Austvik?= Date: Wed, 19 Oct 2022 16:29:25 +0200 Subject: [PATCH 03/13] Only calculate image if channel is linked MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Jørgen Austvik --- .../internal/handler/NanoleafControllerHandler.java | 10 ++++++++++ .../binding/nanoleaf/internal/layout/ShapeType.java | 3 ++- 2 files changed, 12 insertions(+), 1 deletion(-) 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 3d46975a5be0d..efb32dabb85f1 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 @@ -75,6 +75,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.ThingHandlerCallback; import org.openhab.core.thing.binding.ThingHandlerService; import org.openhab.core.types.Command; import org.openhab.core.types.RefreshType; @@ -710,6 +711,15 @@ private void updateProperties() { } private void updateLayout(PanelLayout panelLayout) { + ChannelUID layoutChannel = new ChannelUID(getThing().getUID(), CHANNEL_LAYOUT); + ThingHandlerCallback callback = getCallback(); + if (callback != null) { + if (!callback.isChannelLinked(layoutChannel)) { + // Don't generate image unless it is used + return; + } + } + try { byte[] bytes = NanoleafLayout.render(panelLayout); if (bytes.length > 0) { diff --git a/bundles/org.openhab.binding.nanoleaf/src/main/java/org/openhab/binding/nanoleaf/internal/layout/ShapeType.java b/bundles/org.openhab.binding.nanoleaf/src/main/java/org/openhab/binding/nanoleaf/internal/layout/ShapeType.java index 73ea07ff926ea..d313fe8facc29 100644 --- a/bundles/org.openhab.binding.nanoleaf/src/main/java/org/openhab/binding/nanoleaf/internal/layout/ShapeType.java +++ b/bundles/org.openhab.binding.nanoleaf/src/main/java/org/openhab/binding/nanoleaf/internal/layout/ShapeType.java @@ -22,6 +22,7 @@ */ @NonNullByDefault public enum ShapeType { + UNKNOWN("Unknown", -1, 0, 0), TRIANGLE("Triangle", 0, 150, 3), RHYTHM("Rhythm", 1, 0, 1), SQUUARE("Square", 2, 100, 4), @@ -74,6 +75,6 @@ public static ShapeType valueOf(int id) { } } - throw new IllegalArgumentException("Unknown shape type with id " + id); + return ShapeType.UNKNOWN; } } From 20b0a8e31a33aff4933054201512785195f8701f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B8rgen=20Austvik?= Date: Wed, 19 Oct 2022 16:40:11 +0200 Subject: [PATCH 04/13] White background image MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Jørgen Austvik --- .../nanoleaf/internal/layout/NanoleafLayout.java | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/bundles/org.openhab.binding.nanoleaf/src/main/java/org/openhab/binding/nanoleaf/internal/layout/NanoleafLayout.java b/bundles/org.openhab.binding.nanoleaf/src/main/java/org/openhab/binding/nanoleaf/internal/layout/NanoleafLayout.java index e8f783eccc5a6..9da06cd9f2066 100644 --- a/bundles/org.openhab.binding.nanoleaf/src/main/java/org/openhab/binding/nanoleaf/internal/layout/NanoleafLayout.java +++ b/bundles/org.openhab.binding.nanoleaf/src/main/java/org/openhab/binding/nanoleaf/internal/layout/NanoleafLayout.java @@ -13,6 +13,7 @@ package org.openhab.binding.nanoleaf.internal.layout; +import java.awt.Color; import java.awt.Graphics2D; import java.awt.image.BufferedImage; import java.io.ByteArrayOutputStream; @@ -37,6 +38,11 @@ @NonNullByDefault public class NanoleafLayout { + private static final Color COLOR_BACKGROUND = Color.WHITE; + private static final Color COLOR_PANEL = Color.BLACK; + private static final Color COLOR_SIDE = Color.GRAY; + private static final Color COLOR_TEXT = Color.BLACK; + public static byte[] render(PanelLayout panelLayout) throws IOException { double rotationRadians = 0; GlobalOrientation globalOrientation = panelLayout.getGlobalOrientation(); @@ -67,6 +73,10 @@ public static byte[] render(PanelLayout panelLayout) throws IOException { (max.getY() - min.getY()) + 2 * NanoleafBindingConstants.LAYOUT_BORDER_WIDTH, BufferedImage.TYPE_INT_RGB); Graphics2D g2 = image.createGraphics(); + + g2.setBackground(COLOR_BACKGROUND); + g2.clearRect(0, 0, image.getWidth(), image.getHeight()); + for (PositionDatum panel : panels) { final int expectedSides = ShapeType.valueOf(panel.getShapeType()).getNumSides(); var rotated = new Point2D(panel.getPosX(), panel.getPosY()).rotate(rotationRadians); @@ -74,15 +84,19 @@ public static byte[] render(PanelLayout panelLayout) throws IOException { Point2D current = new Point2D(NanoleafBindingConstants.LAYOUT_BORDER_WIDTH + rotated.getX() - min.getX(), NanoleafBindingConstants.LAYOUT_BORDER_WIDTH - rotated.getY() - min.getY()); + g2.setColor(COLOR_PANEL); g2.fillOval(current.getX() - NanoleafBindingConstants.LAYOUT_LIGHT_RADIUS / 2, current.getY() - NanoleafBindingConstants.LAYOUT_LIGHT_RADIUS / 2, NanoleafBindingConstants.LAYOUT_LIGHT_RADIUS, NanoleafBindingConstants.LAYOUT_LIGHT_RADIUS); + + g2.setColor(COLOR_TEXT); g2.drawString(Integer.toString(panel.getPanelId()), current.getX(), current.getY()); if (sideCounter == 0) { first = current; } + g2.setColor(COLOR_SIDE); if (sideCounter > 0 && sideCounter != expectedSides && prev != null) { g2.drawLine(prev.getX(), prev.getY(), current.getX(), current.getY()); } From dd9ff4a5a0983da89561c2bf3f804ec9ccdd9d1f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B8rgen=20Austvik?= Date: Sat, 22 Oct 2022 20:01:53 +0200 Subject: [PATCH 05/13] Render more shapes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Jørgen Austvik --- .../org.openhab.binding.nanoleaf/README.md | 1 + .../internal/NanoleafBindingConstants.java | 2 +- .../handler/NanoleafControllerHandler.java | 8 ++ .../internal/layout/DrawingAlgorithm.java | 30 ++++++ .../internal/layout/NanoleafLayout.java | 95 +++++++++++++------ .../nanoleaf/internal/layout/Point2D.java | 26 +++++ .../nanoleaf/internal/layout/ShapeType.java | 42 ++++---- .../internal/layout/shape/Hexagon.java | 56 +++++++++++ .../nanoleaf/internal/layout/shape/Point.java | 43 +++++++++ .../nanoleaf/internal/layout/shape/Shape.java | 85 +++++++++++++++++ .../internal/layout/shape/ShapeFactory.java | 44 +++++++++ .../internal/layout/shape/Square.java | 57 +++++++++++ .../internal/layout/shape/Triangle.java | 64 +++++++++++++ .../internal/model/GlobalOrientation.java | 37 ++++++++ .../nanoleaf/internal/model/Layout.java | 50 ++++++++++ .../nanoleaf/internal/model/PanelLayout.java | 63 ++++++++++++ .../internal/model/PositionDatum.java | 56 +++++++---- .../internal/model/GlobalOrientationTest.java | 65 +++++++++++++ .../nanoleaf/internal/model/LayoutTest.java | 71 ++++++++++++++ .../internal/model/PanelLayoutTest.java | 77 +++++++++++++++ .../internal/model/PositionDatumTest.java | 65 +++++++++++++ 21 files changed, 972 insertions(+), 65 deletions(-) create mode 100644 bundles/org.openhab.binding.nanoleaf/src/main/java/org/openhab/binding/nanoleaf/internal/layout/DrawingAlgorithm.java create mode 100644 bundles/org.openhab.binding.nanoleaf/src/main/java/org/openhab/binding/nanoleaf/internal/layout/shape/Hexagon.java create mode 100644 bundles/org.openhab.binding.nanoleaf/src/main/java/org/openhab/binding/nanoleaf/internal/layout/shape/Point.java create mode 100644 bundles/org.openhab.binding.nanoleaf/src/main/java/org/openhab/binding/nanoleaf/internal/layout/shape/Shape.java create mode 100644 bundles/org.openhab.binding.nanoleaf/src/main/java/org/openhab/binding/nanoleaf/internal/layout/shape/ShapeFactory.java create mode 100644 bundles/org.openhab.binding.nanoleaf/src/main/java/org/openhab/binding/nanoleaf/internal/layout/shape/Square.java create mode 100644 bundles/org.openhab.binding.nanoleaf/src/main/java/org/openhab/binding/nanoleaf/internal/layout/shape/Triangle.java create mode 100644 bundles/org.openhab.binding.nanoleaf/src/test/java/org/openhab/binding/nanoleaf/internal/model/GlobalOrientationTest.java create mode 100644 bundles/org.openhab.binding.nanoleaf/src/test/java/org/openhab/binding/nanoleaf/internal/model/LayoutTest.java create mode 100644 bundles/org.openhab.binding.nanoleaf/src/test/java/org/openhab/binding/nanoleaf/internal/model/PanelLayoutTest.java create mode 100644 bundles/org.openhab.binding.nanoleaf/src/test/java/org/openhab/binding/nanoleaf/internal/model/PositionDatumTest.java diff --git a/bundles/org.openhab.binding.nanoleaf/README.md b/bundles/org.openhab.binding.nanoleaf/README.md index af7105626cdd6..d642a23c10f48 100644 --- a/bundles/org.openhab.binding.nanoleaf/README.md +++ b/bundles/org.openhab.binding.nanoleaf/README.md @@ -30,6 +30,7 @@ You can set the **color** for each panel and in the case of a Nanoleaf Canvas or | Light Panels | NL22 | Triangles 1st Generation | X | - | | Shapes Triangle | NL42 | Triangles 2nd Generation (rounded edges) | X | X | | Shapes Hexagon | NL42 | Hexagons | X | X | +| Elements Hexagon | NL52 | Elements Hexagons | X | X | | Shapes Mini Triangles | NL42 | Mini Triangles | x | X | | Canvas | NL29 | Squares | X | X | 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 17900c9a957af..ca9b1249a36b9 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 @@ -79,7 +79,7 @@ public class NanoleafBindingConstants { public static final String API_MIN_FW_VER_CANVAS = "1.1.0"; public static final String MODEL_ID_LIGHTPANELS = "NL22"; - public static final List MODELS_WITH_TOUCHSUPPORT = Arrays.asList("NL29", "NL42"); + public static final List MODELS_WITH_TOUCHSUPPORT = Arrays.asList("NL29", "NL42", "NL52"); public static final String DEVICE_TYPE_LIGHTPANELS = "lightPanels"; public static final String DEVICE_TYPE_TOUCHSUPPORT = "canvas"; // we need to keep this enum for backward // compatibility even though not only canvas type 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 efb32dabb85f1..bd2efd45546cf 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 @@ -107,6 +107,7 @@ public class NanoleafControllerHandler extends BaseBridgeHandler { private @Nullable HttpClient httpClientSSETouchEvent; private @Nullable Request sseTouchjobRequest; private List controllerListeners = new CopyOnWriteArrayList(); + private PanelLayout previousPanelLayout = new PanelLayout(); private @NonNullByDefault({}) ScheduledFuture pairingJob; private @NonNullByDefault({}) ScheduledFuture updateJob; @@ -720,11 +721,18 @@ private void updateLayout(PanelLayout panelLayout) { } } + if (previousPanelLayout.equals(panelLayout)) { + logger.trace("Not rendering panel layout as it is the same as previous rendered panel layout"); + return; + } + try { byte[] bytes = NanoleafLayout.render(panelLayout); if (bytes.length > 0) { updateState(CHANNEL_LAYOUT, new RawType(bytes, "image/png")); } + + previousPanelLayout = panelLayout; } catch (IOException ioex) { logger.warn("Failed to create layout image", ioex); } diff --git a/bundles/org.openhab.binding.nanoleaf/src/main/java/org/openhab/binding/nanoleaf/internal/layout/DrawingAlgorithm.java b/bundles/org.openhab.binding.nanoleaf/src/main/java/org/openhab/binding/nanoleaf/internal/layout/DrawingAlgorithm.java new file mode 100644 index 0000000000000..de9a3b26b2778 --- /dev/null +++ b/bundles/org.openhab.binding.nanoleaf/src/main/java/org/openhab/binding/nanoleaf/internal/layout/DrawingAlgorithm.java @@ -0,0 +1,30 @@ +/** + * Copyright (c) 2010-2022 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.layout; + +import org.eclipse.jdt.annotation.NonNullByDefault; + +/** + * Differentiates how shapes must be drawn + * + * @author Jørgen Austvik - Initial contribution + */ +@NonNullByDefault +public enum DrawingAlgorithm { + NONE, + SQUARE, + TRIANGLE, + HEXAGON, + CORNER, + LINE; +} diff --git a/bundles/org.openhab.binding.nanoleaf/src/main/java/org/openhab/binding/nanoleaf/internal/layout/NanoleafLayout.java b/bundles/org.openhab.binding.nanoleaf/src/main/java/org/openhab/binding/nanoleaf/internal/layout/NanoleafLayout.java index 9da06cd9f2066..29a61706157ac 100644 --- a/bundles/org.openhab.binding.nanoleaf/src/main/java/org/openhab/binding/nanoleaf/internal/layout/NanoleafLayout.java +++ b/bundles/org.openhab.binding.nanoleaf/src/main/java/org/openhab/binding/nanoleaf/internal/layout/NanoleafLayout.java @@ -18,6 +18,7 @@ import java.awt.image.BufferedImage; import java.io.ByteArrayOutputStream; import java.io.IOException; +import java.util.ArrayList; import java.util.Collection; import java.util.List; @@ -25,6 +26,8 @@ import org.eclipse.jdt.annotation.NonNullByDefault; import org.openhab.binding.nanoleaf.internal.NanoleafBindingConstants; +import org.openhab.binding.nanoleaf.internal.layout.shape.Shape; +import org.openhab.binding.nanoleaf.internal.layout.shape.ShapeFactory; import org.openhab.binding.nanoleaf.internal.model.GlobalOrientation; import org.openhab.binding.nanoleaf.internal.model.Layout; import org.openhab.binding.nanoleaf.internal.model.PanelLayout; @@ -61,13 +64,12 @@ public static byte[] render(PanelLayout panelLayout) throws IOException { } Point2D size[] = findSize(panels, rotationRadians); - Point2D min = size[0]; - Point2D max = size[1]; + final Point2D min = size[0]; + final Point2D max = size[1]; Point2D prev = null; Point2D first = null; int sideCounter = 0; - BufferedImage image = new BufferedImage( (max.getX() - min.getX()) + 2 * NanoleafBindingConstants.LAYOUT_BORDER_WIDTH, (max.getY() - min.getY()) + 2 * NanoleafBindingConstants.LAYOUT_BORDER_WIDTH, @@ -78,37 +80,53 @@ public static byte[] render(PanelLayout panelLayout) throws IOException { g2.clearRect(0, 0, image.getWidth(), image.getHeight()); for (PositionDatum panel : panels) { - final int expectedSides = ShapeType.valueOf(panel.getShapeType()).getNumSides(); - var rotated = new Point2D(panel.getPosX(), panel.getPosY()).rotate(rotationRadians); - - Point2D current = new Point2D(NanoleafBindingConstants.LAYOUT_BORDER_WIDTH + rotated.getX() - min.getX(), - NanoleafBindingConstants.LAYOUT_BORDER_WIDTH - rotated.getY() - min.getY()); - - g2.setColor(COLOR_PANEL); - g2.fillOval(current.getX() - NanoleafBindingConstants.LAYOUT_LIGHT_RADIUS / 2, - current.getY() - NanoleafBindingConstants.LAYOUT_LIGHT_RADIUS / 2, - NanoleafBindingConstants.LAYOUT_LIGHT_RADIUS, NanoleafBindingConstants.LAYOUT_LIGHT_RADIUS); + final ShapeType shapeType = ShapeType.valueOf(panel.getShapeType()); + + Shape shape = ShapeFactory.CreateShape(shapeType, panel); + List outline = toPictureLayout(shape.generateOutline(), image.getHeight(), min, rotationRadians); + for (int i = 0; i < outline.size(); i++) { + g2.setColor(COLOR_SIDE); + Point2D pos = outline.get(i); + Point2D nextPos = outline.get((i + 1) % outline.size()); + g2.drawLine(pos.getX(), pos.getY(), nextPos.getX(), nextPos.getY()); + } - g2.setColor(COLOR_TEXT); - g2.drawString(Integer.toString(panel.getPanelId()), current.getX(), current.getY()); + for (int i = 0; i < outline.size(); i++) { + Point2D pos = outline.get(i); + g2.setColor(COLOR_PANEL); + g2.fillOval(pos.getX() - NanoleafBindingConstants.LAYOUT_LIGHT_RADIUS / 2, + pos.getY() - NanoleafBindingConstants.LAYOUT_LIGHT_RADIUS / 2, + NanoleafBindingConstants.LAYOUT_LIGHT_RADIUS, NanoleafBindingConstants.LAYOUT_LIGHT_RADIUS); + } + Point2D current = toPictureLayout(new Point2D(panel.getPosX(), panel.getPosY()), image.getHeight(), min, + rotationRadians); if (sideCounter == 0) { first = current; } g2.setColor(COLOR_SIDE); - if (sideCounter > 0 && sideCounter != expectedSides && prev != null) { - g2.drawLine(prev.getX(), prev.getY(), current.getX(), current.getY()); - } - - sideCounter++; - - if (sideCounter == expectedSides && first != null) { - g2.drawLine(current.getX(), current.getY(), first.getX(), first.getY()); + final int expectedSides = shapeType.getNumSides(); + if (shapeType.getDrawingAlgorithm() == DrawingAlgorithm.CORNER) { + if (sideCounter > 0 && sideCounter != expectedSides && prev != null) { + g2.drawLine(prev.getX(), prev.getY(), current.getX(), current.getY()); + } + + sideCounter++; + + if (sideCounter == expectedSides && first != null) { + g2.drawLine(current.getX(), current.getY(), first.getX(), first.getY()); + sideCounter = 0; + } + } else { sideCounter = 0; } prev = current; + + g2.setColor(COLOR_TEXT); + Point2D textPos = shape.labelPosition(g2, outline); + g2.drawString(Integer.toString(panel.getPanelId()), textPos.getX(), textPos.getY()); } ByteArrayOutputStream out = new ByteArrayOutputStream(); @@ -130,13 +148,34 @@ private static Point2D[] findSize(Collection panels, double rotat int minY = 0; for (PositionDatum panel : panels) { - var rotated = new Point2D(panel.getPosX(), panel.getPosY()).rotate(rotationRadians); - maxX = Math.max(rotated.getX(), maxX); - maxY = Math.max(rotated.getY(), maxY); - minX = Math.min(rotated.getX(), minX); - minY = Math.min(rotated.getY(), minY); + ShapeType shapeType = ShapeType.valueOf(panel.getShapeType()); + Shape shape = ShapeFactory.CreateShape(shapeType, panel); + for (Point2D point : shape.generateOutline()) { + var rotated = point.rotate(rotationRadians); + maxX = Math.max(rotated.getX(), maxX); + maxY = Math.max(rotated.getY(), maxY); + minX = Math.min(rotated.getX(), minX); + minY = Math.min(rotated.getY(), minY); + } } return new Point2D[] { new Point2D(minX, minY), new Point2D(maxX, maxY) }; } + + private static Point2D toPictureLayout(Point2D original, int imageHeight, Point2D min, double rotationRadians) { + Point2D rotated = original.rotate(rotationRadians); + Point2D translated = new Point2D(NanoleafBindingConstants.LAYOUT_BORDER_WIDTH + rotated.getX() - min.getX(), + imageHeight - NanoleafBindingConstants.LAYOUT_BORDER_WIDTH - rotated.getY() + min.getY()); + return translated; + } + + private static List toPictureLayout(List originals, int imageHeight, Point2D min, + double rotationRadians) { + List result = new ArrayList(originals.size()); + for (Point2D original : originals) { + result.add(toPictureLayout(original, imageHeight, min, rotationRadians)); + } + + return result; + } } diff --git a/bundles/org.openhab.binding.nanoleaf/src/main/java/org/openhab/binding/nanoleaf/internal/layout/Point2D.java b/bundles/org.openhab.binding.nanoleaf/src/main/java/org/openhab/binding/nanoleaf/internal/layout/Point2D.java index 7dadd51ed6dc2..c660a6aa6805a 100644 --- a/bundles/org.openhab.binding.nanoleaf/src/main/java/org/openhab/binding/nanoleaf/internal/layout/Point2D.java +++ b/bundles/org.openhab.binding.nanoleaf/src/main/java/org/openhab/binding/nanoleaf/internal/layout/Point2D.java @@ -52,4 +52,30 @@ public Point2D rotate(double radians) { int newY = (int) (sinAngle * x + cosAngle * y); return new Point2D(newX, newY); } + + /** + * Move the point in x and y direction. + * + * @param moveX Amount to move in x direction + * @param moveY Amount to move in y direction + * @return + */ + public Point2D move(int moveX, int moveY) { + return new Point2D(getX() + moveX, getY() + moveY); + } + + /** + * Move the point in x and y direction,. + * + * @param offset Offset to move + * @return + */ + public Point2D move(Point2D offset) { + return move(offset.getX(), offset.getY()); + } + + @Override + public String toString() { + return String.format("x:%d, y:%d", x, y); + } } diff --git a/bundles/org.openhab.binding.nanoleaf/src/main/java/org/openhab/binding/nanoleaf/internal/layout/ShapeType.java b/bundles/org.openhab.binding.nanoleaf/src/main/java/org/openhab/binding/nanoleaf/internal/layout/ShapeType.java index d313fe8facc29..5fefd360d48c0 100644 --- a/bundles/org.openhab.binding.nanoleaf/src/main/java/org/openhab/binding/nanoleaf/internal/layout/ShapeType.java +++ b/bundles/org.openhab.binding.nanoleaf/src/main/java/org/openhab/binding/nanoleaf/internal/layout/ShapeType.java @@ -22,34 +22,36 @@ */ @NonNullByDefault public enum ShapeType { - UNKNOWN("Unknown", -1, 0, 0), - TRIANGLE("Triangle", 0, 150, 3), - RHYTHM("Rhythm", 1, 0, 1), - SQUUARE("Square", 2, 100, 4), - CONTROL_SQUARE_MASTER("Control Square Master", 3, 100, 0), - CONTROL_SQUARE_PASSIVE("Control Square Passive", 4, 100, 0), - SHAPES_HEXAGON("Hexagon (Shapes)", 7, 67, 6), - SHAPES_TRIANGLE("Triangle (Shapes)", 8, 134, 3), - SHAPES_MINI_TRIANGLE("Mini Triangle (Shapes)", 9, 67, 3), - SHAPES_CONTROLLER("Controller (Shapes)", 12, 0, 0), - ELEMENTS_HEXAGON("Elements Hexagon", 14, 134, 6), - ELEMENTS_HEXAGON_CORNER("Elements Hexagon - Corner", 15, 33.5 / 58, 6), - LINES_CONNECTOR("Lines Connector", 16, 11, 1), - LIGHT_LINES("Light Lines", 17, 154, 1), - LINES_LINES_SINGLE("Light Lines - Single Sone", 18, 77, 1), - CONTROLLER_CAP("Controller Cap", 19, 11, 0), - POWER_CONNECTOR("Power Connector", 20, 11, 0); + UNKNOWN("Unknown", -1, 0, 0, DrawingAlgorithm.NONE), + TRIANGLE("Triangle", 0, 150, 3, DrawingAlgorithm.TRIANGLE), + RHYTHM("Rhythm", 1, 0, 1, DrawingAlgorithm.NONE), + SQUARE("Square", 2, 100, 0, DrawingAlgorithm.SQUARE), + CONTROL_SQUARE_MASTER("Control Square Master", 3, 100, 0, DrawingAlgorithm.SQUARE), + CONTROL_SQUARE_PASSIVE("Control Square Passive", 4, 100, 0, DrawingAlgorithm.NONE), + SHAPES_HEXAGON("Hexagon (Shapes)", 7, 67, 6, DrawingAlgorithm.HEXAGON), + SHAPES_TRIANGLE("Triangle (Shapes)", 8, 134, 3, DrawingAlgorithm.TRIANGLE), + SHAPES_MINI_TRIANGLE("Mini Triangle (Shapes)", 9, 67, 3, DrawingAlgorithm.TRIANGLE), + SHAPES_CONTROLLER("Controller (Shapes)", 12, 0, 0, DrawingAlgorithm.NONE), + ELEMENTS_HEXAGON("Elements Hexagon", 14, 134, 6, DrawingAlgorithm.HEXAGON), + ELEMENTS_HEXAGON_CORNER("Elements Hexagon - Corner", 15, 33.5 / 58, 6, DrawingAlgorithm.CORNER), + LINES_CONNECTOR("Lines Connector", 16, 11, 1, DrawingAlgorithm.LINE), + LIGHT_LINES("Light Lines", 17, 154, 1, DrawingAlgorithm.LINE), + LINES_LINES_SINGLE("Light Lines - Single Sone", 18, 77, 1, DrawingAlgorithm.LINE), + CONTROLLER_CAP("Controller Cap", 19, 11, 0, DrawingAlgorithm.NONE), + POWER_CONNECTOR("Power Connector", 20, 11, 0, DrawingAlgorithm.NONE); private final String name; private final int id; private final double sideLength; private final int numSides; + private final DrawingAlgorithm drawingAlgorithm; - ShapeType(String name, int id, double sideLenght, int numSides) { + ShapeType(String name, int id, double sideLenght, int numSides, DrawingAlgorithm drawingAlgorithm) { this.name = name; this.id = id; this.sideLength = sideLenght; this.numSides = numSides; + this.drawingAlgorithm = drawingAlgorithm; } public String getName() { @@ -68,6 +70,10 @@ public int getNumSides() { return numSides; } + public DrawingAlgorithm getDrawingAlgorithm() { + return drawingAlgorithm; + } + public static ShapeType valueOf(int id) { for (ShapeType shapeType : values()) { if (shapeType.getId() == id) { diff --git a/bundles/org.openhab.binding.nanoleaf/src/main/java/org/openhab/binding/nanoleaf/internal/layout/shape/Hexagon.java b/bundles/org.openhab.binding.nanoleaf/src/main/java/org/openhab/binding/nanoleaf/internal/layout/shape/Hexagon.java new file mode 100644 index 0000000000000..894345ca4f69c --- /dev/null +++ b/bundles/org.openhab.binding.nanoleaf/src/main/java/org/openhab/binding/nanoleaf/internal/layout/shape/Hexagon.java @@ -0,0 +1,56 @@ +/** + * Copyright (c) 2010-2022 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.layout.shape; + +import java.awt.Graphics2D; +import java.awt.geom.Rectangle2D; +import java.util.Arrays; +import java.util.List; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.openhab.binding.nanoleaf.internal.layout.Point2D; +import org.openhab.binding.nanoleaf.internal.layout.ShapeType; + +/** + * A triangular shape. + * + * @author Jørgen Austvik - Initial contribution + */ +@NonNullByDefault +public class Hexagon extends Shape { + public Hexagon(ShapeType shapeType, int panelId, Point2D position, int orientation) { + super(shapeType, panelId, position, orientation); + } + + @Override + public List generateOutline() { + Point2D v1 = new Point2D((int) getShapeType().getSideLength(), 0); + Point2D v2 = v1.rotate((1.0 / 3.0) * Math.PI); + Point2D v3 = v1.rotate((2.0 / 3.0) * Math.PI); + Point2D v4 = v1.rotate((3.0 / 3.0) * Math.PI); + Point2D v5 = v1.rotate((4.0 / 3.0) * Math.PI); + Point2D v6 = v1.rotate((5.0 / 3.0) * Math.PI); + return Arrays.asList(v1.move(getPosition()), v2.move(getPosition()), v3.move(getPosition()), + v4.move(getPosition()), v5.move(getPosition()), v6.move(getPosition())); + } + + @Override + public Point2D labelPosition(Graphics2D graphics, List outline) { + Point2D[] bounds = findBounds(outline); + int midX = bounds[0].getX() + (bounds[1].getX() - bounds[0].getX()) / 2; + int midY = bounds[0].getY() + (bounds[1].getY() - bounds[0].getY()) / 2; + + Rectangle2D rect = graphics.getFontMetrics().getStringBounds(Integer.toString(getPanelId()), graphics); + return new Point2D(midX - (int) (rect.getWidth() / 2), midY - (int) (rect.getHeight() / 2)); + } +} diff --git a/bundles/org.openhab.binding.nanoleaf/src/main/java/org/openhab/binding/nanoleaf/internal/layout/shape/Point.java b/bundles/org.openhab.binding.nanoleaf/src/main/java/org/openhab/binding/nanoleaf/internal/layout/shape/Point.java new file mode 100644 index 0000000000000..0ae05dc2b55b2 --- /dev/null +++ b/bundles/org.openhab.binding.nanoleaf/src/main/java/org/openhab/binding/nanoleaf/internal/layout/shape/Point.java @@ -0,0 +1,43 @@ +/** + * Copyright (c) 2010-2022 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.layout.shape; + +import java.awt.Graphics2D; +import java.util.Arrays; +import java.util.List; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.openhab.binding.nanoleaf.internal.layout.Point2D; +import org.openhab.binding.nanoleaf.internal.layout.ShapeType; + +/** + * A shape without any area. + * + * @author Jørgen Austvik - Initial contribution + */ +@NonNullByDefault +public class Point extends Shape { + public Point(ShapeType shapeType, int panelId, Point2D position, int orientation) { + super(shapeType, panelId, position, orientation); + } + + @Override + public List generateOutline() { + return Arrays.asList(getPosition()); + } + + @Override + public Point2D labelPosition(Graphics2D graphics, List outline) { + return outline.get(0); + } +} diff --git a/bundles/org.openhab.binding.nanoleaf/src/main/java/org/openhab/binding/nanoleaf/internal/layout/shape/Shape.java b/bundles/org.openhab.binding.nanoleaf/src/main/java/org/openhab/binding/nanoleaf/internal/layout/shape/Shape.java new file mode 100644 index 0000000000000..99412ba2ea63e --- /dev/null +++ b/bundles/org.openhab.binding.nanoleaf/src/main/java/org/openhab/binding/nanoleaf/internal/layout/shape/Shape.java @@ -0,0 +1,85 @@ +/** + * Copyright (c) 2010-2022 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.layout.shape; + +import java.awt.Graphics2D; +import java.util.List; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.openhab.binding.nanoleaf.internal.layout.Point2D; +import org.openhab.binding.nanoleaf.internal.layout.ShapeType; + +/** + * Shape that can be drawn. + * + * @author Jørgen Austvik - Initial contribution + */ +@NonNullByDefault +public abstract class Shape { + private final ShapeType shapeType; + private final int panelId; + private final Point2D position; + private final int orientation; + + public Shape(ShapeType shapeType, int panelId, Point2D position, int orientation) { + this.shapeType = shapeType; + this.panelId = panelId; + this.position = position; + this.orientation = orientation; + } + + public int getPanelId() { + return panelId; + }; + + public Point2D getPosition() { + return position; + } + + public int getOrientation() { + return orientation; + }; + + public ShapeType getShapeType() { + return shapeType; + } + + /** + * @return The opposite points of the minimum bounding rectangle around this shape. + */ + public Point2D[] findBounds(List outline) { + int minX = Integer.MAX_VALUE; + int minY = Integer.MAX_VALUE; + int maxX = Integer.MIN_VALUE; + int maxY = Integer.MIN_VALUE; + + for (Point2D point : outline) { + maxX = Math.max(point.getX(), maxX); + maxY = Math.max(point.getY(), maxY); + minX = Math.min(point.getX(), minX); + minY = Math.min(point.getY(), minY); + } + + return new Point2D[] { new Point2D(minX, minY), new Point2D(maxX, maxY) }; + } + + /** + * @return The points that make up this shape. + */ + public abstract List generateOutline(); + + /** + * @return The position where the label of the shape should be placed + */ + public abstract Point2D labelPosition(Graphics2D graphics, List outline); +} diff --git a/bundles/org.openhab.binding.nanoleaf/src/main/java/org/openhab/binding/nanoleaf/internal/layout/shape/ShapeFactory.java b/bundles/org.openhab.binding.nanoleaf/src/main/java/org/openhab/binding/nanoleaf/internal/layout/shape/ShapeFactory.java new file mode 100644 index 0000000000000..78e9ec08828ef --- /dev/null +++ b/bundles/org.openhab.binding.nanoleaf/src/main/java/org/openhab/binding/nanoleaf/internal/layout/shape/ShapeFactory.java @@ -0,0 +1,44 @@ +/** + * Copyright (c) 2010-2022 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.layout.shape; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.openhab.binding.nanoleaf.internal.layout.Point2D; +import org.openhab.binding.nanoleaf.internal.layout.ShapeType; +import org.openhab.binding.nanoleaf.internal.model.PositionDatum; + +/** + * Create the correct chape for a given shape type. + * + * @author Jørgen Austvik - Initial contribution + */ +@NonNullByDefault +public class ShapeFactory { + + public static Shape CreateShape(ShapeType shapeType, PositionDatum positionDatum) { + Point2D pos = new Point2D(positionDatum.getPosX(), positionDatum.getPosY()); + switch (shapeType.getDrawingAlgorithm()) { + case SQUARE: + return new Square(shapeType, positionDatum.getPanelId(), pos, positionDatum.getOrientation()); + + case TRIANGLE: + return new Triangle(shapeType, positionDatum.getPanelId(), pos, positionDatum.getOrientation()); + + case HEXAGON: + return new Hexagon(shapeType, positionDatum.getPanelId(), pos, positionDatum.getOrientation()); + + default: + return new Point(shapeType, positionDatum.getPanelId(), pos, positionDatum.getOrientation()); + } + } +} diff --git a/bundles/org.openhab.binding.nanoleaf/src/main/java/org/openhab/binding/nanoleaf/internal/layout/shape/Square.java b/bundles/org.openhab.binding.nanoleaf/src/main/java/org/openhab/binding/nanoleaf/internal/layout/shape/Square.java new file mode 100644 index 0000000000000..a9cf762843df2 --- /dev/null +++ b/bundles/org.openhab.binding.nanoleaf/src/main/java/org/openhab/binding/nanoleaf/internal/layout/shape/Square.java @@ -0,0 +1,57 @@ +/** + * Copyright (c) 2010-2022 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.layout.shape; + +import java.awt.Graphics2D; +import java.awt.geom.Rectangle2D; +import java.util.Arrays; +import java.util.List; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.openhab.binding.nanoleaf.internal.layout.Point2D; +import org.openhab.binding.nanoleaf.internal.layout.ShapeType; + +/** + * A square shape. + * + * @author Jørgen Austvik - Initial contribution + */ +@NonNullByDefault +public class Square extends Shape { + public Square(ShapeType shapeType, int panelId, Point2D position, int orientation) { + super(shapeType, panelId, position, orientation); + } + + @Override + public List generateOutline() { + int sideLength = (int) getShapeType().getSideLength(); + + Point2D current = getPosition(); + Point2D corner2 = new Point2D(current.getX() + sideLength, current.getY()); + Point2D corner3 = new Point2D(current.getX() + sideLength, current.getY() + sideLength); + Point2D corner4 = new Point2D(current.getX(), current.getY() + sideLength); + return Arrays.asList(getPosition(), corner2, corner3, corner4); + } + + @Override + public Point2D labelPosition(Graphics2D graphics, List outline) { + // Center of square is average of oposite corners + Point2D p0 = outline.get(0); + Point2D p2 = outline.get(2); + + Rectangle2D rect = graphics.getFontMetrics().getStringBounds(Integer.toString(getPanelId()), graphics); + + return new Point2D((p0.getX() + p2.getX()) / 2 - (int) (rect.getWidth() / 2), + (p0.getY() + p2.getY()) / 2 - (int) (rect.getHeight() / 2)); + } +} diff --git a/bundles/org.openhab.binding.nanoleaf/src/main/java/org/openhab/binding/nanoleaf/internal/layout/shape/Triangle.java b/bundles/org.openhab.binding.nanoleaf/src/main/java/org/openhab/binding/nanoleaf/internal/layout/shape/Triangle.java new file mode 100644 index 0000000000000..586e89fc6e78a --- /dev/null +++ b/bundles/org.openhab.binding.nanoleaf/src/main/java/org/openhab/binding/nanoleaf/internal/layout/shape/Triangle.java @@ -0,0 +1,64 @@ +/** + * Copyright (c) 2010-2022 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.layout.shape; + +import java.awt.Graphics2D; +import java.awt.geom.Rectangle2D; +import java.util.Arrays; +import java.util.List; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.openhab.binding.nanoleaf.internal.layout.Point2D; +import org.openhab.binding.nanoleaf.internal.layout.ShapeType; + +/** + * A triangular shape. + * + * @author Jørgen Austvik - Initial contribution + */ +@NonNullByDefault +public class Triangle extends Shape { + public Triangle(ShapeType shapeType, int panelId, Point2D position, int orientation) { + super(shapeType, panelId, position, orientation); + } + + @Override + public List generateOutline() { + int height = (int) (getShapeType().getSideLength() * Math.sqrt(3) / 2); + Point2D v1; + if (pointsUp()) { + v1 = new Point2D(0, height * 2 / 3); + } else { + v1 = new Point2D(0, -height * 2 / 3); + } + + Point2D v2 = v1.rotate((2.0 / 3.0) * Math.PI); + Point2D v3 = v1.rotate((-2.0 / 3.0) * Math.PI); + return Arrays.asList(v1.move(getPosition()), v2.move(getPosition()), v3.move(getPosition())); + } + + @Override + public Point2D labelPosition(Graphics2D graphics, List outline) { + Point2D[] bounds = findBounds(outline); + int midX = bounds[0].getX() + (bounds[1].getX() - bounds[0].getX()) / 2; + int midY = bounds[0].getY() + (bounds[1].getY() - bounds[0].getY()) / 2; + + Rectangle2D rect = graphics.getFontMetrics().getStringBounds(Integer.toString(getPanelId()), graphics); + return new Point2D(midX - (int) (rect.getWidth() / 2), midY - (int) (rect.getHeight() / 2)); + } + + private boolean pointsUp() { + // Upward: even multiple of 60 degrees rotation + return ((getOrientation() / 60) % 2) == 0; + } +} diff --git a/bundles/org.openhab.binding.nanoleaf/src/main/java/org/openhab/binding/nanoleaf/internal/model/GlobalOrientation.java b/bundles/org.openhab.binding.nanoleaf/src/main/java/org/openhab/binding/nanoleaf/internal/model/GlobalOrientation.java index cb5d19475f7ee..3bf0751d83475 100644 --- a/bundles/org.openhab.binding.nanoleaf/src/main/java/org/openhab/binding/nanoleaf/internal/model/GlobalOrientation.java +++ b/bundles/org.openhab.binding.nanoleaf/src/main/java/org/openhab/binding/nanoleaf/internal/model/GlobalOrientation.java @@ -12,6 +12,8 @@ */ package org.openhab.binding.nanoleaf.internal.model; +import java.util.Objects; + import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.Nullable; @@ -27,6 +29,15 @@ public class GlobalOrientation { private @Nullable Integer max; private @Nullable Integer min; + public GlobalOrientation() { + } + + public GlobalOrientation(Integer min, Integer max, int value) { + this.min = min; + this.max = max; + this.value = value; + } + public int getValue() { return value; } @@ -50,4 +61,30 @@ public void setMax(Integer max) { public void setMin(Integer min) { this.min = min; } + + @Override + public boolean equals(@Nullable Object o) { + if (this == o) { + return true; + } + + if (o == null || getClass() != o.getClass()) { + return false; + } + + GlobalOrientation go = (GlobalOrientation) o; + return (value == go.getValue()) && (Objects.equals(min, go.getMin())) && (Objects.equals(max, go.getMax())); + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + Integer x = max; + Integer i = min; + result = prime * result + value; + result = prime * result + ((x == null) ? 0 : x.hashCode()); + result = prime * result + ((i == null) ? 0 : i.hashCode()); + return result; + } } 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 a46a28b0aa4f6..462dde09cee62 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 @@ -12,6 +12,7 @@ */ package org.openhab.binding.nanoleaf.internal.model; +import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.TreeMap; @@ -36,6 +37,14 @@ public class Layout { private @Nullable List positionData = null; + public Layout() { + } + + public Layout(List positionData) { + this.positionData = new ArrayList<>(positionData); + this.numPanels = positionData.size(); + } + public int getNumPanels() { return numPanels; } @@ -143,4 +152,45 @@ public String getLayoutView() { return ""; } } + + @Override + public boolean equals(@Nullable Object o) { + if (this == o) { + return true; + } + + if (o == null || getClass() != o.getClass()) { + return false; + } + + Layout l = (Layout) o; + List pd = getPositionData(); + List otherPd = l.getPositionData(); + + boolean positionDataEquals = false; + if (pd == null || otherPd == null) { + if (pd == null && otherPd == null) { + positionDataEquals = true; + } + } else { + positionDataEquals = pd.equals(otherPd); + } + + return (numPanels == l.getNumPanels()) && positionDataEquals; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + getNumPanels(); + List pd = getPositionData(); + if (pd != null) { + for (PositionDatum p : pd) { + result = prime * result + p.hashCode(); + } + } + + return result; + } } diff --git a/bundles/org.openhab.binding.nanoleaf/src/main/java/org/openhab/binding/nanoleaf/internal/model/PanelLayout.java b/bundles/org.openhab.binding.nanoleaf/src/main/java/org/openhab/binding/nanoleaf/internal/model/PanelLayout.java index f7ec9e366347f..732ba34c086c3 100644 --- a/bundles/org.openhab.binding.nanoleaf/src/main/java/org/openhab/binding/nanoleaf/internal/model/PanelLayout.java +++ b/bundles/org.openhab.binding.nanoleaf/src/main/java/org/openhab/binding/nanoleaf/internal/model/PanelLayout.java @@ -26,6 +26,14 @@ public class PanelLayout { private @Nullable Layout layout; private @Nullable GlobalOrientation globalOrientation; + public PanelLayout() { + } + + public PanelLayout(GlobalOrientation globalOrientation, Layout layout) { + this.globalOrientation = globalOrientation; + this.layout = layout; + } + public @Nullable Layout getLayout() { return layout; } @@ -41,4 +49,59 @@ public void setLayout(Layout layout) { public void setGlobalOrientation(GlobalOrientation globalOrientation) { this.globalOrientation = globalOrientation; } + + @Override + public boolean equals(@Nullable Object o) { + if (this == o) { + return true; + } + + if (o == null || getClass() != o.getClass()) { + return false; + } + + PanelLayout pl = (PanelLayout) o; + + GlobalOrientation go = globalOrientation; + GlobalOrientation otherGo = pl.getGlobalOrientation(); + boolean goEquals = false; + if (go == null || otherGo == null) { + if (go == null && otherGo == null) { + goEquals = true; + } + } else { + goEquals = go.equals(otherGo); + } + + Layout l = layout; + Layout otherL = pl.getLayout(); + boolean lEquals = false; + if (l == null || otherL == null) { + if (l == null && otherL == null) { + lEquals = true; + } + } else { + lEquals = l.equals(otherL); + } + + return goEquals && lEquals; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + + GlobalOrientation go = globalOrientation; + if (go != null) { + result = prime * result + go.hashCode(); + } + + Layout l = layout; + if (l != null) { + result = prime * result + l.hashCode(); + } + + return result; + } } diff --git a/bundles/org.openhab.binding.nanoleaf/src/main/java/org/openhab/binding/nanoleaf/internal/model/PositionDatum.java b/bundles/org.openhab.binding.nanoleaf/src/main/java/org/openhab/binding/nanoleaf/internal/model/PositionDatum.java index 7c717ce7a5399..4167339e9f540 100644 --- a/bundles/org.openhab.binding.nanoleaf/src/main/java/org/openhab/binding/nanoleaf/internal/model/PositionDatum.java +++ b/bundles/org.openhab.binding.nanoleaf/src/main/java/org/openhab/binding/nanoleaf/internal/model/PositionDatum.java @@ -12,10 +12,9 @@ */ package org.openhab.binding.nanoleaf.internal.model; -import java.util.HashMap; -import java.util.Map; - import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; +import org.openhab.binding.nanoleaf.internal.layout.ShapeType; import com.google.gson.annotations.SerializedName; @@ -37,21 +36,15 @@ public class PositionDatum { @SerializedName("shapeType") private int shapeType; - private static Map panelSizes = new HashMap(); - public PositionDatum() { - // initialize constant sidelengths for panels. See https://forum.nanoleaf.me/docs chapter 3.3 - if (panelSizes.isEmpty()) { - panelSizes.put(0, 150); // Triangle - panelSizes.put(1, 0); // Rhythm N/A - panelSizes.put(2, 100); // Square - panelSizes.put(3, 100); // Control Square Master - panelSizes.put(4, 100); // Control Square Passive - panelSizes.put(7, 67); // Hexagon - panelSizes.put(8, 134); // Triangle Shapes - panelSizes.put(9, 67); // Mini Triangle Shapes - panelSizes.put(12, 0); // Shapes Controller (N/A) - } + } + + public PositionDatum(int panelId, int posX, int posY, int orientation, int shapeType) { + this.panelId = panelId; + this.posX = posX; + this.posY = posY; + this.orientation = orientation; + this.shapeType = shapeType; } public int getPanelId() { @@ -105,6 +98,33 @@ public void setShapeType(int shapeType) { } public Integer getPanelSize() { - return panelSizes.getOrDefault(shapeType, 0); + return (int) ShapeType.valueOf(shapeType).getSideLength(); + } + + @Override + public boolean equals(@Nullable Object o) { + if (this == o) { + return true; + } + + if (o == null || getClass() != o.getClass()) { + return false; + } + + PositionDatum pd = (PositionDatum) o; + return (posX == pd.getPosX()) && (posY == pd.getPosY()) && (orientation == pd.getOrientation()) + && (shapeType == pd.getShapeType()) && (panelId == pd.getPanelId()); + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + posX; + result = prime * result + posY; + result = prime * result + orientation; + result = prime * result + shapeType; + result = prime * result + panelId; + return result; } } diff --git a/bundles/org.openhab.binding.nanoleaf/src/test/java/org/openhab/binding/nanoleaf/internal/model/GlobalOrientationTest.java b/bundles/org.openhab.binding.nanoleaf/src/test/java/org/openhab/binding/nanoleaf/internal/model/GlobalOrientationTest.java new file mode 100644 index 0000000000000..22be11c7b5189 --- /dev/null +++ b/bundles/org.openhab.binding.nanoleaf/src/test/java/org/openhab/binding/nanoleaf/internal/model/GlobalOrientationTest.java @@ -0,0 +1,65 @@ +/** + * Copyright (c) 2010-2022 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.model; + +import static org.hamcrest.CoreMatchers.*; +import static org.hamcrest.MatcherAssert.assertThat; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +/** + * Test for global orientation + * + * @author Jørgen Austvik - Initial contribution + */ +@NonNullByDefault +public class GlobalOrientationTest { + + @Nullable + GlobalOrientation go1; + + @Nullable + GlobalOrientation go2; // Different from go1 + + @Nullable + GlobalOrientation go3; // Same as go1 + + @BeforeEach + public void setUp() { + go1 = new GlobalOrientation(0, 360, 180); + go2 = new GlobalOrientation(0, 360, 267); + go3 = new GlobalOrientation(0, 360, 180); + } + + @Test + public void testHashCode() { + GlobalOrientation g1 = go1; + GlobalOrientation g2 = go2; + GlobalOrientation g3 = go3; + if (g1 != null && g2 != null && g3 != null) { + assertThat(g1.hashCode(), is(equalTo(g3.hashCode()))); + assertThat(g2.hashCode(), is(not(equalTo(g3.hashCode())))); + } else { + assertThat("Should be initialized", false); + } + } + + @Test + public void testEquals() { + assertThat(go1, is(equalTo(go3))); + assertThat(go2, is(not(equalTo(go3)))); + } +} diff --git a/bundles/org.openhab.binding.nanoleaf/src/test/java/org/openhab/binding/nanoleaf/internal/model/LayoutTest.java b/bundles/org.openhab.binding.nanoleaf/src/test/java/org/openhab/binding/nanoleaf/internal/model/LayoutTest.java new file mode 100644 index 0000000000000..8fd56eaf4817f --- /dev/null +++ b/bundles/org.openhab.binding.nanoleaf/src/test/java/org/openhab/binding/nanoleaf/internal/model/LayoutTest.java @@ -0,0 +1,71 @@ +/** + * Copyright (c) 2010-2022 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.model; + +import static org.hamcrest.CoreMatchers.*; +import static org.hamcrest.MatcherAssert.assertThat; + +import java.util.Arrays; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +/** + * Test for global orientation + * + * @author Jørgen Austvik - Initial contribution + */ +@NonNullByDefault +public class LayoutTest { + + @Nullable + private Layout lo1; + + @Nullable + private Layout lo2; // Different from l1 + + @Nullable + private Layout lo3; // Same as l1 + + @BeforeEach + public void setUp() { + PositionDatum pd1 = new PositionDatum(100, 200, 270, 123, 12); + PositionDatum pd2 = new PositionDatum(100, 220, 240, 123, 2); + PositionDatum pd3 = new PositionDatum(100, 200, 270, 123, 12); + + lo1 = new Layout(Arrays.asList(pd1, pd3)); + lo2 = new Layout(Arrays.asList(pd1, pd2)); + lo3 = new Layout(Arrays.asList(pd1, pd3)); + } + + @Test + public void testHashCode() { + Layout l1 = lo1; + Layout l2 = lo2; + Layout l3 = lo3; + if (l1 != null && l2 != null && l3 != null) { + assertThat(l1.hashCode(), is(equalTo(l3.hashCode()))); + assertThat(l2.hashCode(), is(not(equalTo(l3.hashCode())))); + } else { + assertThat("Should be initialized", false); + } + } + + @Test + public void testEquals() { + assertThat(lo1, is(equalTo(lo3))); + assertThat(lo2, is(not(equalTo(lo3)))); + } +} diff --git a/bundles/org.openhab.binding.nanoleaf/src/test/java/org/openhab/binding/nanoleaf/internal/model/PanelLayoutTest.java b/bundles/org.openhab.binding.nanoleaf/src/test/java/org/openhab/binding/nanoleaf/internal/model/PanelLayoutTest.java new file mode 100644 index 0000000000000..4bc766f62713e --- /dev/null +++ b/bundles/org.openhab.binding.nanoleaf/src/test/java/org/openhab/binding/nanoleaf/internal/model/PanelLayoutTest.java @@ -0,0 +1,77 @@ +/** + * Copyright (c) 2010-2022 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.model; + +import static org.hamcrest.CoreMatchers.*; +import static org.hamcrest.MatcherAssert.assertThat; + +import java.util.Arrays; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +/** + * Test for global orientation + * + * @author Jørgen Austvik - Initial contribution + */ +@NonNullByDefault +public class PanelLayoutTest { + + @Nullable + private PanelLayout pl1; + + @Nullable + private PanelLayout pl2; // Different from pl1 + + @Nullable + private PanelLayout pl3; // Equal to pl1 + + @BeforeEach + public void setUp() { + PositionDatum pd1 = new PositionDatum(100, 200, 270, 123, 12); + PositionDatum pd2 = new PositionDatum(100, 220, 240, 123, 2); + PositionDatum pd3 = new PositionDatum(100, 200, 270, 123, 12); + + Layout l1 = new Layout(Arrays.asList(pd1, pd3)); + Layout l2 = new Layout(Arrays.asList(pd1, pd2)); + Layout l3 = new Layout(Arrays.asList(pd1, pd3)); + + GlobalOrientation go1 = new GlobalOrientation(0, 360, 180); + + pl1 = new PanelLayout(go1, l1); + pl2 = new PanelLayout(go1, l2); + pl3 = new PanelLayout(go1, l3); + } + + @Test + public void testHashCode() { + PanelLayout p1 = pl1; + PanelLayout p2 = pl2; + PanelLayout p3 = pl3; + if (p1 != null && p2 != null && p3 != null) { + assertThat(p1.hashCode(), is(equalTo(p3.hashCode()))); + assertThat(p2.hashCode(), is(not(equalTo(p3.hashCode())))); + } else { + assertThat("Should be initialized", false); + } + } + + @Test + public void testEquals() { + assertThat(pl1, is(equalTo(pl3))); + assertThat(pl2, is(not(equalTo(pl3)))); + } +} diff --git a/bundles/org.openhab.binding.nanoleaf/src/test/java/org/openhab/binding/nanoleaf/internal/model/PositionDatumTest.java b/bundles/org.openhab.binding.nanoleaf/src/test/java/org/openhab/binding/nanoleaf/internal/model/PositionDatumTest.java new file mode 100644 index 0000000000000..b77b4adde2c1c --- /dev/null +++ b/bundles/org.openhab.binding.nanoleaf/src/test/java/org/openhab/binding/nanoleaf/internal/model/PositionDatumTest.java @@ -0,0 +1,65 @@ +/** + * Copyright (c) 2010-2022 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.model; + +import static org.hamcrest.CoreMatchers.*; +import static org.hamcrest.MatcherAssert.assertThat; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +/** + * Test for global orientation + * + * @author Jørgen Austvik - Initial contribution + */ +@NonNullByDefault +public class PositionDatumTest { + + @Nullable + private PositionDatum pd1; + + @Nullable + private PositionDatum pd2; // different from pd1 + + @Nullable + private PositionDatum pd3; // same as pd1 + + @BeforeEach + public void setUp() { + pd1 = new PositionDatum(100, 200, 270, 123, 12); + pd2 = new PositionDatum(100, 220, 240, 123, 2); + pd3 = new PositionDatum(100, 200, 270, 123, 12); + } + + @Test + public void testHashCode() { + PositionDatum p1 = pd1; + PositionDatum p2 = pd2; + PositionDatum p3 = pd3; + if (p1 != null && p2 != null && p3 != null) { + assertThat(p1.hashCode(), is(equalTo(p3.hashCode()))); + assertThat(p2.hashCode(), is(not(equalTo(p3.hashCode())))); + } else { + assertThat("Should be initialized", false); + } + } + + @Test + public void testEquals() { + assertThat(pd1, is(equalTo(pd3))); + assertThat(pd2, is(not(equalTo(pd3)))); + } +} From fd36880968d6a5d64162d1c36333a847c809ff3a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B8rgen=20Austvik?= Date: Mon, 31 Oct 2022 18:57:42 +0100 Subject: [PATCH 06/13] Review comments MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Jørgen Austvik --- .../org.openhab.binding.nanoleaf/README.md | 21 +++++++++++++------ .../internal/NanoleafBindingConstants.java | 2 +- .../internal/layout/NanoleafLayout.java | 5 ++++- 3 files changed, 20 insertions(+), 8 deletions(-) diff --git a/bundles/org.openhab.binding.nanoleaf/README.md b/bundles/org.openhab.binding.nanoleaf/README.md index d642a23c10f48..308dd60e653f4 100644 --- a/bundles/org.openhab.binding.nanoleaf/README.md +++ b/bundles/org.openhab.binding.nanoleaf/README.md @@ -28,10 +28,13 @@ You can set the **color** for each panel and in the case of a Nanoleaf Canvas or | 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 | Hexagons | X | X | +| Shapes Triangles | NL47 | Triangles | X | X | +| Shapes Mini Triangles | NL48 | Mini Triangles | X | X | | Elements Hexagon | NL52 | Elements Hexagons | X | X | -| Shapes Mini Triangles | NL42 | Mini Triangles | x | X | +| Smart Bulb | NL45 | Smart Bulb | - | | +| Lightstrip | NL55 | Lightstrip | - | | +| Lines | NL59 | Lines | - | | | Canvas | NL29 | Squares | X | X | x = Supported (-) = unknown (no device available to test) @@ -71,7 +74,15 @@ In this case: ### Panel Layout -For canvas that use square panels, you can request the layout through a [console command](https://www.openhab.org/docs/administration/console.html): +If you want to program individual panels, it can be hard to figure out which panel has which ID. To make this easier, there is Layout channel on the Nanoleaf controller thing in OpenHAB. +The easiest way to visualize the layout of the individual panels is to open the controller thing in the OpenHAB UI, go to Channels and add a new item to the Layout channel. +Clicking on that image or adding it to a dashboard will show a picture of your canvas with the induvidual thing IDs in the picture. + +If your canvas has elements we dont know how to draw a layout for yet, please reach out, and we will ask for some information and will try to add support for your elements. + +![Image](doc/Layout.jpg) + +There is an alternative method for canvas that use square panels, you can request the layout through a [console command](https://www.openhab.org/docs/administration/console.html): then issue the following command: @@ -93,9 +104,7 @@ Compare the following output with the right picture at the beginning of the arti 41451 ``` - -For other canvases, use the Layout channel on the controller to get a picture of the layout with the thing IDs. - + ## Thing Configuration The controller thing has the following parameters: 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 ca9b1249a36b9..e7e8a2fb5a7ab 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 @@ -79,7 +79,7 @@ public class NanoleafBindingConstants { public static final String API_MIN_FW_VER_CANVAS = "1.1.0"; public static final String MODEL_ID_LIGHTPANELS = "NL22"; - public static final List MODELS_WITH_TOUCHSUPPORT = Arrays.asList("NL29", "NL42", "NL52"); + public static final List MODELS_WITH_TOUCHSUPPORT = Arrays.asList("NL29", "NL42", "NL47", "NL48", "NL52"); public static final String DEVICE_TYPE_LIGHTPANELS = "lightPanels"; public static final String DEVICE_TYPE_TOUCHSUPPORT = "canvas"; // we need to keep this enum for backward // compatibility even though not only canvas type diff --git a/bundles/org.openhab.binding.nanoleaf/src/main/java/org/openhab/binding/nanoleaf/internal/layout/NanoleafLayout.java b/bundles/org.openhab.binding.nanoleaf/src/main/java/org/openhab/binding/nanoleaf/internal/layout/NanoleafLayout.java index 29a61706157ac..9a1a21daa3831 100644 --- a/bundles/org.openhab.binding.nanoleaf/src/main/java/org/openhab/binding/nanoleaf/internal/layout/NanoleafLayout.java +++ b/bundles/org.openhab.binding.nanoleaf/src/main/java/org/openhab/binding/nanoleaf/internal/layout/NanoleafLayout.java @@ -34,7 +34,7 @@ import org.openhab.binding.nanoleaf.internal.model.PositionDatum; /** - * Renders the Nonoleaf layout to an image. + * Renders the Nanoleaf layout to an image. * * @author Jørgen Austvik - Initial contribution */ @@ -108,6 +108,9 @@ public static byte[] render(PanelLayout panelLayout) throws IOException { g2.setColor(COLOR_SIDE); final int expectedSides = shapeType.getNumSides(); if (shapeType.getDrawingAlgorithm() == DrawingAlgorithm.CORNER) { + // Special handling of Elements Hexagon Corners, where we get 6 corners instead of 1 shape. They seem to + // come after each other in the JSON, so this algorithm connects them based on the number of sides the + // shape is expected to have. if (sideCounter > 0 && sideCounter != expectedSides && prev != null) { g2.drawLine(prev.getX(), prev.getY(), current.getX(), current.getY()); } From 0d56f7223497e928ae1bcd985d74ee2a50a5c621 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B8rgen=20Austvik?= Date: Mon, 31 Oct 2022 19:00:13 +0100 Subject: [PATCH 07/13] Review comment MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Jørgen Austvik --- .../binding/nanoleaf/internal/layout/NanoleafLayout.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/bundles/org.openhab.binding.nanoleaf/src/main/java/org/openhab/binding/nanoleaf/internal/layout/NanoleafLayout.java b/bundles/org.openhab.binding.nanoleaf/src/main/java/org/openhab/binding/nanoleaf/internal/layout/NanoleafLayout.java index 9a1a21daa3831..70724333eca84 100644 --- a/bundles/org.openhab.binding.nanoleaf/src/main/java/org/openhab/binding/nanoleaf/internal/layout/NanoleafLayout.java +++ b/bundles/org.openhab.binding.nanoleaf/src/main/java/org/openhab/binding/nanoleaf/internal/layout/NanoleafLayout.java @@ -50,7 +50,7 @@ public static byte[] render(PanelLayout panelLayout) throws IOException { double rotationRadians = 0; GlobalOrientation globalOrientation = panelLayout.getGlobalOrientation(); if (globalOrientation != null) { - rotationRadians = calculateRotation(globalOrientation); + rotationRadians = calculateRotationRadians(globalOrientation); } Layout layout = panelLayout.getLayout(); @@ -137,7 +137,7 @@ public static byte[] render(PanelLayout panelLayout) throws IOException { return out.toByteArray(); } - private static double calculateRotation(GlobalOrientation globalOrientation) { + private static double calculateRotationRadians(GlobalOrientation globalOrientation) { Integer maxObj = globalOrientation.getMax(); int maxValue = maxObj == null ? 360 : (int) maxObj; int value = globalOrientation.getValue(); // 0 - 360 measured counter clockwise. From 188d5683474b6dbdf10d9225f3fd5bc9f198e86e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B8rgen=20Austvik?= Date: Mon, 31 Oct 2022 19:32:26 +0100 Subject: [PATCH 08/13] The review comments github hid MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Jørgen Austvik --- .../org.openhab.binding.nanoleaf/README.md | 2 +- .../doc/Layout.png | Bin 0 -> 25495 bytes .../nanoleaf/internal/layout/Point2D.java | 2 +- .../nanoleaf/internal/layout/ShapeType.java | 3 +- .../internal/layout/shape/Hexagon.java | 2 +- .../resources/OH-INF/i18n/nanoleaf.properties | 2 +- .../OH-INF/i18n/nanoleaf_de.properties | 38 +++++++++--------- .../internal/model/GlobalOrientationTest.java | 1 + .../nanoleaf/internal/model/LayoutTest.java | 1 + .../internal/model/PanelLayoutTest.java | 1 + .../internal/model/PositionDatumTest.java | 1 + 11 files changed, 30 insertions(+), 23 deletions(-) create mode 100644 bundles/org.openhab.binding.nanoleaf/doc/Layout.png diff --git a/bundles/org.openhab.binding.nanoleaf/README.md b/bundles/org.openhab.binding.nanoleaf/README.md index 308dd60e653f4..c84fc863363b6 100644 --- a/bundles/org.openhab.binding.nanoleaf/README.md +++ b/bundles/org.openhab.binding.nanoleaf/README.md @@ -76,7 +76,7 @@ In this case: If you want to program individual panels, it can be hard to figure out which panel has which ID. To make this easier, there is Layout channel on the Nanoleaf controller thing in OpenHAB. The easiest way to visualize the layout of the individual panels is to open the controller thing in the OpenHAB UI, go to Channels and add a new item to the Layout channel. -Clicking on that image or adding it to a dashboard will show a picture of your canvas with the induvidual thing IDs in the picture. +Clicking on that image or adding it to a dashboard will show a picture of your canvas with the individual thing ID in the picture. If your canvas has elements we dont know how to draw a layout for yet, please reach out, and we will ask for some information and will try to add support for your elements. diff --git a/bundles/org.openhab.binding.nanoleaf/doc/Layout.png b/bundles/org.openhab.binding.nanoleaf/doc/Layout.png new file mode 100644 index 0000000000000000000000000000000000000000..a8d684a0ce019fbf7009b9d57351c70d473aa48b GIT binary patch literal 25495 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 literal 0 HcmV?d00001 diff --git a/bundles/org.openhab.binding.nanoleaf/src/main/java/org/openhab/binding/nanoleaf/internal/layout/Point2D.java b/bundles/org.openhab.binding.nanoleaf/src/main/java/org/openhab/binding/nanoleaf/internal/layout/Point2D.java index c660a6aa6805a..921551a2e83f1 100644 --- a/bundles/org.openhab.binding.nanoleaf/src/main/java/org/openhab/binding/nanoleaf/internal/layout/Point2D.java +++ b/bundles/org.openhab.binding.nanoleaf/src/main/java/org/openhab/binding/nanoleaf/internal/layout/Point2D.java @@ -16,7 +16,7 @@ import org.eclipse.jdt.annotation.NonNullByDefault; /** - * Simple pair class. + * Coordinate in 2D space. * * @author Jørgen Austvik - Initial contribution */ diff --git a/bundles/org.openhab.binding.nanoleaf/src/main/java/org/openhab/binding/nanoleaf/internal/layout/ShapeType.java b/bundles/org.openhab.binding.nanoleaf/src/main/java/org/openhab/binding/nanoleaf/internal/layout/ShapeType.java index 5fefd360d48c0..f90262e0e7ece 100644 --- a/bundles/org.openhab.binding.nanoleaf/src/main/java/org/openhab/binding/nanoleaf/internal/layout/ShapeType.java +++ b/bundles/org.openhab.binding.nanoleaf/src/main/java/org/openhab/binding/nanoleaf/internal/layout/ShapeType.java @@ -22,12 +22,13 @@ */ @NonNullByDefault public enum ShapeType { + // side lengths are taken from https://forum.nanoleaf.me/docs chapter 3.3 UNKNOWN("Unknown", -1, 0, 0, DrawingAlgorithm.NONE), TRIANGLE("Triangle", 0, 150, 3, DrawingAlgorithm.TRIANGLE), RHYTHM("Rhythm", 1, 0, 1, DrawingAlgorithm.NONE), SQUARE("Square", 2, 100, 0, DrawingAlgorithm.SQUARE), CONTROL_SQUARE_MASTER("Control Square Master", 3, 100, 0, DrawingAlgorithm.SQUARE), - CONTROL_SQUARE_PASSIVE("Control Square Passive", 4, 100, 0, DrawingAlgorithm.NONE), + CONTROL_SQUARE_PASSIVE("Control Square Passive", 4, 100, 0, DrawingAlgorithm.SQUARE), SHAPES_HEXAGON("Hexagon (Shapes)", 7, 67, 6, DrawingAlgorithm.HEXAGON), SHAPES_TRIANGLE("Triangle (Shapes)", 8, 134, 3, DrawingAlgorithm.TRIANGLE), SHAPES_MINI_TRIANGLE("Mini Triangle (Shapes)", 9, 67, 3, DrawingAlgorithm.TRIANGLE), diff --git a/bundles/org.openhab.binding.nanoleaf/src/main/java/org/openhab/binding/nanoleaf/internal/layout/shape/Hexagon.java b/bundles/org.openhab.binding.nanoleaf/src/main/java/org/openhab/binding/nanoleaf/internal/layout/shape/Hexagon.java index 894345ca4f69c..a292335342d5f 100644 --- a/bundles/org.openhab.binding.nanoleaf/src/main/java/org/openhab/binding/nanoleaf/internal/layout/shape/Hexagon.java +++ b/bundles/org.openhab.binding.nanoleaf/src/main/java/org/openhab/binding/nanoleaf/internal/layout/shape/Hexagon.java @@ -22,7 +22,7 @@ import org.openhab.binding.nanoleaf.internal.layout.ShapeType; /** - * A triangular shape. + * A hexagon shape. * * @author Jørgen Austvik - Initial contribution */ 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 b4189d5aaf3b9..e3ee3da9dbd8c 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 @@ -39,7 +39,7 @@ channel-type.nanoleaf.tap.description = Button events of the panel channel-type.nanoleaf.swipe.label = Swipe channel-type.nanoleaf.swipe.description = Swipe over the panels channel-type.nanoleaf.layout.label = Layout -channel-type.nanoleaf.layout.description = Layout of the shapes +channel-type.nanoleaf.layout.description = Layout of the panels # error messages error.nanoleaf.controller.noIp = IP/host address and/or port are not configured for the 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 37da0e06e1fbf..3d9d941fcb887 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 @@ -1,9 +1,9 @@ binding.nanoleaf.name = Nanoleaf Binding -binding.nanoleaf.description = Binding für die Integration des Nanoleaf Light Panels (Ein mit dem Controller verbundenes Paneel) +binding.nanoleaf.description = Binding f\u00c3\u00bcr die Integration des Nanoleaf Light Panels (Ein mit dem Controller verbundenes Paneel) # thing types thing-type.nanoleaf.controller.name = Nanoleaf Controller -thing-type.nanoleaf.controller.description = Nanoleaf Controller (Brücke) +thing-type.nanoleaf.controller.description = Nanoleaf Controller (Br\u00c3\u00bccke) thing-type.nanoleaf.lightpanel.name = Nanoleaf Paneel thing-type.nanoleaf.lightpanel.description = Ein mit dem Controller verbundenes Paneel @@ -13,17 +13,17 @@ thing-type.config.nanoleaf.controller.address.description = IP Adresse oder Host thing-type.config.nanoleaf.controller.port.label = Port thing-type.config.nanoleaf.controller.port.description = Portnummer des Controllers, z. B. 16021 thing-type.config.nanoleaf.controller.authToken.label = Authentifizierungstoken -thing-type.config.nanoleaf.controller.authToken.description = Token zur Authentifizierung. Um openHAB mit dem Nanoleaf Light Panels zu verbinden (Pairing) den An/Aus-Schalter am Controller für 5-7 Sekunden gedrückthalten, bis die LED zu blinken beginnt. +thing-type.config.nanoleaf.controller.authToken.description = Token zur Authentifizierung. Um openHAB mit dem Nanoleaf Light Panels zu verbinden (Pairing) den An/Aus-Schalter am Controller f\u00c3\u00bcr 5-7 Sekunden gedr\u00c3\u00bcckthalten, bis die LED zu blinken beginnt. thing-type.config.nanoleaf.controller.refreshInterval.label = Aktualisierungsintervall -thing-type.config.nanoleaf.controller.refreshInterval.description = Intervall (in Sekunden) in dem die Kanäle aktualisiert werden -thing-type.config.nanoleaf.controller.deviceType.label = Nanoleaf Gerätetyp +thing-type.config.nanoleaf.controller.refreshInterval.description = Intervall (in Sekunden) in dem die Kan\u00c3\u00a4le aktualisiert werden +thing-type.config.nanoleaf.controller.deviceType.label = Nanoleaf Ger\u00c3\u00a4tetyp thing-type.config.nanoleaf.controller.deviceType.description = Light Panels (alte Dreieck-Modelle) oder Canvas/Shapes (neue Modelle als Quadrat, Dreieck, Hexagon, etc.) thing-type.config.nanoleaf.lightpanel.id.label = Panel ID thing-type.config.nanoleaf.lightpanel.id.description = Vom Controller vergebene ID eines Paneels # channel types channel-type.nanoleaf.colorMode.label = Farbmodus -channel-type.nanoleaf.colorMode.description = Effekt, Hue/Sat oder Farbtemperatur für alle Paneele. +channel-type.nanoleaf.colorMode.description = Effekt, Hue/Sat oder Farbtemperatur f\u00c3\u00bcr alle Paneele. channel-type.nanoleaf.effect.label = Effekt channel-type.nanoleaf.effect.description = Einstellung des Effektes channel-type.nanoleaf.rhythmState.label = Rhythm Status @@ -37,19 +37,21 @@ channel-type.nanoleaf.panelColor.description = Farbe des einzelnen Paneels channel-type.nanoleaf.tap.label = Taster channel-type.nanoleaf.tap.description = Tastevents des Panels channel-type.nanoleaf.swipe.label = Wischen -channel-type.nanoleaf.swipe.description = Über die Panels wischen +channel-type.nanoleaf.swipe.description = \u00c3\u009cber die Panels wischen +channel-type.nanoleaf.layout.label = Layout +channel-type.nanoleaf.layout.description = Layout 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. 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.noIp = IP/Host-Adresse und/oder Port sind f\u00c3\u00bcr den Controller nicht konfiguriert. +error.nanoleaf.controller.incompatibleFirmware = Inkompatible Controller-Firmware. Firmware aktualisieren und das Ger\u00c3\u00a4t neu hinzuf\u00c3\u00bcgen. +error.nanoleaf.controller.noToken = Kein Authentifizierungstoken gefunden. Um das Pairing zu starten, den An/Aus-Knopf am Controller f\u00c3\u00bcr 5-7 Sekunden lang gedr\u00c3\u00bcckt halten, bis die LED anf\u00c3\u00a4ngt zu blinken. +error.nanoleaf.controller.invalidToken = Ung\u00c3\u00bcltiges Authentifizierungstoken. Token \u00c3\u00a4ndern oder das Pairing neu starten durch L\u00c3\u00b6schen des ung\u00c3\u00bcltigen Tokens. +error.nanoleaf.controller.communication = Kommunikationsfehler. Netzwerk und Konfiguration des Controllers pr\u00c3\u00bcfen. +error.nanoleaf.controller.pairingFailed = Pairing fehlgeschlagen. Um das Pairing zu starten, den An/Aus-Knopf am Controller f\u00c3\u00bcr 5-7 Sekunden lang gedr\u00c3\u00bcckt halten, bis die LED anf\u00c3\u00a4ngt zu blinken. +error.nanoleaf.controller.invalidData = Pairing fehlgeschlagen. Ung\u00c3\u00bcltige Daten vom Controller empfangen. error.nanoleaf.controller.noTokenReceived = Pairing fehlgeschlagen. Kein Authentifizierungstoken empfangen. error.nanoleaf.controller.authRequest = Pairing fehlgeschlagen. Berechtigungsanfrage konnte nicht an den Controller gesendet werden. -error.nanoleaf.controller.noClient = Pairing fehlgeschlagen. HTTP Client nicht verfügbar. -error.nanoleaf.controller.runtime = Laufzeitfehler. Siehe openHAB Logdatei für mehr Details. -error.nanoleaf.panel.communication = Paneeldaten konnten nicht empfangen werden. Konfiguration des Controllers prüfen. -error.nanoleaf.panel.controllerOffline = Controller ist nicht erreichbar. Konfiguration prüfen. +error.nanoleaf.controller.noClient = Pairing fehlgeschlagen. HTTP Client nicht verf\u00c3\u00bcgbar. +error.nanoleaf.controller.runtime = Laufzeitfehler. Siehe openHAB Logdatei f\u00c3\u00bcr mehr Details. +error.nanoleaf.panel.communication = Paneeldaten konnten nicht empfangen werden. Konfiguration des Controllers pr\u00c3\u00bcfen. +error.nanoleaf.panel.controllerOffline = Controller ist nicht erreichbar. Konfiguration pr\u00c3\u00bcfen. diff --git a/bundles/org.openhab.binding.nanoleaf/src/test/java/org/openhab/binding/nanoleaf/internal/model/GlobalOrientationTest.java b/bundles/org.openhab.binding.nanoleaf/src/test/java/org/openhab/binding/nanoleaf/internal/model/GlobalOrientationTest.java index 22be11c7b5189..2290e5260e8fd 100644 --- a/bundles/org.openhab.binding.nanoleaf/src/test/java/org/openhab/binding/nanoleaf/internal/model/GlobalOrientationTest.java +++ b/bundles/org.openhab.binding.nanoleaf/src/test/java/org/openhab/binding/nanoleaf/internal/model/GlobalOrientationTest.java @@ -61,5 +61,6 @@ public void testHashCode() { public void testEquals() { assertThat(go1, is(equalTo(go3))); assertThat(go2, is(not(equalTo(go3)))); + assertThat(go3, is(not(equalTo(null)))); } } diff --git a/bundles/org.openhab.binding.nanoleaf/src/test/java/org/openhab/binding/nanoleaf/internal/model/LayoutTest.java b/bundles/org.openhab.binding.nanoleaf/src/test/java/org/openhab/binding/nanoleaf/internal/model/LayoutTest.java index 8fd56eaf4817f..354afbf74e0a0 100644 --- a/bundles/org.openhab.binding.nanoleaf/src/test/java/org/openhab/binding/nanoleaf/internal/model/LayoutTest.java +++ b/bundles/org.openhab.binding.nanoleaf/src/test/java/org/openhab/binding/nanoleaf/internal/model/LayoutTest.java @@ -67,5 +67,6 @@ public void testHashCode() { public void testEquals() { assertThat(lo1, is(equalTo(lo3))); assertThat(lo2, is(not(equalTo(lo3)))); + assertThat(lo3, is(not(equalTo(null)))); } } diff --git a/bundles/org.openhab.binding.nanoleaf/src/test/java/org/openhab/binding/nanoleaf/internal/model/PanelLayoutTest.java b/bundles/org.openhab.binding.nanoleaf/src/test/java/org/openhab/binding/nanoleaf/internal/model/PanelLayoutTest.java index 4bc766f62713e..e1c545f7572af 100644 --- a/bundles/org.openhab.binding.nanoleaf/src/test/java/org/openhab/binding/nanoleaf/internal/model/PanelLayoutTest.java +++ b/bundles/org.openhab.binding.nanoleaf/src/test/java/org/openhab/binding/nanoleaf/internal/model/PanelLayoutTest.java @@ -73,5 +73,6 @@ public void testHashCode() { public void testEquals() { assertThat(pl1, is(equalTo(pl3))); assertThat(pl2, is(not(equalTo(pl3)))); + assertThat(pl3, is(not(equalTo(null)))); } } diff --git a/bundles/org.openhab.binding.nanoleaf/src/test/java/org/openhab/binding/nanoleaf/internal/model/PositionDatumTest.java b/bundles/org.openhab.binding.nanoleaf/src/test/java/org/openhab/binding/nanoleaf/internal/model/PositionDatumTest.java index b77b4adde2c1c..cbaba2d044f26 100644 --- a/bundles/org.openhab.binding.nanoleaf/src/test/java/org/openhab/binding/nanoleaf/internal/model/PositionDatumTest.java +++ b/bundles/org.openhab.binding.nanoleaf/src/test/java/org/openhab/binding/nanoleaf/internal/model/PositionDatumTest.java @@ -61,5 +61,6 @@ public void testHashCode() { public void testEquals() { assertThat(pd1, is(equalTo(pd3))); assertThat(pd2, is(not(equalTo(pd3)))); + assertThat(pd3, is(not(equalTo(null)))); } } From d2a3dc768a1a1f987a77014033fb3c0130e7e0a7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B8rgen=20Austvik?= Date: Thu, 3 Nov 2022 22:01:25 +0100 Subject: [PATCH 09/13] Try to make equals methods more understandable MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Jørgen Austvik --- .../nanoleaf/internal/model/Layout.java | 17 +++++++------ .../nanoleaf/internal/model/PanelLayout.java | 24 +++++++++++++------ 2 files changed, 27 insertions(+), 14 deletions(-) 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 462dde09cee62..c5ced2e9384fe 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 @@ -164,19 +164,22 @@ public boolean equals(@Nullable Object o) { } Layout l = (Layout) o; + + if (numPanels != l.getNumPanels()) { + return false; + } + List pd = getPositionData(); List otherPd = l.getPositionData(); + if (pd == null && otherPd == null) { + return true; + } - boolean positionDataEquals = false; if (pd == null || otherPd == null) { - if (pd == null && otherPd == null) { - positionDataEquals = true; - } - } else { - positionDataEquals = pd.equals(otherPd); + return false; } - return (numPanels == l.getNumPanels()) && positionDataEquals; + return pd.equals(otherPd); } @Override diff --git a/bundles/org.openhab.binding.nanoleaf/src/main/java/org/openhab/binding/nanoleaf/internal/model/PanelLayout.java b/bundles/org.openhab.binding.nanoleaf/src/main/java/org/openhab/binding/nanoleaf/internal/model/PanelLayout.java index 732ba34c086c3..93e822d550895 100644 --- a/bundles/org.openhab.binding.nanoleaf/src/main/java/org/openhab/binding/nanoleaf/internal/model/PanelLayout.java +++ b/bundles/org.openhab.binding.nanoleaf/src/main/java/org/openhab/binding/nanoleaf/internal/model/PanelLayout.java @@ -62,29 +62,39 @@ public boolean equals(@Nullable Object o) { PanelLayout pl = (PanelLayout) o; + // For a panel layout to be equal to another panel layouit, all inner data structures must + // be equal, or they must be null both in this object or the object it is compared with. + GlobalOrientation go = globalOrientation; GlobalOrientation otherGo = pl.getGlobalOrientation(); boolean goEquals = false; if (go == null || otherGo == null) { if (go == null && otherGo == null) { + // If one of the global oriantations are null, the other must also be null + // for them to be equal goEquals = true; } } else { goEquals = go.equals(otherGo); } + if (goEquals == false) { + // No reason to compare layout if global oriantation is different + return false; + } + Layout l = layout; Layout otherL = pl.getLayout(); - boolean lEquals = false; + + if (l == null && otherL == null) { + return true; + } + if (l == null || otherL == null) { - if (l == null && otherL == null) { - lEquals = true; - } - } else { - lEquals = l.equals(otherL); + return false; } - return goEquals && lEquals; + return l.equals(otherL); } @Override From 4612ce8520ea2c01d2db62bd80c631f7c93ebde7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B8rgen=20Austvik?= Date: Fri, 11 Nov 2022 19:10:06 +0100 Subject: [PATCH 10/13] Review comments from Jacob Laursen MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Jørgen Austvik --- .../org.openhab.binding.nanoleaf/README.md | 4 +- .../internal/layout/shape/HexagonCorners.java | 56 +++++++++++++++++++ .../OH-INF/i18n/nanoleaf_de.properties | 2 - 3 files changed, 58 insertions(+), 4 deletions(-) create mode 100644 bundles/org.openhab.binding.nanoleaf/src/main/java/org/openhab/binding/nanoleaf/internal/layout/shape/HexagonCorners.java diff --git a/bundles/org.openhab.binding.nanoleaf/README.md b/bundles/org.openhab.binding.nanoleaf/README.md index c84fc863363b6..54bf6fe82f8ff 100644 --- a/bundles/org.openhab.binding.nanoleaf/README.md +++ b/bundles/org.openhab.binding.nanoleaf/README.md @@ -74,8 +74,8 @@ In this case: ### Panel Layout -If you want to program individual panels, it can be hard to figure out which panel has which ID. To make this easier, there is Layout channel on the Nanoleaf controller thing in OpenHAB. -The easiest way to visualize the layout of the individual panels is to open the controller thing in the OpenHAB UI, go to Channels and add a new item to the Layout channel. +If you want to program individual panels, it can be hard to figure out which panel has which ID. To make this easier, there is Layout channel on the Nanoleaf controller thing in openHAB. +The easiest way to visualize the layout of the individual panels is to open the controller thing in the openHAB UI, go to Channels and add a new item to the Layout channel. Clicking on that image or adding it to a dashboard will show a picture of your canvas with the individual thing ID in the picture. If your canvas has elements we dont know how to draw a layout for yet, please reach out, and we will ask for some information and will try to add support for your elements. diff --git a/bundles/org.openhab.binding.nanoleaf/src/main/java/org/openhab/binding/nanoleaf/internal/layout/shape/HexagonCorners.java b/bundles/org.openhab.binding.nanoleaf/src/main/java/org/openhab/binding/nanoleaf/internal/layout/shape/HexagonCorners.java new file mode 100644 index 0000000000000..a292335342d5f --- /dev/null +++ b/bundles/org.openhab.binding.nanoleaf/src/main/java/org/openhab/binding/nanoleaf/internal/layout/shape/HexagonCorners.java @@ -0,0 +1,56 @@ +/** + * Copyright (c) 2010-2022 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.layout.shape; + +import java.awt.Graphics2D; +import java.awt.geom.Rectangle2D; +import java.util.Arrays; +import java.util.List; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.openhab.binding.nanoleaf.internal.layout.Point2D; +import org.openhab.binding.nanoleaf.internal.layout.ShapeType; + +/** + * A hexagon shape. + * + * @author Jørgen Austvik - Initial contribution + */ +@NonNullByDefault +public class Hexagon extends Shape { + public Hexagon(ShapeType shapeType, int panelId, Point2D position, int orientation) { + super(shapeType, panelId, position, orientation); + } + + @Override + public List generateOutline() { + Point2D v1 = new Point2D((int) getShapeType().getSideLength(), 0); + Point2D v2 = v1.rotate((1.0 / 3.0) * Math.PI); + Point2D v3 = v1.rotate((2.0 / 3.0) * Math.PI); + Point2D v4 = v1.rotate((3.0 / 3.0) * Math.PI); + Point2D v5 = v1.rotate((4.0 / 3.0) * Math.PI); + Point2D v6 = v1.rotate((5.0 / 3.0) * Math.PI); + return Arrays.asList(v1.move(getPosition()), v2.move(getPosition()), v3.move(getPosition()), + v4.move(getPosition()), v5.move(getPosition()), v6.move(getPosition())); + } + + @Override + public Point2D labelPosition(Graphics2D graphics, List outline) { + Point2D[] bounds = findBounds(outline); + int midX = bounds[0].getX() + (bounds[1].getX() - bounds[0].getX()) / 2; + int midY = bounds[0].getY() + (bounds[1].getY() - bounds[0].getY()) / 2; + + Rectangle2D rect = graphics.getFontMetrics().getStringBounds(Integer.toString(getPanelId()), graphics); + return new Point2D(midX - (int) (rect.getWidth() / 2), midY - (int) (rect.getHeight() / 2)); + } +} 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 3d9d941fcb887..bbce2a7d69d22 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 @@ -38,8 +38,6 @@ channel-type.nanoleaf.tap.label = Taster channel-type.nanoleaf.tap.description = Tastevents des Panels channel-type.nanoleaf.swipe.label = Wischen channel-type.nanoleaf.swipe.description = \u00c3\u009cber die Panels wischen -channel-type.nanoleaf.layout.label = Layout -channel-type.nanoleaf.layout.description = Layout des Panels # error messages error.nanoleaf.controller.noIp = IP/Host-Adresse und/oder Port sind f\u00c3\u00bcr den Controller nicht konfiguriert. From 52165ee19c3843d3ab0e19a63dd0f4a221245cf1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B8rgen=20Austvik?= Date: Sat, 12 Nov 2022 12:31:47 +0100 Subject: [PATCH 11/13] Revert "Review comments from Jacob Laursen" This reverts commit 4612ce8520ea2c01d2db62bd80c631f7c93ebde7. --- .../org.openhab.binding.nanoleaf/README.md | 4 +- .../internal/layout/shape/HexagonCorners.java | 56 ------------------- .../OH-INF/i18n/nanoleaf_de.properties | 2 + 3 files changed, 4 insertions(+), 58 deletions(-) delete mode 100644 bundles/org.openhab.binding.nanoleaf/src/main/java/org/openhab/binding/nanoleaf/internal/layout/shape/HexagonCorners.java diff --git a/bundles/org.openhab.binding.nanoleaf/README.md b/bundles/org.openhab.binding.nanoleaf/README.md index 54bf6fe82f8ff..c84fc863363b6 100644 --- a/bundles/org.openhab.binding.nanoleaf/README.md +++ b/bundles/org.openhab.binding.nanoleaf/README.md @@ -74,8 +74,8 @@ In this case: ### Panel Layout -If you want to program individual panels, it can be hard to figure out which panel has which ID. To make this easier, there is Layout channel on the Nanoleaf controller thing in openHAB. -The easiest way to visualize the layout of the individual panels is to open the controller thing in the openHAB UI, go to Channels and add a new item to the Layout channel. +If you want to program individual panels, it can be hard to figure out which panel has which ID. To make this easier, there is Layout channel on the Nanoleaf controller thing in OpenHAB. +The easiest way to visualize the layout of the individual panels is to open the controller thing in the OpenHAB UI, go to Channels and add a new item to the Layout channel. Clicking on that image or adding it to a dashboard will show a picture of your canvas with the individual thing ID in the picture. If your canvas has elements we dont know how to draw a layout for yet, please reach out, and we will ask for some information and will try to add support for your elements. diff --git a/bundles/org.openhab.binding.nanoleaf/src/main/java/org/openhab/binding/nanoleaf/internal/layout/shape/HexagonCorners.java b/bundles/org.openhab.binding.nanoleaf/src/main/java/org/openhab/binding/nanoleaf/internal/layout/shape/HexagonCorners.java deleted file mode 100644 index a292335342d5f..0000000000000 --- a/bundles/org.openhab.binding.nanoleaf/src/main/java/org/openhab/binding/nanoleaf/internal/layout/shape/HexagonCorners.java +++ /dev/null @@ -1,56 +0,0 @@ -/** - * Copyright (c) 2010-2022 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.layout.shape; - -import java.awt.Graphics2D; -import java.awt.geom.Rectangle2D; -import java.util.Arrays; -import java.util.List; - -import org.eclipse.jdt.annotation.NonNullByDefault; -import org.openhab.binding.nanoleaf.internal.layout.Point2D; -import org.openhab.binding.nanoleaf.internal.layout.ShapeType; - -/** - * A hexagon shape. - * - * @author Jørgen Austvik - Initial contribution - */ -@NonNullByDefault -public class Hexagon extends Shape { - public Hexagon(ShapeType shapeType, int panelId, Point2D position, int orientation) { - super(shapeType, panelId, position, orientation); - } - - @Override - public List generateOutline() { - Point2D v1 = new Point2D((int) getShapeType().getSideLength(), 0); - Point2D v2 = v1.rotate((1.0 / 3.0) * Math.PI); - Point2D v3 = v1.rotate((2.0 / 3.0) * Math.PI); - Point2D v4 = v1.rotate((3.0 / 3.0) * Math.PI); - Point2D v5 = v1.rotate((4.0 / 3.0) * Math.PI); - Point2D v6 = v1.rotate((5.0 / 3.0) * Math.PI); - return Arrays.asList(v1.move(getPosition()), v2.move(getPosition()), v3.move(getPosition()), - v4.move(getPosition()), v5.move(getPosition()), v6.move(getPosition())); - } - - @Override - public Point2D labelPosition(Graphics2D graphics, List outline) { - Point2D[] bounds = findBounds(outline); - int midX = bounds[0].getX() + (bounds[1].getX() - bounds[0].getX()) / 2; - int midY = bounds[0].getY() + (bounds[1].getY() - bounds[0].getY()) / 2; - - Rectangle2D rect = graphics.getFontMetrics().getStringBounds(Integer.toString(getPanelId()), graphics); - return new Point2D(midX - (int) (rect.getWidth() / 2), midY - (int) (rect.getHeight() / 2)); - } -} 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 bbce2a7d69d22..3d9d941fcb887 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 @@ -38,6 +38,8 @@ channel-type.nanoleaf.tap.label = Taster channel-type.nanoleaf.tap.description = Tastevents des Panels channel-type.nanoleaf.swipe.label = Wischen channel-type.nanoleaf.swipe.description = \u00c3\u009cber die Panels wischen +channel-type.nanoleaf.layout.label = Layout +channel-type.nanoleaf.layout.description = Layout des Panels # error messages error.nanoleaf.controller.noIp = IP/Host-Adresse und/oder Port sind f\u00c3\u00bcr den Controller nicht konfiguriert. From ce2a1588d6f42485281ebafd66093e85cab76e6a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B8rgen=20Austvik?= Date: Sat, 12 Nov 2022 12:34:47 +0100 Subject: [PATCH 12/13] (Only the) Review comments from Jacob Laursen MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Jørgen Austvik --- bundles/org.openhab.binding.nanoleaf/README.md | 4 ++-- .../src/main/resources/OH-INF/i18n/nanoleaf_de.properties | 2 -- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/bundles/org.openhab.binding.nanoleaf/README.md b/bundles/org.openhab.binding.nanoleaf/README.md index c84fc863363b6..1aacf2e413cf5 100644 --- a/bundles/org.openhab.binding.nanoleaf/README.md +++ b/bundles/org.openhab.binding.nanoleaf/README.md @@ -74,8 +74,8 @@ In this case: ### Panel Layout -If you want to program individual panels, it can be hard to figure out which panel has which ID. To make this easier, there is Layout channel on the Nanoleaf controller thing in OpenHAB. -The easiest way to visualize the layout of the individual panels is to open the controller thing in the OpenHAB UI, go to Channels and add a new item to the Layout channel. +If you want to program individual panels, it can be hard to figure out which panel has which ID. To make this easier, there is Layout channel on the Nanoleaf controller thing in openHAB. +The easiest way to visualize the layout of the individual panels is to open the controller thing in the openHAB UI, go to Channels and add a new item to the Layout channel. Clicking on that image or adding it to a dashboard will show a picture of your canvas with the individual thing ID in the picture. If your canvas has elements we dont know how to draw a layout for yet, please reach out, and we will ask for some information and will try to add support for your elements. 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 3d9d941fcb887..bbce2a7d69d22 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 @@ -38,8 +38,6 @@ channel-type.nanoleaf.tap.label = Taster channel-type.nanoleaf.tap.description = Tastevents des Panels channel-type.nanoleaf.swipe.label = Wischen channel-type.nanoleaf.swipe.description = \u00c3\u009cber die Panels wischen -channel-type.nanoleaf.layout.label = Layout -channel-type.nanoleaf.layout.description = Layout des Panels # error messages error.nanoleaf.controller.noIp = IP/Host-Adresse und/oder Port sind f\u00c3\u00bcr den Controller nicht konfiguriert. From f7a73557df579f708d4032888a0b16d82d3b25bc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B8rgen=20Austvik?= Date: Sat, 12 Nov 2022 12:53:57 +0100 Subject: [PATCH 13/13] Revert the german translations where the charset was changed wrongly MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Jørgen Austvik --- .../OH-INF/i18n/nanoleaf_de.properties | 36 +++++++++---------- 1 file changed, 18 insertions(+), 18 deletions(-) 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 bbce2a7d69d22..37da0e06e1fbf 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 @@ -1,9 +1,9 @@ binding.nanoleaf.name = Nanoleaf Binding -binding.nanoleaf.description = Binding f\u00c3\u00bcr die Integration des Nanoleaf Light Panels (Ein mit dem Controller verbundenes Paneel) +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 -thing-type.nanoleaf.controller.description = Nanoleaf Controller (Br\u00c3\u00bccke) +thing-type.nanoleaf.controller.description = Nanoleaf Controller (Brücke) thing-type.nanoleaf.lightpanel.name = Nanoleaf Paneel thing-type.nanoleaf.lightpanel.description = Ein mit dem Controller verbundenes Paneel @@ -13,17 +13,17 @@ thing-type.config.nanoleaf.controller.address.description = IP Adresse oder Host thing-type.config.nanoleaf.controller.port.label = Port thing-type.config.nanoleaf.controller.port.description = Portnummer des Controllers, z. B. 16021 thing-type.config.nanoleaf.controller.authToken.label = Authentifizierungstoken -thing-type.config.nanoleaf.controller.authToken.description = Token zur Authentifizierung. Um openHAB mit dem Nanoleaf Light Panels zu verbinden (Pairing) den An/Aus-Schalter am Controller f\u00c3\u00bcr 5-7 Sekunden gedr\u00c3\u00bcckthalten, bis die LED zu blinken beginnt. +thing-type.config.nanoleaf.controller.authToken.description = Token zur Authentifizierung. Um openHAB mit dem Nanoleaf Light Panels zu verbinden (Pairing) den An/Aus-Schalter am Controller für 5-7 Sekunden gedrückthalten, bis die LED zu blinken beginnt. thing-type.config.nanoleaf.controller.refreshInterval.label = Aktualisierungsintervall -thing-type.config.nanoleaf.controller.refreshInterval.description = Intervall (in Sekunden) in dem die Kan\u00c3\u00a4le aktualisiert werden -thing-type.config.nanoleaf.controller.deviceType.label = Nanoleaf Ger\u00c3\u00a4tetyp +thing-type.config.nanoleaf.controller.refreshInterval.description = Intervall (in Sekunden) in dem die Kanäle aktualisiert werden +thing-type.config.nanoleaf.controller.deviceType.label = Nanoleaf Gerätetyp thing-type.config.nanoleaf.controller.deviceType.description = Light Panels (alte Dreieck-Modelle) oder Canvas/Shapes (neue Modelle als Quadrat, Dreieck, Hexagon, etc.) thing-type.config.nanoleaf.lightpanel.id.label = Panel ID thing-type.config.nanoleaf.lightpanel.id.description = Vom Controller vergebene ID eines Paneels # channel types channel-type.nanoleaf.colorMode.label = Farbmodus -channel-type.nanoleaf.colorMode.description = Effekt, Hue/Sat oder Farbtemperatur f\u00c3\u00bcr alle Paneele. +channel-type.nanoleaf.colorMode.description = Effekt, Hue/Sat oder Farbtemperatur für alle Paneele. channel-type.nanoleaf.effect.label = Effekt channel-type.nanoleaf.effect.description = Einstellung des Effektes channel-type.nanoleaf.rhythmState.label = Rhythm Status @@ -37,19 +37,19 @@ channel-type.nanoleaf.panelColor.description = Farbe des einzelnen Paneels channel-type.nanoleaf.tap.label = Taster channel-type.nanoleaf.tap.description = Tastevents des Panels channel-type.nanoleaf.swipe.label = Wischen -channel-type.nanoleaf.swipe.description = \u00c3\u009cber die Panels wischen +channel-type.nanoleaf.swipe.description = Über die Panels wischen # error messages -error.nanoleaf.controller.noIp = IP/Host-Adresse und/oder Port sind f\u00c3\u00bcr den Controller nicht konfiguriert. -error.nanoleaf.controller.incompatibleFirmware = Inkompatible Controller-Firmware. Firmware aktualisieren und das Ger\u00c3\u00a4t neu hinzuf\u00c3\u00bcgen. -error.nanoleaf.controller.noToken = Kein Authentifizierungstoken gefunden. Um das Pairing zu starten, den An/Aus-Knopf am Controller f\u00c3\u00bcr 5-7 Sekunden lang gedr\u00c3\u00bcckt halten, bis die LED anf\u00c3\u00a4ngt zu blinken. -error.nanoleaf.controller.invalidToken = Ung\u00c3\u00bcltiges Authentifizierungstoken. Token \u00c3\u00a4ndern oder das Pairing neu starten durch L\u00c3\u00b6schen des ung\u00c3\u00bcltigen Tokens. -error.nanoleaf.controller.communication = Kommunikationsfehler. Netzwerk und Konfiguration des Controllers pr\u00c3\u00bcfen. -error.nanoleaf.controller.pairingFailed = Pairing fehlgeschlagen. Um das Pairing zu starten, den An/Aus-Knopf am Controller f\u00c3\u00bcr 5-7 Sekunden lang gedr\u00c3\u00bcckt halten, bis die LED anf\u00c3\u00a4ngt zu blinken. -error.nanoleaf.controller.invalidData = Pairing fehlgeschlagen. Ung\u00c3\u00bcltige Daten vom Controller empfangen. +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. 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. error.nanoleaf.controller.authRequest = Pairing fehlgeschlagen. Berechtigungsanfrage konnte nicht an den Controller gesendet werden. -error.nanoleaf.controller.noClient = Pairing fehlgeschlagen. HTTP Client nicht verf\u00c3\u00bcgbar. -error.nanoleaf.controller.runtime = Laufzeitfehler. Siehe openHAB Logdatei f\u00c3\u00bcr mehr Details. -error.nanoleaf.panel.communication = Paneeldaten konnten nicht empfangen werden. Konfiguration des Controllers pr\u00c3\u00bcfen. -error.nanoleaf.panel.controllerOffline = Controller ist nicht erreichbar. Konfiguration pr\u00c3\u00bcfen. +error.nanoleaf.controller.noClient = Pairing fehlgeschlagen. HTTP Client nicht verfügbar. +error.nanoleaf.controller.runtime = Laufzeitfehler. Siehe openHAB Logdatei für mehr Details. +error.nanoleaf.panel.communication = Paneeldaten konnten nicht empfangen werden. Konfiguration des Controllers prüfen. +error.nanoleaf.panel.controllerOffline = Controller ist nicht erreichbar. Konfiguration prüfen.