mirror of
https://github.com/openhab/openhab-addons.git
synced 2025-01-10 23:22:02 +01:00
[boschshc] Add scenario channel (#15752)
Signed-off-by: Patrick Gell <patgit023@gmail.com>
This commit is contained in:
parent
1eacf67f34
commit
1b466fb319
@ -4,6 +4,7 @@ Binding for the Bosch Smart Home.
|
|||||||
|
|
||||||
- [Bosch Smart Home Binding](#bosch-smart-home-binding)
|
- [Bosch Smart Home Binding](#bosch-smart-home-binding)
|
||||||
- [Supported Things](#supported-things)
|
- [Supported Things](#supported-things)
|
||||||
|
- [Smart Home Controller](#smart-home-controller)
|
||||||
- [In-Wall Switch](#in-wall-switch)
|
- [In-Wall Switch](#in-wall-switch)
|
||||||
- [Compact Smart Plug](#compact-smart-plug)
|
- [Compact Smart Plug](#compact-smart-plug)
|
||||||
- [Twinguard Smoke Detector](#twinguard-smoke-detector)
|
- [Twinguard Smoke Detector](#twinguard-smoke-detector)
|
||||||
@ -27,6 +28,16 @@ Binding for the Bosch Smart Home.
|
|||||||
|
|
||||||
## Supported Things
|
## Supported Things
|
||||||
|
|
||||||
|
### Smart Home Controller
|
||||||
|
The Smart Home Controller is the central hub that allows you to monitor and control your smart home devices from one place.
|
||||||
|
|
||||||
|
**Bridge Type ID**: ``shc``
|
||||||
|
|
||||||
|
| Channel Type ID | Item Type | Writable | Description |
|
||||||
|
|--------------------|-----------|:--------:|-------------------------------------------------------------------------|
|
||||||
|
| scenario-triggered | String | ☐ | Name of the triggered scenario (e.g. by the Universal Switch Flex) |
|
||||||
|
| trigger-scenario | String | ☑ | Name of a scenario to be triggered on the Bosch Smart Home Controller. |
|
||||||
|
|
||||||
### In-Wall Switch
|
### In-Wall Switch
|
||||||
|
|
||||||
A simple light control.
|
A simple light control.
|
||||||
|
@ -51,6 +51,8 @@ public class BoschSHCBindingConstants {
|
|||||||
|
|
||||||
// List of all Channel IDs
|
// List of all Channel IDs
|
||||||
// Auto-generated from thing-types.xml via script, don't modify
|
// Auto-generated from thing-types.xml via script, don't modify
|
||||||
|
public static final String CHANNEL_SCENARIO_TRIGGERED = "scenario-triggered";
|
||||||
|
public static final String CHANNEL_TRIGGER_SCENARIO = "trigger-scenario";
|
||||||
public static final String CHANNEL_POWER_SWITCH = "power-switch";
|
public static final String CHANNEL_POWER_SWITCH = "power-switch";
|
||||||
public static final String CHANNEL_TEMPERATURE = "temperature";
|
public static final String CHANNEL_TEMPERATURE = "temperature";
|
||||||
public static final String CHANNEL_TEMPERATURE_RATING = "temperature-rating";
|
public static final String CHANNEL_TEMPERATURE_RATING = "temperature-rating";
|
||||||
|
@ -34,11 +34,13 @@ import org.eclipse.jetty.client.api.Request;
|
|||||||
import org.eclipse.jetty.client.api.Response;
|
import org.eclipse.jetty.client.api.Response;
|
||||||
import org.eclipse.jetty.http.HttpStatus;
|
import org.eclipse.jetty.http.HttpStatus;
|
||||||
import org.eclipse.jetty.util.ssl.SslContextFactory;
|
import org.eclipse.jetty.util.ssl.SslContextFactory;
|
||||||
|
import org.openhab.binding.boschshc.internal.devices.BoschSHCBindingConstants;
|
||||||
import org.openhab.binding.boschshc.internal.devices.BoschSHCHandler;
|
import org.openhab.binding.boschshc.internal.devices.BoschSHCHandler;
|
||||||
import org.openhab.binding.boschshc.internal.devices.bridge.dto.Device;
|
import org.openhab.binding.boschshc.internal.devices.bridge.dto.Device;
|
||||||
import org.openhab.binding.boschshc.internal.devices.bridge.dto.DeviceServiceData;
|
import org.openhab.binding.boschshc.internal.devices.bridge.dto.DeviceServiceData;
|
||||||
import org.openhab.binding.boschshc.internal.devices.bridge.dto.LongPollResult;
|
import org.openhab.binding.boschshc.internal.devices.bridge.dto.LongPollResult;
|
||||||
import org.openhab.binding.boschshc.internal.devices.bridge.dto.Room;
|
import org.openhab.binding.boschshc.internal.devices.bridge.dto.Room;
|
||||||
|
import org.openhab.binding.boschshc.internal.devices.bridge.dto.Scenario;
|
||||||
import org.openhab.binding.boschshc.internal.discovery.ThingDiscoveryService;
|
import org.openhab.binding.boschshc.internal.discovery.ThingDiscoveryService;
|
||||||
import org.openhab.binding.boschshc.internal.exceptions.BoschSHCException;
|
import org.openhab.binding.boschshc.internal.exceptions.BoschSHCException;
|
||||||
import org.openhab.binding.boschshc.internal.exceptions.LongPollingFailedException;
|
import org.openhab.binding.boschshc.internal.exceptions.LongPollingFailedException;
|
||||||
@ -46,7 +48,9 @@ import org.openhab.binding.boschshc.internal.exceptions.PairingFailedException;
|
|||||||
import org.openhab.binding.boschshc.internal.serialization.GsonUtils;
|
import org.openhab.binding.boschshc.internal.serialization.GsonUtils;
|
||||||
import org.openhab.binding.boschshc.internal.services.dto.BoschSHCServiceState;
|
import org.openhab.binding.boschshc.internal.services.dto.BoschSHCServiceState;
|
||||||
import org.openhab.binding.boschshc.internal.services.dto.JsonRestExceptionResponse;
|
import org.openhab.binding.boschshc.internal.services.dto.JsonRestExceptionResponse;
|
||||||
|
import org.openhab.core.library.types.StringType;
|
||||||
import org.openhab.core.thing.Bridge;
|
import org.openhab.core.thing.Bridge;
|
||||||
|
import org.openhab.core.thing.Channel;
|
||||||
import org.openhab.core.thing.ChannelUID;
|
import org.openhab.core.thing.ChannelUID;
|
||||||
import org.openhab.core.thing.Thing;
|
import org.openhab.core.thing.Thing;
|
||||||
import org.openhab.core.thing.ThingStatus;
|
import org.openhab.core.thing.ThingStatus;
|
||||||
@ -55,6 +59,7 @@ import org.openhab.core.thing.binding.BaseBridgeHandler;
|
|||||||
import org.openhab.core.thing.binding.ThingHandler;
|
import org.openhab.core.thing.binding.ThingHandler;
|
||||||
import org.openhab.core.thing.binding.ThingHandlerService;
|
import org.openhab.core.thing.binding.ThingHandlerService;
|
||||||
import org.openhab.core.types.Command;
|
import org.openhab.core.types.Command;
|
||||||
|
import org.openhab.core.types.RefreshType;
|
||||||
import org.osgi.framework.Bundle;
|
import org.osgi.framework.Bundle;
|
||||||
import org.osgi.framework.FrameworkUtil;
|
import org.osgi.framework.FrameworkUtil;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
@ -99,8 +104,11 @@ public class BridgeHandler extends BaseBridgeHandler {
|
|||||||
*/
|
*/
|
||||||
private @Nullable ThingDiscoveryService thingDiscoveryService;
|
private @Nullable ThingDiscoveryService thingDiscoveryService;
|
||||||
|
|
||||||
|
private final ScenarioHandler scenarioHandler;
|
||||||
|
|
||||||
public BridgeHandler(Bridge bridge) {
|
public BridgeHandler(Bridge bridge) {
|
||||||
super(bridge);
|
super(bridge);
|
||||||
|
scenarioHandler = new ScenarioHandler();
|
||||||
|
|
||||||
this.longPolling = new LongPolling(this.scheduler, this::handleLongPollResult, this::handleLongPollFailure);
|
this.longPolling = new LongPolling(this.scheduler, this::handleLongPollResult, this::handleLongPollFailure);
|
||||||
}
|
}
|
||||||
@ -195,6 +203,11 @@ public class BridgeHandler extends BaseBridgeHandler {
|
|||||||
@Override
|
@Override
|
||||||
public void handleCommand(ChannelUID channelUID, Command command) {
|
public void handleCommand(ChannelUID channelUID, Command command) {
|
||||||
// commands are handled by individual device handlers
|
// commands are handled by individual device handlers
|
||||||
|
BoschHttpClient localHttpClient = httpClient;
|
||||||
|
if (BoschSHCBindingConstants.CHANNEL_TRIGGER_SCENARIO.equals(channelUID.getId())
|
||||||
|
&& !RefreshType.REFRESH.equals(command) && localHttpClient != null) {
|
||||||
|
scenarioHandler.triggerScenario(localHttpClient, command.toString());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -410,8 +423,15 @@ public class BridgeHandler extends BaseBridgeHandler {
|
|||||||
* @param result Results from Long Polling
|
* @param result Results from Long Polling
|
||||||
*/
|
*/
|
||||||
private void handleLongPollResult(LongPollResult result) {
|
private void handleLongPollResult(LongPollResult result) {
|
||||||
for (DeviceServiceData deviceServiceData : result.result) {
|
for (BoschSHCServiceState serviceState : result.result) {
|
||||||
handleDeviceServiceData(deviceServiceData);
|
if (serviceState instanceof DeviceServiceData deviceServiceData) {
|
||||||
|
handleDeviceServiceData(deviceServiceData);
|
||||||
|
} else if (serviceState instanceof Scenario scenario) {
|
||||||
|
final Channel channel = this.getThing().getChannel(BoschSHCBindingConstants.CHANNEL_SCENARIO_TRIGGERED);
|
||||||
|
if (channel != null && isLinked(channel.getUID())) {
|
||||||
|
updateState(channel.getUID(), new StringType(scenario.name));
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -0,0 +1,110 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) 2010-2023 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.boschshc.internal.devices.bridge;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Optional;
|
||||||
|
import java.util.concurrent.ExecutionException;
|
||||||
|
import java.util.concurrent.TimeoutException;
|
||||||
|
|
||||||
|
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||||
|
import org.eclipse.jetty.client.api.ContentResponse;
|
||||||
|
import org.eclipse.jetty.client.api.Request;
|
||||||
|
import org.eclipse.jetty.http.HttpMethod;
|
||||||
|
import org.eclipse.jetty.http.HttpStatus;
|
||||||
|
import org.openhab.binding.boschshc.internal.devices.bridge.dto.Scenario;
|
||||||
|
import org.openhab.binding.boschshc.internal.exceptions.BoschSHCException;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handler for executing a scenario.
|
||||||
|
*
|
||||||
|
* @author Patrick Gell - Initial contribution
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
@NonNullByDefault
|
||||||
|
public class ScenarioHandler {
|
||||||
|
|
||||||
|
private final Logger logger = LoggerFactory.getLogger(getClass());
|
||||||
|
|
||||||
|
protected ScenarioHandler() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public void triggerScenario(final BoschHttpClient httpClient, final String scenarioName) {
|
||||||
|
|
||||||
|
final Scenario[] scenarios;
|
||||||
|
try {
|
||||||
|
scenarios = getAvailableScenarios(httpClient);
|
||||||
|
} catch (BoschSHCException e) {
|
||||||
|
logger.debug("unable to read the available scenarios from Bosch Smart Home Conteroller", e);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
final Optional<Scenario> scenario = Arrays.stream(scenarios).filter(s -> s.name.equals(scenarioName))
|
||||||
|
.findFirst();
|
||||||
|
if (scenario.isPresent()) {
|
||||||
|
sendPOSTRequest(httpClient.getBoschSmartHomeUrl(String.format("scenarios/%s/triggers", scenario.get().id)),
|
||||||
|
httpClient);
|
||||||
|
} else {
|
||||||
|
if (logger.isDebugEnabled()) {
|
||||||
|
logger.debug("Scenario '{}' was not found in the list of available scenarios {}", scenarioName,
|
||||||
|
prettyLogScenarios(scenarios));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private Scenario[] getAvailableScenarios(final BoschHttpClient httpClient) throws BoschSHCException {
|
||||||
|
final Request request = httpClient.createRequest(httpClient.getBoschSmartHomeUrl("scenarios"), HttpMethod.GET);
|
||||||
|
try {
|
||||||
|
return httpClient.sendRequest(request, Scenario[].class, Scenario::isValid, null);
|
||||||
|
} catch (InterruptedException e) {
|
||||||
|
logger.debug("Scenario call was interrupted", e);
|
||||||
|
Thread.currentThread().interrupt();
|
||||||
|
} catch (TimeoutException e) {
|
||||||
|
logger.debug("Scenario call timed out", e);
|
||||||
|
} catch (ExecutionException e) {
|
||||||
|
logger.debug("Exception occurred during scenario call", e);
|
||||||
|
}
|
||||||
|
|
||||||
|
return new Scenario[] {};
|
||||||
|
}
|
||||||
|
|
||||||
|
private void sendPOSTRequest(final String url, final BoschHttpClient httpClient) {
|
||||||
|
try {
|
||||||
|
final Request request = httpClient.createRequest(url, HttpMethod.POST);
|
||||||
|
final ContentResponse response = request.send();
|
||||||
|
if (HttpStatus.ACCEPTED_202 != response.getStatus()) {
|
||||||
|
logger.debug("{} - {} failed with {}: {}", HttpMethod.POST, url, response.getStatus(),
|
||||||
|
response.getContentAsString());
|
||||||
|
}
|
||||||
|
} catch (InterruptedException e) {
|
||||||
|
logger.debug("Scenario call was interrupted", e);
|
||||||
|
Thread.currentThread().interrupt();
|
||||||
|
} catch (TimeoutException e) {
|
||||||
|
logger.debug("Scenario call timed out", e);
|
||||||
|
} catch (ExecutionException e) {
|
||||||
|
logger.debug("Exception occurred during scenario call", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private String prettyLogScenarios(final Scenario[] scenarios) {
|
||||||
|
final StringBuilder builder = new StringBuilder();
|
||||||
|
builder.append("[");
|
||||||
|
for (Scenario scenario : scenarios) {
|
||||||
|
builder.append("\n ");
|
||||||
|
builder.append(scenario);
|
||||||
|
}
|
||||||
|
builder.append("\n]");
|
||||||
|
return builder.toString();
|
||||||
|
}
|
||||||
|
}
|
@ -14,6 +14,8 @@ package org.openhab.binding.boschshc.internal.devices.bridge.dto;
|
|||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
|
||||||
|
import org.openhab.binding.boschshc.internal.services.dto.BoschSHCServiceState;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Response of the Controller for a Long Poll API call.
|
* Response of the Controller for a Long Poll API call.
|
||||||
*
|
*
|
||||||
@ -35,6 +37,6 @@ public class LongPollResult {
|
|||||||
* ],"jsonrpc":"2.0"}
|
* ],"jsonrpc":"2.0"}
|
||||||
*/
|
*/
|
||||||
|
|
||||||
public ArrayList<DeviceServiceData> result;
|
public ArrayList<BoschSHCServiceState> result;
|
||||||
public String jsonrpc;
|
public String jsonrpc;
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,64 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) 2010-2023 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.boschshc.internal.devices.bridge.dto;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
|
||||||
|
import org.openhab.binding.boschshc.internal.services.dto.BoschSHCServiceState;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A scenario as represented by the controller.
|
||||||
|
*
|
||||||
|
* Json example:
|
||||||
|
* {
|
||||||
|
* "@type": "scenarioTriggered",
|
||||||
|
* "name": "My scenario",
|
||||||
|
* "id": "509bd737-eed0-40b7-8caa-e8686a714399",
|
||||||
|
* "lastTimeTriggered": "1693758693032"
|
||||||
|
* }
|
||||||
|
*
|
||||||
|
* @author Patrick Gell - Initial contribution
|
||||||
|
*/
|
||||||
|
public class Scenario extends BoschSHCServiceState {
|
||||||
|
|
||||||
|
public String name;
|
||||||
|
public String id;
|
||||||
|
public String lastTimeTriggered;
|
||||||
|
|
||||||
|
public Scenario() {
|
||||||
|
super("scenarioTriggered");
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Scenario createScenario(final String id, final String name, final String lastTimeTriggered) {
|
||||||
|
final Scenario scenario = new Scenario();
|
||||||
|
|
||||||
|
scenario.id = id;
|
||||||
|
scenario.name = name;
|
||||||
|
scenario.lastTimeTriggered = lastTimeTriggered;
|
||||||
|
return scenario;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Boolean isValid(Scenario[] scenarios) {
|
||||||
|
return Arrays.stream(scenarios).allMatch(scenario -> (scenario.id != null));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
final StringBuilder sb = new StringBuilder("Scenario{");
|
||||||
|
sb.append("name='").append(name).append("'");
|
||||||
|
sb.append(", id='").append(id).append("'");
|
||||||
|
sb.append(", lastTimeTriggered='").append(lastTimeTriggered).append("'");
|
||||||
|
sb.append('}');
|
||||||
|
return sb.toString();
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,66 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) 2010-2023 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.boschshc.internal.serialization;
|
||||||
|
|
||||||
|
import java.lang.reflect.Type;
|
||||||
|
|
||||||
|
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||||
|
import org.eclipse.jdt.annotation.Nullable;
|
||||||
|
import org.openhab.binding.boschshc.internal.devices.bridge.dto.DeviceServiceData;
|
||||||
|
import org.openhab.binding.boschshc.internal.devices.bridge.dto.Scenario;
|
||||||
|
import org.openhab.binding.boschshc.internal.services.dto.BoschSHCServiceState;
|
||||||
|
|
||||||
|
import com.google.gson.JsonDeserializationContext;
|
||||||
|
import com.google.gson.JsonDeserializer;
|
||||||
|
import com.google.gson.JsonElement;
|
||||||
|
import com.google.gson.JsonObject;
|
||||||
|
import com.google.gson.JsonParseException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Utility class for JSON deserialization of device data and triggered scenarios using Google Gson.
|
||||||
|
*
|
||||||
|
* @author Patrick Gell - Initial contribution
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
@NonNullByDefault
|
||||||
|
public class BoschServiceDataDeserializer implements JsonDeserializer<BoschSHCServiceState> {
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
@Override
|
||||||
|
public BoschSHCServiceState deserialize(JsonElement jsonElement, Type type,
|
||||||
|
JsonDeserializationContext jsonDeserializationContext) throws JsonParseException {
|
||||||
|
|
||||||
|
JsonObject jsonObject = jsonElement.getAsJsonObject();
|
||||||
|
JsonElement dataType = jsonObject.get("@type");
|
||||||
|
switch (dataType.getAsString()) {
|
||||||
|
case "DeviceServiceData" -> {
|
||||||
|
var deviceServiceData = new DeviceServiceData();
|
||||||
|
deviceServiceData.deviceId = jsonObject.get("deviceId").getAsString();
|
||||||
|
deviceServiceData.state = jsonObject.get("state");
|
||||||
|
deviceServiceData.id = jsonObject.get("id").getAsString();
|
||||||
|
deviceServiceData.path = jsonObject.get("path").getAsString();
|
||||||
|
return deviceServiceData;
|
||||||
|
}
|
||||||
|
case "scenarioTriggered" -> {
|
||||||
|
var scenario = new Scenario();
|
||||||
|
scenario.id = jsonObject.get("id").getAsString();
|
||||||
|
scenario.name = jsonObject.get("name").getAsString();
|
||||||
|
scenario.lastTimeTriggered = jsonObject.get("lastTimeTriggered").getAsString();
|
||||||
|
return scenario;
|
||||||
|
}
|
||||||
|
default -> {
|
||||||
|
return new BoschSHCServiceState(dataType.getAsString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -13,6 +13,7 @@
|
|||||||
package org.openhab.binding.boschshc.internal.serialization;
|
package org.openhab.binding.boschshc.internal.serialization;
|
||||||
|
|
||||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||||
|
import org.openhab.binding.boschshc.internal.services.dto.BoschSHCServiceState;
|
||||||
|
|
||||||
import com.google.gson.Gson;
|
import com.google.gson.Gson;
|
||||||
import com.google.gson.GsonBuilder;
|
import com.google.gson.GsonBuilder;
|
||||||
@ -35,6 +36,7 @@ public final class GsonUtils {
|
|||||||
* This instance does not serialize or deserialize fields named <code>logger</code>.
|
* This instance does not serialize or deserialize fields named <code>logger</code>.
|
||||||
*/
|
*/
|
||||||
public static final Gson DEFAULT_GSON_INSTANCE = new GsonBuilder()
|
public static final Gson DEFAULT_GSON_INSTANCE = new GsonBuilder()
|
||||||
|
.registerTypeAdapter(BoschSHCServiceState.class, new BoschServiceDataDeserializer())
|
||||||
.addSerializationExclusionStrategy(new LoggerExclusionStrategy())
|
.addSerializationExclusionStrategy(new LoggerExclusionStrategy())
|
||||||
.addDeserializationExclusionStrategy(new LoggerExclusionStrategy()).create();
|
.addDeserializationExclusionStrategy(new LoggerExclusionStrategy()).create();
|
||||||
}
|
}
|
||||||
|
@ -37,7 +37,7 @@ public class BoschSHCServiceState {
|
|||||||
@SerializedName("@type")
|
@SerializedName("@type")
|
||||||
public final String type;
|
public final String type;
|
||||||
|
|
||||||
protected BoschSHCServiceState(String type) {
|
public BoschSHCServiceState(String type) {
|
||||||
this.type = type;
|
this.type = type;
|
||||||
|
|
||||||
if (stateType == null) {
|
if (stateType == null) {
|
||||||
|
@ -105,6 +105,8 @@ channel-type.boschshc.purity-rating.label = Purity Rating
|
|||||||
channel-type.boschshc.purity-rating.description = Rating of the air purity.
|
channel-type.boschshc.purity-rating.description = Rating of the air purity.
|
||||||
channel-type.boschshc.purity.label = Purity
|
channel-type.boschshc.purity.label = Purity
|
||||||
channel-type.boschshc.purity.description = Purity of the air. A higher value indicates a higher pollution.
|
channel-type.boschshc.purity.description = Purity of the air. A higher value indicates a higher pollution.
|
||||||
|
channel-type.boschshc.scenario-triggered.label = Scenario Triggered
|
||||||
|
channel-type.boschshc.scenario-triggered.description = Name of the triggered scenario
|
||||||
channel-type.boschshc.setpoint-temperature.label = Setpoint Temperature
|
channel-type.boschshc.setpoint-temperature.label = Setpoint Temperature
|
||||||
channel-type.boschshc.setpoint-temperature.description = Desired temperature.
|
channel-type.boschshc.setpoint-temperature.description = Desired temperature.
|
||||||
channel-type.boschshc.silent-mode.label = Silent Mode
|
channel-type.boschshc.silent-mode.label = Silent Mode
|
||||||
@ -126,6 +128,8 @@ channel-type.boschshc.temperature-rating.state.option.MEDIUM = Medium Temperatur
|
|||||||
channel-type.boschshc.temperature-rating.state.option.BAD = Bad Temperature
|
channel-type.boschshc.temperature-rating.state.option.BAD = Bad Temperature
|
||||||
channel-type.boschshc.temperature.label = Temperature
|
channel-type.boschshc.temperature.label = Temperature
|
||||||
channel-type.boschshc.temperature.description = Current measured temperature.
|
channel-type.boschshc.temperature.description = Current measured temperature.
|
||||||
|
channel-type.boschshc.trigger-scenario.label = Trigger Scenario
|
||||||
|
channel-type.boschshc.trigger-scenario.description = Name of the scenario to trigger
|
||||||
channel-type.boschshc.valve-tappet-position.label = Valve Tappet Position
|
channel-type.boschshc.valve-tappet-position.label = Valve Tappet Position
|
||||||
channel-type.boschshc.valve-tappet-position.description = Current open ratio (0 to 100).
|
channel-type.boschshc.valve-tappet-position.description = Current open ratio (0 to 100).
|
||||||
|
|
||||||
|
@ -9,6 +9,15 @@
|
|||||||
<label>Smart Home Controller</label>
|
<label>Smart Home Controller</label>
|
||||||
<description>The Bosch Smart Home Bridge representing the Bosch Smart Home Controller.</description>
|
<description>The Bosch Smart Home Bridge representing the Bosch Smart Home Controller.</description>
|
||||||
|
|
||||||
|
<channels>
|
||||||
|
<channel id="scenario-triggered" typeId="scenario-triggered"/>
|
||||||
|
<channel id="trigger-scenario" typeId="trigger-scenario"/>
|
||||||
|
</channels>
|
||||||
|
|
||||||
|
<properties>
|
||||||
|
<property name="thingTypeVersion">1</property>
|
||||||
|
</properties>
|
||||||
|
|
||||||
<config-description-ref uri="thing-type:boschshc:bridge"/>
|
<config-description-ref uri="thing-type:boschshc:bridge"/>
|
||||||
</bridge-type>
|
</bridge-type>
|
||||||
|
|
||||||
@ -520,4 +529,16 @@
|
|||||||
</state>
|
</state>
|
||||||
</channel-type>
|
</channel-type>
|
||||||
|
|
||||||
|
<channel-type id="scenario-triggered">
|
||||||
|
<item-type>String</item-type>
|
||||||
|
<label>Scenario Triggered</label>
|
||||||
|
<description>Name of the triggered scenario</description>
|
||||||
|
<state readOnly="true"/>
|
||||||
|
</channel-type>
|
||||||
|
<channel-type id="trigger-scenario">
|
||||||
|
<item-type>String</item-type>
|
||||||
|
<label>Trigger Scenario</label>
|
||||||
|
<description>Name of the scenario to trigger</description>
|
||||||
|
</channel-type>
|
||||||
|
|
||||||
</thing:thing-descriptions>
|
</thing:thing-descriptions>
|
||||||
|
@ -0,0 +1,15 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="yes" ?>
|
||||||
|
<update:update-descriptions xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
xmlns:update="https://openhab.org/schemas/update-description/v1.0.0"
|
||||||
|
xsi:schemaLocation="https://openhab.org/schemas/update-description/v1.0.0 https://openhab.org/schemas/update-description-1.0.0.xsd">
|
||||||
|
<thing-type uid="boschshc:shc">
|
||||||
|
<instruction-set targetVersion="1">
|
||||||
|
<add-channel id="scenario-triggered">
|
||||||
|
<type>boschshc:scenario-triggered</type>
|
||||||
|
</add-channel>
|
||||||
|
<add-channel id="trigger-scenario">
|
||||||
|
<type>boschshc:trigger-scenario</type>
|
||||||
|
</add-channel>
|
||||||
|
</instruction-set>
|
||||||
|
</thing-type>
|
||||||
|
</update:update-descriptions>
|
@ -48,6 +48,7 @@ import org.mockito.Mock;
|
|||||||
import org.mockito.junit.jupiter.MockitoExtension;
|
import org.mockito.junit.jupiter.MockitoExtension;
|
||||||
import org.openhab.binding.boschshc.internal.devices.bridge.dto.DeviceServiceData;
|
import org.openhab.binding.boschshc.internal.devices.bridge.dto.DeviceServiceData;
|
||||||
import org.openhab.binding.boschshc.internal.devices.bridge.dto.LongPollResult;
|
import org.openhab.binding.boschshc.internal.devices.bridge.dto.LongPollResult;
|
||||||
|
import org.openhab.binding.boschshc.internal.devices.bridge.dto.Scenario;
|
||||||
import org.openhab.binding.boschshc.internal.devices.bridge.dto.SubscribeResult;
|
import org.openhab.binding.boschshc.internal.devices.bridge.dto.SubscribeResult;
|
||||||
import org.openhab.binding.boschshc.internal.exceptions.BoschSHCException;
|
import org.openhab.binding.boschshc.internal.exceptions.BoschSHCException;
|
||||||
import org.openhab.binding.boschshc.internal.exceptions.LongPollingFailedException;
|
import org.openhab.binding.boschshc.internal.exceptions.LongPollingFailedException;
|
||||||
@ -237,7 +238,8 @@ class LongPollingTest {
|
|||||||
verify(longPollHandler).accept(longPollResultCaptor.capture());
|
verify(longPollHandler).accept(longPollResultCaptor.capture());
|
||||||
LongPollResult longPollResult = longPollResultCaptor.getValue();
|
LongPollResult longPollResult = longPollResultCaptor.getValue();
|
||||||
assertEquals(1, longPollResult.result.size());
|
assertEquals(1, longPollResult.result.size());
|
||||||
DeviceServiceData longPollResultItem = longPollResult.result.get(0);
|
assertEquals(longPollResult.result.get(0).getClass(), DeviceServiceData.class);
|
||||||
|
DeviceServiceData longPollResultItem = (DeviceServiceData) longPollResult.result.get(0);
|
||||||
assertEquals("hdm:HomeMaticIP:3014F711A0001916D859A8A9", longPollResultItem.deviceId);
|
assertEquals("hdm:HomeMaticIP:3014F711A0001916D859A8A9", longPollResultItem.deviceId);
|
||||||
assertEquals("/devices/hdm:HomeMaticIP:3014F711A0001916D859A8A9/services/PowerSwitch", longPollResultItem.path);
|
assertEquals("/devices/hdm:HomeMaticIP:3014F711A0001916D859A8A9/services/PowerSwitch", longPollResultItem.path);
|
||||||
assertEquals("PowerSwitch", longPollResultItem.id);
|
assertEquals("PowerSwitch", longPollResultItem.id);
|
||||||
@ -246,6 +248,48 @@ class LongPollingTest {
|
|||||||
assertEquals("ON", stateObject.get("switchState").getAsString());
|
assertEquals("ON", stateObject.get("switchState").getAsString());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void startLongPolling_receiveScenario()
|
||||||
|
throws InterruptedException, TimeoutException, ExecutionException, BoschSHCException {
|
||||||
|
// when(httpClient.getBoschSmartHomeUrl(anyString())).thenCallRealMethod();
|
||||||
|
when(httpClient.getBoschShcUrl(anyString())).thenCallRealMethod();
|
||||||
|
|
||||||
|
Request subscribeRequest = mock(Request.class);
|
||||||
|
when(httpClient.createRequest(anyString(), same(HttpMethod.POST),
|
||||||
|
argThat((JsonRpcRequest r) -> "RE/subscribe".equals(r.method)))).thenReturn(subscribeRequest);
|
||||||
|
SubscribeResult subscribeResult = new SubscribeResult();
|
||||||
|
when(httpClient.sendRequest(any(), same(SubscribeResult.class), any(), any())).thenReturn(subscribeResult);
|
||||||
|
|
||||||
|
Request longPollRequest = mock(Request.class);
|
||||||
|
when(httpClient.createRequest(anyString(), same(HttpMethod.POST),
|
||||||
|
argThat((JsonRpcRequest r) -> "RE/longPoll".equals(r.method)))).thenReturn(longPollRequest);
|
||||||
|
|
||||||
|
fixture.start(httpClient);
|
||||||
|
|
||||||
|
ArgumentCaptor<CompleteListener> completeListener = ArgumentCaptor.forClass(CompleteListener.class);
|
||||||
|
verify(longPollRequest).send(completeListener.capture());
|
||||||
|
|
||||||
|
BufferingResponseListener bufferingResponseListener = (BufferingResponseListener) completeListener.getValue();
|
||||||
|
|
||||||
|
String longPollResultJSON = "{\"result\":[{\"@type\": \"scenarioTriggered\",\"name\": \"My scenario\",\"id\": \"509bd737-eed0-40b7-8caa-e8686a714399\",\"lastTimeTriggered\": \"1693758693032\"}],\"jsonrpc\":\"2.0\"}\n";
|
||||||
|
Response response = mock(Response.class);
|
||||||
|
bufferingResponseListener.onContent(response,
|
||||||
|
ByteBuffer.wrap(longPollResultJSON.getBytes(StandardCharsets.UTF_8)));
|
||||||
|
|
||||||
|
Result result = mock(Result.class);
|
||||||
|
bufferingResponseListener.onComplete(result);
|
||||||
|
|
||||||
|
ArgumentCaptor<LongPollResult> longPollResultCaptor = ArgumentCaptor.forClass(LongPollResult.class);
|
||||||
|
verify(longPollHandler).accept(longPollResultCaptor.capture());
|
||||||
|
LongPollResult longPollResult = longPollResultCaptor.getValue();
|
||||||
|
assertEquals(1, longPollResult.result.size());
|
||||||
|
assertEquals(longPollResult.result.get(0).getClass(), Scenario.class);
|
||||||
|
Scenario longPollResultItem = (Scenario) longPollResult.result.get(0);
|
||||||
|
assertEquals("509bd737-eed0-40b7-8caa-e8686a714399", longPollResultItem.id);
|
||||||
|
assertEquals("My scenario", longPollResultItem.name);
|
||||||
|
assertEquals("1693758693032", longPollResultItem.lastTimeTriggered);
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void startSubscriptionFailure()
|
void startSubscriptionFailure()
|
||||||
throws InterruptedException, TimeoutException, ExecutionException, BoschSHCException {
|
throws InterruptedException, TimeoutException, ExecutionException, BoschSHCException {
|
||||||
|
@ -0,0 +1,172 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) 2010-2023 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.boschshc.internal.devices.bridge;
|
||||||
|
|
||||||
|
import static org.mockito.Mockito.*;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.UUID;
|
||||||
|
import java.util.concurrent.ExecutionException;
|
||||||
|
import java.util.concurrent.TimeoutException;
|
||||||
|
|
||||||
|
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||||
|
import org.eclipse.jetty.client.api.ContentResponse;
|
||||||
|
import org.eclipse.jetty.client.api.Request;
|
||||||
|
import org.eclipse.jetty.http.HttpMethod;
|
||||||
|
import org.eclipse.jetty.http.HttpStatus;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
import org.junit.jupiter.api.extension.ExtendWith;
|
||||||
|
import org.junit.jupiter.params.ParameterizedTest;
|
||||||
|
import org.junit.jupiter.params.provider.MethodSource;
|
||||||
|
import org.mockito.junit.jupiter.MockitoExtension;
|
||||||
|
import org.openhab.binding.boschshc.internal.devices.bridge.dto.Scenario;
|
||||||
|
import org.openhab.binding.boschshc.internal.exceptions.BoschSHCException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Unit tests for {@link ScenarioHandler}.
|
||||||
|
*
|
||||||
|
* @author Patrick Gell - Initial contribution
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
@NonNullByDefault
|
||||||
|
@ExtendWith(MockitoExtension.class)
|
||||||
|
class ScenarioHandlerTest {
|
||||||
|
|
||||||
|
private final Scenario[] existingScenarios = List.of(
|
||||||
|
Scenario.createScenario(UUID.randomUUID().toString(), "Scenario 1",
|
||||||
|
String.valueOf(System.currentTimeMillis())),
|
||||||
|
Scenario.createScenario(UUID.randomUUID().toString(), "Scenario 2",
|
||||||
|
String.valueOf(System.currentTimeMillis()))
|
||||||
|
|
||||||
|
).toArray(Scenario[]::new);
|
||||||
|
|
||||||
|
protected static Exception[] exceptionData() {
|
||||||
|
return List.of(new BoschSHCException(), new InterruptedException(), new TimeoutException(),
|
||||||
|
new ExecutionException(new BoschSHCException())).toArray(Exception[]::new);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected static Exception[] httpExceptionData() {
|
||||||
|
return List
|
||||||
|
.of(new InterruptedException(), new TimeoutException(), new ExecutionException(new BoschSHCException()))
|
||||||
|
.toArray(Exception[]::new);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void triggerScenario_ShouldSendPOST_ToBoschAPI() throws Exception {
|
||||||
|
// GIVEN
|
||||||
|
final var httpClient = mock(BoschHttpClient.class);
|
||||||
|
final var request = mock(Request.class);
|
||||||
|
final var contentResponse = mock(ContentResponse.class);
|
||||||
|
when(httpClient.getBoschSmartHomeUrl(anyString())).thenReturn("http://localhost/smartHome/scenarios")
|
||||||
|
.thenReturn("http://localhost/smartHome/scenarios/1234/triggers");
|
||||||
|
when(httpClient.createRequest(anyString(), any(HttpMethod.class))).thenReturn(request).thenReturn(request);
|
||||||
|
when(httpClient.sendRequest(any(Request.class), any(), any(), any())).thenReturn(existingScenarios);
|
||||||
|
when(request.send()).thenReturn(contentResponse);
|
||||||
|
when(contentResponse.getStatus()).thenReturn(HttpStatus.OK_200);
|
||||||
|
|
||||||
|
final var handler = new ScenarioHandler();
|
||||||
|
|
||||||
|
// WHEN
|
||||||
|
handler.triggerScenario(httpClient, "Scenario 1");
|
||||||
|
|
||||||
|
// THEN
|
||||||
|
verify(httpClient).getBoschSmartHomeUrl("scenarios");
|
||||||
|
verify(request).send();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void triggerScenario_ShouldNoSendPOST_ToScenarioNameDoesNotExist() throws Exception {
|
||||||
|
// GIVEN
|
||||||
|
final var httpClient = mock(BoschHttpClient.class);
|
||||||
|
final var request = mock(Request.class);
|
||||||
|
when(httpClient.getBoschSmartHomeUrl(anyString())).thenReturn("http://localhost/smartHome/scenarios")
|
||||||
|
.thenReturn("http://localhost/smartHome/scenarios/1234/triggers");
|
||||||
|
when(httpClient.createRequest(anyString(), any(HttpMethod.class))).thenReturn(request).thenReturn(request);
|
||||||
|
when(httpClient.sendRequest(any(Request.class), any(), any(), any())).thenReturn(existingScenarios);
|
||||||
|
|
||||||
|
final var handler = new ScenarioHandler();
|
||||||
|
|
||||||
|
// WHEN
|
||||||
|
handler.triggerScenario(httpClient, "not existing Scenario");
|
||||||
|
|
||||||
|
// THEN
|
||||||
|
verify(httpClient).getBoschSmartHomeUrl("scenarios");
|
||||||
|
verify(request, times(0)).send();
|
||||||
|
}
|
||||||
|
|
||||||
|
@ParameterizedTest
|
||||||
|
@MethodSource("exceptionData")
|
||||||
|
void triggerScenario_ShouldNotPanic_IfBoschAPIThrowsException(final Exception exception) throws Exception {
|
||||||
|
// GIVEN
|
||||||
|
final var httpClient = mock(BoschHttpClient.class);
|
||||||
|
final var request = mock(Request.class);
|
||||||
|
when(httpClient.getBoschSmartHomeUrl(anyString())).thenReturn("http://localhost/smartHome/scenarios")
|
||||||
|
.thenReturn("http://localhost/smartHome/scenarios/1234/triggers");
|
||||||
|
when(httpClient.createRequest(anyString(), any(HttpMethod.class))).thenReturn(request);
|
||||||
|
when(httpClient.sendRequest(any(Request.class), any(), any(), any())).thenThrow(exception);
|
||||||
|
|
||||||
|
final var handler = new ScenarioHandler();
|
||||||
|
|
||||||
|
// WHEN
|
||||||
|
handler.triggerScenario(httpClient, "Scenario 1");
|
||||||
|
|
||||||
|
// THEN
|
||||||
|
verify(httpClient).getBoschSmartHomeUrl("scenarios");
|
||||||
|
verify(request, times(0)).send();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void triggerScenario_ShouldNotPanic_IfPOSTIsNotSuccessful() throws Exception {
|
||||||
|
// GIVEN
|
||||||
|
final var httpClient = mock(BoschHttpClient.class);
|
||||||
|
final var request = mock(Request.class);
|
||||||
|
final var contentResponse = mock(ContentResponse.class);
|
||||||
|
when(httpClient.getBoschSmartHomeUrl(anyString())).thenReturn("http://localhost/smartHome/scenarios")
|
||||||
|
.thenReturn("http://localhost/smartHome/scenarios/1234/triggers");
|
||||||
|
when(httpClient.createRequest(anyString(), any(HttpMethod.class))).thenReturn(request).thenReturn(request);
|
||||||
|
when(httpClient.sendRequest(any(Request.class), any(), any(), any())).thenReturn(existingScenarios);
|
||||||
|
when(request.send()).thenReturn(contentResponse);
|
||||||
|
when(contentResponse.getStatus()).thenReturn(HttpStatus.METHOD_NOT_ALLOWED_405);
|
||||||
|
|
||||||
|
final var handler = new ScenarioHandler();
|
||||||
|
|
||||||
|
// WHEN
|
||||||
|
handler.triggerScenario(httpClient, "Scenario 1");
|
||||||
|
|
||||||
|
// THEN
|
||||||
|
verify(httpClient).getBoschSmartHomeUrl("scenarios");
|
||||||
|
verify(request).send();
|
||||||
|
}
|
||||||
|
|
||||||
|
@ParameterizedTest
|
||||||
|
@MethodSource("httpExceptionData")
|
||||||
|
void triggerScenario_ShouldNotPanic_IfPOSTThrowsException(final Exception exception) throws Exception {
|
||||||
|
// GIVEN
|
||||||
|
final var httpClient = mock(BoschHttpClient.class);
|
||||||
|
final var request = mock(Request.class);
|
||||||
|
when(httpClient.getBoschSmartHomeUrl(anyString())).thenReturn("http://localhost/smartHome/scenarios")
|
||||||
|
.thenReturn("http://localhost/smartHome/scenarios/1234/triggers");
|
||||||
|
when(httpClient.createRequest(anyString(), any(HttpMethod.class))).thenReturn(request).thenReturn(request);
|
||||||
|
when(httpClient.sendRequest(any(Request.class), any(), any(), any())).thenReturn(existingScenarios);
|
||||||
|
when(request.send()).thenThrow(exception);
|
||||||
|
|
||||||
|
final var handler = new ScenarioHandler();
|
||||||
|
|
||||||
|
// WHEN
|
||||||
|
handler.triggerScenario(httpClient, "Scenario 1");
|
||||||
|
|
||||||
|
// THEN
|
||||||
|
verify(httpClient).getBoschSmartHomeUrl("scenarios");
|
||||||
|
verify(request).send();
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,71 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) 2010-2023 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.boschshc.internal.serialization;
|
||||||
|
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||||
|
|
||||||
|
import java.util.HashSet;
|
||||||
|
|
||||||
|
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
import org.openhab.binding.boschshc.internal.devices.bridge.dto.DeviceServiceData;
|
||||||
|
import org.openhab.binding.boschshc.internal.devices.bridge.dto.LongPollResult;
|
||||||
|
import org.openhab.binding.boschshc.internal.devices.bridge.dto.Scenario;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Unit tests for {@link BoschServiceDataDeserializer}.
|
||||||
|
*
|
||||||
|
* @author Patrick Gell - Initial contribution
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
@NonNullByDefault
|
||||||
|
class BoschServiceDataDeserializerTest {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void deserializationOfLongPollingResult() {
|
||||||
|
var resultJson = """
|
||||||
|
{
|
||||||
|
"result": [
|
||||||
|
{
|
||||||
|
"@type": "scenarioTriggered",
|
||||||
|
"name": "MyTriggeredScenario",
|
||||||
|
"id": "509bd737-eed0-40b7-8caa-e8686a714399",
|
||||||
|
"lastTimeTriggered": "1689417526720"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path":"/devices/hdm:HomeMaticIP:3014F711A0001916D859A8A9/services/PowerSwitch",
|
||||||
|
"@type":"DeviceServiceData",
|
||||||
|
"id":"PowerSwitch",
|
||||||
|
"state":{
|
||||||
|
"@type":"powerSwitchState",
|
||||||
|
"switchState":"ON"
|
||||||
|
},
|
||||||
|
"deviceId":"hdm:HomeMaticIP:3014F711A0001916D859A8A9"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"jsonrpc": "2.0"
|
||||||
|
}
|
||||||
|
""";
|
||||||
|
|
||||||
|
var longPollResult = GsonUtils.DEFAULT_GSON_INSTANCE.fromJson(resultJson, LongPollResult.class);
|
||||||
|
assertNotNull(longPollResult);
|
||||||
|
assertEquals(2, longPollResult.result.size());
|
||||||
|
|
||||||
|
var resultClasses = new HashSet<>(longPollResult.result.stream().map(e -> e.getClass().getName()).toList());
|
||||||
|
assertEquals(2, resultClasses.size());
|
||||||
|
assertTrue(resultClasses.contains(DeviceServiceData.class.getName()));
|
||||||
|
assertTrue(resultClasses.contains(Scenario.class.getName()));
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user