diff --git a/bundles/org.openhab.binding.nanoleaf/README.md b/bundles/org.openhab.binding.nanoleaf/README.md index ca0527ff4a2cd..7f4f30f3168e3 100644 --- a/bundles/org.openhab.binding.nanoleaf/README.md +++ b/bundles/org.openhab.binding.nanoleaf/README.md @@ -59,9 +59,10 @@ Troubleshooting: In seldom cases (in particular together with updating the bindi **Knowing which panel has which id** Unfortunately it is not easy to find out which panel gets which id while this is pretty important if you have lots of them and you want to assign rules to it. -Don't worry: the binding comes with some helpful support in the background (this works only well for the canvas device!) +Don't worry: the binding comes with some helpful support in the background the canvas type (this is only provided for the canvas device because triangles can have weird layouts that are hard to express in a log output) - fire up your browser and open the openhab server on port 9001 which shows the logs. +- Set up a switch item with the channel panelLayout on the controller (see NanoRetrieveLayout below) and set the switch to true - look out for something like "Panel layout and ids" in the logs. Below that you will see a panel layout similar to Compare the following output with the right picture at the beginning of the article @@ -75,8 +76,9 @@ Compare the following output with the right picture at the beginning of the arti 41451 -``` - +``` + +Disclaimer: this works best with square devices and not necessarily well with triangles due to the more geometrically flexible layout. ## Thing Configuration @@ -114,6 +116,7 @@ The controller bridge has the following channels: | rhythmState | Switch | Connection state of the rhythm module | Yes | | rhythmActive | Switch | Activity state of the rhythm module | Yes | | rhythmMode | Number | Sound source for the rhythm module. 0=Microphone, 1=Aux cable | No | +| panelLayout | Switch | Set to true will log out panel layout (returns to off automatically | No | A lightpanel thing has the following channels: @@ -168,15 +171,17 @@ The following files provide a full example for a configuration (using a things f ### nanoleaf.things ``` -Bridge nanoleaf:controller:MyLightPanels [ address="192.168.1.100", port=16021, authToken="AbcDefGhiJk879LmNopqRstUv1234WxyZ", refreshInterval=60 ] { +Bridge nanoleaf:controller:MyLightPanels @ "mylocation" [ address="192.168.1.100", port=16021, authToken="AbcDefGhiJk879LmNopqRstUv1234WxyZ", refreshInterval=60 ] { Thing lightpanel 135 [ id=135 ] Thing lightpanel 158 [ id=158 ] } ``` If you define your device statically in the thing file, autodiscovery of the same thing is suppressed by using + * the [address="..." ] of the controller * and the [id=123] of the lightpanel + in the bracket to identify the uniqueness of the discovered device. Therefore it is recommended to the give the controller a fixed ip address. Note: To generate the `authToken`: @@ -208,6 +213,8 @@ String NanoleafEffect "Effect" { channel="nanoleaf:controller:MyLightPanels:effe Switch NanoleafRhythmState "Rhythm connected [MAP(nanoleaf.map):%s]" { channel="nanoleaf:controller:MyLightPanels:rhythmState" } Switch NanoleafRhythmActive "Rhythm active [MAP(nanoleaf.map):%s]" { channel="nanoleaf:controller:MyLightPanels:rhythmActive" } Number NanoleafRhythmSource "Rhythm source [%s]" { channel="nanoleaf:controller:MyLightPanels:rhythmMode" } +Switch NanoRetrieveLayout "Nano Layout" { channel="nanoleaf:controller:D81E7A7E424E:panelLayout" } + // note that the next to items use the exact same channel but the two different types Color and Dimmer to control different parameters Color Panel1Color "Panel 1" { channel="nanoleaf:lightpanel:MyLightPanels:135:panelColor" } Dimmer Panel1Brightness "Panel 1" { channel="nanoleaf:lightpanel:MyLightPanels:135:panelColor" } @@ -236,6 +243,7 @@ sitemap nanoleaf label="Nanoleaf" Text item=NanoleafRhythmState Text item=NanoleafRhythmActive Selection item=NanoleafRhythmSource mappings=[0="Microphone", 1="Aux"] + Switch item=NanoRetrieveLayout } Frame label="Panels" { 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 f3ac23ba10a62..9f133ba06dcef 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 @@ -50,6 +50,7 @@ public class NanoleafBindingConstants { public static final String CHANNEL_RHYTHM_STATE = "rhythmState"; public static final String CHANNEL_RHYTHM_ACTIVE = "rhythmActive"; public static final String CHANNEL_RHYTHM_MODE = "rhythmMode"; + public static final String CHANNEL_PANEL_LAYOUT = "panelLayout"; // List of light panel channels public static final String CHANNEL_PANEL_COLOR = "panelColor"; diff --git a/bundles/org.openhab.binding.nanoleaf/src/main/java/org/openhab/binding/nanoleaf/internal/NanoleafInterruptedException.java b/bundles/org.openhab.binding.nanoleaf/src/main/java/org/openhab/binding/nanoleaf/internal/NanoleafInterruptedException.java new file mode 100644 index 0000000000000..ab5e3bafb88a5 --- /dev/null +++ b/bundles/org.openhab.binding.nanoleaf/src/main/java/org/openhab/binding/nanoleaf/internal/NanoleafInterruptedException.java @@ -0,0 +1,30 @@ +/** + * Copyright (c) 2010-2020 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.nanoleaf.internal; + +import org.eclipse.jdt.annotation.NonNullByDefault; + +/** + * Exception if request to Nanoleaf OpenAPI has been interrupted which is normally intended + * + * @author Stefan Höhn - Initial contribution + */ +@NonNullByDefault +public class NanoleafInterruptedException extends NanoleafException { + + private static final long serialVersionUID = -6941678941424234257L; + + public NanoleafInterruptedException(String message, InterruptedException interruptedException) { + super(message); + } +} 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 4b1406360bd67..d1e640a48ef18 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 @@ -51,7 +51,7 @@ public class OpenAPIUtils { private static final Pattern FIRMWARE_VERSION_PATTERN = Pattern.compile("(\\d+)\\.(\\d+)\\.(\\d+)"); public static Request requestBuilder(HttpClient httpClient, NanoleafControllerConfig controllerConfig, - String apiOperation, HttpMethod method) throws NanoleafException, NanoleafUnauthorizedException { + String apiOperation, HttpMethod method) throws NanoleafException { URI requestURI = getUri(controllerConfig, apiOperation, null); LOGGER.trace("RequestBuilder: Sending Request {}:{} {} ", requestURI.getHost(), requestURI.getPort(), requestURI.getPath()); @@ -88,11 +88,11 @@ public static URI getUri(NanoleafControllerConfig controllerConfig, String apiOp } public static ContentResponse sendOpenAPIRequest(Request request) - throws NanoleafException, NanoleafUnauthorizedException { + throws NanoleafException { try { traceSendRequest(request); - - ContentResponse openAPIResponse = request.send(); + ContentResponse openAPIResponse; + openAPIResponse = request.send(); if (LOGGER.isTraceEnabled()) { LOGGER.trace("API response from Nanoleaf controller: {}", openAPIResponse.getContentAsString()); } @@ -114,7 +114,7 @@ public static ContentResponse sendOpenAPIRequest(Request request) openAPIResponse.getStatus())); } } - } catch (ExecutionException | TimeoutException | InterruptedException clientException) { + } catch (ExecutionException | TimeoutException clientException) { if (clientException.getCause() instanceof HttpResponseException && ((HttpResponseException) clientException.getCause()).getResponse() .getStatus() == HttpStatus.UNAUTHORIZED_401) { @@ -122,6 +122,8 @@ public static ContentResponse sendOpenAPIRequest(Request request) throw new NanoleafUnauthorizedException("Invalid authorization token"); } throw new NanoleafException("Failed to send OpenAPI request", clientException); + } catch ( InterruptedException interruptedException) { + throw new NanoleafInterruptedException("OpenAPI request has been interrupted", interruptedException); } } diff --git a/bundles/org.openhab.binding.nanoleaf/src/main/java/org/openhab/binding/nanoleaf/internal/discovery/NanoleafMDNSDiscoveryParticipant.java b/bundles/org.openhab.binding.nanoleaf/src/main/java/org/openhab/binding/nanoleaf/internal/discovery/NanoleafMDNSDiscoveryParticipant.java index bb29a2d4c80be..cab6aa7b0825a 100644 --- a/bundles/org.openhab.binding.nanoleaf/src/main/java/org/openhab/binding/nanoleaf/internal/discovery/NanoleafMDNSDiscoveryParticipant.java +++ b/bundles/org.openhab.binding.nanoleaf/src/main/java/org/openhab/binding/nanoleaf/internal/discovery/NanoleafMDNSDiscoveryParticipant.java @@ -38,7 +38,7 @@ * The {@link NanoleafMDNSDiscoveryParticipant} is responsible for discovering new Nanoleaf controllers (bridges). * * @author Martin Raepple - Initial contribution - * @author Stefan Höhn + * @author Stefan Höhn - further improvements for static defined things * @see MSDN Discovery */ @Component(immediate = true, configurationPid = "discovery.nanoleaf") @@ -84,6 +84,7 @@ public String getServiceType() { logger.warn("Nanoleaf controller firmware is too old. Must be {} or higher", MODEL_ID_LIGHTPANELS.equals(modelId) ? API_MIN_FW_VER_LIGHTPANELS : API_MIN_FW_VER_CANVAS); } + final DiscoveryResult result = DiscoveryResultBuilder.create(uid).withThingType(getThingType(service)) .withProperties(properties).withLabel(service.getName()).withRepresentationProperty(CONFIG_ADDRESS) .build(); 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 2fb71fc291587..85e5e6e0cc129 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 @@ -52,10 +52,7 @@ import org.eclipse.smarthome.core.thing.binding.BaseBridgeHandler; import org.eclipse.smarthome.core.types.Command; import org.eclipse.smarthome.core.types.RefreshType; -import org.openhab.binding.nanoleaf.internal.NanoleafControllerListener; -import org.openhab.binding.nanoleaf.internal.NanoleafException; -import org.openhab.binding.nanoleaf.internal.NanoleafUnauthorizedException; -import org.openhab.binding.nanoleaf.internal.OpenAPIUtils; +import org.openhab.binding.nanoleaf.internal.*; import org.openhab.binding.nanoleaf.internal.config.NanoleafControllerConfig; import org.openhab.binding.nanoleaf.internal.model.*; import org.slf4j.Logger; @@ -131,16 +128,14 @@ public void initialize() { updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_PENDING, "@text/error.nanoleaf.controller.noIp"); stopAllJobs(); - return; } else if (!StringUtils.isEmpty(getThing().getProperties().get(Thing.PROPERTY_FIRMWARE_VERSION)) && !OpenAPIUtils.checkRequiredFirmware(getThing().getProperties().get(Thing.PROPERTY_MODEL_ID), - getThing().getProperties().get(Thing.PROPERTY_FIRMWARE_VERSION))) { + getThing().getProperties().get(Thing.PROPERTY_FIRMWARE_VERSION))) { logger.warn("Nanoleaf controller firmware is too old: {}. Must be equal or higher than {}", getThing().getProperties().get(Thing.PROPERTY_FIRMWARE_VERSION), API_MIN_FW_VER_LIGHTPANELS); updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, "@text/error.nanoleaf.controller.incompatibleFirmware"); stopAllJobs(); - return; } else if (StringUtils.isEmpty(getAuthToken())) { logger.debug("No token found. Start pairing background job"); updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_PENDING, @@ -148,7 +143,6 @@ public void initialize() { startPairingJob(); stopUpdateJob(); stopPanelDiscoveryJob(); - return; } else { logger.debug("Controller is online. Stop pairing job, start update & panel discovery jobs"); updateStatus(ThingStatus.ONLINE); @@ -181,6 +175,7 @@ public void handleCommand(ChannelUID channelUID, Command command) { case CHANNEL_COLOR: case CHANNEL_COLOR_TEMPERATURE: case CHANNEL_COLOR_TEMPERATURE_ABS: + case CHANNEL_PANEL_LAYOUT: sendStateCommand(channelUID.getId(), command); break; case CHANNEL_EFFECT: @@ -320,9 +315,9 @@ private synchronized void stopPanelDiscoveryJob() { private synchronized void startTouchJob() { NanoleafControllerConfig config = getConfigAs(NanoleafControllerConfig.class); - if (config.deviceType != DEVICE_TYPE_CANVAS) { - logger.debug("NOT starting TouchJob for Panel {} because it has wrong device type {}", - this.getThing().getUID(), config.deviceType); + if (!config.deviceType.equals(DEVICE_TYPE_CANVAS)) { + logger.debug("NOT starting TouchJob for Panel {} because it has wrong device type '{}' vs required '{}'", + this.getThing().getUID(), config.deviceType, DEVICE_TYPE_CANVAS); return; } else logger.debug("Starting TouchJob for Panel {}", this.getThing().getUID()); @@ -392,7 +387,6 @@ private void runPairing() { if (authTokenResponse.getStatus() != HttpStatus.OK_200) { logger.debug("Pairing pending for {}. Controller returns status code {}", this.getThing().getUID(), authTokenResponse.getStatus()); - return; } else { // get auth token from response @Nullable @@ -456,6 +450,8 @@ private void runPanelDiscovery() { updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_PENDING, "@text/error.nanoleaf.controller.noToken"); } + } catch (NanoleafInterruptedException nie) { + logger.info("Panel discovery has been stopped."); } catch (NanoleafException ne) { logger.warn("Failed to discover panels: ", ne); updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, @@ -534,7 +530,7 @@ public void onComplete(@Nullable Result result) { /** * Interate over all gathered touch events and apply them to the panel they belong to - * + * * @param touchEvents */ private void handleTouchEvents(TouchEvents touchEvents) { @@ -557,11 +553,13 @@ private void handleTouchEvents(TouchEvents touchEvents) { }); } - private void updateFromControllerInfo() throws NanoleafException, NanoleafUnauthorizedException { + private void updateFromControllerInfo() throws NanoleafException { logger.debug("Update channels for controller {}", thing.getUID()); this.controllerInfo = receiveControllerInfo(); - if (controllerInfo == null) + if (controllerInfo == null) { + logger.debug("No Controller Info has been provided"); return; + } final State state = controllerInfo.getState(); OnOffType powerState = state.getOnOff(); @@ -610,6 +608,8 @@ private void updateFromControllerInfo() throws NanoleafException, NanoleafUnauth Map properties = editProperties(); properties.put(Thing.PROPERTY_SERIAL_NUMBER, controllerInfo.getSerialNo()); properties.put(Thing.PROPERTY_FIRMWARE_VERSION, controllerInfo.getFirmwareVersion()); + properties.put(Thing.PROPERTY_MODEL_ID, controllerInfo.getModel()); + properties.put(Thing.PROPERTY_VENDOR, controllerInfo.getManufacturer()); updateProperties(properties); Configuration config = editConfiguration(); @@ -639,11 +639,6 @@ private void updateFromControllerInfo() throws NanoleafException, NanoleafUnauth panelHandler.updatePanelColorChannel(); } }); - - @Nullable - Layout layout = controllerInfo.getPanelLayout().getLayout(); - String layoutView = (layout != null) ? layout.getLayoutView() : ""; - logger.info("Panel layout and ids for controller {} \n{}", thing.getUID(), layoutView); } private ControllerInfo receiveControllerInfo() throws NanoleafException, NanoleafUnauthorizedException { @@ -763,6 +758,13 @@ private void sendStateCommand(String channel, Command command) throws NanoleafEx return; } break; + case CHANNEL_PANEL_LAYOUT: + @Nullable + Layout layout = controllerInfo.getPanelLayout().getLayout(); + String layoutView = (layout != null) ? layout.getLayoutView() : ""; + logger.info("Panel layout and ids for controller {} \n{}", thing.getUID(), layoutView); + updateState(CHANNEL_PANEL_LAYOUT, OnOffType.OFF); + break; default: logger.warn("Unhandled command type: {}", command.getClass().getName()); return; 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 e1aa52c17841e..d9c6fbe2f5a30 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 @@ -72,8 +72,9 @@ public String getLayoutView() { int miny = Integer.MAX_VALUE; int maxy = Integer.MIN_VALUE; - for (int index = 0; index < numPanels; index++) { - if (positionData != null) { + final int noofDefinedPanels = positionData.size(); + for (int index = 0; index < noofDefinedPanels; index++) { + if (positionData != null ) { @Nullable PositionDatum panel = positionData.get(index); @@ -97,11 +98,11 @@ public String getLayoutView() { int shiftWidth = getSideLength() / 2; int lineY = maxy; - Map map = new TreeMap<>(); + Map map; while (lineY >= miny) { map = new TreeMap<>(); - for (int index = 0; index < numPanels; index++) { + for (int index = 0; index < noofDefinedPanels; index++) { if (positionData != null) { @Nullable diff --git a/bundles/org.openhab.binding.nanoleaf/src/main/resources/ESH-INF/i18n/nanoleaf.properties b/bundles/org.openhab.binding.nanoleaf/src/main/resources/ESH-INF/i18n/nanoleaf.properties index d0ad0df3b7328..4f7f992a145d1 100644 --- a/bundles/org.openhab.binding.nanoleaf/src/main/resources/ESH-INF/i18n/nanoleaf.properties +++ b/bundles/org.openhab.binding.nanoleaf/src/main/resources/ESH-INF/i18n/nanoleaf.properties @@ -1,5 +1,5 @@ binding.nanoleaf.name = Nanoleaf Binding -binding.nanoleaf.description = Integrates the Nanoleaf Light Panels (v100320) +binding.nanoleaf.description = Integrates the Nanoleaf Light Panels (v150320) # thing types thing-type.nanoleaf.controller.name = Nanoleaf Controller @@ -40,6 +40,8 @@ channel-type.nanoleaf.rhythmActive.label = Rhythm Active channel-type.nanoleaf.rhythmActive.description = Activity state of the rhythm module channel-type.nanoleaf.rhythmMode.label = Rhythm Mode channel-type.nanoleaf.rhythmMode.description = Sound source for the rhythm module (microphone or aux cable) +channel-type.nanoleaf.panelLayout.label = Panel Layout +channel-type.nanoleaf.panelLayout.description = Creates a panel layout upon request channel-type.nanoleaf.panelColor.label = Panel Color channel-type.nanoleaf.panelColor.description = Color of the individual panel channel-type.nanoleaf.singleTap.label = SingleTap diff --git a/bundles/org.openhab.binding.nanoleaf/src/main/resources/ESH-INF/i18n/nanoleaf_de.properties b/bundles/org.openhab.binding.nanoleaf/src/main/resources/ESH-INF/i18n/nanoleaf_de.properties index 01496c8df34e4..aeebf1f8297f2 100644 --- a/bundles/org.openhab.binding.nanoleaf/src/main/resources/ESH-INF/i18n/nanoleaf_de.properties +++ b/bundles/org.openhab.binding.nanoleaf/src/main/resources/ESH-INF/i18n/nanoleaf_de.properties @@ -1,5 +1,5 @@ binding.nanoleaf.name = Nanoleaf Binding -binding.nanoleaf.description = Binding für die Integration des Nanoleaf Light Panels (v100320) +binding.nanoleaf.description = Binding für die Integration des Nanoleaf Light Panels (v150320) # thing types thing-type.nanoleaf.controller.name = Nanoleaf Controller @@ -40,6 +40,8 @@ channel-type.nanoleaf.rhythmActive.label = Rhythm Aktiv channel-type.nanoleaf.rhythmActive.description = Zeigt an ob das Mikrofon des Rhythm Modules ativ ist. channel-type.nanoleaf.rhythmMode.label = Rhythm Modus channel-type.nanoleaf.rhythmMode.description = Erlaubt den Wechsel zwischen eingebautem Mikrofon und AUX-Kabel. +channel-type.nanoleaf.panelLayout.label = PanelLayout +channel-type.nanoleaf.panelLayout.description = Erzeugt auf Anfrage ein Panel-Layout channel-type.nanoleaf.panelColor.label = Paneelfarbe channel-type.nanoleaf.panelColor.description = Farbe des einzelnen Paneels channel-type.nanoleaf.singleTap.label = Einzel-Tap diff --git a/bundles/org.openhab.binding.nanoleaf/src/main/resources/ESH-INF/thing/lightpanels.xml b/bundles/org.openhab.binding.nanoleaf/src/main/resources/ESH-INF/thing/lightpanels.xml index 96c4ae40de72e..5f753ef6b93a3 100644 --- a/bundles/org.openhab.binding.nanoleaf/src/main/resources/ESH-INF/thing/lightpanels.xml +++ b/bundles/org.openhab.binding.nanoleaf/src/main/resources/ESH-INF/thing/lightpanels.xml @@ -18,6 +18,7 @@ + @@ -141,4 +142,11 @@ @text/channel-type.nanoleaf.doubleTap.description + + + Switch + + @text/channel-type.nanoleaf.panelLayout.description + + diff --git a/bundles/org.openhab.binding.nanoleaf/src/test/java/org/openhab/binding/nanoleaf/internal/LayoutTest.java b/bundles/org.openhab.binding.nanoleaf/src/test/java/org/openhab/binding/nanoleaf/internal/LayoutTest.java index d67fdc6ee97aa..d6a73caab37ab 100644 --- a/bundles/org.openhab.binding.nanoleaf/src/test/java/org/openhab/binding/nanoleaf/internal/LayoutTest.java +++ b/bundles/org.openhab.binding.nanoleaf/src/test/java/org/openhab/binding/nanoleaf/internal/LayoutTest.java @@ -34,10 +34,13 @@ public class LayoutTest { private final Gson gson = new Gson(); String layout1Json = ""; + String layoutInconsistentPanelNoJson = ""; @Before public void setup() { layout1Json = "{\"numPanels\":14,\"sideLength\":100,\"positionData\":[{\"panelId\":41451,\"x\":350,\"y\":0,\"o\":0,\"shapeType\":3},{\"panelId\":8134,\"x\":350,\"y\":150,\"o\":0,\"shapeType\":2},{\"panelId\":58086,\"x\":200,\"y\":100,\"o\":270,\"shapeType\":2},{\"panelId\":38724,\"x\":300,\"y\":200,\"o\":0,\"shapeType\":2},{\"panelId\":48111,\"x\":200,\"y\":200,\"o\":270,\"shapeType\":2},{\"panelId\":56093,\"x\":100,\"y\":200,\"o\":0,\"shapeType\":2},{\"panelId\":55836,\"x\":0,\"y\":200,\"o\":0,\"shapeType\":2},{\"panelId\":31413,\"x\":100,\"y\":300,\"o\":90,\"shapeType\":2},{\"panelId\":9162,\"x\":300,\"y\":300,\"o\":90,\"shapeType\":2},{\"panelId\":13276,\"x\":400,\"y\":300,\"o\":90,\"shapeType\":2},{\"panelId\":17870,\"x\":400,\"y\":200,\"o\":0,\"shapeType\":2},{\"panelId\":5164,\"x\":500,\"y\":200,\"o\":0,\"shapeType\":2},{\"panelId\":64279,\"x\":600,\"y\":200,\"o\":0,\"shapeType\":2},{\"panelId\":39755,\"x\":500,\"y\":100,\"o\":90,\"shapeType\":2}]}"; + // panel number is not consistent to returned panels in array but it should still work + layoutInconsistentPanelNoJson = "{\"numPanels\":15,\"sideLength\":100,\"positionData\":[{\"panelId\":41451,\"x\":350,\"y\":0,\"o\":0,\"shapeType\":3},{\"panelId\":8134,\"x\":350,\"y\":150,\"o\":0,\"shapeType\":2},{\"panelId\":58086,\"x\":200,\"y\":100,\"o\":270,\"shapeType\":2},{\"panelId\":38724,\"x\":300,\"y\":200,\"o\":0,\"shapeType\":2},{\"panelId\":48111,\"x\":200,\"y\":200,\"o\":270,\"shapeType\":2},{\"panelId\":56093,\"x\":100,\"y\":200,\"o\":0,\"shapeType\":2},{\"panelId\":55836,\"x\":0,\"y\":200,\"o\":0,\"shapeType\":2},{\"panelId\":31413,\"x\":100,\"y\":300,\"o\":90,\"shapeType\":2},{\"panelId\":9162,\"x\":300,\"y\":300,\"o\":90,\"shapeType\":2},{\"panelId\":13276,\"x\":400,\"y\":300,\"o\":90,\"shapeType\":2},{\"panelId\":17870,\"x\":400,\"y\":200,\"o\":0,\"shapeType\":2},{\"panelId\":5164,\"x\":500,\"y\":200,\"o\":0,\"shapeType\":2},{\"panelId\":64279,\"x\":600,\"y\":200,\"o\":0,\"shapeType\":2},{\"panelId\":39755,\"x\":500,\"y\":100,\"o\":90,\"shapeType\":2}]}"; } @Test @@ -54,4 +57,19 @@ public void testTheRightLayoutView() { + " \n" + " 41451 \n"))); } + + @Test + public void testTheInconsistentLayoutView() { + @Nullable + Layout layout = gson.fromJson(layoutInconsistentPanelNoJson, Layout.class); + String layoutView = layout.getLayoutView(); + assertThat(layoutView, + is(equalTo(" 31413 9162 13276 \n" + + " \n" + + "55836 56093 48111 38724 17870 5164 64279 \n" + + " 8134 \n" + + " 58086 39755 \n" + + " \n" + + " 41451 \n"))); + } }