diff --git a/Dockerfile b/Dockerfile
index 780e8f4..53855e7 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -46,7 +46,7 @@ WORKDIR /opt/build
#COPY --from=app-build /opt/build/vclu/target/vclu.jar .
RUN $JAVA_HOME/bin/jlink \
- --add-modules java.base,java.net.http,java.xml,java.naming,java.management,jdk.zipfs,jdk.crypto.ec,jdk.httpserver \
+ --add-modules java.base,java.net.http,java.xml,java.naming,java.management,jdk.zipfs,jdk.crypto.ec,jdk.httpserver,jdk.unsupported \
# --add-modules $(jdeps --ignore-missing-deps --print-module-deps vclu.jar),java.base,java.xml,java.naming,java.management,java.sql,java.instrument,jdk.zipfs \
--strip-debug \
--no-man-pages \
diff --git a/entrypoint.sh b/entrypoint.sh
index ace3a1e..fb06190 100644
--- a/entrypoint.sh
+++ b/entrypoint.sh
@@ -4,6 +4,8 @@ exec "setpriv" "--reuid" "ubuntu" "--regid" "ubuntu" "--clear-groups" "--ambient
"$JAVA_HOME/bin/java" \
"-XX:+DisableAttachMechanism" \
"-server" "-Xshare:off" "-XX:+UseContainerSupport" "-XX:+UseZGC" "-XX:+UseDynamicNumberOfGCThreads" \
+ "--enable-native-access=ALL-UNNAMED" \
+ "--sun-misc-unsafe-memory-access=allow" \
"-XX:+ExitOnOutOfMemoryError" \
"-Djava.net.preferIPv6Addresses=false" \
"-Djava.net.preferIPv4Stack=true" \
diff --git a/modules/common/src/main/java/pl/psobiech/opengr8on/util/ThreadUtil.java b/modules/common/src/main/java/pl/psobiech/opengr8on/util/ThreadUtil.java
index cdb8cbe..aed9c60 100644
--- a/modules/common/src/main/java/pl/psobiech/opengr8on/util/ThreadUtil.java
+++ b/modules/common/src/main/java/pl/psobiech/opengr8on/util/ThreadUtil.java
@@ -38,7 +38,7 @@ public class ThreadUtil {
System.setProperty("jdk.virtualThreadScheduler.maxPoolSize", String.valueOf(maxPoolSize));
System.setProperty("jdk.virtualThreadScheduler.parallelism", String.valueOf(MIN_RUNNABLE));
- System.setProperty("jdk.tracePinnedThreads", "short");
+ //System.setProperty("jdk.tracePinnedThreads", "short");
LOGGER.debug("Virtual Threads: {}-{}", MIN_RUNNABLE, maxPoolSize);
diff --git a/modules/parent/pom.xml b/modules/parent/pom.xml
index 0791ad8..4208124 100644
--- a/modules/parent/pom.xml
+++ b/modules/parent/pom.xml
@@ -51,8 +51,8 @@
1.3.7
2.32.19
- 1.2.5
- 4.2.5.Final
+ 1.3.10
+ 4.1.128.Final
2.0.17
1.5.19
@@ -240,11 +240,22 @@
${jstachio.version}
-
- org.eclipse.paho
- org.eclipse.paho.client.mqttv3
- ${paho.mqtt.version}
+ com.hivemq
+ hivemq-mqtt-client
+ ${hivemq-mqtt-client.version}
+
+
+ io.netty
+ netty-codec-http
+ ${netty.version}
+
+
+ io.netty
+ netty-transport-native-epoll
+ ${netty.version}
+ linux-x86_64
+ runtime
@@ -307,25 +318,12 @@
-
- io.netty
- netty-codec-http
- ${netty.version}
- test
-
io.netty
netty-codec-mqtt
${netty.version}
test
-
- io.netty
- netty-transport-native-epoll
- ${netty.version}
- linux-x86_64
- test
-
diff --git a/modules/vclu/pom.xml b/modules/vclu/pom.xml
index 676bbc8..e091f86 100644
--- a/modules/vclu/pom.xml
+++ b/modules/vclu/pom.xml
@@ -71,8 +71,18 @@
- org.eclipse.paho
- org.eclipse.paho.client.mqttv3
+ com.hivemq
+ hivemq-mqtt-client
+
+
+ io.netty
+ netty-codec-http
+
+
+ io.netty
+ netty-transport-native-epoll
+ linux-x86_64
+ runtime
@@ -85,22 +95,11 @@
moquette-broker
test
-
- io.netty
- netty-codec-http
- test
-
io.netty
netty-codec-mqtt
test
-
- io.netty
- netty-transport-native-epoll
- linux-x86_64
- test
-
diff --git a/modules/vclu/src/main/java/pl/psobiech/opengr8on/vclu/Main.java b/modules/vclu/src/main/java/pl/psobiech/opengr8on/vclu/Main.java
index 9746e09..dec92b3 100644
--- a/modules/vclu/src/main/java/pl/psobiech/opengr8on/vclu/Main.java
+++ b/modules/vclu/src/main/java/pl/psobiech/opengr8on/vclu/Main.java
@@ -34,6 +34,7 @@
import java.nio.charset.StandardCharsets;
import java.nio.file.Path;
import java.nio.file.Paths;
+import java.util.List;
import java.util.concurrent.CountDownLatch;
/**
@@ -46,11 +47,7 @@ private Main() {
// NOP
}
- public static void main(String[] args) throws Exception {
- if (args.length < 1) {
- throw new UnexpectedException("Missing argument: Network Interface Name or local IP Address");
- }
-
+ static void main(String[] args) throws Exception {
final Path runtimeDirectory = Paths.get("./runtime").toAbsolutePath();
final Path rootDirectory = runtimeDirectory.resolve("root");
@@ -61,12 +58,22 @@ public static void main(String[] args) throws Exception {
LOGGER.info("Current VCLU PIN: {}", new String(cluKeys.pin(), StandardCharsets.US_ASCII));
- final String networkInterfaceNameOrIpAddress = args[args.length - 1];
- final NetworkInterfaceDto networkInterface = IPv4AddressUtil.getLocalIPv4NetworkInterfaceByName(networkInterfaceNameOrIpAddress)
- .or(() ->
- IPv4AddressUtil.getLocalIPv4NetworkInterfaceByIpAddress(networkInterfaceNameOrIpAddress)
- )
- .orElseThrow(() -> new UnexpectedException("Cannot find IP address of interface: " + networkInterfaceNameOrIpAddress));
+ final NetworkInterfaceDto networkInterface;
+ if (args.length > 0) {
+ final String networkInterfaceNameOrIpAddress = args[args.length - 1];
+ networkInterface = IPv4AddressUtil.getLocalIPv4NetworkInterfaceByName(networkInterfaceNameOrIpAddress)
+ .or(() ->
+ IPv4AddressUtil.getLocalIPv4NetworkInterfaceByIpAddress(networkInterfaceNameOrIpAddress)
+ )
+ .orElseThrow(() -> new UnexpectedException("Cannot find IP address of interface: " + networkInterfaceNameOrIpAddress));
+ } else {
+ final List networkInterfaces = IPv4AddressUtil.getLocalIPv4NetworkInterfaces();
+ if (networkInterfaces.isEmpty()) {
+ throw new UnexpectedException("No network interfaces found");
+ }
+
+ networkInterface = networkInterfaces.getFirst();
+ }
final CLUDevice cluDevice = readCluDevice(aDriveDirectory, networkInterface, cluKeys);
diff --git a/modules/vclu/src/main/java/pl/psobiech/opengr8on/vclu/MqttClient.java b/modules/vclu/src/main/java/pl/psobiech/opengr8on/vclu/MqttClient.java
index c71805b..7c69c97 100644
--- a/modules/vclu/src/main/java/pl/psobiech/opengr8on/vclu/MqttClient.java
+++ b/modules/vclu/src/main/java/pl/psobiech/opengr8on/vclu/MqttClient.java
@@ -19,27 +19,47 @@
package pl.psobiech.opengr8on.vclu;
import com.fasterxml.jackson.core.JsonProcessingException;
-import org.eclipse.paho.client.mqttv3.*;
+import com.hivemq.client.mqtt.*;
+import com.hivemq.client.mqtt.datatypes.MqttQos;
+import com.hivemq.client.mqtt.datatypes.MqttTopic;
+import com.hivemq.client.mqtt.lifecycle.MqttClientAutoReconnect;
+import com.hivemq.client.mqtt.mqtt5.Mqtt5AsyncClient;
+import com.hivemq.client.mqtt.mqtt5.Mqtt5Client;
+import com.hivemq.client.mqtt.mqtt5.message.auth.Mqtt5SimpleAuth;
+import com.hivemq.client.mqtt.mqtt5.message.connect.Mqtt5Connect;
+import com.hivemq.client.mqtt.mqtt5.message.publish.Mqtt5Publish;
+import com.hivemq.client.mqtt.mqtt5.message.subscribe.Mqtt5Subscribe;
+import com.hivemq.client.mqtt.mqtt5.message.unsubscribe.Mqtt5Unsubscribe;
+import io.reactivex.internal.schedulers.ExecutorScheduler;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+import pl.psobiech.opengr8on.exceptions.UncheckedInterruptedException;
import pl.psobiech.opengr8on.exceptions.UnexpectedException;
import pl.psobiech.opengr8on.util.*;
-import pl.psobiech.opengr8on.vclu.system.objects.MqttTopic;
import pl.psobiech.opengr8on.vclu.system.objects.VirtualCLU;
import pl.psobiech.opengr8on.vclu.util.TlsUtil;
+import javax.net.ssl.KeyManagerFactory;
+import javax.net.ssl.TrustManagerFactory;
import java.io.Closeable;
+import java.io.IOException;
import java.net.URI;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
+import java.security.KeyStore;
+import java.security.KeyStoreException;
+import java.security.NoSuchAlgorithmException;
+import java.security.UnrecoverableKeyException;
+import java.security.cert.CertificateException;
+import java.security.cert.X509Certificate;
import java.util.*;
+import java.util.concurrent.ExecutionException;
import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.TimeUnit;
import java.util.function.Consumer;
public class MqttClient implements Closeable {
- public static final int MQTT_QOS_AT_LEAST_ONCE = 1;
-
private static final Logger LOGGER = LoggerFactory.getLogger(MqttClient.class);
private static final String SCHEME_TCP = "tcp";
@@ -48,22 +68,25 @@ public class MqttClient implements Closeable {
private static final int KEEP_ALIVE_INTERVAL_SECONDS = 10;
- private static final int MAX_INFLIGHT = 256;
-
// the mqtt client requires at least 4 threads (also, it does not support virtual threads)
- private final ScheduledExecutorService executor = ThreadUtil.daemonScheduler(5, "MQTT");
+ private final ScheduledExecutorService executor = ThreadUtil.virtualScheduler("MQTT");
+
+ private final Map>> mqttSubscriptions = new Hashtable<>();
+
+ private Mqtt5AsyncClient mqttClient;
- private final Map>> mqttSubscriptions = new Hashtable<>();
+ public boolean isStarted() {
+ return mqttClient != null;
+ }
- private MqttAsyncClient mqttClient;
+ public void start(
+ String mqttUrl, String name,
+ Path caCertificatePath, Path clientCertificatePath, Path clientKeyPath,
+ VirtualCLU virtualClu
+ ) {
+ final URI mqttUri = URI.create(mqttUrl);
- private static MqttConnectOptions createConnectionOptions(URI mqttUri, Path caCertificatePath, Path certificatePath, Path keyPath) {
- final MqttConnectOptions options = new MqttConnectOptions();
- options.setConnectionTimeout(CONNECTION_TIMEOUT_SECONDS);
- options.setKeepAliveInterval(KEEP_ALIVE_INTERVAL_SECONDS);
- options.setAutomaticReconnect(true);
- options.setCleanSession(false);
- options.setMaxInflight(MAX_INFLIGHT);
+ Mqtt5SimpleAuth simpleAuth = null;
final String userInfo = mqttUri.getUserInfo();
if (userInfo != null) {
@@ -71,145 +94,169 @@ private static MqttConnectOptions createConnectionOptions(URI mqttUri, Path caCe
if (userInfoPartsOptional.isPresent()) {
final String[] userInfoParts = userInfoPartsOptional.get();
- options.setUserName(userInfoParts[0]);
- options.setPassword(userInfoParts[1].toCharArray());
+ simpleAuth = Mqtt5SimpleAuth.builder()
+ .username(userInfoParts[0])
+ .password(userInfoParts[1].getBytes(StandardCharsets.UTF_8))
+ .build();
}
}
+ final MqttClientSslConfig mqttClientSslConfig;
if (!SCHEME_TCP.equals(mqttUri.getScheme()) && Files.exists(caCertificatePath)) {
- options.setSocketFactory(
- TlsUtil.createSocketFactory(
- caCertificatePath,
- certificatePath, keyPath
- )
- );
- }
+ final KeyManagerFactory clientKeyManagerFactory;
+ final TrustManagerFactory caTrustManagerFactory;
+ try {
+ final KeyStore caKeyStore = KeyStore.getInstance(KeyStore.getDefaultType());
+ caKeyStore.load(null, null);
+ caKeyStore.setCertificateEntry("certificate", TlsUtil.readCertificate(caCertificatePath));
+
+ final KeyStore clientKeyStore = KeyStore.getInstance(KeyStore.getDefaultType());
+ clientKeyStore.load(null, null);
+ if (Files.exists(clientCertificatePath) && Files.exists(clientKeyPath)) {
+ final X509Certificate clientCertificate = TlsUtil.readCertificate(clientCertificatePath);
+ clientKeyStore.setCertificateEntry("certificate", clientCertificate);
+ clientKeyStore.setKeyEntry("key", TlsUtil.readPrivateKey(clientKeyPath), null, new java.security.cert.Certificate[]{clientCertificate});
+ }
- return options;
- }
+ clientKeyManagerFactory = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
+ clientKeyManagerFactory.init(clientKeyStore, null);
- public boolean isStarted() {
- return mqttClient != null;
- }
+ caTrustManagerFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
+ caTrustManagerFactory.init(caKeyStore);
+ } catch (CertificateException | KeyStoreException | IOException | NoSuchAlgorithmException |
+ UnrecoverableKeyException e) {
+ throw new UnexpectedException("Could not initialize SSL context", e);
+ }
- public void start(
- String mqttUrl, String name,
- Path caCertificatePath, Path certificatePath, Path keyPath,
- VirtualCLU virtualClu
- ) {
- final URI mqttUri = URI.create(mqttUrl);
+ mqttClientSslConfig = MqttClientSslConfig.builder()
+ .trustManagerFactory(caTrustManagerFactory)
+ .keyManagerFactory(clientKeyManagerFactory)
+ .build();
- try {
- mqttClient = new MqttAsyncClient(
- mqttUrl, name,
- null,
- new ScheduledExecutorPingSender(executor),
- executor
- );
-
- mqttClient.setManualAcks(true);
- mqttClient.setCallback(new MqttCallback() {
- @Override
- public void connectionLost(Throwable throwable) {
- onMqttConnectionChange(virtualClu, null);
- }
+ } else {
+ mqttClientSslConfig = null;
+ }
- @Override
- public void messageArrived(String topic, MqttMessage message) {
- try {
- for (MqttTopic mqttTopic : virtualClu.getMqttTopics()) {
- try {
- mqttTopic.onMessage(
- topic, message.getPayload(), () -> {
- }
- );
- } catch (Exception e) {
- LOGGER.error(e.getMessage(), e);
- }
- }
+ final MqttWebSocketConfig webSocketConfig;
+ if (mqttUri.getScheme().startsWith("ws")) {
+ webSocketConfig = MqttWebSocketConfig.builder()
+ .build();
+ } else {
+ webSocketConfig = null;
+ }
- final List> consumers = mqttSubscriptions.getOrDefault(topic, Collections.emptyList());
- for (Consumer consumer : consumers) {
- try {
- consumer.accept(message.getPayload());
- } catch (Exception e) {
- LOGGER.error(e.getMessage(), e);
+ final MqttClientTransportConfig transportConfig = MqttClientTransportConfig.builder()
+ .webSocketWithDefaultConfig()
+ .webSocketConfig(webSocketConfig)
+ .sslWithDefaultConfig()
+ .sslConfig(mqttClientSslConfig)
+ .serverHost(mqttUri.getHost())
+ .serverPort(mqttUri.getPort())
+ .socketConnectTimeout(CONNECTION_TIMEOUT_SECONDS, TimeUnit.SECONDS)
+ .mqttConnectTimeout(CONNECTION_TIMEOUT_SECONDS, TimeUnit.SECONDS)
+ .build();
+
+ mqttClient = Mqtt5Client.builder()
+ .identifier(name + "/" + ServerVersion.get() + "-" + UUID.randomUUID())
+ .simpleAuth(simpleAuth)
+ .transportConfig(
+ transportConfig
+ )
+ .automaticReconnect(
+ MqttClientAutoReconnect.builder()
+ .build()
+ )
+ .executorConfig(
+ MqttClientExecutorConfig.builder()
+ .applicationScheduler(
+ new ExecutorScheduler(executor, true)
+ )
+ .nettyExecutor(executor)
+ .build()
+ )
+ .addConnectedListener(context -> onMqttConnectionChange(virtualClu, null))
+ .addDisconnectedListener(context -> onMqttConnectionChange(virtualClu, context.getCause()))
+ .buildAsync();
+
+ mqttClient.connect(
+ Mqtt5Connect.builder()
+ .cleanStart(false)
+ .build()
+ );
+
+ mqttClient.publishes(
+ MqttGlobalPublishFilter.SUBSCRIBED,
+ mqtt5Publish -> {
+ boolean ack = false;
+ for (Map.Entry>> entry : mqttSubscriptions.entrySet()) {
+ if (mqtt5Publish.getTopic().filter()
+ .matches(entry.getKey())) {
+ for (Consumer consumer : entry.getValue()) {
+ consumer.accept(mqtt5Publish.getPayloadAsBytes());
}
+
+ ack = true;
}
- } finally {
- executor.submit(() -> {
- try {
- mqttClient.messageArrivedComplete(message.getId(), message.getQos());
- } catch (MqttException e) {
- LOGGER.error(e.getMessage(), e);
- }
- });
}
- }
-
- @Override
- public void deliveryComplete(IMqttDeliveryToken deliveryToken) {
- // NOP
- }
- });
-
- final MqttConnectOptions options = createConnectionOptions(mqttUri, caCertificatePath, certificatePath, keyPath);
- mqttClient.connect(options, null, new IMqttActionListener() {
- @Override
- public void onSuccess(IMqttToken asyncActionToken) {
- onMqttConnectionChange(virtualClu, null);
- }
-
- @Override
- public void onFailure(IMqttToken asyncActionToken, Throwable exception) {
- onMqttConnectionChange(virtualClu, exception);
- }
- });
- } catch (MqttException e) {
- throw new UnexpectedException(e);
- }
+ if (ack) {
+ mqtt5Publish.acknowledge();
+ }
+ });
}
private void onMqttConnectionChange(VirtualCLU virtualClu, Throwable exception) {
- final boolean connected = mqttClient.isConnected();
- LOGGER.debug("MQTT {} Connected: {}", mqttClient.getClientId(), connected, exception);
+ final boolean connected = mqttClient.getState().isConnected();
+ LOGGER.debug("MQTT {} Connected: {}", mqttClient.getConfig().getClientIdentifier(), connected, exception);
virtualClu.setMqttConnected(connected);
}
- public void subscribe(Set topicFilterSet) throws MqttException {
- final String[] topicFilters = topicFilterSet.toArray(String[]::new);
-
- final int[] qos = new int[topicFilters.length];
- Arrays.fill(qos, MQTT_QOS_AT_LEAST_ONCE);
-
- LOGGER.trace("MQTT {} Subscribe: {} / MQTT_QOS_AT_LEAST_ONCE", mqttClient.getClientId(), Arrays.toString(topicFilters));
-
- mqttClient.subscribe(topicFilters, qos);
- }
-
public void subscribe(String topicFilter, Consumer consumer) {
- mqttSubscriptions.computeIfAbsent(topicFilter, ignored -> new ArrayList<>())
- .add(consumer);
+ LOGGER.trace("MQTT {} Subscribe: {} / MQTT_QOS_AT_LEAST_ONCE", mqttClient.getConfig().getClientIdentifier(), topicFilter);
try {
- subscribe(topicFilter);
- } catch (MqttException e) {
- throw new UnexpectedException(e);
+ mqttClient.subscribe(
+ Mqtt5Subscribe.builder()
+ .topicFilter(topicFilter)
+ .qos(MqttQos.AT_LEAST_ONCE)
+ .build(),
+ mqtt5Publish -> consumer.accept(mqtt5Publish.getPayloadAsBytes())
+ )
+ .get();
+ } catch (InterruptedException e) {
+ throw new UncheckedInterruptedException(e);
+ } catch (ExecutionException e) {
+ throw new UnexpectedException(e.getCause());
}
}
- public void subscribe(String topicFilter) throws MqttException {
- LOGGER.trace("MQTT {} Subscribe: {} / MQTT_QOS_AT_LEAST_ONCE", mqttClient.getClientId(), topicFilter);
+ public void subscribe(String topicFilter) {
+ LOGGER.trace("MQTT {} Subscribe: {} / MQTT_QOS_AT_LEAST_ONCE", mqttClient.getConfig().getClientIdentifier(), topicFilter);
- mqttClient.subscribe(topicFilter, MQTT_QOS_AT_LEAST_ONCE);
+ try {
+ mqttClient.subscribe(
+ Mqtt5Subscribe.builder()
+ .topicFilter(topicFilter)
+ .qos(MqttQos.AT_LEAST_ONCE)
+ .build()
+ )
+ .get();
+ } catch (InterruptedException e) {
+ throw new UncheckedInterruptedException(e);
+ } catch (ExecutionException e) {
+ throw new UnexpectedException(e.getCause());
+ }
}
- public void unsubscribe(String topicFilter) throws MqttException {
- LOGGER.trace("MQTT {} Unsubscribe: {}", mqttClient.getClientId(), topicFilter);
+ public void unsubscribe(String topicFilter) {
+ LOGGER.trace("MQTT {} Unsubscribe: {}", mqttClient.getConfig().getClientIdentifier(), topicFilter);
- mqttClient.unsubscribe(topicFilter);
+ mqttClient.unsubscribe(
+ Mqtt5Unsubscribe.builder()
+ .topicFilter(topicFilter)
+ .build()
+ );
}
public void tryPublish(String topic, Object payloadObject) {
@@ -228,7 +275,7 @@ public void tryPublish(String topic, Object payloadObject, boolean retain) {
try {
publish(topic, payload, retain);
- } catch (MqttException | RuntimeException e) {
+ } catch (RuntimeException e) {
LOGGER.error("Could not publish message ({}) to topic {}", HexUtil.asString(payload), topic, e);
}
}
@@ -249,23 +296,26 @@ public static byte[] parsePayload(Object payloadObject) {
}
}
- public int publish(String topic, byte[] payload) throws MqttException {
- return publish(topic, payload, false);
+ public void publish(String topic, byte[] payload) {
+ publish(topic, payload, false);
}
- public int publish(String topic, byte[] payload, boolean retained) throws MqttException {
- LOGGER.trace("MQTT {} Publish: {} / {}", mqttClient.getClientId(), topic, ToStringUtil.toString(payload));
+ public void publish(String topic, byte[] payload, boolean retained) {
+ LOGGER.trace("MQTT {} Publish: {} / {}", mqttClient.getConfig().getClientIdentifier(), topic, ToStringUtil.toString(payload));
- final IMqttDeliveryToken publish = mqttClient.publish(
- topic, payload,
- MQTT_QOS_AT_LEAST_ONCE, retained
- );
-
- if (publish == null) {
- throw new UnexpectedException("mqtt publish returned null");
+ try {
+ mqttClient.publish(
+ Mqtt5Publish.builder()
+ .topic(topic)
+ .payload(payload)
+ .build()
+ )
+ .get();
+ } catch (InterruptedException e) {
+ throw new UnexpectedException(e);
+ } catch (ExecutionException e) {
+ throw new UnexpectedException(e.getCause());
}
-
- return publish.getMessageId();
}
@Override
@@ -277,14 +327,9 @@ public void close() {
public void stop() {
if (mqttClient != null) {
- try {
- mqttClient.disconnect();
- } catch (MqttException e) {
- LOGGER.error(e.getMessage(), e);
- }
+ mqttClient.disconnect();
}
- IOUtil.closeQuietly(mqttClient);
mqttClient = null;
}
}
diff --git a/modules/vclu/src/main/java/pl/psobiech/opengr8on/vclu/mqtt/discovery/MqttDiscovery.java b/modules/vclu/src/main/java/pl/psobiech/opengr8on/vclu/mqtt/discovery/MqttDiscovery.java
index f0a6ddc..f9cccea 100644
--- a/modules/vclu/src/main/java/pl/psobiech/opengr8on/vclu/mqtt/discovery/MqttDiscovery.java
+++ b/modules/vclu/src/main/java/pl/psobiech/opengr8on/vclu/mqtt/discovery/MqttDiscovery.java
@@ -15,6 +15,12 @@ public class MqttDiscovery extends MqttJson {
@JsonProperty("~")
private final String rootTopic;
+ @JsonProperty("availability_topic")
+ private final String availabilityTopic;
+
+ @JsonProperty("availability_mode")
+ private final String availabilityMode = "latest";
+
@JsonProperty("command_topic")
private final String setStateTopic;
@@ -41,7 +47,7 @@ public class MqttDiscovery extends MqttJson {
public MqttDiscovery(
String name, String uniqueId,
- String rootTopic, String setStateTopic, String stateTopic,
+ String rootTopic, String availabilityTopic, String setStateTopic, String stateTopic,
String deviceClass, String unitOfMeasurement,
String schema, String valueTemplate,
MqttDiscoveryDevice device, MqttDiscoveryOrigin origin
@@ -50,6 +56,7 @@ public MqttDiscovery(
this.uniqueId = uniqueId;
this.rootTopic = rootTopic;
+ this.availabilityTopic = StringUtils.stripToNull(availabilityTopic);
this.setStateTopic = StringUtils.stripToNull(setStateTopic);
this.stateTopic = StringUtils.stripToNull(stateTopic);
@@ -87,6 +94,10 @@ public String getDiscoveryTopic() {
return getAbsoluteTopic(getRootTopic(), "~/config");
}
+ public String getAvailabilityTopic() {
+ return getAbsoluteTopic(getRootTopic(), availabilityTopic);
+ }
+
public String getSetStateTopic() {
return getAbsoluteTopic(getRootTopic(), setStateTopic);
}
diff --git a/modules/vclu/src/main/java/pl/psobiech/opengr8on/vclu/mqtt/discovery/MqttDiscoveryButton.java b/modules/vclu/src/main/java/pl/psobiech/opengr8on/vclu/mqtt/discovery/MqttDiscoveryButton.java
index 8ab43e3..7deb4b5 100644
--- a/modules/vclu/src/main/java/pl/psobiech/opengr8on/vclu/mqtt/discovery/MqttDiscoveryButton.java
+++ b/modules/vclu/src/main/java/pl/psobiech/opengr8on/vclu/mqtt/discovery/MqttDiscoveryButton.java
@@ -12,17 +12,17 @@ public class MqttDiscoveryButton extends MqttDiscovery {
public MqttDiscoveryButton(
String name, String uniqueId,
- String rootTopic, String commandTopic, String stateTopic,
+ String rootTopic, String availabilityTopic, String commandTopic, String stateTopic,
String deviceClass, String unitOfMeasurement,
String schema, String valueTemplate, Set eventTypes,
MqttDiscoveryDevice device
) {
super(
name, uniqueId,
- rootTopic, commandTopic, stateTopic,
+ rootTopic, availabilityTopic, commandTopic, stateTopic,
deviceClass, unitOfMeasurement,
schema, valueTemplate,
- device, new MqttDiscoveryOrigin()
+ device, MqttDiscoveryOrigin.INSTANCE
);
this.eventTypes = eventTypes;
diff --git a/modules/vclu/src/main/java/pl/psobiech/opengr8on/vclu/mqtt/discovery/MqttDiscoveryLight.java b/modules/vclu/src/main/java/pl/psobiech/opengr8on/vclu/mqtt/discovery/MqttDiscoveryLight.java
index edc26fa..0718932 100644
--- a/modules/vclu/src/main/java/pl/psobiech/opengr8on/vclu/mqtt/discovery/MqttDiscoveryLight.java
+++ b/modules/vclu/src/main/java/pl/psobiech/opengr8on/vclu/mqtt/discovery/MqttDiscoveryLight.java
@@ -12,17 +12,17 @@ public class MqttDiscoveryLight extends MqttDiscovery {
public MqttDiscoveryLight(
String name, String uniqueId,
- String rootTopic, String commandTopic, String stateTopic,
+ String rootTopic, String availabilityTopic, String commandTopic, String stateTopic,
String deviceClass, String unitOfMeasurement,
String schema, String valueTemplate, Set supportedColorModes,
MqttDiscoveryDevice device
) {
super(
name, uniqueId,
- rootTopic, commandTopic, stateTopic,
+ rootTopic, availabilityTopic, commandTopic, stateTopic,
deviceClass, unitOfMeasurement,
schema, valueTemplate,
- device, new MqttDiscoveryOrigin()
+ device, MqttDiscoveryOrigin.INSTANCE
);
this.supportedColorModes = supportedColorModes;
diff --git a/modules/vclu/src/main/java/pl/psobiech/opengr8on/vclu/mqtt/discovery/MqttDiscoveryNumericFloat.java b/modules/vclu/src/main/java/pl/psobiech/opengr8on/vclu/mqtt/discovery/MqttDiscoveryNumericFloat.java
index 7693b84..7f36d62 100644
--- a/modules/vclu/src/main/java/pl/psobiech/opengr8on/vclu/mqtt/discovery/MqttDiscoveryNumericFloat.java
+++ b/modules/vclu/src/main/java/pl/psobiech/opengr8on/vclu/mqtt/discovery/MqttDiscoveryNumericFloat.java
@@ -13,18 +13,18 @@ public class MqttDiscoveryNumericFloat extends MqttDiscovery {
public MqttDiscoveryNumericFloat(
String name, String uniqueId,
- String rootTopic, String commandTopic, String stateTopic,
+ String rootTopic, String availabilityTopic, String commandTopic, String stateTopic,
String deviceClass, String unitOfMeasurement,
String schema, Float max, Float min,
MqttDiscoveryDevice device
) {
super(
name, uniqueId,
- rootTopic, commandTopic, stateTopic,
+ rootTopic, availabilityTopic, commandTopic, stateTopic,
deviceClass, unitOfMeasurement,
schema, "{{ value | float }}",
device,
- new MqttDiscoveryOrigin()
+ MqttDiscoveryOrigin.INSTANCE
);
this.max = max;
diff --git a/modules/vclu/src/main/java/pl/psobiech/opengr8on/vclu/mqtt/discovery/MqttDiscoveryOrigin.java b/modules/vclu/src/main/java/pl/psobiech/opengr8on/vclu/mqtt/discovery/MqttDiscoveryOrigin.java
index 26a4198..464fbc1 100644
--- a/modules/vclu/src/main/java/pl/psobiech/opengr8on/vclu/mqtt/discovery/MqttDiscoveryOrigin.java
+++ b/modules/vclu/src/main/java/pl/psobiech/opengr8on/vclu/mqtt/discovery/MqttDiscoveryOrigin.java
@@ -7,6 +7,8 @@
@JsonInclude(JsonInclude.Include.NON_NULL)
public class MqttDiscoveryOrigin {
+ public static MqttDiscoveryOrigin INSTANCE = new MqttDiscoveryOrigin();
+
private final String name;
@JsonProperty("sw_version")
@@ -15,7 +17,7 @@ public class MqttDiscoveryOrigin {
@JsonProperty("url")
private final String url;
- public MqttDiscoveryOrigin() {
+ private MqttDiscoveryOrigin() {
this(
"opengr8on", ServerVersion.get(),
"https://github.com/psobiech/opengr8on"
diff --git a/modules/vclu/src/main/java/pl/psobiech/opengr8on/vclu/mqtt/discovery/MqttDiscoveryShutter.java b/modules/vclu/src/main/java/pl/psobiech/opengr8on/vclu/mqtt/discovery/MqttDiscoveryShutter.java
index e1d4b06..dfafab6 100644
--- a/modules/vclu/src/main/java/pl/psobiech/opengr8on/vclu/mqtt/discovery/MqttDiscoveryShutter.java
+++ b/modules/vclu/src/main/java/pl/psobiech/opengr8on/vclu/mqtt/discovery/MqttDiscoveryShutter.java
@@ -31,17 +31,17 @@ public class MqttDiscoveryShutter extends MqttDiscovery {
public MqttDiscoveryShutter(
String name, String uniqueId,
- String rootTopic, String commandTopic, String stateTopic, String positionStateTopic, String setPositionTopic,
+ String rootTopic, String availabilityTopic, String commandTopic, String stateTopic, String positionStateTopic, String setPositionTopic,
String deviceClass, String unitOfMeasurement,
String schema, String valueTemplate, String setPositionTemplate,
MqttDiscoveryDevice device
) {
super(
name, uniqueId,
- rootTopic, commandTopic, stateTopic,
+ rootTopic, availabilityTopic, commandTopic, stateTopic,
deviceClass, unitOfMeasurement,
schema, valueTemplate,
- device, new MqttDiscoveryOrigin()
+ device, MqttDiscoveryOrigin.INSTANCE
);
this.positionStateTopic = positionStateTopic;
diff --git a/modules/vclu/src/main/java/pl/psobiech/opengr8on/vclu/system/objects/MqttTopic.java b/modules/vclu/src/main/java/pl/psobiech/opengr8on/vclu/system/objects/MqttTopic.java
index 3638986..fd01381 100644
--- a/modules/vclu/src/main/java/pl/psobiech/opengr8on/vclu/system/objects/MqttTopic.java
+++ b/modules/vclu/src/main/java/pl/psobiech/opengr8on/vclu/system/objects/MqttTopic.java
@@ -20,7 +20,7 @@
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonNode;
-import org.eclipse.paho.client.mqttv3.MqttException;
+import com.hivemq.client.mqtt.datatypes.MqttTopicFilter;
import org.luaj.vm2.LuaValue;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -113,7 +113,7 @@ private LuaValue subscribe(LuaValue arg1) {
}
return LuaValue.TRUE;
- } catch (MqttException e) {
+ } catch (RuntimeException e) {
LOGGER.error(e.getMessage(), e);
}
@@ -130,7 +130,7 @@ private LuaValue unsubscribe(LuaValue arg1) {
}
return LuaValue.TRUE;
- } catch (MqttException e) {
+ } catch (RuntimeException e) {
LOGGER.error(e.getMessage(), e);
}
@@ -158,7 +158,7 @@ private LuaValue publish(LuaValue topicArg, LuaValue messageArg) {
mqttClient.publish(topic, payload);
return LuaValue.TRUE;
- } catch (IOException | MqttException e) {
+ } catch (IOException | RuntimeException e) {
LOGGER.error(e.getMessage(), e);
}
@@ -178,7 +178,7 @@ public void onMessage(String topic, byte[] payload, Runnable acknowledged) {
private boolean isSubscribedTo(String topic) {
for (String topicFilter : topicFilters) {
- if (org.eclipse.paho.client.mqttv3.MqttTopic.isMatched(topicFilter, topic)) {
+ if (MqttTopicFilter.of(topicFilter).matches(com.hivemq.client.mqtt.datatypes.MqttTopic.of(topic))) {
return true;
}
}
diff --git a/modules/vclu/src/main/java/pl/psobiech/opengr8on/vclu/system/objects/VirtualCLU.java b/modules/vclu/src/main/java/pl/psobiech/opengr8on/vclu/system/objects/VirtualCLU.java
index 91b23df..5aa9522 100644
--- a/modules/vclu/src/main/java/pl/psobiech/opengr8on/vclu/system/objects/VirtualCLU.java
+++ b/modules/vclu/src/main/java/pl/psobiech/opengr8on/vclu/system/objects/VirtualCLU.java
@@ -22,7 +22,6 @@
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.fasterxml.jackson.databind.node.TextNode;
-import org.eclipse.paho.client.mqttv3.MqttException;
import org.luaj.vm2.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -297,13 +296,13 @@ private LuaValue mqttSendDiscovery(Varargs args) {
name,
uniqueId,
rootTopic,
- "~/set", "~/state",
+ null, "~/set", "~/state",
deviceClass,
unit,
null,
valueTemplate,
new MqttDiscoveryDevice(clu),
- new MqttDiscoveryOrigin()
+ MqttDiscoveryOrigin.INSTANCE
);
try {
@@ -313,7 +312,7 @@ private LuaValue mqttSendDiscovery(Varargs args) {
ObjectMapperFactory.JSON.writeValueAsBytes(discoveryMessage),
true
);
- } catch (MqttException | JsonProcessingException e) {
+ } catch (JsonProcessingException e) {
throw new UnexpectedException("Could not publish discovery message for " + discoveryMessage.getUniqueId(), e);
}
@@ -350,15 +349,11 @@ private LuaValue mqttSendValue(LuaValue arg1, LuaValue arg2) {
final String discoveryPrefix = get(Features.MQTT_DISCOVERY_PREFIX).checkjstring();
final String rootTopic = "%s/%s/%s".formatted(discoveryPrefix, "sensor", uniqueId);
- try {
- mqttClient
- .publish(
- rootTopic + "/state",
- state.getBytes(StandardCharsets.UTF_8)
- );
- } catch (MqttException e) {
- LOGGER.error("Could not publish state update message for {}", uniqueId, e);
- }
+ mqttClient
+ .publish(
+ rootTopic + "/state",
+ state.getBytes(StandardCharsets.UTF_8)
+ );
return LuaValue.NIL;
}
diff --git a/modules/vclu/src/main/java/pl/psobiech/opengr8on/vclu/system/objects/remoteclu/BasicRemoteCLUSensor.java b/modules/vclu/src/main/java/pl/psobiech/opengr8on/vclu/system/objects/remoteclu/BasicRemoteCLUSensor.java
index ea4b949..456207b 100644
--- a/modules/vclu/src/main/java/pl/psobiech/opengr8on/vclu/system/objects/remoteclu/BasicRemoteCLUSensor.java
+++ b/modules/vclu/src/main/java/pl/psobiech/opengr8on/vclu/system/objects/remoteclu/BasicRemoteCLUSensor.java
@@ -1,7 +1,6 @@
package pl.psobiech.opengr8on.vclu.system.objects.remoteclu;
import com.fasterxml.jackson.databind.JsonNode;
-import org.eclipse.paho.client.mqttv3.MqttException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import pl.psobiech.opengr8on.util.ToStringUtil;
@@ -133,7 +132,7 @@ protected JsonNode pushState(JsonNode lastState, JsonNode newState) {
);
return stateNode;
- } catch (MqttException | RuntimeException e) {
+ } catch (RuntimeException e) {
LOGGER.error("Could not publish state update message for {}", discoveryMessage.getUniqueId(), e);
return lastState;
diff --git a/modules/vclu/src/main/java/pl/psobiech/opengr8on/vclu/system/objects/remoteclu/RemoteCLU.java b/modules/vclu/src/main/java/pl/psobiech/opengr8on/vclu/system/objects/remoteclu/RemoteCLU.java
index d606a86..27b93b1 100644
--- a/modules/vclu/src/main/java/pl/psobiech/opengr8on/vclu/system/objects/remoteclu/RemoteCLU.java
+++ b/modules/vclu/src/main/java/pl/psobiech/opengr8on/vclu/system/objects/remoteclu/RemoteCLU.java
@@ -227,7 +227,6 @@ private void initMqttDiscovery(String discoveryPrefix) {
public void mqttOnValueChange(String nameOnCLU, LuaValue arg2) {
final RemoteCLUDevice remoteCLUDevice = devices.get(nameOnCLU);
-
if (remoteCLUDevice != null) {
LOGGER.trace("Received event: mqttOnValueChange(\"{}->{}\")", name, nameOnCLU);
diff --git a/modules/vclu/src/main/java/pl/psobiech/opengr8on/vclu/system/objects/remoteclu/RemoteCLUButton.java b/modules/vclu/src/main/java/pl/psobiech/opengr8on/vclu/system/objects/remoteclu/RemoteCLUButton.java
index 442a898..ff30fa8 100644
--- a/modules/vclu/src/main/java/pl/psobiech/opengr8on/vclu/system/objects/remoteclu/RemoteCLUButton.java
+++ b/modules/vclu/src/main/java/pl/psobiech/opengr8on/vclu/system/objects/remoteclu/RemoteCLUButton.java
@@ -2,7 +2,6 @@
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonNode;
-import org.eclipse.paho.client.mqttv3.MqttException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import pl.psobiech.opengr8on.util.ObjectMapperFactory;
@@ -43,7 +42,8 @@ public RemoteCLUButton(
this.discoveryMessage = new MqttDiscoveryButton(
object.getName(),
uniqueId,
- "%s/%s/%s".formatted(discoveryPrefix, "event", uniqueId), null, "~/state",
+ "%s/%s/%s".formatted(discoveryPrefix, "event", uniqueId),
+ null, null, "~/state",
"button",
null,
"json",
@@ -82,7 +82,7 @@ private void sendDiscoveryMessage() {
ObjectMapperFactory.JSON.writeValueAsBytes(discoveryMessage),
true
);
- } catch (MqttException | JsonProcessingException | RuntimeException e) {
+ } catch (JsonProcessingException | RuntimeException e) {
LOGGER.error("Could not publish discovery message for {}", discoveryMessage.getUniqueId(), e);
}
}
diff --git a/modules/vclu/src/main/java/pl/psobiech/opengr8on/vclu/system/objects/remoteclu/RemoteCLUDimmer.java b/modules/vclu/src/main/java/pl/psobiech/opengr8on/vclu/system/objects/remoteclu/RemoteCLUDimmer.java
index 9e289c6..d5b131a 100644
--- a/modules/vclu/src/main/java/pl/psobiech/opengr8on/vclu/system/objects/remoteclu/RemoteCLUDimmer.java
+++ b/modules/vclu/src/main/java/pl/psobiech/opengr8on/vclu/system/objects/remoteclu/RemoteCLUDimmer.java
@@ -31,7 +31,8 @@ public RemoteCLUDimmer(
new MqttDiscoveryLight(
object.getName(),
uniqueId,
- "%s/%s/%s".formatted(discoveryPrefix, "light", uniqueId), "~/set", "~/state",
+ "%s/%s/%s".formatted(discoveryPrefix, "light", uniqueId),
+ null, "~/set", "~/state",
null,
null,
"json",
diff --git a/modules/vclu/src/main/java/pl/psobiech/opengr8on/vclu/system/objects/remoteclu/RemoteCLULedRgbLight.java b/modules/vclu/src/main/java/pl/psobiech/opengr8on/vclu/system/objects/remoteclu/RemoteCLULedRgbLight.java
index 8819a24..dfb83c0 100644
--- a/modules/vclu/src/main/java/pl/psobiech/opengr8on/vclu/system/objects/remoteclu/RemoteCLULedRgbLight.java
+++ b/modules/vclu/src/main/java/pl/psobiech/opengr8on/vclu/system/objects/remoteclu/RemoteCLULedRgbLight.java
@@ -4,7 +4,6 @@
import com.fasterxml.jackson.databind.node.IntNode;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.fasterxml.jackson.databind.node.TextNode;
-import org.eclipse.paho.client.mqttv3.MqttException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import pl.psobiech.opengr8on.util.HexUtil;
@@ -66,7 +65,7 @@ public RemoteCLULedRgbLight(
object.getName(),
uniqueId,
"%s/%s/%s".formatted(discoveryPrefix, "light", uniqueId),
- "~/set", "~/state",
+ null, "~/set", "~/state",
null,
null,
"json",
@@ -96,7 +95,8 @@ private static MqttDiscoveryLight childLightDeviceDiscoveryMessage(SpecificObjec
return new MqttDiscoveryLight(
"%s_%s".formatted(object.getName(), valueName),
colourUniqueId,
- "%s/%s/%s".formatted(discoveryPrefix, "light", colourUniqueId), "~/set", "~/state",
+ "%s/%s/%s".formatted(discoveryPrefix, "light", colourUniqueId),
+ null, "~/set", "~/state",
null,
null,
"json",
@@ -229,7 +229,7 @@ protected JsonNode pushState(JsonNode lastState, JsonNode newState) {
}
return stateNode;
- } catch (MqttException | RuntimeException e) {
+ } catch (RuntimeException e) {
LOGGER.error("Could not publish state update message for {}", discoveryMessage.getUniqueId(), e);
return lastState;
diff --git a/modules/vclu/src/main/java/pl/psobiech/opengr8on/vclu/system/objects/remoteclu/RemoteCLULight.java b/modules/vclu/src/main/java/pl/psobiech/opengr8on/vclu/system/objects/remoteclu/RemoteCLULight.java
index f576123..3e98b5d 100644
--- a/modules/vclu/src/main/java/pl/psobiech/opengr8on/vclu/system/objects/remoteclu/RemoteCLULight.java
+++ b/modules/vclu/src/main/java/pl/psobiech/opengr8on/vclu/system/objects/remoteclu/RemoteCLULight.java
@@ -30,7 +30,8 @@ public RemoteCLULight(
new MqttDiscoveryLight(
object.getName(),
uniqueId,
- "%s/%s/%s".formatted(discoveryPrefix, "light", uniqueId), "~/set", "~/state",
+ "%s/%s/%s".formatted(discoveryPrefix, "light", uniqueId),
+ null, "~/set", "~/state",
null,
null,
"json",
diff --git a/modules/vclu/src/main/java/pl/psobiech/opengr8on/vclu/system/objects/remoteclu/RemoteCLULuminositySensor.java b/modules/vclu/src/main/java/pl/psobiech/opengr8on/vclu/system/objects/remoteclu/RemoteCLULuminositySensor.java
index 575d0ac..9953dd8 100644
--- a/modules/vclu/src/main/java/pl/psobiech/opengr8on/vclu/system/objects/remoteclu/RemoteCLULuminositySensor.java
+++ b/modules/vclu/src/main/java/pl/psobiech/opengr8on/vclu/system/objects/remoteclu/RemoteCLULuminositySensor.java
@@ -27,7 +27,8 @@ public RemoteCLULuminositySensor(
new MqttDiscoveryNumericFloat(
object.getName(),
uniqueId,
- "%s/%s/%s".formatted(discoveryPrefix, "sensor", uniqueId), null, "~/state",
+ "%s/%s/%s".formatted(discoveryPrefix, "sensor", uniqueId),
+ null, null, "~/state",
null,
"%",
null,
diff --git a/modules/vclu/src/main/java/pl/psobiech/opengr8on/vclu/system/objects/remoteclu/RemoteCLUShutter.java b/modules/vclu/src/main/java/pl/psobiech/opengr8on/vclu/system/objects/remoteclu/RemoteCLUShutter.java
index aab4480..dbf6a34 100644
--- a/modules/vclu/src/main/java/pl/psobiech/opengr8on/vclu/system/objects/remoteclu/RemoteCLUShutter.java
+++ b/modules/vclu/src/main/java/pl/psobiech/opengr8on/vclu/system/objects/remoteclu/RemoteCLUShutter.java
@@ -2,7 +2,6 @@
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonNode;
-import org.eclipse.paho.client.mqttv3.MqttException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import pl.psobiech.opengr8on.util.ObjectMapperFactory;
@@ -65,7 +64,7 @@ public RemoteCLUShutter(
object.getName(),
uniqueId,
"%s/%s/%s".formatted(discoveryPrefix, "cover", uniqueId),
- "~/set", "~/state",
+ null, "~/set", "~/state",
"~/position/state", "~/position/set",
"shutter",
"%",
@@ -203,7 +202,7 @@ protected JsonNode pushState(JsonNode lastState, JsonNode newState) {
);
return stateNode;
- } catch (MqttException | RuntimeException e) {
+ } catch (RuntimeException e) {
LOGGER.error("Could not publish state update message for {}", discoveryMessage.getUniqueId(), e);
return lastState;
diff --git a/modules/vclu/src/main/java/pl/psobiech/opengr8on/vclu/system/objects/remoteclu/RemoteCLUTemperatureSensor.java b/modules/vclu/src/main/java/pl/psobiech/opengr8on/vclu/system/objects/remoteclu/RemoteCLUTemperatureSensor.java
index 39cdc07..e6f07d0 100644
--- a/modules/vclu/src/main/java/pl/psobiech/opengr8on/vclu/system/objects/remoteclu/RemoteCLUTemperatureSensor.java
+++ b/modules/vclu/src/main/java/pl/psobiech/opengr8on/vclu/system/objects/remoteclu/RemoteCLUTemperatureSensor.java
@@ -28,7 +28,8 @@ public RemoteCLUTemperatureSensor(
new MqttDiscoveryNumericFloat(
object.getName(),
uniqueId,
- "%s/%s/%s".formatted(discoveryPrefix, "sensor", uniqueId), null, "~/state",
+ "%s/%s/%s".formatted(discoveryPrefix, "sensor", uniqueId),
+ null, null, "~/state",
"temperature",
object.getFeatures().stream()
.filter(feature1 -> feature1.getName().equalsIgnoreCase("Value"))
diff --git a/modules/vclu/src/main/java/pl/psobiech/opengr8on/vclu/system/objects/remoteclu/RemoteCLUVoltageSensor.java b/modules/vclu/src/main/java/pl/psobiech/opengr8on/vclu/system/objects/remoteclu/RemoteCLUVoltageSensor.java
index 3ae9252..e83f897 100644
--- a/modules/vclu/src/main/java/pl/psobiech/opengr8on/vclu/system/objects/remoteclu/RemoteCLUVoltageSensor.java
+++ b/modules/vclu/src/main/java/pl/psobiech/opengr8on/vclu/system/objects/remoteclu/RemoteCLUVoltageSensor.java
@@ -27,7 +27,8 @@ public RemoteCLUVoltageSensor(
new MqttDiscoveryNumericFloat(
object.getName(),
uniqueId,
- "%s/%s/%s".formatted(discoveryPrefix, "sensor", uniqueId), null, "~/state",
+ "%s/%s/%s".formatted(discoveryPrefix, "sensor", uniqueId),
+ null, null, "~/state",
"voltage",
object.getFeatures().stream()
.filter(feature1 -> feature1.getName().equalsIgnoreCase("value"))
diff --git a/modules/vclu/src/main/java/pl/psobiech/opengr8on/vclu/util/TlsUtil.java b/modules/vclu/src/main/java/pl/psobiech/opengr8on/vclu/util/TlsUtil.java
index 2b67a96..d99f91f 100644
--- a/modules/vclu/src/main/java/pl/psobiech/opengr8on/vclu/util/TlsUtil.java
+++ b/modules/vclu/src/main/java/pl/psobiech/opengr8on/vclu/util/TlsUtil.java
@@ -96,7 +96,7 @@ public static SSLSocketFactory createSocketFactory(Path caCertificatePath, Path
}
}
- private static X509Certificate readCertificate(Path path) {
+ public static X509Certificate readCertificate(Path path) {
try (InputStream inputStream = new ByteArrayInputStream(readPem(path))) {
return (X509Certificate) CERTIFICATE_FACTORY.generateCertificate(inputStream);
} catch (IOException | CertificateException e) {
@@ -104,7 +104,7 @@ private static X509Certificate readCertificate(Path path) {
}
}
- private static PrivateKey readPrivateKey(Path path) {
+ public static PrivateKey readPrivateKey(Path path) {
final PKCS8EncodedKeySpec encodedKeySpec = new PKCS8EncodedKeySpec(
readPem(path)
);
diff --git a/modules/vclu/src/test/java/pl/psobiech/opengr8on/vclu/ServerTest.java b/modules/vclu/src/test/java/pl/psobiech/opengr8on/vclu/ServerTest.java
index f6c68d5..4530a07 100644
--- a/modules/vclu/src/test/java/pl/psobiech/opengr8on/vclu/ServerTest.java
+++ b/modules/vclu/src/test/java/pl/psobiech/opengr8on/vclu/ServerTest.java
@@ -55,7 +55,7 @@ void normalMode() throws Exception {
}
@Test
- @Timeout(30)
+ @Timeout(60)
void emergencyMode() throws Exception {
execute(
server -> {