added migrated 2.x add-ons

Signed-off-by: Kai Kreuzer <kai@openhab.org>
This commit is contained in:
Kai Kreuzer
2020-09-21 01:58:32 +02:00
parent bbf1a7fd29
commit 6df6783b60
11662 changed files with 1302875 additions and 11 deletions

View File

@@ -0,0 +1,27 @@
<?xml version="1.0" encoding="UTF-8"?>
<classpath>
<classpathentry kind="src" output="target/classes" path="src/main/java">
<attributes>
<attribute name="optional" value="true"/>
<attribute name="maven.pomderived" value="true"/>
</attributes>
</classpathentry>
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-11">
<attributes>
<attribute name="maven.pomderived" value="true"/>
</attributes>
</classpathentry>
<classpathentry kind="con" path="org.eclipse.m2e.MAVEN2_CLASSPATH_CONTAINER">
<attributes>
<attribute name="maven.pomderived" value="true"/>
</attributes>
</classpathentry>
<classpathentry kind="src" output="target/test-classes" path="src/test/java">
<attributes>
<attribute name="optional" value="true"/>
<attribute name="maven.pomderived" value="true"/>
<attribute name="test" value="true"/>
</attributes>
</classpathentry>
<classpathentry kind="output" path="target/classes"/>
</classpath>

View File

@@ -0,0 +1,23 @@
<?xml version="1.0" encoding="UTF-8"?>
<projectDescription>
<name>org.openhab.binding.mqtt.homie.tests</name>
<comment></comment>
<projects>
</projects>
<buildSpec>
<buildCommand>
<name>org.eclipse.jdt.core.javabuilder</name>
<arguments>
</arguments>
</buildCommand>
<buildCommand>
<name>org.eclipse.m2e.core.maven2Builder</name>
<arguments>
</arguments>
</buildCommand>
</buildSpec>
<natures>
<nature>org.eclipse.jdt.core.javanature</nature>
<nature>org.eclipse.m2e.core.maven2Nature</nature>
</natures>
</projectDescription>

View File

@@ -0,0 +1,13 @@
This content is produced and maintained by the openHAB project.
* Project home: https://www.openhab.org
== Declared Project Licenses
This program and the accompanying materials are made available under the terms
of the Eclipse Public License 2.0 which is available at
https://www.eclipse.org/legal/epl-2.0/.
== Source Code
https://github.com/openhab/openhab-addons

View File

