diff --git a/bundles/org.openhab.binding.nanoleaf/README.md b/bundles/org.openhab.binding.nanoleaf/README.md index c6226731f..df5d8f52a 100644 --- a/bundles/org.openhab.binding.nanoleaf/README.md +++ b/bundles/org.openhab.binding.nanoleaf/README.md @@ -33,8 +33,8 @@ You can set the **color** for each panel and in the case of a Nanoleaf Canvas or | Shapes Mini Triangles | NL48 | Mini Triangles | X | X | | Elements Hexagon | NL52 | Elements Hexagons | X | X | | Smart Bulb | NL45 | Smart Bulb | - | | -| Lightstrip | NL55 | Lightstrip | - | | -| Lines | NL59 | Lines | - | | +| Lightstrip | NL55 | Lightstrip | - | - | +| Lines | NL59 | Lines | X | | | Canvas | NL29 | Squares | X | X | x = Supported (-) = unknown (no device available to test) 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 471dafb36..4e63d3de4 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 @@ -35,9 +35,9 @@ public enum ShapeType { SHAPES_CONTROLLER("Controller (Shapes)", 12, 0, 0, 1, DrawingAlgorithm.NONE), ELEMENTS_HEXAGON("Elements Hexagon", 14, 134, 6, 1, DrawingAlgorithm.HEXAGON), ELEMENTS_HEXAGON_CORNER("Elements Hexagon - Corner", 15, 58, 6, 6, DrawingAlgorithm.CORNER), - LINES_CONNECTOR("Lines Connector", 16, 11, 1, 1, DrawingAlgorithm.LINE), + LINES_CONNECTOR("Lines Connector", 16, 11, 1, 1, DrawingAlgorithm.NONE), LIGHT_LINES("Light Lines", 17, 154, 1, 1, DrawingAlgorithm.LINE), - LINES_LINES_SINGLE("Light Lines - Single Sone", 18, 77, 1, 1, DrawingAlgorithm.LINE), + LINES_LINES_SINGLE("Light Lines - Single Sone", 18, 77, 2, 2, DrawingAlgorithm.LINE), CONTROLLER_CAP("Controller Cap", 19, 11, 0, 1, DrawingAlgorithm.NONE), POWER_CONNECTOR("Power Connector", 20, 11, 0, 1, DrawingAlgorithm.NONE); diff --git a/bundles/org.openhab.binding.nanoleaf/src/main/java/org/openhab/binding/nanoleaf/internal/layout/shape/NoDraw.java b/bundles/org.openhab.binding.nanoleaf/src/main/java/org/openhab/binding/nanoleaf/internal/layout/shape/NoDraw.java new file mode 100644 index 000000000..991066fd6 --- /dev/null +++ b/bundles/org.openhab.binding.nanoleaf/src/main/java/org/openhab/binding/nanoleaf/internal/layout/shape/NoDraw.java @@ -0,0 +1,50 @@ +/** + * 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.DrawingSettings; +import org.openhab.binding.nanoleaf.internal.layout.PanelState; +import org.openhab.binding.nanoleaf.internal.layout.Point2D; +import org.openhab.binding.nanoleaf.internal.layout.ShapeType; + +/** + * A panel that shouldn't be drawn (power connector, controller, ...). + * + * Especially lines can have controllers and power connectors etc on top of each other. + * + * @author Jørgen Austvik - Initial contribution + */ +@NonNullByDefault +public class NoDraw extends Panel { + + private final Point2D position; + + public NoDraw(ShapeType shapeType, int panelId, Point2D position) { + super(shapeType); + this.position = position; + } + + @Override + public List generateOutline() { + return Arrays.asList(position); + } + + @Override + public void draw(Graphics2D graphics, DrawingSettings settings, PanelState state) { + } +} diff --git a/bundles/org.openhab.binding.nanoleaf/src/main/java/org/openhab/binding/nanoleaf/internal/layout/shape/PanelFactory.java b/bundles/org.openhab.binding.nanoleaf/src/main/java/org/openhab/binding/nanoleaf/internal/layout/shape/PanelFactory.java index ec15a0f67..f0425f45b 100644 --- a/bundles/org.openhab.binding.nanoleaf/src/main/java/org/openhab/binding/nanoleaf/internal/layout/shape/PanelFactory.java +++ b/bundles/org.openhab.binding.nanoleaf/src/main/java/org/openhab/binding/nanoleaf/internal/layout/shape/PanelFactory.java @@ -81,13 +81,21 @@ public class PanelFactory { Point2D pos3 = new Point2D(hexShape.getPosX(), hexShape.getPosY()); return new Hexagon(shapeType, hexShape.getPanelId(), pos3, hexShape.getOrientation()); + case LINE: + return new SingleLine(shapeType, positionDatum); + case CORNER: return new HexagonCorners(shapeType, positionDatum); + case NONE: + PositionDatum noShape = positionDatum.get(0); + Point2D pos4 = new Point2D(noShape.getPosX(), noShape.getPosY()); + return new NoDraw(shapeType, noShape.getPanelId(), pos4); + default: PositionDatum shape = positionDatum.get(0); - Point2D pos4 = new Point2D(shape.getPosX(), shape.getPosY()); - return new Point(shapeType, shape.getPanelId(), pos4); + Point2D pos5 = new Point2D(shape.getPosX(), shape.getPosY()); + return new Point(shapeType, shape.getPanelId(), pos5); } } } diff --git a/bundles/org.openhab.binding.nanoleaf/src/main/java/org/openhab/binding/nanoleaf/internal/layout/shape/SingleLine.java b/bundles/org.openhab.binding.nanoleaf/src/main/java/org/openhab/binding/nanoleaf/internal/layout/shape/SingleLine.java new file mode 100644 index 000000000..2097f422a --- /dev/null +++ b/bundles/org.openhab.binding.nanoleaf/src/main/java/org/openhab/binding/nanoleaf/internal/layout/shape/SingleLine.java @@ -0,0 +1,128 @@ +/** + * 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.BasicStroke; +import java.awt.Color; +import java.awt.Graphics2D; +import java.awt.Polygon; +import java.awt.Stroke; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.openhab.binding.nanoleaf.internal.layout.DrawingSettings; +import org.openhab.binding.nanoleaf.internal.layout.ImagePoint2D; +import org.openhab.binding.nanoleaf.internal.layout.PanelState; +import org.openhab.binding.nanoleaf.internal.layout.Point2D; +import org.openhab.binding.nanoleaf.internal.layout.ShapeType; +import org.openhab.binding.nanoleaf.internal.model.PositionDatum; +import org.openhab.core.library.types.HSBType; + +/** + * A single line. + * + * @author Jørgen Austvik - Initial contribution + */ +@NonNullByDefault +public class SingleLine extends Panel { + + private static final int CORNER_DIAMETER = 4; + private static final int LINE_WIDTH = 6; + + private final List corners; + + public SingleLine(ShapeType shapeType, List corners) { + super(shapeType); + + this.corners = Collections.unmodifiableList(new ArrayList<>(corners)); + } + + @Override + public List generateOutline() { + List result = new ArrayList<>(corners.size()); + for (PositionDatum corner : corners) { + result.add(new Point2D(corner.getPosX(), corner.getPosY())); + } + + return result; + } + + @Override + public void draw(Graphics2D graphics, DrawingSettings settings, PanelState state) { + List outline = settings.generateImagePoints(generateOutline()); + Polygon p = new Polygon(); + for (int i = 0; i < outline.size(); i++) { + ImagePoint2D pos = outline.get(i); + p.addPoint(pos.getX(), pos.getY()); + } + + if (settings.shouldFillWithColor()) { + Color corner1Color = getColor(corners.get(0).getPanelId(), state); + Color corner2Color = getColor(corners.get(0).getPanelId(), state); + + ImagePoint2D center = findCenter(outline); + + Stroke oldStroke = graphics.getStroke(); + Stroke lineStroke = new BasicStroke(LINE_WIDTH, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND); + graphics.setStroke(lineStroke); + graphics.setColor(corner1Color); + graphics.drawLine(outline.get(0).getX(), outline.get(0).getY(), center.getX(), center.getY()); + graphics.setColor(corner2Color); + graphics.drawLine(outline.get(1).getX(), outline.get(1).getY(), center.getX(), center.getY()); + graphics.setStroke(oldStroke); + } + + if (settings.shouldDrawOutline()) { + graphics.setColor(settings.getOutlineColor()); + graphics.drawPolygon(p); + } + + if (settings.shouldDrawCorners()) { + for (PositionDatum corner : corners) { + ImagePoint2D position = settings.generateImagePoint(new Point2D(corner.getPosX(), corner.getPosY())); + graphics.setColor(getColor(corner.getPanelId(), state)); + graphics.fillOval(position.getX() - CORNER_DIAMETER / 2, position.getY() - CORNER_DIAMETER / 2, + CORNER_DIAMETER, CORNER_DIAMETER); + + if (settings.shouldDrawOutline()) { + graphics.setColor(settings.getOutlineColor()); + graphics.drawOval(position.getX() - CORNER_DIAMETER / 2, position.getY() - CORNER_DIAMETER / 2, + CORNER_DIAMETER, CORNER_DIAMETER); + } + } + } + + if (settings.shouldDrawLabels()) { + graphics.setColor(settings.getLabelColor()); + + for (PositionDatum corner : corners) { + ImagePoint2D position = settings.generateImagePoint(new Point2D(corner.getPosX(), corner.getPosY())); + graphics.drawString(Integer.toString(corner.getPanelId()), position.getX(), position.getY()); + } + } + } + + private ImagePoint2D findCenter(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; + return new ImagePoint2D(midX, midY); + } + + private static Color getColor(int panelId, PanelState state) { + HSBType color = state.getHSBForPanel(panelId); + return new Color(color.getRGB()); + } +} diff --git a/bundles/org.openhab.binding.nanoleaf/src/test/java/org/openhab/binding/nanoleaf/internal/layout/NanoleafLayoutTest.java b/bundles/org.openhab.binding.nanoleaf/src/test/java/org/openhab/binding/nanoleaf/internal/layout/NanoleafLayoutTest.java index c6344abd5..e77048794 100644 --- a/bundles/org.openhab.binding.nanoleaf/src/test/java/org/openhab/binding/nanoleaf/internal/layout/NanoleafLayoutTest.java +++ b/bundles/org.openhab.binding.nanoleaf/src/test/java/org/openhab/binding/nanoleaf/internal/layout/NanoleafLayoutTest.java @@ -47,7 +47,8 @@ public class NanoleafLayoutTest { static @Nullable Path temporaryDirectory; @ParameterizedTest - @ValueSource(strings = { "lasvegas.json", "theduck.json", "squares.json", "wings.json", "spaceinvader.json" }) + @ValueSource(strings = { "lasvegas.json", "theduck.json", "squares.json", "wings.json", "spaceinvader.json", + "lines.json" }) public void testFile(String fileName) throws Exception { Path file = Path.of("src/test/resources/", fileName); assertTrue(Files.exists(file), "File should exist: " + file); diff --git a/bundles/org.openhab.binding.nanoleaf/src/test/resources/lines.json b/bundles/org.openhab.binding.nanoleaf/src/test/resources/lines.json new file mode 100644 index 000000000..592d88627 --- /dev/null +++ b/bundles/org.openhab.binding.nanoleaf/src/test/resources/lines.json @@ -0,0 +1 @@ +{"name":"Lines","serialNo":"S1234567","manufacturer":"Nanoleaf","firmwareVersion":"7.1.0","hardwareVersion":"0.0.4-0","model":"NL59","discovery":null,"effects":{"effectsList":["Beat Drop","Cotton Candy","Cozy Blaze","Fiesta","Good Vibes","Hip Hop","Jalapeno Heat","Kaleidoscope","Mountain Air","Mystic Night","Neon Dreams","Northern Lights","Pop Rocks","Prism","Radioactive","Raspberry Lemonade","Sakura Blossom","Starry Sky","Tuscany Sunset","Waterfall"],"select":"Raspberry Lemonade"},"firmwareUpgrade":null,"panelLayout":{"globalOrientation":{"value":88,"max":360,"min":0},"layout":{"numPanels":13,"sideLength":154,"positionData":[{"panelId":15376,"x":227,"y":337,"o":0,"shapeType":20},{"panelId":12105,"x":227,"y":212,"o":0,"shapeType":18},{"panelId":56841,"x":227,"y":289,"o":0,"shapeType":18},{"panelId":24127,"x":227,"y":164,"o":120,"shapeType":16},{"panelId":55776,"x":119,"y":274,"o":300,"shapeType":18},{"panelId":10400,"x":185,"y":313,"o":300,"shapeType":18},{"panelId":54069,"x":77,"y":250,"o":180,"shapeType":16},{"panelId":6876,"x":119,"y":226,"o":240,"shapeType":18},{"panelId":35357,"x":185,"y":188,"o":240,"shapeType":18},{"panelId":45376,"x":77,"y":125,"o":0,"shapeType":18},{"panelId":16384,"x":77,"y":202,"o":0,"shapeType":18},{"panelId":49828,"x":77,"y":77,"o":300,"shapeType":16},{"panelId":5591,"x":227,"y":337,"o":0,"shapeType":19}]}},"qkihnokomhartlnp":{},"schedules":{},"state":{"brightness":{"value":63,"max":100,"min":0},"colorMode":"effect","ct":{"value":4000,"max":6500,"min":1200},"hue":{"value":0,"max":360,"min":0},"on":{"value":true},"sat":{"value":0,"max":100,"min":0}}}