From dce93233256bc13ec8bba10ee1f653cbebd8ddd8 Mon Sep 17 00:00:00 2001 From: Piotr Sobiech Date: Fri, 14 Nov 2025 08:46:48 +0100 Subject: [PATCH 1/2] * Increase unit test emergencyMode timeout, because windows is too slow Signed-off-by: Piotr Sobiech --- .../src/test/java/pl/psobiech/opengr8on/vclu/ServerTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 -> { From d4e97be41c1adeba4c1c08fed6e395393b424370 Mon Sep 17 00:00:00 2001 From: Piotr Sobiech Date: Mon, 17 Nov 2025 23:52:55 +0100 Subject: [PATCH 2/2] * Refactor into a supported MQTT client library * Enable VCLU start in a first available network interface if not specified Signed-off-by: Piotr Sobiech --- Dockerfile | 2 +- entrypoint.sh | 2 + .../psobiech/opengr8on/util/ThreadUtil.java | 2 +- modules/parent/pom.xml | 36 +- modules/vclu/pom.xml | 25 +- .../java/pl/psobiech/opengr8on/vclu/Main.java | 29 +- .../psobiech/opengr8on/vclu/MqttClient.java | 333 ++++++++++-------- .../vclu/mqtt/discovery/MqttDiscovery.java | 13 +- .../mqtt/discovery/MqttDiscoveryButton.java | 6 +- .../mqtt/discovery/MqttDiscoveryLight.java | 6 +- .../discovery/MqttDiscoveryNumericFloat.java | 6 +- .../mqtt/discovery/MqttDiscoveryOrigin.java | 4 +- .../mqtt/discovery/MqttDiscoveryShutter.java | 6 +- .../vclu/system/objects/MqttTopic.java | 10 +- .../vclu/system/objects/VirtualCLU.java | 21 +- .../remoteclu/BasicRemoteCLUSensor.java | 3 +- .../system/objects/remoteclu/RemoteCLU.java | 1 - .../objects/remoteclu/RemoteCLUButton.java | 6 +- .../objects/remoteclu/RemoteCLUDimmer.java | 3 +- .../remoteclu/RemoteCLULedRgbLight.java | 8 +- .../objects/remoteclu/RemoteCLULight.java | 3 +- .../remoteclu/RemoteCLULuminositySensor.java | 3 +- .../objects/remoteclu/RemoteCLUShutter.java | 5 +- .../remoteclu/RemoteCLUTemperatureSensor.java | 3 +- .../remoteclu/RemoteCLUVoltageSensor.java | 3 +- .../psobiech/opengr8on/vclu/util/TlsUtil.java | 4 +- 26 files changed, 302 insertions(+), 241 deletions(-) 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) );