added migrated 2.x add-ons
Signed-off-by: Kai Kreuzer <kai@openhab.org>
This commit is contained in:
27
itests/org.openhab.binding.mqtt.homie.tests/.classpath
Normal file
27
itests/org.openhab.binding.mqtt.homie.tests/.classpath
Normal 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>
|
||||
23
itests/org.openhab.binding.mqtt.homie.tests/.project
Normal file
23
itests/org.openhab.binding.mqtt.homie.tests/.project
Normal 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>
|
||||
13
itests/org.openhab.binding.mqtt.homie.tests/NOTICE
Normal file
13
itests/org.openhab.binding.mqtt.homie.tests/NOTICE
Normal 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
|
||||
73
itests/org.openhab.binding.mqtt.homie.tests/itest.bndrun
Normal file
73
itests/org.openhab.binding.mqtt.homie.tests/itest.bndrun
Normal 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
|
||||
104
itests/org.openhab.binding.mqtt.homie.tests/pom.xml
Normal file
104
itests/org.openhab.binding.mqtt.homie.tests/pom.xml
Normal 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>
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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");
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user