@@ -0,0 +1,73 @@
-include: ../itest-common.bndrun
Bundle-SymbolicName: ${project.artifactId}
Fragment-Host: org.openhab.binding.mqtt.homie
-runrequires: \
bnd.identity;id='org.openhab.binding.mqtt.homie.tests',\
bnd.identity;id='org.openhab.core.binding.xml',\
bnd.identity;id='org.openhab.core.thing.xml',\
bnd.identity;id='org.openhab.io.mqttembeddedbroker'
#
# done
#
-runbundles: \
ch.qos.logback.core;version='[1.2.3,1.2.4)',\
com.google.gson;version='[2.8.2,2.8.3)',\
javax.measure.unit-api;version='[1.0.0,1.0.1)',\
org.apache.commons.collections;version='[3.2.1,3.2.2)',\
org.apache.commons.io;version='[2.2.0,2.2.1)',\
org.apache.commons.lang;version='[2.6.0,2.6.1)',\
org.apache.felix.configadmin;version='[1.9.8,1.9.9)',\
org.apache.felix.http.servlet-api;version='[1.1.2,1.1.3)',\
org.apache.felix.scr;version='[2.1.10,2.1.11)',\
org.apache.servicemix.specs.activation-api-1.1;version='[2.9.0,2.9.1)',\
org.apache.servicemix.specs.jaxb-api-2.2;version='[2.9.0,2.9.1)',\
org.apache.servicemix.specs.stax-api-1.2;version='[2.9.0,2.9.1)',\
org.eclipse.equinox.event;version='[1.4.300,1.4.301)',\
org.eclipse.jetty.http;version='[9.4.11,9.4.12)',\
org.eclipse.jetty.io;version='[9.4.11,9.4.12)',\
org.eclipse.jetty.security;version='[9.4.11,9.4.12)',\
org.eclipse.jetty.server;version='[9.4.11,9.4.12)',\
org.eclipse.jetty.servlet;version='[9.4.11,9.4.12)',\
org.eclipse.jetty.util;version='[9.4.11,9.4.12)',\
org.eclipse.paho.client.mqttv3;version='[1.2.1,1.2.2)',\
org.objenesis;version='[2.6.0,2.6.1)',\
org.openhab.binding.mqtt;version='[2.5.2,2.5.3)',\
org.openhab.binding.mqtt.generic;version='[2.5.2,2.5.3)',\
org.openhab.binding.mqtt.homie;version='[2.5.2,2.5.3)',\
org.openhab.binding.mqtt.homie.tests;version='[2.5.2,2.5.3)',\
org.openhab.core;version='[2.5.0,2.5.1)',\
org.openhab.core.config.core;version='[2.5.0,2.5.1)',\
org.openhab.core.config.discovery;version='[2.5.0,2.5.1)',\
org.openhab.core.io.console;version='[2.5.0,2.5.1)',\
org.openhab.core.io.transport.mqtt;version='[2.5.0,2.5.1)',\
org.openhab.core.test;version='[2.5.0,2.5.1)',\
org.openhab.core.thing;version='[2.5.0,2.5.1)',\
org.openhab.core.transform;version='[2.5.0,2.5.1)',\
org.osgi.service.event;version='[1.4.0,1.4.1)',\
osgi.enroute.hamcrest.wrapper;version='[1.3.0,1.3.1)',\
osgi.enroute.junit.wrapper;version='[4.12.0,4.12.1)',\
slf4j.api;version='[1.7.25,1.7.26)',\
org.apache.servicemix.bundles.xstream;version='[1.4.7,1.4.8)',\
org.openhab.core.binding.xml;version='[2.5.0,2.5.1)',\
org.openhab.core.config.xml;version='[2.5.0,2.5.1)',\
org.openhab.core.thing.xml;version='[2.5.0,2.5.1)',\
com.h2database.mvstore;version='[1.4.199,1.4.200)',\
io.netty.buffer;version='[4.1.42,4.1.43)',\
io.netty.codec;version='[4.1.42,4.1.43)',\
io.netty.codec-mqtt;version='[4.1.42,4.1.43)',\
io.netty.common;version='[4.1.42,4.1.43)',\
io.netty.handler;version='[4.1.42,4.1.43)',\
io.netty.resolver;version='[4.1.42,4.1.43)',\
io.netty.transport;version='[4.1.42,4.1.43)',\
org.openhab.io.mqttembeddedbroker;version='[2.5.0,2.5.1)',\
tec.uom.lib.uom-lib-common;version='[1.0.3,1.0.4)',\
tec.uom.se;version='[1.0.10,1.0.11)',\
org.apache.servicemix.bundles.jaxb-impl;version='[2.2.11,2.2.12)',\
ch.qos.logback.classic;version='[1.2.3,1.2.4)',\
net.bytebuddy.byte-buddy;version='[1.9.10,1.9.11)',\
net.bytebuddy.byte-buddy-agent;version='[1.9.10,1.9.11)',\
org.mockito.mockito-core;version='[3.1.0,3.1.1)'
-runvm: -Dio.netty.noUnsafe=true

View File

