[Nanoleaf] Support lines (#13881)

Lines is a Nanoleaf shape we haven't been able to test earlier.

Now we have tested them, and added support for painting them as well.

They do unfortunately not support touch gestures.

Signed-off-by: Jørgen Austvik <jaustvik@acm.org>
This commit is contained in:
Jørgen Austvik 2022-12-08 22:39:45 +01:00 committed by GitHub
parent 0e68936663
commit 02398b14a3
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 195 additions and 7 deletions

View File

@ -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)

View File

@ -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);

View File

@ -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<Point2D> generateOutline() {
return Arrays.asList(position);
}
@Override
public void draw(Graphics2D graphics, DrawingSettings settings, PanelState state) {
}
}

View File

@ -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);
}
}
}

View File

@ -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<PositionDatum> corners;
public SingleLine(ShapeType shapeType, List<PositionDatum> corners) {
super(shapeType);
this.corners = Collections.unmodifiableList(new ArrayList<>(corners));
}
@Override
public List<Point2D> generateOutline() {
List<Point2D> 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<ImagePoint2D> 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<ImagePoint2D> 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());
}
}

View File

@ -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);

View File

@ -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}}}