[hdpowerview] Add support for scene groups (#11534)
* Add support for scene collections. Fixes #11533 Signed-off-by: Jacob Laursen <jacob-github@vindvejr.dk> * Add unit test for parsing of scene collections response. Signed-off-by: Jacob Laursen <jacob-github@vindvejr.dk> * Add default i18n properties file. Signed-off-by: Jacob Laursen <jacob-github@vindvejr.dk> * Fix CAT: File does not end with a newline. Signed-off-by: Jacob Laursen <jacob-github@vindvejr.dk> * Update documentation with scene collections. Signed-off-by: Jacob Laursen <jacob-github@vindvejr.dk> * Fix CAT: File does not end with a newline. Signed-off-by: Jacob Laursen <jacob-github@vindvejr.dk> * Fix formatting. Signed-off-by: Jacob Laursen <jacob-github@vindvejr.dk> * Fix CAT: File does not end with a newline. Signed-off-by: Jacob Laursen <jacob-github@vindvejr.dk> * Split offline tests into separate distinct tests. Signed-off-by: Jacob Laursen <jacob-github@vindvejr.dk> * Increase test coverage for scene/scene collection parsing. Signed-off-by: Jacob Laursen <jacob-github@vindvejr.dk> * Internationalization of dynamic scene/scene collection channels. Signed-off-by: Jacob Laursen <jacob-github@vindvejr.dk> * Rename scene collections to scene groups. Renamed for all user-oriented texts/references to be consistent with now abandoned feature of the PowerView app. Signed-off-by: Jacob Laursen <jacob-github@vindvejr.dk> * Change custom text keys to not collide with framework. Signed-off-by: Jacob Laursen <jacob-github@vindvejr.dk> * Avoid multiple thing updates. Signed-off-by: Jacob Laursen <jacob-github@vindvejr.dk> * Add missing label/description texts for secondary channel. Signed-off-by: Jacob Laursen <jacob-github@vindvejr.dk> * Remove unneeded @Nullable annotations. Signed-off-by: Jacob Laursen <jacob-github@vindvejr.dk>
This commit is contained in:
parent
2973f6d890
commit
8c83c27c57
@ -60,12 +60,13 @@ However, the configuration parameters are described below:
|
|||||||
|
|
||||||
### Channels for PowerView Hub
|
### Channels for PowerView Hub
|
||||||
|
|
||||||
Scene channels will be added dynamically to the binding as they are discovered in the hub.
|
Scene and scene group channels will be added dynamically to the binding as they are discovered in the hub.
|
||||||
Each scene channel will have an entry in the hub as shown below, whereby different scenes have different `id` values:
|
Each scene/scene group channel will have an entry in the hub as shown below, whereby different scenes/scene groups
|
||||||
|
have different `id` values:
|
||||||
|
|
||||||
| Channel | Item Type | Description |
|
| Channel | Item Type | Description |
|
||||||
|----------|-----------| ------------|
|
|----------|-----------| ------------|
|
||||||
| id | Switch | Turning this to ON will activate the scene. Scenes are stateless in the PowerView hub; they have no on/off state. Note: include `{autoupdate="false"}` in the item configuration to avoid having to reset it to off after use. |
|
| id | Switch | Turning this to ON will activate the scene/scene group. Scenes/scene groups are stateless in the PowerView hub; they have no on/off state. Note: include `{autoupdate="false"}` in the item configuration to avoid having to reset it to off after use. |
|
||||||
|
|
||||||
### Channels for PowerView Shade
|
### Channels for PowerView Shade
|
||||||
|
|
||||||
|
|||||||
@ -26,6 +26,7 @@ import org.openhab.core.thing.ThingTypeUID;
|
|||||||
*
|
*
|
||||||
* @author Andy Lintner - Initial contribution
|
* @author Andy Lintner - Initial contribution
|
||||||
* @author Andrew Fiddian-Green - Added support for secondary rail positions
|
* @author Andrew Fiddian-Green - Added support for secondary rail positions
|
||||||
|
* @author Jacob Laursen - Add support for scene groups
|
||||||
*/
|
*/
|
||||||
@NonNullByDefault
|
@NonNullByDefault
|
||||||
public class HDPowerViewBindingConstants {
|
public class HDPowerViewBindingConstants {
|
||||||
@ -46,6 +47,7 @@ public class HDPowerViewBindingConstants {
|
|||||||
public static final String CHANNEL_SHADE_SIGNAL_STRENGTH = "signalStrength";
|
public static final String CHANNEL_SHADE_SIGNAL_STRENGTH = "signalStrength";
|
||||||
|
|
||||||
public static final String CHANNELTYPE_SCENE_ACTIVATE = "scene-activate";
|
public static final String CHANNELTYPE_SCENE_ACTIVATE = "scene-activate";
|
||||||
|
public static final String CHANNELTYPE_SCENE_GROUP_ACTIVATE = "scene-group-activate";
|
||||||
|
|
||||||
public static final List<String> NETBIOS_NAMES = Arrays.asList("PDBU-Hub3.0", "PowerView-Hub");
|
public static final List<String> NETBIOS_NAMES = Arrays.asList("PDBU-Hub3.0", "PowerView-Hub");
|
||||||
|
|
||||||
|
|||||||
@ -21,6 +21,8 @@ import org.openhab.binding.hdpowerview.internal.discovery.HDPowerViewShadeDiscov
|
|||||||
import org.openhab.binding.hdpowerview.internal.handler.HDPowerViewHubHandler;
|
import org.openhab.binding.hdpowerview.internal.handler.HDPowerViewHubHandler;
|
||||||
import org.openhab.binding.hdpowerview.internal.handler.HDPowerViewShadeHandler;
|
import org.openhab.binding.hdpowerview.internal.handler.HDPowerViewShadeHandler;
|
||||||
import org.openhab.core.config.discovery.DiscoveryService;
|
import org.openhab.core.config.discovery.DiscoveryService;
|
||||||
|
import org.openhab.core.i18n.LocaleProvider;
|
||||||
|
import org.openhab.core.i18n.TranslationProvider;
|
||||||
import org.openhab.core.io.net.http.HttpClientFactory;
|
import org.openhab.core.io.net.http.HttpClientFactory;
|
||||||
import org.openhab.core.thing.Bridge;
|
import org.openhab.core.thing.Bridge;
|
||||||
import org.openhab.core.thing.Thing;
|
import org.openhab.core.thing.Thing;
|
||||||
@ -28,6 +30,7 @@ import org.openhab.core.thing.ThingTypeUID;
|
|||||||
import org.openhab.core.thing.binding.BaseThingHandlerFactory;
|
import org.openhab.core.thing.binding.BaseThingHandlerFactory;
|
||||||
import org.openhab.core.thing.binding.ThingHandler;
|
import org.openhab.core.thing.binding.ThingHandler;
|
||||||
import org.openhab.core.thing.binding.ThingHandlerFactory;
|
import org.openhab.core.thing.binding.ThingHandlerFactory;
|
||||||
|
import org.osgi.service.component.ComponentContext;
|
||||||
import org.osgi.service.component.annotations.Activate;
|
import org.osgi.service.component.annotations.Activate;
|
||||||
import org.osgi.service.component.annotations.Component;
|
import org.osgi.service.component.annotations.Component;
|
||||||
import org.osgi.service.component.annotations.Reference;
|
import org.osgi.service.component.annotations.Reference;
|
||||||
@ -43,10 +46,16 @@ import org.osgi.service.component.annotations.Reference;
|
|||||||
public class HDPowerViewHandlerFactory extends BaseThingHandlerFactory {
|
public class HDPowerViewHandlerFactory extends BaseThingHandlerFactory {
|
||||||
|
|
||||||
private final HttpClient httpClient;
|
private final HttpClient httpClient;
|
||||||
|
private final HDPowerViewTranslationProvider translationProvider;
|
||||||
|
|
||||||
@Activate
|
@Activate
|
||||||
public HDPowerViewHandlerFactory(@Reference HttpClientFactory httpClientFactory) {
|
public HDPowerViewHandlerFactory(@Reference HttpClientFactory httpClientFactory,
|
||||||
|
final @Reference TranslationProvider i18nProvider, final @Reference LocaleProvider localeProvider,
|
||||||
|
ComponentContext componentContext) {
|
||||||
|
super.activate(componentContext);
|
||||||
this.httpClient = httpClientFactory.getCommonHttpClient();
|
this.httpClient = httpClientFactory.getCommonHttpClient();
|
||||||
|
this.translationProvider = new HDPowerViewTranslationProvider(getBundleContext().getBundle(), i18nProvider,
|
||||||
|
localeProvider);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -59,7 +68,7 @@ public class HDPowerViewHandlerFactory extends BaseThingHandlerFactory {
|
|||||||
ThingTypeUID thingTypeUID = thing.getThingTypeUID();
|
ThingTypeUID thingTypeUID = thing.getThingTypeUID();
|
||||||
|
|
||||||
if (thingTypeUID.equals(HDPowerViewBindingConstants.THING_TYPE_HUB)) {
|
if (thingTypeUID.equals(HDPowerViewBindingConstants.THING_TYPE_HUB)) {
|
||||||
HDPowerViewHubHandler handler = new HDPowerViewHubHandler((Bridge) thing, httpClient);
|
HDPowerViewHubHandler handler = new HDPowerViewHubHandler((Bridge) thing, httpClient, translationProvider);
|
||||||
registerService(new HDPowerViewShadeDiscoveryService(handler));
|
registerService(new HDPowerViewShadeDiscoveryService(handler));
|
||||||
return handler;
|
return handler;
|
||||||
} else if (thingTypeUID.equals(HDPowerViewBindingConstants.THING_TYPE_SHADE)) {
|
} else if (thingTypeUID.equals(HDPowerViewBindingConstants.THING_TYPE_SHADE)) {
|
||||||
|
|||||||
@ -0,0 +1,47 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) 2010-2021 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.hdpowerview.internal;
|
||||||
|
|
||||||
|
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||||
|
import org.eclipse.jdt.annotation.Nullable;
|
||||||
|
import org.openhab.core.i18n.LocaleProvider;
|
||||||
|
import org.openhab.core.i18n.TranslationProvider;
|
||||||
|
import org.osgi.framework.Bundle;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@link HDPowerViewTranslationProvider} provides i18n message lookup
|
||||||
|
*
|
||||||
|
* @author Jacob Laursen - Initial contribution
|
||||||
|
*/
|
||||||
|
@NonNullByDefault
|
||||||
|
public class HDPowerViewTranslationProvider {
|
||||||
|
|
||||||
|
private final Bundle bundle;
|
||||||
|
private final TranslationProvider i18nProvider;
|
||||||
|
private final LocaleProvider localeProvider;
|
||||||
|
|
||||||
|
public HDPowerViewTranslationProvider(Bundle bundle, TranslationProvider i18nProvider,
|
||||||
|
LocaleProvider localeProvider) {
|
||||||
|
this.bundle = bundle;
|
||||||
|
this.i18nProvider = i18nProvider;
|
||||||
|
this.localeProvider = localeProvider;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getText(String key, @Nullable Object... arguments) {
|
||||||
|
String text = i18nProvider.getText(bundle, key, key, localeProvider.getLocale(), arguments);
|
||||||
|
if (text != null) {
|
||||||
|
return text;
|
||||||
|
}
|
||||||
|
return key;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -28,6 +28,7 @@ import org.eclipse.jetty.http.HttpStatus;
|
|||||||
import org.openhab.binding.hdpowerview.internal.api.ShadePosition;
|
import org.openhab.binding.hdpowerview.internal.api.ShadePosition;
|
||||||
import org.openhab.binding.hdpowerview.internal.api.requests.ShadeMove;
|
import org.openhab.binding.hdpowerview.internal.api.requests.ShadeMove;
|
||||||
import org.openhab.binding.hdpowerview.internal.api.requests.ShadeStop;
|
import org.openhab.binding.hdpowerview.internal.api.requests.ShadeStop;
|
||||||
|
import org.openhab.binding.hdpowerview.internal.api.responses.SceneCollections;
|
||||||
import org.openhab.binding.hdpowerview.internal.api.responses.Scenes;
|
import org.openhab.binding.hdpowerview.internal.api.responses.Scenes;
|
||||||
import org.openhab.binding.hdpowerview.internal.api.responses.Shade;
|
import org.openhab.binding.hdpowerview.internal.api.responses.Shade;
|
||||||
import org.openhab.binding.hdpowerview.internal.api.responses.Shades;
|
import org.openhab.binding.hdpowerview.internal.api.responses.Shades;
|
||||||
@ -42,6 +43,7 @@ import com.google.gson.JsonParseException;
|
|||||||
*
|
*
|
||||||
* @author Andy Lintner - Initial contribution
|
* @author Andy Lintner - Initial contribution
|
||||||
* @author Andrew Fiddian-Green - Added support for secondary rail positions
|
* @author Andrew Fiddian-Green - Added support for secondary rail positions
|
||||||
|
* @author Jacob Laursen - Add support for scene groups
|
||||||
*/
|
*/
|
||||||
@NonNullByDefault
|
@NonNullByDefault
|
||||||
public class HDPowerViewWebTargets {
|
public class HDPowerViewWebTargets {
|
||||||
@ -61,6 +63,8 @@ public class HDPowerViewWebTargets {
|
|||||||
private final String shades;
|
private final String shades;
|
||||||
private final String sceneActivate;
|
private final String sceneActivate;
|
||||||
private final String scenes;
|
private final String scenes;
|
||||||
|
private final String sceneCollectionActivate;
|
||||||
|
private final String sceneCollections;
|
||||||
|
|
||||||
private final Gson gson = new Gson();
|
private final Gson gson = new Gson();
|
||||||
private final HttpClient httpClient;
|
private final HttpClient httpClient;
|
||||||
@ -101,6 +105,8 @@ public class HDPowerViewWebTargets {
|
|||||||
shades = base + "shades/";
|
shades = base + "shades/";
|
||||||
sceneActivate = base + "scenes";
|
sceneActivate = base + "scenes";
|
||||||
scenes = base + "scenes/";
|
scenes = base + "scenes/";
|
||||||
|
sceneCollectionActivate = base + "sceneCollections";
|
||||||
|
sceneCollections = base + "sceneCollections/";
|
||||||
this.httpClient = httpClient;
|
this.httpClient = httpClient;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -156,6 +162,33 @@ public class HDPowerViewWebTargets {
|
|||||||
invoke(HttpMethod.GET, sceneActivate, Query.of("sceneId", Integer.toString(sceneId)), null);
|
invoke(HttpMethod.GET, sceneActivate, Query.of("sceneId", Integer.toString(sceneId)), null);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fetches a JSON package that describes all scene collections in the hub, and wraps it in
|
||||||
|
* a SceneCollections class instance
|
||||||
|
*
|
||||||
|
* @return SceneCollections class instance
|
||||||
|
* @throws JsonParseException if there is a JSON parsing error
|
||||||
|
* @throws HubProcessingException if there is any processing error
|
||||||
|
* @throws HubMaintenanceException if the hub is down for maintenance
|
||||||
|
*/
|
||||||
|
public @Nullable SceneCollections getSceneCollections()
|
||||||
|
throws JsonParseException, HubProcessingException, HubMaintenanceException {
|
||||||
|
String json = invoke(HttpMethod.GET, sceneCollections, null, null);
|
||||||
|
return gson.fromJson(json, SceneCollections.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Instructs the hub to execute a specific scene collection
|
||||||
|
*
|
||||||
|
* @param sceneCollectionId id of the scene collection to be executed
|
||||||
|
* @throws HubProcessingException if there is any processing error
|
||||||
|
* @throws HubMaintenanceException if the hub is down for maintenance
|
||||||
|
*/
|
||||||
|
public void activateSceneCollection(int sceneCollectionId) throws HubProcessingException, HubMaintenanceException {
|
||||||
|
invoke(HttpMethod.GET, sceneCollectionActivate,
|
||||||
|
Query.of("sceneCollectionId", Integer.toString(sceneCollectionId)), null);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Invoke a call on the hub server to retrieve information or send a command
|
* Invoke a call on the hub server to retrieve information or send a command
|
||||||
*
|
*
|
||||||
|
|||||||
@ -0,0 +1,52 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) 2010-2021 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.hdpowerview.internal.api.responses;
|
||||||
|
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
|
import java.util.Base64;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||||
|
import org.eclipse.jdt.annotation.Nullable;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* State of all Scenes in an HD PowerView hub
|
||||||
|
*
|
||||||
|
* @author Jacob Laursen - Initial contribution
|
||||||
|
*/
|
||||||
|
@NonNullByDefault
|
||||||
|
public class SceneCollections {
|
||||||
|
|
||||||
|
public @Nullable List<SceneCollection> sceneCollectionData;
|
||||||
|
public @Nullable List<Integer> sceneCollectionIds;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* the following SuppressWarnings annotation is because the Eclipse compiler
|
||||||
|
* does NOT expect a NonNullByDefault annotation on the inner class, since it is
|
||||||
|
* implicitly inherited from the outer class, whereas the Maven compiler always
|
||||||
|
* requires an explicit NonNullByDefault annotation on all classes
|
||||||
|
*/
|
||||||
|
@SuppressWarnings("null")
|
||||||
|
@NonNullByDefault
|
||||||
|
public static class SceneCollection {
|
||||||
|
public int id;
|
||||||
|
public @Nullable String name;
|
||||||
|
public int order;
|
||||||
|
public int colorId;
|
||||||
|
public int iconId;
|
||||||
|
|
||||||
|
public String getName() {
|
||||||
|
return new String(Base64.getDecoder().decode(name), StandardCharsets.UTF_8);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -12,6 +12,7 @@
|
|||||||
*/
|
*/
|
||||||
package org.openhab.binding.hdpowerview.internal.api.responses;
|
package org.openhab.binding.hdpowerview.internal.api.responses;
|
||||||
|
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
import java.util.Base64;
|
import java.util.Base64;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
@ -46,7 +47,7 @@ public class Scenes {
|
|||||||
public int iconId;
|
public int iconId;
|
||||||
|
|
||||||
public String getName() {
|
public String getName() {
|
||||||
return new String(Base64.getDecoder().decode(name));
|
return new String(Base64.getDecoder().decode(name), StandardCharsets.UTF_8);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -26,9 +26,12 @@ import org.eclipse.jdt.annotation.NonNullByDefault;
|
|||||||
import org.eclipse.jdt.annotation.Nullable;
|
import org.eclipse.jdt.annotation.Nullable;
|
||||||
import org.eclipse.jetty.client.HttpClient;
|
import org.eclipse.jetty.client.HttpClient;
|
||||||
import org.openhab.binding.hdpowerview.internal.HDPowerViewBindingConstants;
|
import org.openhab.binding.hdpowerview.internal.HDPowerViewBindingConstants;
|
||||||
|
import org.openhab.binding.hdpowerview.internal.HDPowerViewTranslationProvider;
|
||||||
import org.openhab.binding.hdpowerview.internal.HDPowerViewWebTargets;
|
import org.openhab.binding.hdpowerview.internal.HDPowerViewWebTargets;
|
||||||
import org.openhab.binding.hdpowerview.internal.HubMaintenanceException;
|
import org.openhab.binding.hdpowerview.internal.HubMaintenanceException;
|
||||||
import org.openhab.binding.hdpowerview.internal.HubProcessingException;
|
import org.openhab.binding.hdpowerview.internal.HubProcessingException;
|
||||||
|
import org.openhab.binding.hdpowerview.internal.api.responses.SceneCollections;
|
||||||
|
import org.openhab.binding.hdpowerview.internal.api.responses.SceneCollections.SceneCollection;
|
||||||
import org.openhab.binding.hdpowerview.internal.api.responses.Scenes;
|
import org.openhab.binding.hdpowerview.internal.api.responses.Scenes;
|
||||||
import org.openhab.binding.hdpowerview.internal.api.responses.Scenes.Scene;
|
import org.openhab.binding.hdpowerview.internal.api.responses.Scenes.Scene;
|
||||||
import org.openhab.binding.hdpowerview.internal.api.responses.Shades;
|
import org.openhab.binding.hdpowerview.internal.api.responses.Shades;
|
||||||
@ -59,12 +62,14 @@ import com.google.gson.JsonParseException;
|
|||||||
*
|
*
|
||||||
* @author Andy Lintner - Initial contribution
|
* @author Andy Lintner - Initial contribution
|
||||||
* @author Andrew Fiddian-Green - Added support for secondary rail positions
|
* @author Andrew Fiddian-Green - Added support for secondary rail positions
|
||||||
|
* @author Jacob Laursen - Add support for scene groups
|
||||||
*/
|
*/
|
||||||
@NonNullByDefault
|
@NonNullByDefault
|
||||||
public class HDPowerViewHubHandler extends BaseBridgeHandler {
|
public class HDPowerViewHubHandler extends BaseBridgeHandler {
|
||||||
|
|
||||||
private final Logger logger = LoggerFactory.getLogger(HDPowerViewHubHandler.class);
|
private final Logger logger = LoggerFactory.getLogger(HDPowerViewHubHandler.class);
|
||||||
private final HttpClient httpClient;
|
private final HttpClient httpClient;
|
||||||
|
private final HDPowerViewTranslationProvider translationProvider;
|
||||||
|
|
||||||
private long refreshInterval;
|
private long refreshInterval;
|
||||||
private long hardRefreshPositionInterval;
|
private long hardRefreshPositionInterval;
|
||||||
@ -78,9 +83,14 @@ public class HDPowerViewHubHandler extends BaseBridgeHandler {
|
|||||||
private final ChannelTypeUID sceneChannelTypeUID = new ChannelTypeUID(HDPowerViewBindingConstants.BINDING_ID,
|
private final ChannelTypeUID sceneChannelTypeUID = new ChannelTypeUID(HDPowerViewBindingConstants.BINDING_ID,
|
||||||
HDPowerViewBindingConstants.CHANNELTYPE_SCENE_ACTIVATE);
|
HDPowerViewBindingConstants.CHANNELTYPE_SCENE_ACTIVATE);
|
||||||
|
|
||||||
public HDPowerViewHubHandler(Bridge bridge, HttpClient httpClient) {
|
private final ChannelTypeUID sceneCollectionChannelTypeUID = new ChannelTypeUID(
|
||||||
|
HDPowerViewBindingConstants.BINDING_ID, HDPowerViewBindingConstants.CHANNELTYPE_SCENE_GROUP_ACTIVATE);
|
||||||
|
|
||||||
|
public HDPowerViewHubHandler(Bridge bridge, HttpClient httpClient,
|
||||||
|
HDPowerViewTranslationProvider translationProvider) {
|
||||||
super(bridge);
|
super(bridge);
|
||||||
this.httpClient = httpClient;
|
this.httpClient = httpClient;
|
||||||
|
this.translationProvider = translationProvider;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -90,21 +100,30 @@ public class HDPowerViewHubHandler extends BaseBridgeHandler {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!OnOffType.ON.equals(command)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
Channel channel = getThing().getChannel(channelUID.getId());
|
Channel channel = getThing().getChannel(channelUID.getId());
|
||||||
if (channel != null && sceneChannelTypeUID.equals(channel.getChannelTypeUID())) {
|
if (channel == null) {
|
||||||
if (OnOffType.ON.equals(command)) {
|
return;
|
||||||
try {
|
}
|
||||||
HDPowerViewWebTargets webTargets = this.webTargets;
|
|
||||||
if (webTargets == null) {
|
try {
|
||||||
throw new ProcessingException("Web targets not initialized");
|
HDPowerViewWebTargets webTargets = this.webTargets;
|
||||||
}
|
if (webTargets == null) {
|
||||||
webTargets.activateScene(Integer.parseInt(channelUID.getId()));
|
throw new ProcessingException("Web targets not initialized");
|
||||||
} catch (HubMaintenanceException e) {
|
|
||||||
// exceptions are logged in HDPowerViewWebTargets
|
|
||||||
} catch (NumberFormatException | HubProcessingException e) {
|
|
||||||
logger.debug("Unexpected error {}", e.getMessage());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
int id = Integer.parseInt(channelUID.getId());
|
||||||
|
if (sceneChannelTypeUID.equals(channel.getChannelTypeUID())) {
|
||||||
|
webTargets.activateScene(id);
|
||||||
|
} else if (sceneCollectionChannelTypeUID.equals(channel.getChannelTypeUID())) {
|
||||||
|
webTargets.activateSceneCollection(id);
|
||||||
|
}
|
||||||
|
} catch (HubMaintenanceException e) {
|
||||||
|
// exceptions are logged in HDPowerViewWebTargets
|
||||||
|
} catch (NumberFormatException | HubProcessingException e) {
|
||||||
|
logger.debug("Unexpected error {}", e.getMessage());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -115,7 +134,8 @@ public class HDPowerViewHubHandler extends BaseBridgeHandler {
|
|||||||
String host = config.host;
|
String host = config.host;
|
||||||
|
|
||||||
if (host == null || host.isEmpty()) {
|
if (host == null || host.isEmpty()) {
|
||||||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, "Host address must be set");
|
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR,
|
||||||
|
"@text/offline.conf-error-no-host-address");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -196,6 +216,7 @@ public class HDPowerViewHubHandler extends BaseBridgeHandler {
|
|||||||
logger.debug("Polling for state");
|
logger.debug("Polling for state");
|
||||||
pollShades();
|
pollShades();
|
||||||
pollScenes();
|
pollScenes();
|
||||||
|
pollSceneCollections();
|
||||||
} catch (JsonParseException e) {
|
} catch (JsonParseException e) {
|
||||||
logger.warn("Bridge returned a bad JSON response: {}", e.getMessage());
|
logger.warn("Bridge returned a bad JSON response: {}", e.getMessage());
|
||||||
} catch (HubProcessingException e) {
|
} catch (HubProcessingException e) {
|
||||||
@ -266,7 +287,9 @@ public class HDPowerViewHubHandler extends BaseBridgeHandler {
|
|||||||
}
|
}
|
||||||
logger.debug("Received data for {} scenes", sceneData.size());
|
logger.debug("Received data for {} scenes", sceneData.size());
|
||||||
|
|
||||||
Map<String, Channel> idChannelMap = getIdChannelMap();
|
Map<String, Channel> idChannelMap = getIdSceneChannelMap();
|
||||||
|
List<Channel> allChannels = new ArrayList<>(getThing().getChannels());
|
||||||
|
boolean isChannelListChanged = false;
|
||||||
for (Scene scene : sceneData) {
|
for (Scene scene : sceneData) {
|
||||||
// remove existing scene channel from the map
|
// remove existing scene channel from the map
|
||||||
String sceneId = Integer.toString(scene.id);
|
String sceneId = Integer.toString(scene.id);
|
||||||
@ -276,9 +299,12 @@ public class HDPowerViewHubHandler extends BaseBridgeHandler {
|
|||||||
} else {
|
} else {
|
||||||
// create a new scene channel
|
// create a new scene channel
|
||||||
ChannelUID channelUID = new ChannelUID(getThing().getUID(), sceneId);
|
ChannelUID channelUID = new ChannelUID(getThing().getUID(), sceneId);
|
||||||
|
String description = translationProvider.getText("dynamic-channel.scene-activate.description",
|
||||||
|
scene.getName());
|
||||||
Channel channel = ChannelBuilder.create(channelUID, "Switch").withType(sceneChannelTypeUID)
|
Channel channel = ChannelBuilder.create(channelUID, "Switch").withType(sceneChannelTypeUID)
|
||||||
.withLabel(scene.getName()).withDescription("Activates the scene " + scene.getName()).build();
|
.withLabel(scene.getName()).withDescription(description).build();
|
||||||
updateThing(editThing().withChannel(channel).build());
|
allChannels.add(channel);
|
||||||
|
isChannelListChanged = true;
|
||||||
logger.debug("Creating new channel for scene '{}'", sceneId);
|
logger.debug("Creating new channel for scene '{}'", sceneId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -286,8 +312,62 @@ public class HDPowerViewHubHandler extends BaseBridgeHandler {
|
|||||||
// remove any previously created channels that no longer exist
|
// remove any previously created channels that no longer exist
|
||||||
if (!idChannelMap.isEmpty()) {
|
if (!idChannelMap.isEmpty()) {
|
||||||
logger.debug("Removing {} orphan scene channels", idChannelMap.size());
|
logger.debug("Removing {} orphan scene channels", idChannelMap.size());
|
||||||
List<Channel> allChannels = new ArrayList<>(getThing().getChannels());
|
|
||||||
allChannels.removeAll(idChannelMap.values());
|
allChannels.removeAll(idChannelMap.values());
|
||||||
|
isChannelListChanged = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isChannelListChanged) {
|
||||||
|
updateThing(editThing().withChannels(allChannels).build());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void pollSceneCollections() throws JsonParseException, HubProcessingException, HubMaintenanceException {
|
||||||
|
HDPowerViewWebTargets webTargets = this.webTargets;
|
||||||
|
if (webTargets == null) {
|
||||||
|
throw new ProcessingException("Web targets not initialized");
|
||||||
|
}
|
||||||
|
|
||||||
|
SceneCollections sceneCollections = webTargets.getSceneCollections();
|
||||||
|
if (sceneCollections == null) {
|
||||||
|
throw new JsonParseException("Missing 'sceneCollections' element");
|
||||||
|
}
|
||||||
|
|
||||||
|
List<SceneCollection> sceneCollectionData = sceneCollections.sceneCollectionData;
|
||||||
|
if (sceneCollectionData == null) {
|
||||||
|
throw new JsonParseException("Missing 'sceneCollections.sceneCollectionData' element");
|
||||||
|
}
|
||||||
|
logger.debug("Received data for {} sceneCollections", sceneCollectionData.size());
|
||||||
|
|
||||||
|
Map<String, Channel> idChannelMap = getIdSceneCollectionChannelMap();
|
||||||
|
List<Channel> allChannels = new ArrayList<>(getThing().getChannels());
|
||||||
|
boolean isChannelListChanged = false;
|
||||||
|
for (SceneCollection sceneCollection : sceneCollectionData) {
|
||||||
|
// remove existing scene collection channel from the map
|
||||||
|
String sceneCollectionId = Integer.toString(sceneCollection.id);
|
||||||
|
if (idChannelMap.containsKey(sceneCollectionId)) {
|
||||||
|
idChannelMap.remove(sceneCollectionId);
|
||||||
|
logger.debug("Keeping channel for existing scene collection '{}'", sceneCollectionId);
|
||||||
|
} else {
|
||||||
|
// create a new scene collection channel
|
||||||
|
ChannelUID channelUID = new ChannelUID(getThing().getUID(), sceneCollectionId);
|
||||||
|
String description = translationProvider.getText("dynamic-channel.scene-group-activate.description",
|
||||||
|
sceneCollection.getName());
|
||||||
|
Channel channel = ChannelBuilder.create(channelUID, "Switch").withType(sceneCollectionChannelTypeUID)
|
||||||
|
.withLabel(sceneCollection.getName()).withDescription(description).build();
|
||||||
|
allChannels.add(channel);
|
||||||
|
isChannelListChanged = true;
|
||||||
|
logger.debug("Creating new channel for scene collection '{}'", sceneCollectionId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// remove any previously created channels that no longer exist
|
||||||
|
if (!idChannelMap.isEmpty()) {
|
||||||
|
logger.debug("Removing {} orphan scene collection channels", idChannelMap.size());
|
||||||
|
allChannels.removeAll(idChannelMap.values());
|
||||||
|
isChannelListChanged = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isChannelListChanged) {
|
||||||
updateThing(editThing().withChannels(allChannels).build());
|
updateThing(editThing().withChannels(allChannels).build());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -313,7 +393,7 @@ public class HDPowerViewHubHandler extends BaseBridgeHandler {
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
private Map<String, Channel> getIdChannelMap() {
|
private Map<String, Channel> getIdSceneChannelMap() {
|
||||||
Map<String, Channel> ret = new HashMap<>();
|
Map<String, Channel> ret = new HashMap<>();
|
||||||
for (Channel channel : getThing().getChannels()) {
|
for (Channel channel : getThing().getChannels()) {
|
||||||
if (sceneChannelTypeUID.equals(channel.getChannelTypeUID())) {
|
if (sceneChannelTypeUID.equals(channel.getChannelTypeUID())) {
|
||||||
@ -323,6 +403,16 @@ public class HDPowerViewHubHandler extends BaseBridgeHandler {
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private Map<String, Channel> getIdSceneCollectionChannelMap() {
|
||||||
|
Map<String, Channel> ret = new HashMap<>();
|
||||||
|
for (Channel channel : getThing().getChannels()) {
|
||||||
|
if (sceneCollectionChannelTypeUID.equals(channel.getChannelTypeUID())) {
|
||||||
|
ret.put(channel.getUID().getId(), channel);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
private void requestRefreshShadePositions() {
|
private void requestRefreshShadePositions() {
|
||||||
Map<Thing, String> thingIdMap = getThingIdMap();
|
Map<Thing, String> thingIdMap = getThingIdMap();
|
||||||
for (Entry<Thing, String> item : thingIdMap.entrySet()) {
|
for (Entry<Thing, String> item : thingIdMap.entrySet()) {
|
||||||
|
|||||||
@ -0,0 +1,44 @@
|
|||||||
|
# binding
|
||||||
|
|
||||||
|
binding.hdpowerview.name = Hunter Douglas PowerView Binding
|
||||||
|
binding.hdpowerview.description = The Hunter Douglas PowerView binding provides access to the Hunter Douglas line of PowerView shades.
|
||||||
|
|
||||||
|
# thing types
|
||||||
|
|
||||||
|
thing-type.hdpowerview.hub.label = PowerView Hub
|
||||||
|
thing-type.hdpowerview.hub.description = Hunter Douglas (Luxaflex) PowerView Hub
|
||||||
|
thing-type.hdpowerview.shade.label = PowerView Shade
|
||||||
|
thing-type.hdpowerview.shade.description = Hunter Douglas (Luxaflex) PowerView Shade
|
||||||
|
thing-type.hdpowerview.shade.channel.secondary.label = Secondary Position
|
||||||
|
thing-type.hdpowerview.shade.channel.secondary.description = The secondary vertical position (on top-down/bottom-up shades)
|
||||||
|
|
||||||
|
# thing types config
|
||||||
|
|
||||||
|
thing-type.config.hdpowerview.hub.hardRefresh.label = Hard Position Refresh Interval
|
||||||
|
thing-type.config.hdpowerview.hub.hardRefresh.description = The number of minutes between hard refreshes of positions from the PowerView Hub (or 0 to disable)
|
||||||
|
thing-type.config.hdpowerview.hub.hardRefreshBatteryLevel.label = Hard Battery Level Refresh Interval
|
||||||
|
thing-type.config.hdpowerview.hub.hardRefreshBatteryLevel.description = The number of hours between hard refreshes of battery levels from the PowerView Hub (or 0 to disable, default is weekly)
|
||||||
|
thing-type.config.hdpowerview.hub.host.label = Host
|
||||||
|
thing-type.config.hdpowerview.hub.host.description = The Host address of the PowerView Hub
|
||||||
|
thing-type.config.hdpowerview.hub.refresh.label = Refresh Interval
|
||||||
|
thing-type.config.hdpowerview.hub.refresh.description = The number of milliseconds between fetches of the PowerView Hub shade state
|
||||||
|
thing-type.config.hdpowerview.shade.id.label = ID
|
||||||
|
thing-type.config.hdpowerview.shade.id.description = The numeric ID of the PowerView Shade in the Hub
|
||||||
|
|
||||||
|
# channel types
|
||||||
|
|
||||||
|
channel-type.hdpowerview.battery-voltage.label = Battery Voltage
|
||||||
|
channel-type.hdpowerview.battery-voltage.description = Battery voltage reported by the shade
|
||||||
|
channel-type.hdpowerview.shade-position.label = Position
|
||||||
|
channel-type.hdpowerview.shade-position.description = The vertical position of the shade
|
||||||
|
channel-type.hdpowerview.shade-vane.label = Vane
|
||||||
|
channel-type.hdpowerview.shade-vane.description = The opening of the slats in the shade
|
||||||
|
|
||||||
|
# thing status descriptions
|
||||||
|
|
||||||
|
offline.conf-error-no-host-address = Host address must be set
|
||||||
|
|
||||||
|
# dynamic channels
|
||||||
|
|
||||||
|
dynamic-channel.scene-activate.description = Activates the scene ''{0}''
|
||||||
|
dynamic-channel.scene-group-activate.description = Activates the scene group ''{0}''
|
||||||
@ -89,7 +89,11 @@
|
|||||||
<channel-type id="scene-activate">
|
<channel-type id="scene-activate">
|
||||||
<item-type>Switch</item-type>
|
<item-type>Switch</item-type>
|
||||||
<label>Activate</label>
|
<label>Activate</label>
|
||||||
<description>Activates the scene</description>
|
</channel-type>
|
||||||
|
|
||||||
|
<channel-type id="scene-group-activate">
|
||||||
|
<item-type>Switch</item-type>
|
||||||
|
<label>Activate</label>
|
||||||
</channel-type>
|
</channel-type>
|
||||||
|
|
||||||
<channel-type id="battery-voltage" advanced="true">
|
<channel-type id="battery-voltage" advanced="true">
|
||||||
|
|||||||
@ -16,11 +16,12 @@ import static org.junit.jupiter.api.Assertions.*;
|
|||||||
import static org.openhab.binding.hdpowerview.internal.api.ActuatorClass.*;
|
import static org.openhab.binding.hdpowerview.internal.api.ActuatorClass.*;
|
||||||
import static org.openhab.binding.hdpowerview.internal.api.CoordinateSystem.*;
|
import static org.openhab.binding.hdpowerview.internal.api.CoordinateSystem.*;
|
||||||
|
|
||||||
import java.io.BufferedReader;
|
|
||||||
import java.io.FileReader;
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.nio.file.Files;
|
||||||
|
import java.nio.file.Paths;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.regex.Pattern;
|
import java.util.regex.Pattern;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||||
import org.eclipse.jdt.annotation.Nullable;
|
import org.eclipse.jdt.annotation.Nullable;
|
||||||
@ -31,6 +32,8 @@ import org.openhab.binding.hdpowerview.internal.HubMaintenanceException;
|
|||||||
import org.openhab.binding.hdpowerview.internal.HubProcessingException;
|
import org.openhab.binding.hdpowerview.internal.HubProcessingException;
|
||||||
import org.openhab.binding.hdpowerview.internal.api.CoordinateSystem;
|
import org.openhab.binding.hdpowerview.internal.api.CoordinateSystem;
|
||||||
import org.openhab.binding.hdpowerview.internal.api.ShadePosition;
|
import org.openhab.binding.hdpowerview.internal.api.ShadePosition;
|
||||||
|
import org.openhab.binding.hdpowerview.internal.api.responses.SceneCollections;
|
||||||
|
import org.openhab.binding.hdpowerview.internal.api.responses.SceneCollections.SceneCollection;
|
||||||
import org.openhab.binding.hdpowerview.internal.api.responses.Scenes;
|
import org.openhab.binding.hdpowerview.internal.api.responses.Scenes;
|
||||||
import org.openhab.binding.hdpowerview.internal.api.responses.Scenes.Scene;
|
import org.openhab.binding.hdpowerview.internal.api.responses.Scenes.Scene;
|
||||||
import org.openhab.binding.hdpowerview.internal.api.responses.Shade;
|
import org.openhab.binding.hdpowerview.internal.api.responses.Shade;
|
||||||
@ -47,6 +50,7 @@ import com.google.gson.JsonParseException;
|
|||||||
* Unit tests for HD PowerView binding
|
* Unit tests for HD PowerView binding
|
||||||
*
|
*
|
||||||
* @author Andrew Fiddian-Green - Initial contribution
|
* @author Andrew Fiddian-Green - Initial contribution
|
||||||
|
* @author Jacob Laursen - Add support for scene groups
|
||||||
*/
|
*/
|
||||||
@NonNullByDefault
|
@NonNullByDefault
|
||||||
public class HDPowerViewJUnitTests {
|
public class HDPowerViewJUnitTests {
|
||||||
@ -58,14 +62,9 @@ public class HDPowerViewJUnitTests {
|
|||||||
* load a test JSON string from a file
|
* load a test JSON string from a file
|
||||||
*/
|
*/
|
||||||
private String loadJson(String fileName) {
|
private String loadJson(String fileName) {
|
||||||
try (FileReader file = new FileReader(String.format("src/test/resources/%s.json", fileName));
|
try {
|
||||||
BufferedReader reader = new BufferedReader(file)) {
|
return Files.readAllLines(Paths.get(String.format("src/test/resources/%s.json", fileName))).stream()
|
||||||
StringBuilder builder = new StringBuilder();
|
.collect(Collectors.joining());
|
||||||
String line;
|
|
||||||
while ((line = reader.readLine()) != null) {
|
|
||||||
builder.append(line).append("\n");
|
|
||||||
}
|
|
||||||
return builder.toString();
|
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
fail(e.getMessage());
|
fail(e.getMessage());
|
||||||
}
|
}
|
||||||
@ -287,80 +286,107 @@ public class HDPowerViewJUnitTests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Run a series of OFFLINE tests on the JSON parsing machinery
|
* Test generic JSON shades response
|
||||||
*/
|
*/
|
||||||
@Test
|
@Test
|
||||||
public void testOfflineJsonParsing() {
|
public void shadeResponseIsParsedCorrectly() throws JsonParseException {
|
||||||
final Gson gson = new Gson();
|
final Gson gson = new Gson();
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
Shades shades;
|
Shades shades;
|
||||||
// test generic JSON shades response
|
String json = loadJson("shades");
|
||||||
try {
|
assertNotEquals("", json);
|
||||||
@Nullable
|
shades = gson.fromJson(json, Shades.class);
|
||||||
String json = loadJson("shades");
|
assertNotNull(shades);
|
||||||
assertNotNull(json);
|
}
|
||||||
assertNotEquals("", json);
|
|
||||||
shades = gson.fromJson(json, Shades.class);
|
|
||||||
assertNotNull(shades);
|
|
||||||
} catch (JsonParseException e) {
|
|
||||||
fail(e.getMessage());
|
|
||||||
}
|
|
||||||
|
|
||||||
// test generic JSON scenes response
|
/**
|
||||||
try {
|
* Test generic JSON scene response
|
||||||
@Nullable
|
*/
|
||||||
String json = loadJson("scenes");
|
@Test
|
||||||
assertNotNull(json);
|
public void sceneResponseIsParsedCorrectly() throws JsonParseException {
|
||||||
assertNotEquals("", json);
|
final Gson gson = new Gson();
|
||||||
@Nullable
|
String json = loadJson("scenes");
|
||||||
Scenes scenes = gson.fromJson(json, Scenes.class);
|
assertNotEquals("", json);
|
||||||
assertNotNull(scenes);
|
|
||||||
} catch (JsonParseException e) {
|
|
||||||
fail(e.getMessage());
|
|
||||||
}
|
|
||||||
|
|
||||||
// test the JSON parsing for a duette top down bottom up shade
|
@Nullable
|
||||||
try {
|
Scenes scenes = gson.fromJson(json, Scenes.class);
|
||||||
@Nullable
|
assertNotNull(scenes);
|
||||||
ShadeData shadeData = null;
|
|
||||||
String json = loadJson("duette");
|
|
||||||
assertNotNull(json);
|
|
||||||
assertNotEquals("", json);
|
|
||||||
|
|
||||||
shades = gson.fromJson(json, Shades.class);
|
@Nullable
|
||||||
assertNotNull(shades);
|
List<Scene> sceneData = scenes.sceneData;
|
||||||
@Nullable
|
assertNotNull(sceneData);
|
||||||
List<ShadeData> shadesData = shades.shadeData;
|
|
||||||
assertNotNull(shadesData);
|
|
||||||
|
|
||||||
assertEquals(1, shadesData.size());
|
assertEquals(4, sceneData.size());
|
||||||
shadeData = shadesData.get(0);
|
@Nullable
|
||||||
assertNotNull(shadeData);
|
Scene scene = sceneData.get(0);
|
||||||
|
assertEquals("Door Open", scene.getName());
|
||||||
|
assertEquals(18097, scene.id);
|
||||||
|
}
|
||||||
|
|
||||||
assertEquals("Gardin 1", shadeData.getName());
|
/**
|
||||||
assertEquals(63778, shadeData.id);
|
* Test generic JSON scene collection response
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
public void sceneCollectionResponseIsParsedCorrectly() throws JsonParseException {
|
||||||
|
final Gson gson = new Gson();
|
||||||
|
String json = loadJson("sceneCollections");
|
||||||
|
assertNotEquals("", json);
|
||||||
|
|
||||||
ShadePosition shadePos = shadeData.positions;
|
@Nullable
|
||||||
assertNotNull(shadePos);
|
SceneCollections sceneCollections = gson.fromJson(json, SceneCollections.class);
|
||||||
assertEquals(ZERO_IS_CLOSED, shadePos.getCoordinateSystem(PRIMARY_ACTUATOR));
|
assertNotNull(sceneCollections);
|
||||||
|
@Nullable
|
||||||
|
List<SceneCollection> sceneCollectionData = sceneCollections.sceneCollectionData;
|
||||||
|
assertNotNull(sceneCollectionData);
|
||||||
|
|
||||||
State pos = shadePos.getState(PRIMARY_ACTUATOR, ZERO_IS_CLOSED);
|
assertEquals(1, sceneCollectionData.size());
|
||||||
assertEquals(PercentType.class, pos.getClass());
|
@Nullable
|
||||||
assertEquals(59, ((PercentType) pos).intValue());
|
SceneCollection sceneCollection = sceneCollectionData.get(0);
|
||||||
|
assertEquals("Børn op", sceneCollection.getName());
|
||||||
|
assertEquals(27119, sceneCollection.id);
|
||||||
|
}
|
||||||
|
|
||||||
pos = shadePos.getState(SECONDARY_ACTUATOR, ZERO_IS_OPEN);
|
/**
|
||||||
assertEquals(PercentType.class, pos.getClass());
|
* Test the JSON parsing for a duette top down bottom up shade
|
||||||
assertEquals(35, ((PercentType) pos).intValue());
|
*/
|
||||||
|
@Test
|
||||||
|
public void duetteTopDownBottomUpShadeIsParsedCorrectly() throws JsonParseException {
|
||||||
|
final Gson gson = new Gson();
|
||||||
|
String json = loadJson("duette");
|
||||||
|
assertNotEquals("", json);
|
||||||
|
|
||||||
pos = shadePos.getState(PRIMARY_ACTUATOR, VANE_COORDS);
|
@Nullable
|
||||||
assertEquals(UnDefType.class, pos.getClass());
|
Shades shades = gson.fromJson(json, Shades.class);
|
||||||
|
assertNotNull(shades);
|
||||||
|
@Nullable
|
||||||
|
List<ShadeData> shadesData = shades.shadeData;
|
||||||
|
assertNotNull(shadesData);
|
||||||
|
|
||||||
assertEquals(3, shadeData.batteryStatus);
|
assertEquals(1, shadesData.size());
|
||||||
|
@Nullable
|
||||||
|
ShadeData shadeData = shadesData.get(0);
|
||||||
|
assertNotNull(shadeData);
|
||||||
|
|
||||||
assertEquals(4, shadeData.signalStrength);
|
assertEquals("Gardin 1", shadeData.getName());
|
||||||
} catch (JsonParseException e) {
|
assertEquals(63778, shadeData.id);
|
||||||
fail(e.getMessage());
|
|
||||||
}
|
ShadePosition shadePos = shadeData.positions;
|
||||||
|
assertNotNull(shadePos);
|
||||||
|
assertEquals(ZERO_IS_CLOSED, shadePos.getCoordinateSystem(PRIMARY_ACTUATOR));
|
||||||
|
|
||||||
|
State pos = shadePos.getState(PRIMARY_ACTUATOR, ZERO_IS_CLOSED);
|
||||||
|
assertEquals(PercentType.class, pos.getClass());
|
||||||
|
assertEquals(59, ((PercentType) pos).intValue());
|
||||||
|
|
||||||
|
pos = shadePos.getState(SECONDARY_ACTUATOR, ZERO_IS_OPEN);
|
||||||
|
assertEquals(PercentType.class, pos.getClass());
|
||||||
|
assertEquals(35, ((PercentType) pos).intValue());
|
||||||
|
|
||||||
|
pos = shadePos.getState(PRIMARY_ACTUATOR, VANE_COORDS);
|
||||||
|
assertEquals(UnDefType.class, pos.getClass());
|
||||||
|
|
||||||
|
assertEquals(3, shadeData.batteryStatus);
|
||||||
|
|
||||||
|
assertEquals(4, shadeData.signalStrength);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -0,0 +1,15 @@
|
|||||||
|
{
|
||||||
|
"sceneCollectionIds": [
|
||||||
|
27119
|
||||||
|
],
|
||||||
|
"sceneCollectionData": [
|
||||||
|
{
|
||||||
|
"name": "QsO4cm4gb3A=",
|
||||||
|
"colorId": 12,
|
||||||
|
"iconId": 17,
|
||||||
|
"id": 27119,
|
||||||
|
"order": 0,
|
||||||
|
"hkAssist": false
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
Loading…
x
Reference in New Issue
Block a user