@@ -0,0 +1,104 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.openhab.addons.itests</groupId>
<artifactId>org.openhab.addons.reactor.itests</artifactId>
<version>2.5.9-SNAPSHOT</version>
</parent>
<artifactId>org.openhab.binding.mqtt.homie.tests</artifactId>
<name>openHAB Add-ons :: Integration Tests :: MQTT Homie Tests</name>
<properties>
<netty.version>4.1.42.Final</netty.version>
</properties>
<dependencies>
<dependency>
<groupId>org.openhab.addons.bundles</groupId>
<artifactId>org.openhab.binding.mqtt</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.openhab.addons.bundles</groupId>
<artifactId>org.openhab.binding.mqtt.generic</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.openhab.addons.bundles</groupId>
<artifactId>org.openhab.binding.mqtt.homie</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.openhab.addons.bundles</groupId>
<artifactId>org.openhab.io.mqttembeddedbroker</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>com.github.j-n-k</groupId>
<artifactId>moquette-broker</artifactId>
<version>0.13.0.OH2</version>
<exclusions>
<exclusion>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
</exclusion>
<exclusion>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
</exclusion>
<exclusion>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>io.netty</groupId>
<artifactId>netty-common</artifactId>
<version>${netty.version}</version>
</dependency>
<dependency>
<groupId>io.netty</groupId>
<artifactId>netty-buffer</artifactId>
<version>${netty.version}</version>
</dependency>
<dependency>
<groupId>io.netty</groupId>
<artifactId>netty-transport</artifactId>
<version>${netty.version}</version>
</dependency>
<dependency>
<groupId>io.netty</groupId>
<artifactId>netty-codec</artifactId>
<version>${netty.version}</version>
</dependency>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2-mvstore</artifactId>
<version>1.4.199</version>
</dependency>
<dependency>
<groupId>io.netty</groupId>
<artifactId>netty-codec-mqtt</artifactId>
<version>${netty.version}</version>
</dependency>
<dependency>
<groupId>io.netty</groupId>
<artifactId>netty-resolver</artifactId>
<version>${netty.version}</version>
</dependency>
<dependency>
<groupId>io.netty</groupId>
<artifactId>netty-handler</artifactId>
<version>${netty.version}</version>
</dependency>
</dependencies>
</project>

View File

@@ -0,0 +1,91 @@
/**
* Copyright (c) 2010-2020 Contributors to the openHAB project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.mqtt;
import static org.junit.Assert.assertTrue;
import java.util.concurrent.Semaphore;
import java.util.concurrent.TimeUnit;
import org.eclipse.jdt.annotation.NonNull;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.openhab.core.io.transport.mqtt.MqttBrokerConnection;
import org.openhab.core.io.transport.mqtt.MqttConnectionObserver;
import org.openhab.core.io.transport.mqtt.MqttConnectionState;
import org.openhab.core.io.transport.mqtt.MqttService;
import org.openhab.core.io.transport.mqtt.MqttServiceObserver;
import org.openhab.io.mqttembeddedbroker.Constants;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* A full implementation test, that starts the embedded MQTT broker and publishes a homeassistant MQTT discovery device
* tree.
*
* @author David Graeff - Initial contribution
*/
@NonNullByDefault
public class EmbeddedBrokerTools {
private final Logger logger = LoggerFactory.getLogger(EmbeddedBrokerTools.class);
public @Nullable MqttBrokerConnection embeddedConnection = null;
/**
* Request the embedded broker connection from the {@link MqttService} and wait for a connection to be established.
*
* @throws InterruptedException
*/
public MqttBrokerConnection waitForConnection(MqttService mqttService) throws InterruptedException {
embeddedConnection = mqttService.getBrokerConnection(Constants.CLIENTID);
if (embeddedConnection == null) {
Semaphore semaphore = new Semaphore(1);
semaphore.acquire();
MqttServiceObserver observer = new MqttServiceObserver() {
@Override
public void brokerAdded(@NonNull String brokerID, @NonNull MqttBrokerConnection broker) {
if (brokerID.equals(Constants.CLIENTID)) {
embeddedConnection = broker;
semaphore.release();
}
}
@Override
public void brokerRemoved(@NonNull String brokerID, @NonNull MqttBrokerConnection broker) {
}
};
mqttService.addBrokersListener(observer);
assertTrue("Wait for embedded connection client failed", semaphore.tryAcquire(700, TimeUnit.MILLISECONDS));
}
MqttBrokerConnection embeddedConnection = this.embeddedConnection;
if (embeddedConnection == null) {
throw new IllegalStateException();
}
logger.warn("waitForConnection {}", embeddedConnection.connectionState());
Semaphore semaphore = new Semaphore(1);
semaphore.acquire();
MqttConnectionObserver mqttConnectionObserver = (state, error) -> {
if (state == MqttConnectionState.CONNECTED) {
semaphore.release();
}
};
embeddedConnection.addConnectionObserver(mqttConnectionObserver);
if (embeddedConnection.connectionState() == MqttConnectionState.CONNECTED) {
semaphore.release();
}
assertTrue("Connection " + embeddedConnection.getClientId() + " failed. State: "
+ embeddedConnection.connectionState(), semaphore.tryAcquire(500, TimeUnit.MILLISECONDS));
return embeddedConnection;
}
}

