[nest] Add support for Smart Device Management (SDM) API (#8947)

* [nest] Add support for Smart Device Management (SDM) API

* Reworks WWN implementation so that the thing types have a wwn_ prefix and the classes have a WWN prefix and reside in a 'wwn' package
* Adds an SDM implementation which is also based on: https://github.com/bhigg-code/openhab-addons/tree/2.5.x/bundles/org.openhab.binding.nestdeviceaccess
* Adds unit tests for (de)serialization of the SDM and Pub/Sub API requests and responses
* Updates the binding documentation for the changes and additions

Fixes #8664

Also-by: Brian Higginbotham <brianhigginbothamtx@gmail.com>
Signed-off-by: Wouter Born <github@maindrain.net>

* Fix and improve documentation

Signed-off-by: Wouter Born <github@maindrain.net>

* Always use UTF8 when decoding SDM events

Signed-off-by: Wouter Born <github@maindrain.net>
This commit is contained in:
Wouter Born
2021-06-20 19:59:46 +02:00
committed by GitHub
parent ec7c3a528f
commit 6296eba14c
160 changed files with 7668 additions and 1186 deletions

View File

@@ -10,7 +10,7 @@
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.nest.internal.data;
package org.openhab.binding.nest.internal.wwn.dto;
import java.io.BufferedReader;
import java.io.IOException;
@@ -23,16 +23,16 @@ import java.util.stream.Collectors;
import javax.measure.Unit;
import javax.measure.quantity.Temperature;
import org.openhab.binding.nest.internal.NestUtils;
import org.openhab.binding.nest.internal.wwn.WWNUtils;
import org.openhab.core.library.unit.ImperialUnits;
import org.openhab.core.library.unit.SIUnits;
/**
* Utility class for working with Nest test data in unit tests.
*
* @author Wouter Born - Increase test coverage
* @author Wouter Born - Initial contribution
*/
public final class NestDataUtil {
public final class WWNDataUtil {
public static final String COMPLETE_DATA_FILE_NAME = "top-level-streaming-data.json";
public static final String INCOMPLETE_DATA_FILE_NAME = "top-level-streaming-data-incomplete.json";
@@ -61,20 +61,20 @@ public final class NestDataUtil {
public static final String THERMOSTAT1_DEVICE_ID = "G1jouHN5yl6mXFaQw5iGwXOu-iQr8PMV";
public static final String THERMOSTAT1_WHERE_ID = "z8fK075vJJPPWnXxLx1m3GskRSZQ64iQydB59k-UPsKQrCrjN0yXiw";
private NestDataUtil() {
private WWNDataUtil() {
// Hidden utility class constructor
}
public static Reader openDataReader(String fileName) throws UnsupportedEncodingException {
String packagePath = (NestDataUtil.class.getPackage().getName()).replaceAll("\\.", "/");
String packagePath = (WWNDataUtil.class.getPackage().getName()).replaceAll("\\.", "/");
String filePath = "/" + packagePath + "/" + fileName;
InputStream inputStream = NestDataUtil.class.getClassLoader().getResourceAsStream(filePath);
InputStream inputStream = WWNDataUtil.class.getClassLoader().getResourceAsStream(filePath);
return new InputStreamReader(inputStream, "UTF-8");
}
public static <T> T fromJson(String fileName, Class<T> dataClass) throws IOException {
try (Reader reader = openDataReader(fileName)) {
return NestUtils.fromJson(reader, dataClass);
return WWNUtils.fromJson(reader, dataClass);
}
}

View File

@@ -10,10 +10,10 @@
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.nest.internal.data;
package org.openhab.binding.nest.internal.wwn.dto;
import static org.junit.jupiter.api.Assertions.*;
import static org.openhab.binding.nest.internal.data.NestDataUtil.*;
import static org.openhab.binding.nest.internal.wwn.dto.WWNDataUtil.*;
import java.io.IOException;
import java.text.SimpleDateFormat;
@@ -30,9 +30,9 @@ import org.slf4j.LoggerFactory;
* @author David Bennett - Initial contribution
* @author Wouter Born - Increase test coverage
*/
public class GsonParsingTest {
public class WWNGsonParsingTest {
private final Logger logger = LoggerFactory.getLogger(GsonParsingTest.class);
private final Logger logger = LoggerFactory.getLogger(WWNGsonParsingTest.class);
private static void assertEqualDateTime(String expected, Date actual) {
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'");
@@ -41,7 +41,7 @@ public class GsonParsingTest {
@Test
public void verifyCompleteInput() throws IOException {
TopLevelData topLevel = fromJson("top-level-data.json", TopLevelData.class);
WWNTopLevelData topLevel = fromJson("top-level-data.json", WWNTopLevelData.class);
assertEquals(topLevel.getDevices().getThermostats().size(), 1);
assertNotNull(topLevel.getDevices().getThermostats().get(THERMOSTAT1_DEVICE_ID));
@@ -57,12 +57,12 @@ public class GsonParsingTest {
@Test
public void verifyCompleteStreamingInput() throws IOException {
TopLevelStreamingData topLevelStreamingData = fromJson("top-level-streaming-data.json",
TopLevelStreamingData.class);
WWNTopLevelStreamingData topLevelStreamingData = fromJson("top-level-streaming-data.json",
WWNTopLevelStreamingData.class);
assertEquals("/", topLevelStreamingData.getPath());
TopLevelData data = topLevelStreamingData.getData();
WWNTopLevelData data = topLevelStreamingData.getData();
assertEquals(data.getDevices().getThermostats().size(), 1);
assertNotNull(data.getDevices().getThermostats().get(THERMOSTAT1_DEVICE_ID));
assertEquals(data.getDevices().getCameras().size(), 2);
@@ -77,7 +77,7 @@ public class GsonParsingTest {
@Test
public void verifyThermostat() throws IOException {
Thermostat thermostat = fromJson("thermostat-data.json", Thermostat.class);
WWNThermostat thermostat = fromJson("thermostat-data.json", WWNThermostat.class);
logger.debug("Thermostat: {}", thermostat);
assertTrue(thermostat.isOnline());
@@ -97,12 +97,12 @@ public class GsonParsingTest {
assertEquals(Double.valueOf(12.5), thermostat.getEcoTemperatureLow());
assertEquals(Double.valueOf(22.0), thermostat.getLockedTempMax());
assertEquals(Double.valueOf(20.0), thermostat.getLockedTempMin());
assertEquals(Thermostat.Mode.HEAT, thermostat.getMode());
assertEquals(WWNThermostat.Mode.HEAT, thermostat.getMode());
assertEquals("Living Room (Living Room)", thermostat.getName());
assertEquals("Living Room Thermostat (Living Room)", thermostat.getNameLong());
assertEquals(null, thermostat.getPreviousHvacMode());
assertEquals("5.6-7", thermostat.getSoftwareVersion());
assertEquals(Thermostat.State.OFF, thermostat.getHvacState());
assertEquals(WWNThermostat.State.OFF, thermostat.getHvacState());
assertEquals(STRUCTURE1_STRUCTURE_ID, thermostat.getStructureId());
assertEquals(Double.valueOf(15.5), thermostat.getTargetTemperature());
assertEquals(Double.valueOf(24.0), thermostat.getTargetTemperatureHigh());
@@ -115,22 +115,22 @@ public class GsonParsingTest {
@Test
public void thermostatTimeToTargetSupportedValueParsing() {
assertEquals((Integer) 0, Thermostat.parseTimeToTarget("~0"));
assertEquals((Integer) 5, Thermostat.parseTimeToTarget("<5"));
assertEquals((Integer) 10, Thermostat.parseTimeToTarget("<10"));
assertEquals((Integer) 15, Thermostat.parseTimeToTarget("~15"));
assertEquals((Integer) 90, Thermostat.parseTimeToTarget("~90"));
assertEquals((Integer) 120, Thermostat.parseTimeToTarget(">120"));
assertEquals((Integer) 0, WWNThermostat.parseTimeToTarget("~0"));
assertEquals((Integer) 5, WWNThermostat.parseTimeToTarget("<5"));
assertEquals((Integer) 10, WWNThermostat.parseTimeToTarget("<10"));
assertEquals((Integer) 15, WWNThermostat.parseTimeToTarget("~15"));
assertEquals((Integer) 90, WWNThermostat.parseTimeToTarget("~90"));
assertEquals((Integer) 120, WWNThermostat.parseTimeToTarget(">120"));
}
@Test
public void thermostatTimeToTargetUnsupportedValueParsing() {
assertThrows(NumberFormatException.class, () -> Thermostat.parseTimeToTarget("#5"));
assertThrows(NumberFormatException.class, () -> WWNThermostat.parseTimeToTarget("#5"));
}
@Test
public void verifyCamera() throws IOException {
Camera camera = fromJson("camera-data.json", Camera.class);
WWNCamera camera = fromJson("camera-data.json", WWNCamera.class);
logger.debug("Camera: {}", camera);
assertTrue(camera.isOnline());
@@ -166,7 +166,7 @@ public class GsonParsingTest {
@Test
public void verifySmokeDetector() throws IOException {
SmokeDetector smokeDetector = fromJson("smoke-detector-data.json", SmokeDetector.class);
WWNSmokeDetector smokeDetector = fromJson("smoke-detector-data.json", WWNSmokeDetector.class);
logger.debug("SmokeDetector: {}", smokeDetector);
assertTrue(smokeDetector.isOnline());
@@ -175,17 +175,17 @@ public class GsonParsingTest {
assertEquals("Downstairs", smokeDetector.getName());
assertEquals("Downstairs Nest Protect", smokeDetector.getNameLong());
assertEqualDateTime("2017-02-02T20:53:05.338Z", smokeDetector.getLastConnection());
assertEquals(SmokeDetector.BatteryHealth.OK, smokeDetector.getBatteryHealth());
assertEquals(SmokeDetector.AlarmState.OK, smokeDetector.getCoAlarmState());
assertEquals(SmokeDetector.AlarmState.OK, smokeDetector.getSmokeAlarmState());
assertEquals(WWNSmokeDetector.BatteryHealth.OK, smokeDetector.getBatteryHealth());
assertEquals(WWNSmokeDetector.AlarmState.OK, smokeDetector.getCoAlarmState());
assertEquals(WWNSmokeDetector.AlarmState.OK, smokeDetector.getSmokeAlarmState());
assertEquals("3.1rc9", smokeDetector.getSoftwareVersion());
assertEquals(STRUCTURE1_STRUCTURE_ID, smokeDetector.getStructureId());
assertEquals(SmokeDetector.UiColorState.GREEN, smokeDetector.getUiColorState());
assertEquals(WWNSmokeDetector.UiColorState.GREEN, smokeDetector.getUiColorState());
}
@Test
public void verifyAccessToken() throws IOException {
AccessTokenData accessToken = fromJson("access-token-data.json", AccessTokenData.class);
WWNAccessTokenData accessToken = fromJson("access-token-data.json", WWNAccessTokenData.class);
logger.debug("AccessTokenData: {}", accessToken);
assertEquals("access_token", accessToken.getAccessToken());
@@ -194,13 +194,13 @@ public class GsonParsingTest {
@Test
public void verifyStructure() throws IOException {
Structure structure = fromJson("structure-data.json", Structure.class);
WWNStructure structure = fromJson("structure-data.json", WWNStructure.class);
logger.debug("Structure: {}", structure);
assertEquals("Home", structure.getName());
assertEquals("US", structure.getCountryCode());
assertEquals("98056", structure.getPostalCode());
assertEquals(Structure.HomeAwayState.HOME, structure.getAway());
assertEquals(WWNStructure.HomeAwayState.HOME, structure.getAway());
assertEqualDateTime("2017-02-02T03:10:08.000Z", structure.getEtaBegin());
assertNull(structure.getEta());
assertNull(structure.getPeakPeriodEndTime());
@@ -212,7 +212,7 @@ public class GsonParsingTest {
@Test
public void verifyError() throws IOException {
ErrorData error = fromJson("error-data.json", ErrorData.class);
WWNErrorData error = fromJson("error-data.json", WWNErrorData.class);
logger.debug("ErrorData: {}", error);
assertEquals("blocked", error.getError());

View File

@@ -10,7 +10,7 @@
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.nest.handler;
package org.openhab.binding.nest.internal.wwn.handler;
import static org.hamcrest.CoreMatchers.*;
import static org.hamcrest.MatcherAssert.assertThat;
@@ -25,10 +25,8 @@ import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import org.openhab.binding.nest.internal.config.NestBridgeConfiguration;
import org.openhab.binding.nest.internal.handler.NestBridgeHandler;
import org.openhab.binding.nest.internal.handler.NestRedirectUrlSupplier;
import org.openhab.binding.nest.test.NestTestBridgeHandler;
import org.openhab.binding.nest.internal.wwn.config.WWNAccountConfiguration;
import org.openhab.binding.nest.internal.wwn.test.WWNTestAccountHandler;
import org.openhab.core.config.core.Configuration;
import org.openhab.core.thing.Bridge;
import org.openhab.core.thing.ThingStatus;
@@ -38,12 +36,12 @@ import org.openhab.core.thing.binding.ThingHandlerCallback;
import org.osgi.service.jaxrs.client.SseEventSourceFactory;
/**
* Tests cases for {@link NestBridgeHandler}.
* Tests cases for {@link WWNAccountHandler}.
*
* @author David Bennett - Initial contribution
*/
@ExtendWith(MockitoExtension.class)
public class NestBridgeHandlerTest {
public class WWNAccountHandlerTest {
private ThingHandler handler;
@@ -52,11 +50,11 @@ public class NestBridgeHandlerTest {
private @Mock ClientBuilder clientBuilder;
private @Mock Configuration configuration;
private @Mock SseEventSourceFactory eventSourceFactory;
private @Mock NestRedirectUrlSupplier redirectUrlSupplier;
private @Mock WWNRedirectUrlSupplier redirectUrlSupplier;
@BeforeEach
public void beforeEach() {
handler = new NestTestBridgeHandler(bridge, clientBuilder, eventSourceFactory, "http://localhost");
handler = new WWNTestAccountHandler(bridge, clientBuilder, eventSourceFactory, "http://localhost");
handler.setCallback(callback);
}
@@ -64,8 +62,8 @@ public class NestBridgeHandlerTest {
@Test
public void initializeShouldCallTheCallback() {
when(bridge.getConfiguration()).thenReturn(configuration);
NestBridgeConfiguration bridgeConfig = new NestBridgeConfiguration();
when(configuration.as(eq(NestBridgeConfiguration.class))).thenReturn(bridgeConfig);
WWNAccountConfiguration bridgeConfig = new WWNAccountConfiguration();
when(configuration.as(eq(WWNAccountConfiguration.class))).thenReturn(bridgeConfig);
bridgeConfig.accessToken = "my token";
// we expect the handler#initialize method to call the callback during execution and

View File

@@ -10,12 +10,12 @@
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.nest.handler;
package org.openhab.binding.nest.internal.wwn.handler;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.core.Is.is;
import static org.openhab.binding.nest.internal.NestBindingConstants.*;
import static org.openhab.binding.nest.internal.data.NestDataUtil.*;
import static org.openhab.binding.nest.internal.wwn.WWNBindingConstants.*;
import static org.openhab.binding.nest.internal.wwn.dto.WWNDataUtil.*;
import static org.openhab.core.library.types.OnOffType.*;
import java.io.IOException;
@@ -23,8 +23,7 @@ import java.util.HashMap;
import java.util.Map;
import org.junit.jupiter.api.Test;
import org.openhab.binding.nest.internal.config.NestDeviceConfiguration;
import org.openhab.binding.nest.internal.handler.NestCameraHandler;
import org.openhab.binding.nest.internal.wwn.config.WWNDeviceConfiguration;
import org.openhab.core.config.core.Configuration;
import org.openhab.core.library.types.StringType;
import org.openhab.core.thing.Bridge;
@@ -35,23 +34,23 @@ import org.openhab.core.thing.ThingUID;
import org.openhab.core.thing.binding.builder.ThingBuilder;
/**
* Tests for {@link NestCameraHandler}.
* Tests for {@link WWNCameraHandler}.
*
* @author Wouter Born - Increase test coverage
* @author Wouter Born - Initial contribution
*/
public class NestCameraHandlerTest extends NestThingHandlerOSGiTest {
public class WWNCameraHandlerTest extends WWNThingHandlerOSGiTest {
private static final ThingUID CAMERA_UID = new ThingUID(THING_TYPE_CAMERA, "camera1");
private static final int CHANNEL_COUNT = 20;
public NestCameraHandlerTest() {
super(NestCameraHandler.class);
public WWNCameraHandlerTest() {
super(WWNCameraHandler.class);
}
@Override
protected Thing buildThing(Bridge bridge) {
Map<String, Object> properties = new HashMap<>();
properties.put(NestDeviceConfiguration.DEVICE_ID, CAMERA1_DEVICE_ID);
properties.put(WWNDeviceConfiguration.DEVICE_ID, CAMERA1_DEVICE_ID);
return ThingBuilder.create(THING_TYPE_CAMERA, CAMERA_UID).withLabel("Test Camera").withBridge(bridge.getUID())
.withChannels(buildChannels(THING_TYPE_CAMERA, CAMERA_UID))

View File

@@ -10,12 +10,12 @@
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.nest.handler;
package org.openhab.binding.nest.internal.wwn.handler;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.core.Is.is;
import static org.openhab.binding.nest.internal.NestBindingConstants.*;
import static org.openhab.binding.nest.internal.data.NestDataUtil.*;
import static org.openhab.binding.nest.internal.wwn.WWNBindingConstants.*;
import static org.openhab.binding.nest.internal.wwn.dto.WWNDataUtil.*;
import static org.openhab.core.library.types.OnOffType.OFF;
import java.io.IOException;
@@ -23,8 +23,7 @@ import java.util.HashMap;
import java.util.Map;
import org.junit.jupiter.api.Test;
import org.openhab.binding.nest.internal.config.NestDeviceConfiguration;
import org.openhab.binding.nest.internal.handler.NestSmokeDetectorHandler;
import org.openhab.binding.nest.internal.wwn.config.WWNDeviceConfiguration;
import org.openhab.core.config.core.Configuration;
import org.openhab.core.library.types.StringType;
import org.openhab.core.thing.Bridge;
@@ -35,23 +34,23 @@ import org.openhab.core.thing.ThingUID;
import org.openhab.core.thing.binding.builder.ThingBuilder;
/**
* Tests for {@link NestSmokeDetectorHandler}.
* Tests for {@link WWNSmokeDetectorHandler}.
*
* @author Wouter Born - Increase test coverage
* @author Wouter Born - Initial contribution
*/
public class NestSmokeDetectorHandlerTest extends NestThingHandlerOSGiTest {
public class WWNSmokeDetectorHandlerTest extends WWNThingHandlerOSGiTest {
private static final ThingUID SMOKE_DETECTOR_UID = new ThingUID(THING_TYPE_SMOKE_DETECTOR, "smoke1");
private static final int CHANNEL_COUNT = 7;
public NestSmokeDetectorHandlerTest() {
super(NestSmokeDetectorHandler.class);
public WWNSmokeDetectorHandlerTest() {
super(WWNSmokeDetectorHandler.class);
}
@Override
protected Thing buildThing(Bridge bridge) {
Map<String, Object> properties = new HashMap<>();
properties.put(NestDeviceConfiguration.DEVICE_ID, SMOKE1_DEVICE_ID);
properties.put(WWNDeviceConfiguration.DEVICE_ID, SMOKE1_DEVICE_ID);
return ThingBuilder.create(THING_TYPE_SMOKE_DETECTOR, SMOKE_DETECTOR_UID).withLabel("Test Smoke Detector")
.withBridge(bridge.getUID()).withChannels(buildChannels(THING_TYPE_SMOKE_DETECTOR, SMOKE_DETECTOR_UID))

View File

@@ -10,12 +10,12 @@
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.nest.handler;
package org.openhab.binding.nest.internal.wwn.handler;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.core.Is.is;
import static org.openhab.binding.nest.internal.NestBindingConstants.*;
import static org.openhab.binding.nest.internal.data.NestDataUtil.*;
import static org.openhab.binding.nest.internal.wwn.WWNBindingConstants.*;
import static org.openhab.binding.nest.internal.wwn.dto.WWNDataUtil.*;
import static org.openhab.core.library.types.OnOffType.OFF;
import java.io.IOException;
@@ -23,8 +23,7 @@ import java.util.HashMap;
import java.util.Map;
import org.junit.jupiter.api.Test;
import org.openhab.binding.nest.internal.config.NestStructureConfiguration;
import org.openhab.binding.nest.internal.handler.NestStructureHandler;
import org.openhab.binding.nest.internal.wwn.config.WWNStructureConfiguration;
import org.openhab.core.config.core.Configuration;
import org.openhab.core.library.types.StringType;
import org.openhab.core.thing.Bridge;
@@ -35,23 +34,23 @@ import org.openhab.core.thing.ThingUID;
import org.openhab.core.thing.binding.builder.ThingBuilder;
/**
* Tests for {@link NestStructureHandler}.
* Tests for {@link WWNStructureHandler}.
*
* @author Wouter Born - Increase test coverage
* @author Wouter Born - Initial contribution
*/
public class NestStructureHandlerTest extends NestThingHandlerOSGiTest {
public class WWNStructureHandlerTest extends WWNThingHandlerOSGiTest {
private static final ThingUID STRUCTURE_UID = new ThingUID(THING_TYPE_STRUCTURE, "structure1");
private static final int CHANNEL_COUNT = 11;
public NestStructureHandlerTest() {
super(NestStructureHandler.class);
public WWNStructureHandlerTest() {
super(WWNStructureHandler.class);
}
@Override
protected Thing buildThing(Bridge bridge) {
Map<String, Object> properties = new HashMap<>();
properties.put(NestStructureConfiguration.STRUCTURE_ID, STRUCTURE1_STRUCTURE_ID);
properties.put(WWNStructureConfiguration.STRUCTURE_ID, STRUCTURE1_STRUCTURE_ID);
return ThingBuilder.create(THING_TYPE_STRUCTURE, STRUCTURE_UID).withLabel("Test Structure")
.withBridge(bridge.getUID()).withChannels(buildChannels(THING_TYPE_STRUCTURE, STRUCTURE_UID))

View File

@@ -10,12 +10,12 @@
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.nest.handler;
package org.openhab.binding.nest.internal.wwn.handler;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.core.Is.is;
import static org.openhab.binding.nest.internal.NestBindingConstants.*;
import static org.openhab.binding.nest.internal.data.NestDataUtil.*;
import static org.openhab.binding.nest.internal.wwn.WWNBindingConstants.*;
import static org.openhab.binding.nest.internal.wwn.dto.WWNDataUtil.*;
import static org.openhab.core.library.types.OnOffType.*;
import static org.openhab.core.library.unit.ImperialUnits.FAHRENHEIT;
import static org.openhab.core.library.unit.SIUnits.CELSIUS;
@@ -25,8 +25,7 @@ import java.util.HashMap;
import java.util.Map;
import org.junit.jupiter.api.Test;
import org.openhab.binding.nest.internal.config.NestDeviceConfiguration;
import org.openhab.binding.nest.internal.handler.NestThermostatHandler;
import org.openhab.binding.nest.internal.wwn.config.WWNDeviceConfiguration;
import org.openhab.core.config.core.Configuration;
import org.openhab.core.library.types.QuantityType;
import org.openhab.core.library.types.StringType;
@@ -39,23 +38,23 @@ import org.openhab.core.thing.ThingUID;
import org.openhab.core.thing.binding.builder.ThingBuilder;
/**
* Tests for {@link NestThermostatHandler}.
* Tests for {@link WWNThermostatHandler}.
*
* @author Wouter Born - Increase test coverage
* @author Wouter Born - Initial contribution
*/
public class NestThermostatHandlerTest extends NestThingHandlerOSGiTest {
public class WWNThermostatHandlerTest extends WWNThingHandlerOSGiTest {
private static final ThingUID THERMOSTAT_UID = new ThingUID(THING_TYPE_THERMOSTAT, "thermostat1");
private static final int CHANNEL_COUNT = 25;
public NestThermostatHandlerTest() {
super(NestThermostatHandler.class);
public WWNThermostatHandlerTest() {
super(WWNThermostatHandler.class);
}
@Override
protected Thing buildThing(Bridge bridge) {
Map<String, Object> properties = new HashMap<>();
properties.put(NestDeviceConfiguration.DEVICE_ID, THERMOSTAT1_DEVICE_ID);
properties.put(WWNDeviceConfiguration.DEVICE_ID, THERMOSTAT1_DEVICE_ID);
return ThingBuilder.create(THING_TYPE_THERMOSTAT, THERMOSTAT_UID).withLabel("Test Thermostat")
.withBridge(bridge.getUID()).withChannels(buildChannels(THING_TYPE_THERMOSTAT, THERMOSTAT_UID))

View File

@@ -10,14 +10,14 @@
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.nest.handler;
package org.openhab.binding.nest.internal.wwn.handler;
import static org.hamcrest.CoreMatchers.*;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.core.Is.is;
import static org.hamcrest.core.IsNot.not;
import static org.mockito.Mockito.*;
import static org.openhab.binding.nest.internal.rest.NestStreamingRestClient.PUT;
import static org.openhab.binding.nest.internal.wwn.rest.WWNStreamingRestClient.PUT;
import java.io.IOException;
import java.time.Instant;
@@ -36,12 +36,11 @@ import org.eclipse.jetty.servlet.ServletHolder;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.BeforeEach;
import org.openhab.binding.nest.internal.config.NestBridgeConfiguration;
import org.openhab.binding.nest.internal.handler.NestBaseHandler;
import org.openhab.binding.nest.test.NestTestApiServlet;
import org.openhab.binding.nest.test.NestTestBridgeHandler;
import org.openhab.binding.nest.test.NestTestHandlerFactory;
import org.openhab.binding.nest.test.NestTestServer;
import org.openhab.binding.nest.internal.wwn.config.WWNAccountConfiguration;
import org.openhab.binding.nest.internal.wwn.test.WWNTestAccountHandler;
import org.openhab.binding.nest.internal.wwn.test.WWNTestApiServlet;
import org.openhab.binding.nest.internal.wwn.test.WWNTestHandlerFactory;
import org.openhab.binding.nest.internal.wwn.test.WWNTestServer;
import org.openhab.core.config.core.Configuration;
import org.openhab.core.events.EventPublisher;
import org.openhab.core.items.Item;
@@ -84,21 +83,21 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* {@link NestThingHandlerOSGiTest} is an abstract base class for Nest OSGi based tests.
* {@link WWNThingHandlerOSGiTest} is an abstract base class for Nest OSGi based tests.
*
* @author Wouter Born - Increase test coverage
* @author Wouter Born - Initial contribution
*/
public abstract class NestThingHandlerOSGiTest extends JavaOSGiTest {
public abstract class WWNThingHandlerOSGiTest extends JavaOSGiTest {
private static final String SERVER_HOST = "127.0.0.1";
private static final int SERVER_PORT = TestPortUtil.findFreePort();
private static final int SERVER_TIMEOUT = -1;
private static final String REDIRECT_URL = "http://" + SERVER_HOST + ":" + SERVER_PORT;
private final Logger logger = LoggerFactory.getLogger(NestThingHandlerOSGiTest.class);
private final Logger logger = LoggerFactory.getLogger(WWNThingHandlerOSGiTest.class);
private static NestTestServer server;
private static NestTestApiServlet servlet = new NestTestApiServlet();
private static WWNTestServer server;
private static WWNTestApiServlet servlet = new WWNTestApiServlet();
private ChannelTypeRegistry channelTypeRegistry;
private ChannelGroupTypeRegistry channelGroupTypeRegistry;
@@ -111,23 +110,23 @@ public abstract class NestThingHandlerOSGiTest extends JavaOSGiTest {
private VolatileStorageService volatileStorageService = new VolatileStorageService();
protected Bridge bridge;
protected NestTestBridgeHandler bridgeHandler;
protected WWNTestAccountHandler bridgeHandler;
protected Thing thing;
protected NestBaseHandler<?> thingHandler;
private Class<? extends NestBaseHandler<?>> thingClass;
protected WWNBaseHandler<?> thingHandler;
private Class<? extends WWNBaseHandler<?>> thingClass;
private NestTestHandlerFactory nestTestHandlerFactory;
private WWNTestHandlerFactory nestTestHandlerFactory;
private @NonNullByDefault({}) ClientBuilder clientBuilder;
private @NonNullByDefault({}) SseEventSourceFactory eventSourceFactory;
public NestThingHandlerOSGiTest(Class<? extends NestBaseHandler<?>> thingClass) {
public WWNThingHandlerOSGiTest(Class<? extends WWNBaseHandler<?>> thingClass) {
this.thingClass = thingClass;
}
@BeforeAll
public static void setUpClass() throws Exception {
ServletHolder holder = new ServletHolder(servlet);
server = new NestTestServer(SERVER_HOST, SERVER_PORT, SERVER_TIMEOUT, holder);
server = new WWNTestServer(SERVER_HOST, SERVER_PORT, SERVER_TIMEOUT, holder);
server.startServer();
}
@@ -168,18 +167,18 @@ public abstract class NestThingHandlerOSGiTest extends JavaOSGiTest {
ComponentContext componentContext = mock(ComponentContext.class);
when(componentContext.getBundleContext()).thenReturn(bundleContext);
nestTestHandlerFactory = new NestTestHandlerFactory(clientBuilder, eventSourceFactory);
nestTestHandlerFactory = new WWNTestHandlerFactory(clientBuilder, eventSourceFactory);
nestTestHandlerFactory.activate(componentContext,
Map.of(NestTestHandlerFactory.REDIRECT_URL_CONFIG_PROPERTY, REDIRECT_URL));
Map.of(WWNTestHandlerFactory.REDIRECT_URL_CONFIG_PROPERTY, REDIRECT_URL));
registerService(nestTestHandlerFactory);
nestTestHandlerFactory = getService(ThingHandlerFactory.class, NestTestHandlerFactory.class);
nestTestHandlerFactory = getService(ThingHandlerFactory.class, WWNTestHandlerFactory.class);
assertThat("Could not get NestTestHandlerFactory", nestTestHandlerFactory, is(notNullValue()));
bridge = buildBridge();
thing = buildThing(bridge);
bridgeHandler = addThing(bridge, NestTestBridgeHandler.class);
bridgeHandler = addThing(bridge, WWNTestAccountHandler.class);
thingHandler = addThing(thing, thingClass);
createAndLinkItems();
@@ -203,13 +202,13 @@ public abstract class NestThingHandlerOSGiTest extends JavaOSGiTest {
protected Bridge buildBridge() {
Map<String, Object> properties = new HashMap<>();
properties.put(NestBridgeConfiguration.ACCESS_TOKEN,
properties.put(WWNAccountConfiguration.ACCESS_TOKEN,
"c.eQ5QBBPiFOTNzPHbmZPcE9yPZ7GayzLusifgQR2DQRFNyUS9ESvlhJF0D7vG8Y0TFV39zX1vIOsWrv8RKCMrFepNUb9FqHEboa4MtWLUsGb4tD9oBh0jrV4HooJUmz5sVA5KZR0dkxyLYyPc");
properties.put(NestBridgeConfiguration.PINCODE, "64P2XRYT");
properties.put(NestBridgeConfiguration.PRODUCT_ID, "8fdf9885-ca07-4252-1aa3-f3d5ca9589e0");
properties.put(NestBridgeConfiguration.PRODUCT_SECRET, "QITLR3iyUlWaj9dbvCxsCKp4f");
properties.put(WWNAccountConfiguration.PINCODE, "64P2XRYT");
properties.put(WWNAccountConfiguration.PRODUCT_ID, "8fdf9885-ca07-4252-1aa3-f3d5ca9589e0");
properties.put(WWNAccountConfiguration.PRODUCT_SECRET, "QITLR3iyUlWaj9dbvCxsCKp4f");
return BridgeBuilder.create(NestTestBridgeHandler.THING_TYPE_TEST_BRIDGE, "test_account")
return BridgeBuilder.create(WWNTestAccountHandler.THING_TYPE_TEST_BRIDGE, "test_account")
.withLabel("Test Account").withConfiguration(new Configuration(properties)).build();
}

View File

@@ -10,32 +10,31 @@
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.nest.test;
package org.openhab.binding.nest.internal.wwn.test;
import static org.openhab.binding.nest.internal.NestBindingConstants.BINDING_ID;
import static org.openhab.binding.nest.internal.wwn.WWNBindingConstants.BINDING_ID;
import java.util.Collections;
import java.util.Properties;
import java.util.Set;
import javax.ws.rs.client.ClientBuilder;
import org.openhab.binding.nest.internal.exceptions.InvalidAccessTokenException;
import org.openhab.binding.nest.internal.handler.NestBridgeHandler;
import org.openhab.binding.nest.internal.handler.NestRedirectUrlSupplier;
import org.openhab.binding.nest.internal.wwn.exceptions.InvalidWWNAccessTokenException;
import org.openhab.binding.nest.internal.wwn.handler.WWNAccountHandler;
import org.openhab.binding.nest.internal.wwn.handler.WWNRedirectUrlSupplier;
import org.openhab.core.thing.Bridge;
import org.openhab.core.thing.ThingTypeUID;
import org.osgi.service.jaxrs.client.SseEventSourceFactory;
/**
* The {@link NestTestBridgeHandler} is a {@link NestBridgeHandler} modified for testing. Using the
* The {@link WWNTestAccountHandler} is a {@link WWNAccountHandler} modified for testing. Using the
* {@link NestTestRedirectUrlSupplier} it will always connect to same provided {@link #redirectUrl}.
*
* @author Wouter Born - Increase test coverage
* @author Wouter Born - Initial contribution
*/
public class NestTestBridgeHandler extends NestBridgeHandler {
public class WWNTestAccountHandler extends WWNAccountHandler {
class NestTestRedirectUrlSupplier extends NestRedirectUrlSupplier {
class NestTestRedirectUrlSupplier extends WWNRedirectUrlSupplier {
NestTestRedirectUrlSupplier(Properties httpHeaders) {
super(httpHeaders);
@@ -48,19 +47,19 @@ public class NestTestBridgeHandler extends NestBridgeHandler {
}
}
public static final ThingTypeUID THING_TYPE_TEST_BRIDGE = new ThingTypeUID(BINDING_ID, "test_account");
public static final Set<ThingTypeUID> SUPPORTED_THING_TYPES = Collections.singleton(THING_TYPE_TEST_BRIDGE);
public static final ThingTypeUID THING_TYPE_TEST_BRIDGE = new ThingTypeUID(BINDING_ID, "wwn_test_account");
public static final Set<ThingTypeUID> SUPPORTED_THING_TYPES = Set.of(THING_TYPE_TEST_BRIDGE);
private String redirectUrl;
public NestTestBridgeHandler(Bridge bridge, ClientBuilder clientBuilder, SseEventSourceFactory eventSourceFactory,
public WWNTestAccountHandler(Bridge bridge, ClientBuilder clientBuilder, SseEventSourceFactory eventSourceFactory,
String redirectUrl) {
super(bridge, clientBuilder, eventSourceFactory);
this.redirectUrl = redirectUrl;
}
@Override
protected NestRedirectUrlSupplier createRedirectUrlSupplier() throws InvalidAccessTokenException {
protected WWNRedirectUrlSupplier createRedirectUrlSupplier() throws InvalidWWNAccessTokenException {
return new NestTestRedirectUrlSupplier(getHttpHeaders());
}
}

View File

@@ -10,10 +10,10 @@
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.nest.test;
package org.openhab.binding.nest.internal.wwn.test;
import static org.openhab.binding.nest.internal.NestBindingConstants.*;
import static org.openhab.binding.nest.internal.rest.NestStreamingRestClient.*;
import static org.openhab.binding.nest.internal.wwn.WWNBindingConstants.*;
import static org.openhab.binding.nest.internal.wwn.rest.WWNStreamingRestClient.*;
import java.io.IOException;
import java.io.InputStreamReader;
@@ -39,11 +39,11 @@ import com.google.gson.GsonBuilder;
import com.google.gson.reflect.TypeToken;
/**
* The {@link NestTestApiServlet} mocks the Nest API during tests.
* The {@link WWNTestApiServlet} mocks the Nest API during tests.
*
* @author Wouter Born - Increase test coverage
* @author Wouter Born - Initial contribution
*/
public class NestTestApiServlet extends HttpServlet {
public class WWNTestApiServlet extends HttpServlet {
private static final long serialVersionUID = -5414910055159062745L;
@@ -52,7 +52,7 @@ public class NestTestApiServlet extends HttpServlet {
private static final String UPDATE_PATHS[] = { NEST_CAMERA_UPDATE_PATH, NEST_SMOKE_ALARM_UPDATE_PATH,
NEST_STRUCTURE_UPDATE_PATH, NEST_THERMOSTAT_UPDATE_PATH };
private final Logger logger = LoggerFactory.getLogger(NestTestApiServlet.class);
private final Logger logger = LoggerFactory.getLogger(WWNTestApiServlet.class);
private class SseEvent {
private String name;

View File

@@ -10,7 +10,7 @@
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.nest.test;
package org.openhab.binding.nest.internal.wwn.test;
import java.util.HashMap;
import java.util.Hashtable;
@@ -20,8 +20,8 @@ import javax.ws.rs.client.ClientBuilder;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.openhab.binding.nest.internal.discovery.NestDiscoveryService;
import org.openhab.binding.nest.internal.handler.NestBridgeHandler;
import org.openhab.binding.nest.internal.wwn.discovery.WWNDiscoveryService;
import org.openhab.binding.nest.internal.wwn.handler.WWNAccountHandler;
import org.openhab.core.config.discovery.DiscoveryService;
import org.openhab.core.thing.Bridge;
import org.openhab.core.thing.Thing;
@@ -38,12 +38,12 @@ import org.osgi.service.component.annotations.Reference;
import org.osgi.service.jaxrs.client.SseEventSourceFactory;
/**
* The {@link NestTestHandlerFactory} is responsible for creating test things and thing handlers.
* The {@link WWNTestHandlerFactory} is responsible for creating test things and thing handlers.
*
* @author Wouter Born - Increase test coverage
* @author Wouter Born - Initial contribution
*/
@NonNullByDefault
public class NestTestHandlerFactory extends BaseThingHandlerFactory implements ThingHandlerFactory {
public class WWNTestHandlerFactory extends BaseThingHandlerFactory implements ThingHandlerFactory {
public static final String REDIRECT_URL_CONFIG_PROPERTY = "redirect.url";
@@ -54,7 +54,7 @@ public class NestTestHandlerFactory extends BaseThingHandlerFactory implements T
private String redirectUrl = "http://localhost";
@Activate
public NestTestHandlerFactory(@Reference ClientBuilder clientBuilder,
public WWNTestHandlerFactory(@Reference ClientBuilder clientBuilder,
@Reference SseEventSourceFactory eventSourceFactory) {
this.clientBuilder = clientBuilder;
this.eventSourceFactory = eventSourceFactory;
@@ -62,7 +62,7 @@ public class NestTestHandlerFactory extends BaseThingHandlerFactory implements T
@Override
public boolean supportsThingType(ThingTypeUID thingTypeUID) {
return NestTestBridgeHandler.SUPPORTED_THING_TYPES.contains(thingTypeUID);
return WWNTestAccountHandler.SUPPORTED_THING_TYPES.contains(thingTypeUID);
}
@Activate
@@ -82,10 +82,11 @@ public class NestTestHandlerFactory extends BaseThingHandlerFactory implements T
@Override
protected @Nullable ThingHandler createHandler(Thing thing) {
ThingTypeUID thingTypeUID = thing.getThingTypeUID();
if (thingTypeUID.equals(NestTestBridgeHandler.THING_TYPE_TEST_BRIDGE)) {
NestTestBridgeHandler handler = new NestTestBridgeHandler((Bridge) thing, clientBuilder, eventSourceFactory,
if (thingTypeUID.equals(WWNTestAccountHandler.THING_TYPE_TEST_BRIDGE)) {
WWNTestAccountHandler handler = new WWNTestAccountHandler((Bridge) thing, clientBuilder, eventSourceFactory,
redirectUrl);
NestDiscoveryService service = new NestDiscoveryService(handler);
WWNDiscoveryService service = new WWNDiscoveryService();
service.setThingHandler(handler);
// Register the discovery service.
discoveryService.put(handler.getThing().getUID(),
bundleContext.registerService(DiscoveryService.class.getName(), service, new Hashtable<>()));
@@ -101,11 +102,11 @@ public class NestTestHandlerFactory extends BaseThingHandlerFactory implements T
*/
@Override
protected void removeHandler(ThingHandler thingHandler) {
if (thingHandler instanceof NestBridgeHandler) {
if (thingHandler instanceof WWNAccountHandler) {
ServiceRegistration<?> registration = discoveryService.get(thingHandler.getThing().getUID());
if (registration != null) {
// Unregister the discovery service.
NestDiscoveryService service = (NestDiscoveryService) bundleContext
WWNDiscoveryService service = (WWNDiscoveryService) bundleContext
.getService(registration.getReference());
service.deactivate();
registration.unregister();

View File

@@ -10,7 +10,7 @@
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.nest.test;
package org.openhab.binding.nest.internal.wwn.test;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.server.ServerConnector;
@@ -24,11 +24,11 @@ import org.slf4j.LoggerFactory;
*
* Based on {@code TestServer} of the FS Internet Radio Binding.
*
* @author Velin Yordanov - initial contribution
* @author Velin Yordanov - Initial contribution
* @author Wouter Born - Increase test coverage
*/
public class NestTestServer {
private final Logger logger = LoggerFactory.getLogger(NestTestServer.class);
public class WWNTestServer {
private final Logger logger = LoggerFactory.getLogger(WWNTestServer.class);
private Server server;
private String host;
@@ -36,7 +36,7 @@ public class NestTestServer {
private int timeout;
private ServletHolder servletHolder;
public NestTestServer(String host, int port, int timeout, ServletHolder servletHolder) {
public WWNTestServer(String host, int port, int timeout, ServletHolder servletHolder) {
this.host = host;
this.port = port;
this.timeout = timeout;