mirror of
https://github.com/openhab/openhab-addons.git
synced 2025-01-10 15:11:59 +01:00
[Nanoleaf] Visualize layout (#13552)
* Visualize Nanoleaf layout * Only calculate image if channel is linked * White background image * Render more shapes Signed-off-by: Jørgen Austvik <jaustvik@acm.org>
This commit is contained in:
parent
160e0c2548
commit
fbd06ec709
@ -28,9 +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 Mini Triangles | NL42 | Mini Triangles | x | X |
|
||||
| Shapes Triangles | NL47 | Triangles | X | X |
|
||||
| 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 | - | |
|
||||
| Canvas | NL29 | Squares | X | X |
|
||||
|
||||
x = Supported (-) = unknown (no device available to test)
|
||||
@ -70,9 +74,15 @@ 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.
|
||||
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.
|
||||
|
||||
For canvas that use square panels, you can request the layout through a [console command](https://www.openhab.org/docs/administration/console.html):
|
||||
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:
|
||||
|
||||
@ -94,7 +104,7 @@ Compare the following output with the right picture at the beginning of the arti
|
||||
41451
|
||||
|
||||
```
|
||||
|
||||
|
||||
## Thing Configuration
|
||||
|
||||
The controller thing has the following parameters:
|
||||
|
BIN
bundles/org.openhab.binding.nanoleaf/doc/Layout.png
Normal file
BIN
bundles/org.openhab.binding.nanoleaf/doc/Layout.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 25 KiB |
@ -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";
|
||||
@ -78,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<String> MODELS_WITH_TOUCHSUPPORT = Arrays.asList("NL29", "NL42");
|
||||
public static final List<String> 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
|
||||
@ -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;
|
||||
}
|
||||
|
@ -154,11 +154,7 @@ public class OpenAPIUtils {
|
||||
|
||||
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]);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -14,6 +14,7 @@ package org.openhab.binding.nanoleaf.internal.handler;
|
||||
|
||||
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.OpenAPIUtils;
|
||||
import org.openhab.binding.nanoleaf.internal.commanddescription.NanoleafCommandDescriptionProvider;
|
||||
import org.openhab.binding.nanoleaf.internal.config.NanoleafControllerConfig;
|
||||
import org.openhab.binding.nanoleaf.internal.discovery.NanoleafPanelsDiscoveryService;
|
||||
import org.openhab.binding.nanoleaf.internal.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.HSBType;
|
||||
import org.openhab.core.library.types.IncreaseDecreaseType;
|
||||
import org.openhab.core.library.types.OnOffType;
|
||||
import org.openhab.core.library.types.PercentType;
|
||||
import org.openhab.core.library.types.RawType;
|
||||
import org.openhab.core.library.types.StringType;
|
||||
import org.openhab.core.thing.Bridge;
|
||||
import org.openhab.core.thing.ChannelUID;
|
||||
@ -72,6 +75,7 @@ import org.openhab.core.thing.Thing;
|
||||
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;
|
||||
@ -103,6 +107,7 @@ public class NanoleafControllerHandler extends BaseBridgeHandler {
|
||||
private @Nullable HttpClient httpClientSSETouchEvent;
|
||||
private @Nullable Request sseTouchjobRequest;
|
||||
private List<NanoleafControllerListener> controllerListeners = new CopyOnWriteArrayList<NanoleafControllerListener>();
|
||||
private PanelLayout previousPanelLayout = new PanelLayout();
|
||||
|
||||
private @NonNullByDefault({}) ScheduledFuture<?> pairingJob;
|
||||
private @NonNullByDefault({}) ScheduledFuture<?> updateJob;
|
||||
@ -664,6 +669,7 @@ public class NanoleafControllerHandler extends BaseBridgeHandler {
|
||||
|
||||
updateProperties();
|
||||
updateConfiguration();
|
||||
updateLayout(controllerInfo.getPanelLayout());
|
||||
|
||||
for (NanoleafControllerListener controllerListener : controllerListeners) {
|
||||
controllerListener.onControllerInfoFetched(getThing().getUID(), controllerInfo);
|
||||
@ -705,6 +711,33 @@ public class NanoleafControllerHandler extends BaseBridgeHandler {
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
private ControllerInfo receiveControllerInfo() throws NanoleafException, NanoleafUnauthorizedException {
|
||||
ContentResponse controllerlInfoJSON = OpenAPIUtils.sendOpenAPIRequest(OpenAPIUtils.requestBuilder(httpClient,
|
||||
getControllerConfig(), API_GET_CONTROLLER_INFO, HttpMethod.GET));
|
||||
|
@ -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;
|
||||
}
|
@ -0,0 +1,184 @@
|
||||
/**
|
||||
* 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.Color;
|
||||
import java.awt.Graphics2D;
|
||||
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;
|
||||
|
||||
import javax.imageio.ImageIO;
|
||||
|
||||
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;
|
||||
import org.openhab.binding.nanoleaf.internal.model.PositionDatum;
|
||||
|
||||
/**
|
||||
* Renders the Nanoleaf layout to an image.
|
||||
*
|
||||
* @author Jørgen Austvik - Initial contribution
|
||||
*/
|
||||
@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();
|
||||
if (globalOrientation != null) {
|
||||
rotationRadians = calculateRotationRadians(globalOrientation);
|
||||
}
|
||||
|
||||
Layout layout = panelLayout.getLayout();
|
||||
if (layout == null) {
|
||||
return new byte[] {};
|
||||
}
|
||||
|
||||
List<PositionDatum> panels = layout.getPositionData();
|
||||
if (panels == null) {
|
||||
return new byte[] {};
|
||||
}
|
||||
|
||||
Point2D size[] = findSize(panels, rotationRadians);
|
||||
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,
|
||||
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 ShapeType shapeType = ShapeType.valueOf(panel.getShapeType());
|
||||
|
||||
Shape shape = ShapeFactory.CreateShape(shapeType, panel);
|
||||
List<Point2D> 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());
|
||||
}
|
||||
|
||||
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);
|
||||
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());
|
||||
}
|
||||
|
||||
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();
|
||||
ImageIO.write(image, "png", out);
|
||||
return out.toByteArray();
|
||||
}
|
||||
|
||||
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.
|
||||
return ((double) (maxValue - value)) * (Math.PI / 180);
|
||||
}
|
||||
|
||||
private static Point2D[] findSize(Collection<PositionDatum> panels, double rotationRadians) {
|
||||
int maxX = 0;
|
||||
int maxY = 0;
|
||||
int minX = 0;
|
||||
int minY = 0;
|
||||
|
||||
for (PositionDatum panel : panels) {
|
||||
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<Point2D> toPictureLayout(List<Point2D> originals, int imageHeight, Point2D min,
|
||||
double rotationRadians) {
|
||||
List<Point2D> result = new ArrayList<Point2D>(originals.size());
|
||||
for (Point2D original : originals) {
|
||||
result.add(toPictureLayout(original, imageHeight, min, rotationRadians));
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
@ -0,0 +1,81 @@
|
||||
/**
|
||||
* 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;
|
||||
|
||||
/**
|
||||
* Coordinate in 2D space.
|
||||
*
|
||||
* @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);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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);
|
||||
}
|
||||
}
|
@ -0,0 +1,87 @@
|
||||
/**
|
||||
* 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 {
|
||||
// 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.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),
|
||||
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, DrawingAlgorithm drawingAlgorithm) {
|
||||
this.name = name;
|
||||
this.id = id;
|
||||
this.sideLength = sideLenght;
|
||||
this.numSides = numSides;
|
||||
this.drawingAlgorithm = drawingAlgorithm;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public int getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public double getSideLength() {
|
||||
return sideLength;
|
||||
}
|
||||
|
||||
public int getNumSides() {
|
||||
return numSides;
|
||||
}
|
||||
|
||||
public DrawingAlgorithm getDrawingAlgorithm() {
|
||||
return drawingAlgorithm;
|
||||
}
|
||||
|
||||
public static ShapeType valueOf(int id) {
|
||||
for (ShapeType shapeType : values()) {
|
||||
if (shapeType.getId() == id) {
|
||||
return shapeType;
|
||||
}
|
||||
}
|
||||
|
||||
return ShapeType.UNKNOWN;
|
||||
}
|
||||
}
|
@ -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<Point2D> 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<Point2D> 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));
|
||||
}
|
||||
}
|
@ -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<Point2D> generateOutline() {
|
||||
return Arrays.asList(getPosition());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Point2D labelPosition(Graphics2D graphics, List<Point2D> outline) {
|
||||
return outline.get(0);
|
||||
}
|
||||
}
|
@ -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<Point2D> 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<Point2D> generateOutline();
|
||||
|
||||
/**
|
||||
* @return The position where the label of the shape should be placed
|
||||
*/
|
||||
public abstract Point2D labelPosition(Graphics2D graphics, List<Point2D> outline);
|
||||
}
|
@ -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());
|
||||
}
|
||||
}
|
||||
}
|
@ -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<Point2D> 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<Point2D> 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));
|
||||
}
|
||||
}
|
@ -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<Point2D> 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<Point2D> 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;
|
||||
}
|
||||
}
|
@ -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 class GlobalOrientation {
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
@ -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<PositionDatum> positionData = null;
|
||||
|
||||
public Layout() {
|
||||
}
|
||||
|
||||
public Layout(List<PositionDatum> positionData) {
|
||||
this.positionData = new ArrayList<>(positionData);
|
||||
this.numPanels = positionData.size();
|
||||
}
|
||||
|
||||
public int getNumPanels() {
|
||||
return numPanels;
|
||||
}
|
||||
@ -143,4 +152,48 @@ public class Layout {
|
||||
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;
|
||||
|
||||
if (numPanels != l.getNumPanels()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
List<PositionDatum> pd = getPositionData();
|
||||
List<PositionDatum> otherPd = l.getPositionData();
|
||||
if (pd == null && otherPd == null) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (pd == null || otherPd == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return pd.equals(otherPd);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
final int prime = 31;
|
||||
int result = 1;
|
||||
result = prime * result + getNumPanels();
|
||||
List<PositionDatum> pd = getPositionData();
|
||||
if (pd != null) {
|
||||
for (PositionDatum p : pd) {
|
||||
result = prime * result + p.hashCode();
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
@ -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,69 @@ public class PanelLayout {
|
||||
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;
|
||||
|
||||
// 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();
|
||||
|
||||
if (l == null && otherL == null) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (l == null || otherL == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return l.equals(otherL);
|
||||
}
|
||||
|
||||
@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;
|
||||
}
|
||||
}
|
||||
|
@ -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<Integer, Integer> panelSizes = new HashMap<Integer, Integer>();
|
||||
|
||||
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 class PositionDatum {
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
@ -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 panels
|
||||
|
||||
# error messages
|
||||
error.nanoleaf.controller.noIp = IP/host address and/or port are not configured for the controller.
|
||||
|
@ -18,6 +18,7 @@
|
||||
<channel id="rhythmActive" typeId="rhythmActive"/>
|
||||
<channel id="rhythmMode" typeId="rhythmMode"/>
|
||||
<channel id="swipe" typeId="swipe"/>
|
||||
<channel id="layout" typeId="layout"/>
|
||||
</channels>
|
||||
|
||||
<properties>
|
||||
@ -107,4 +108,10 @@
|
||||
</event>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="layout">
|
||||
<item-type>Image</item-type>
|
||||
<label>@text/channel-type.nanoleaf.layout.label</label>
|
||||
<description>@text/channel-type.nanoleaf.layout.description</description>
|
||||
</channel-type>
|
||||
|
||||
</thing:thing-descriptions>
|
||||
|
@ -0,0 +1,66 @@
|
||||
/**
|
||||
* 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))));
|
||||
assertThat(go3, is(not(equalTo(null))));
|
||||
}
|
||||
}
|
@ -0,0 +1,72 @@
|
||||
/**
|
||||
* 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))));
|
||||
assertThat(lo3, is(not(equalTo(null))));
|
||||
}
|
||||
}
|
@ -0,0 +1,78 @@
|
||||
/**
|
||||
* 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))));
|
||||
assertThat(pl3, is(not(equalTo(null))));
|
||||
}
|
||||
}
|
@ -0,0 +1,66 @@
|
||||
/**
|
||||
* 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))));
|
||||
assertThat(pd3, is(not(equalTo(null))));
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user