View File

@@ -0,0 +1,321 @@
/**
* Copyright (c) 2010-2020 Contributors to the openHAB project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.mqtt;
import static org.hamcrest.CoreMatchers.is;
import static org.junit.Assert.*;
import static org.mockito.ArgumentMatchers.*;
import static org.mockito.Mockito.*;
import static org.mockito.MockitoAnnotations.initMocks;
import java.nio.charset.StandardCharsets;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import org.openhab.core.library.types.DecimalType;
import org.openhab.core.library.types.OnOffType;
import org.openhab.core.types.UnDefType;
import org.openhab.core.io.transport.mqtt.MqttBrokerConnection;
import org.openhab.core.io.transport.mqtt.MqttConnectionObserver;
import org.openhab.core.io.transport.mqtt.MqttConnectionState;
import org.openhab.core.io.transport.mqtt.MqttService;
import org.openhab.core.test.java.JavaOSGiTest;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.mockito.Mock;
import org.mockito.invocation.InvocationOnMock;
import org.openhab.binding.mqtt.generic.ChannelState;
import org.openhab.binding.mqtt.generic.tools.ChildMap;
import org.openhab.binding.mqtt.generic.tools.WaitForTopicValue;
import org.openhab.binding.mqtt.homie.internal.handler.HomieThingHandler;
import org.openhab.binding.mqtt.homie.internal.homie300.Device;
import org.openhab.binding.mqtt.homie.internal.homie300.DeviceAttributes;
import org.openhab.binding.mqtt.homie.internal.homie300.DeviceAttributes.ReadyState;
import org.openhab.binding.mqtt.homie.internal.homie300.DeviceCallback;
import org.openhab.binding.mqtt.homie.internal.homie300.Node;
import org.openhab.binding.mqtt.homie.internal.homie300.NodeAttributes;
import org.openhab.binding.mqtt.homie.internal.homie300.Property;
import org.openhab.binding.mqtt.homie.internal.homie300.PropertyAttributes;
import org.openhab.binding.mqtt.homie.internal.homie300.PropertyAttributes.DataTypeEnum;
import org.openhab.binding.mqtt.homie.internal.homie300.PropertyHelper;
/**
* A full implementation test, that starts the embedded MQTT broker and publishes a homie device tree.
*
* @author David Graeff - Initial contribution
*/
public class HomieImplementationTest extends JavaOSGiTest {
private static final String BASE_TOPIC = "homie";
private static final String DEVICE_ID = ThingChannelConstants.testHomieThing.getId();
private static final String DEVICE_TOPIC = BASE_TOPIC + "/" + DEVICE_ID;
private MqttService mqttService;
private MqttBrokerConnection embeddedConnection;
private MqttBrokerConnection connection;
private int registeredTopics = 100;
// The handler is not tested here, so just mock the callback
@Mock
DeviceCallback callback;
// A handler mock is required to verify that channel value changes have been received
@Mock
HomieThingHandler handler;
private ScheduledExecutorService scheduler;
/**
* Create an observer that fails the test as soon as the broker client connection changes its connection state
* to something else then CONNECTED.
*/
private MqttConnectionObserver failIfChange = (state, error) -> assertThat(state,
is(MqttConnectionState.CONNECTED));
private String propertyTestTopic;
@Before
public void setUp() throws InterruptedException, ExecutionException, TimeoutException {
registerVolatileStorageService();
initMocks(this);
mqttService = getService(MqttService.class);
embeddedConnection = new EmbeddedBrokerTools().waitForConnection(mqttService);
embeddedConnection.setQos(1);
embeddedConnection.setRetain(true);
connection = new MqttBrokerConnection(embeddedConnection.getHost(), embeddedConnection.getPort(),
embeddedConnection.isSecure(), "homie");
connection.setQos(1);
connection.setPersistencePath(Paths.get("subconn"));
connection.start().get(500, TimeUnit.MILLISECONDS);
assertThat(connection.connectionState(), is(MqttConnectionState.CONNECTED));
// If the connection state changes in between -> fail
connection.addConnectionObserver(failIfChange);
List<CompletableFuture<Boolean>> futures = new ArrayList<>();
futures.add(embeddedConnection.publish(DEVICE_TOPIC + "/$homie", "3.0".getBytes()));
futures.add(embeddedConnection.publish(DEVICE_TOPIC + "/$name", "Name".getBytes()));
futures.add(embeddedConnection.publish(DEVICE_TOPIC + "/$state", "ready".getBytes()));
futures.add(embeddedConnection.publish(DEVICE_TOPIC + "/$nodes", "testnode".getBytes()));
// Add homie node topics
final String testNode = DEVICE_TOPIC + "/testnode";
futures.add(embeddedConnection.publish(testNode + "/$name", "Testnode".getBytes()));
futures.add(embeddedConnection.publish(testNode + "/$type", "Type".getBytes()));
futures.add(
embeddedConnection.publish(testNode + "/$properties", "temperature,doorbell,testRetain".getBytes()));
// Add homie property topics
final String property = testNode + "/temperature";
futures.add(embeddedConnection.publish(property, "10".getBytes()));
futures.add(embeddedConnection.publish(property + "/$name", "Testprop".getBytes()));
futures.add(embeddedConnection.publish(property + "/$settable", "true".getBytes()));
futures.add(embeddedConnection.publish(property + "/$unit", "°C".getBytes(StandardCharsets.UTF_8)));
futures.add(embeddedConnection.publish(property + "/$datatype", "float".getBytes()));
futures.add(embeddedConnection.publish(property + "/$format", "-100:100".getBytes()));
final String propertyBellTopic = testNode + "/doorbell";
futures.add(embeddedConnection.publish(propertyBellTopic + "/$name", "Doorbell".getBytes()));
futures.add(embeddedConnection.publish(propertyBellTopic + "/$settable", "false".getBytes()));
futures.add(embeddedConnection.publish(propertyBellTopic + "/$retained", "false".getBytes()));
futures.add(embeddedConnection.publish(propertyBellTopic + "/$datatype", "boolean".getBytes()));
this.propertyTestTopic = testNode + "/testRetain";
futures.add(embeddedConnection.publish(propertyTestTopic + "/$name", "Test".getBytes()));
futures.add(embeddedConnection.publish(propertyTestTopic + "/$settable", "true".getBytes()));
futures.add(embeddedConnection.publish(propertyTestTopic + "/$retained", "false".getBytes()));
futures.add(embeddedConnection.publish(propertyTestTopic + "/$datatype", "boolean".getBytes()));
registeredTopics = futures.size();
CompletableFuture.allOf(futures.toArray(new CompletableFuture[0])).get(1000, TimeUnit.MILLISECONDS);
scheduler = new ScheduledThreadPoolExecutor(6);
}
@After
public void tearDown() throws InterruptedException, ExecutionException, TimeoutException {
if (connection != null) {
connection.removeConnectionObserver(failIfChange);
connection.stop().get(500, TimeUnit.MILLISECONDS);
}
scheduler.shutdownNow();
}
@Test
public void retrieveAllTopics() throws InterruptedException, ExecutionException, TimeoutException {
// four topics are not under /testnode !
CountDownLatch c = new CountDownLatch(registeredTopics - 4);
connection.subscribe(DEVICE_TOPIC + "/testnode/#", (topic, payload) -> c.countDown()).get(5000,
TimeUnit.MILLISECONDS);
assertTrue("Connection " + connection.getClientId() + " not retrieving all topics ",
c.await(5000, TimeUnit.MILLISECONDS));
}
@Test
public void retrieveOneAttribute() throws InterruptedException, ExecutionException {
WaitForTopicValue watcher = new WaitForTopicValue(connection, DEVICE_TOPIC + "/$homie");
assertThat(watcher.waitForTopicValue(1000), is("3.0"));
}
@SuppressWarnings("null")
@Test
public void retrieveAttributes() throws InterruptedException, ExecutionException {
assertThat(connection.hasSubscribers(), is(false));
Node node = new Node(DEVICE_TOPIC, "testnode", ThingChannelConstants.testHomieThing, callback,
new NodeAttributes());
Property property = spy(
new Property(DEVICE_TOPIC + "/testnode", node, "temperature", callback, new PropertyAttributes()));
// Create a scheduler
ScheduledExecutorService scheduler = new ScheduledThreadPoolExecutor(4);
property.subscribe(connection, scheduler, 500).get();
assertThat(property.attributes.settable, is(true));
assertThat(property.attributes.retained, is(true));
assertThat(property.attributes.name, is("Testprop"));
assertThat(property.attributes.unit, is("°C"));
assertThat(property.attributes.datatype, is(DataTypeEnum.float_));
waitForAssert(() -> assertThat(property.attributes.format, is("-100:100")));
verify(property, timeout(500).atLeastOnce()).attributesReceived();
// Receive property value
ChannelState channelState = spy(property.getChannelState());
PropertyHelper.setChannelState(property, channelState);
property.startChannel(connection, scheduler, 500).get();
verify(channelState).start(any(), any(), anyInt());
verify(channelState, timeout(500)).processMessage(any(), any());
verify(callback).updateChannelState(any(), any());
assertThat(property.getChannelState().getCache().getChannelState(), is(new DecimalType(10)));
property.stop().get();
assertThat(connection.hasSubscribers(), is(false));
}
// Inject a spy'ed property
public Property createSpyProperty(InvocationOnMock invocation) {
final Node node = (Node) invocation.getMock();
final String id = (String) invocation.getArguments()[0];
return spy(node.createProperty(id, spy(new PropertyAttributes())));
}
// Inject a spy'ed node
public Node createSpyNode(InvocationOnMock invocation) {
final Device device = (Device) invocation.getMock();
final String id = (String) invocation.getArguments()[0];
// Create the node
Node node = spy(device.createNode(id, spy(new NodeAttributes())));
// Intercept creating a property in the next call and inject a spy'ed property.
doAnswer(this::createSpyProperty).when(node).createProperty(any());
return node;
}
@SuppressWarnings("null")
@Test
public void parseHomieTree() throws InterruptedException, ExecutionException, TimeoutException {
// Create a Homie Device object. Because spied Nodes are required for call verification,
// the full Device constructor need to be used and a ChildMap object need to be created manually.
ChildMap<Node> nodeMap = new ChildMap<>();
Device device = spy(
new Device(ThingChannelConstants.testHomieThing, callback, new DeviceAttributes(), nodeMap));
// Intercept creating a node in initialize()->start() and inject a spy'ed node.
doAnswer(this::createSpyNode).when(device).createNode(any());
// initialize the device, subscribe and wait.
device.initialize(BASE_TOPIC, DEVICE_ID, Collections.emptyList());
device.subscribe(connection, scheduler, 1500).get();
assertThat(device.isInitialized(), is(true));
// Check device attributes
assertThat(device.attributes.homie, is("3.0"));
assertThat(device.attributes.name, is("Name"));
assertThat(device.attributes.state, is(ReadyState.ready));
assertThat(device.attributes.nodes.length, is(1));
verify(device, times(4)).attributeChanged(any(), any(), any(), any(), anyBoolean());
verify(callback).readyStateChanged(eq(ReadyState.ready));
verify(device).attributesReceived(any(), any(), anyInt());
// Expect 1 node
assertThat(device.nodes.size(), is(1));
// Check node and node attributes
Node node = device.nodes.get("testnode");
verify(node).subscribe(any(), any(), anyInt());
verify(node).attributesReceived(any(), any(), anyInt());
verify(node.attributes).subscribeAndReceive(any(), any(), anyString(), any(), anyInt());
assertThat(node.attributes.type, is("Type"));
assertThat(node.attributes.name, is("Testnode"));
// Expect 2 property
assertThat(node.properties.size(), is(3));
// Check property and property attributes
Property property = node.properties.get("temperature");
assertThat(property.attributes.settable, is(true));
assertThat(property.attributes.retained, is(true));
assertThat(property.attributes.name, is("Testprop"));
assertThat(property.attributes.unit, is("°C"));
assertThat(property.attributes.datatype, is(DataTypeEnum.float_));
assertThat(property.attributes.format, is("-100:100"));
verify(property).attributesReceived();
assertNotNull(property.getChannelState());
assertThat(property.getType().getState().getMinimum().intValue(), is(-100));
assertThat(property.getType().getState().getMaximum().intValue(), is(100));
// Check property and property attributes
Property propertyBell = node.properties.get("doorbell");
verify(propertyBell).attributesReceived();
assertThat(propertyBell.attributes.settable, is(false));
assertThat(propertyBell.attributes.retained, is(false));
assertThat(propertyBell.attributes.name, is("Doorbell"));
assertThat(propertyBell.attributes.datatype, is(DataTypeEnum.boolean_));
// The device->node->property tree is ready. Now subscribe to property values.
device.startChannels(connection, scheduler, 50, handler).get();
assertThat(propertyBell.getChannelState().isStateful(), is(false));
assertThat(propertyBell.getChannelState().getCache().getChannelState(), is(UnDefType.UNDEF));
assertThat(property.getChannelState().getCache().getChannelState(), is(new DecimalType(10)));
property = node.properties.get("testRetain");
WaitForTopicValue watcher = new WaitForTopicValue(embeddedConnection, propertyTestTopic + "/set");
// Watch the topic. Publish a retain=false value to MQTT
property.getChannelState().publishValue(OnOffType.OFF).get();
assertThat(watcher.waitForTopicValue(1000), is("false"));
// Publish a retain=false value to MQTT.
property.getChannelState().publishValue(OnOffType.ON).get();
// No value is expected to be retained on this MQTT topic
waitForAssert(() -> {
try {
WaitForTopicValue w = new WaitForTopicValue(embeddedConnection, propertyTestTopic + "/set");
assertNull(w.waitForTopicValue(50));
} catch (InterruptedException | ExecutionException e) {
}
}, 500, 100);
}
}

View File

@@ -0,0 +1,27 @@
/**
* Copyright (c) 2010-2020 Contributors to the openHAB project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.mqtt;
import static org.openhab.binding.mqtt.homie.generic.internal.MqttBindingConstants.HOMIE300_MQTT_THING;
import org.openhab.core.thing.ThingUID;
/**
* Static test definitions, like thing, bridge and channel definitions
*
* @author David Graeff - Initial contribution
*/
public class ThingChannelConstants {
// Common ThingUID and ChannelUIDs
public final static ThingUID testHomieThing = new ThingUID(HOMIE300_MQTT_THING, "device123");
}

View File

@@ -0,0 +1,29 @@
/**
* Copyright (c) 2010-2020 Contributors to the openHAB project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.mqtt.homie.internal.homie300;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.openhab.binding.mqtt.generic.ChannelState;
/**
* Helper to access {@link Property} internals.
*
* @author David Graeff - Initial contribution
*/
@NonNullByDefault
public class PropertyHelper {
public static void setChannelState(Property property, @Nullable ChannelState channelState) {
property.channelState = channelState;
}
}