From bfbb75e8a830a003b8d7c37eafa65957b9b7ec1e Mon Sep 17 00:00:00 2001 From: Jacob Laursen Date: Mon, 20 Oct 2025 23:37:10 +0200 Subject: [PATCH 1/3] Improve multicast interface configuration Signed-off-by: Jacob Laursen --- bundles/org.openhab.binding.miele/README.md | 4 +- .../miele/internal/MieleBindingConstants.java | 2 +- .../internal/config/XGW3000Configuration.java | 29 +++++ .../MieleMDNSDiscoveryParticipant.java | 10 +- .../internal/handler/MieleBridgeHandler.java | 117 +++++++++--------- .../resources/OH-INF/i18n/miele.properties | 6 +- .../main/resources/OH-INF/thing/xgw3000.xml | 20 ++- 7 files changed, 110 insertions(+), 78 deletions(-) create mode 100644 bundles/org.openhab.binding.miele/src/main/java/org/openhab/binding/miele/internal/config/XGW3000Configuration.java diff --git a/bundles/org.openhab.binding.miele/README.md b/bundles/org.openhab.binding.miele/README.md index 8d29af7444975..315414faa5955 100644 --- a/bundles/org.openhab.binding.miele/README.md +++ b/bundles/org.openhab.binding.miele/README.md @@ -48,7 +48,7 @@ discovery.miele:removalGracePeriod=30 | Configuration Parameter | Description | |-------------------------|----------------------------------------------------------------------------------------------------------------------------------| | ipAddress | Network address of the Miele@home gateway | -| interface | Network address of openHAB host interface where the binding will listen for multicast events coming from the Miele@home gateway. | +| multicastInterface | Name of the openHAB host interface where the binding will listen for multicast events coming from the Miele@home gateway. | | userName | Name of a registered Miele@home user. | | password | Password for the registered Miele@home user. | | language | Language for state, program and phase texts. Leave blank for system language. | @@ -423,7 +423,7 @@ See oven. ## things/miele.things ```java -Bridge miele:xgw3000:home [ipAddress="192.168.0.18", interface="192.168.0.5"] { +Bridge miele:xgw3000:home [ipAddress="192.168.0.18", multicastInterface="eth0"] { Things: Thing fridgefreezer freezer [uid="00124b000424be44#2"] Thing hood hood [uid="001d63fffe020685#210"] diff --git a/bundles/org.openhab.binding.miele/src/main/java/org/openhab/binding/miele/internal/MieleBindingConstants.java b/bundles/org.openhab.binding.miele/src/main/java/org/openhab/binding/miele/internal/MieleBindingConstants.java index fc18aa183d09d..26ff0c81d8a77 100644 --- a/bundles/org.openhab.binding.miele/src/main/java/org/openhab/binding/miele/internal/MieleBindingConstants.java +++ b/bundles/org.openhab.binding.miele/src/main/java/org/openhab/binding/miele/internal/MieleBindingConstants.java @@ -119,7 +119,7 @@ public class MieleBindingConstants { // Bridge config properties public static final String HOST = "ipAddress"; - public static final String INTERFACE = "interface"; + public static final String MULTICAST_INTERFACE = "multicastInterface"; public static final String USER_NAME = "userName"; public static final String PASSWORD = "password"; public static final String LANGUAGE = "language"; diff --git a/bundles/org.openhab.binding.miele/src/main/java/org/openhab/binding/miele/internal/config/XGW3000Configuration.java b/bundles/org.openhab.binding.miele/src/main/java/org/openhab/binding/miele/internal/config/XGW3000Configuration.java new file mode 100644 index 0000000000000..01ba0aa181e48 --- /dev/null +++ b/bundles/org.openhab.binding.miele/src/main/java/org/openhab/binding/miele/internal/config/XGW3000Configuration.java @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2010-2025 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.miele.internal.config; + +import org.eclipse.jdt.annotation.NonNullByDefault; + +/** + * The {@link XGW3000Configuration} class contains fields mapping thing configuration parameters. + * + * @author Jacob Laursen - Initial contribution + */ +@NonNullByDefault +public class XGW3000Configuration { + public String ipAddress = ""; + public String multicastInterface = ""; + public String userName = ""; + public String password = ""; + public String language = ""; +} diff --git a/bundles/org.openhab.binding.miele/src/main/java/org/openhab/binding/miele/internal/discovery/MieleMDNSDiscoveryParticipant.java b/bundles/org.openhab.binding.miele/src/main/java/org/openhab/binding/miele/internal/discovery/MieleMDNSDiscoveryParticipant.java index 52589280dfad3..f017b7f290091 100644 --- a/bundles/org.openhab.binding.miele/src/main/java/org/openhab/binding/miele/internal/discovery/MieleMDNSDiscoveryParticipant.java +++ b/bundles/org.openhab.binding.miele/src/main/java/org/openhab/binding/miele/internal/discovery/MieleMDNSDiscoveryParticipant.java @@ -14,6 +14,7 @@ import java.io.IOException; import java.net.InetAddress; +import java.net.NetworkInterface; import java.net.Socket; import java.util.HashMap; import java.util.Map; @@ -124,12 +125,13 @@ public String getServiceType() { try { socket = new Socket(addresses[0], 80); InetAddress ourAddress = socket.getLocalAddress(); - String interfaceIpAddress = ourAddress.getHostAddress(); + NetworkInterface ni = NetworkInterface.getByInetAddress(ourAddress); + String interfaceName = ni.getName(); socket.close(); - properties.put(MieleBindingConstants.INTERFACE, interfaceIpAddress); - logger.debug("Discovered Miele@home gateway with IP address {} and interface IP address {}", ipAddress, - interfaceIpAddress); + properties.put(MieleBindingConstants.MULTICAST_INTERFACE, interfaceName); + logger.debug("Discovered Miele@home gateway with IP address {} and multicast interface {}", ipAddress, + interfaceName); } catch (IOException e) { logger.warn("An exception occurred while connecting to the Miele Gateway: '{}'", e.getMessage()); return null; diff --git a/bundles/org.openhab.binding.miele/src/main/java/org/openhab/binding/miele/internal/handler/MieleBridgeHandler.java b/bundles/org.openhab.binding.miele/src/main/java/org/openhab/binding/miele/internal/handler/MieleBridgeHandler.java index a3c081ba953c3..f544c88f8710c 100644 --- a/bundles/org.openhab.binding.miele/src/main/java/org/openhab/binding/miele/internal/handler/MieleBridgeHandler.java +++ b/bundles/org.openhab.binding.miele/src/main/java/org/openhab/binding/miele/internal/handler/MieleBridgeHandler.java @@ -18,7 +18,6 @@ import java.net.DatagramPacket; import java.net.InetAddress; import java.net.InetSocketAddress; -import java.net.InterfaceAddress; import java.net.MulticastSocket; import java.net.NetworkInterface; import java.net.SocketException; @@ -28,7 +27,6 @@ import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.Collection; -import java.util.Enumeration; import java.util.IllformedLocaleException; import java.util.Iterator; import java.util.List; @@ -42,7 +40,6 @@ import java.util.concurrent.Future; import java.util.concurrent.ScheduledFuture; import java.util.concurrent.TimeUnit; -import java.util.regex.Pattern; import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.Nullable; @@ -52,6 +49,7 @@ import org.openhab.binding.miele.internal.api.dto.DeviceClassObject; import org.openhab.binding.miele.internal.api.dto.DeviceProperty; import org.openhab.binding.miele.internal.api.dto.HomeDevice; +import org.openhab.binding.miele.internal.config.XGW3000Configuration; import org.openhab.binding.miele.internal.discovery.MieleApplianceDiscoveryService; import org.openhab.binding.miele.internal.exceptions.MieleRpcException; import org.openhab.core.common.NamedThreadFactory; @@ -85,15 +83,13 @@ public class MieleBridgeHandler extends BaseBridgeHandler { public static final Set SUPPORTED_THING_TYPES = Set.of(THING_TYPE_XGW3000); - private static final Pattern IP_PATTERN = Pattern - .compile("^(([01]?\\d\\d?|2[0-4]\\d|25[0-5])\\.){3}([01]?\\d\\d?|2[0-4]\\d|25[0-5])$"); - private static final int POLLING_PERIOD_SECONDS = 15; private static final int JSON_RPC_PORT = 2810; private static final String JSON_RPC_MULTICAST_IP1 = "239.255.68.139"; private static final String JSON_RPC_MULTICAST_IP2 = "224.255.68.139"; private static final int MULTICAST_TIMEOUT_MILLIS = 100; private static final int MULTICAST_SLEEP_MILLIS = 500; + private static final String LEGACY_INTERFACE_PARAMETER = "interface"; private final Logger logger = LoggerFactory.getLogger(MieleBridgeHandler.class); @@ -101,6 +97,7 @@ public class MieleBridgeHandler extends BaseBridgeHandler { private final HttpClient httpClient; private final Gson gson = new Gson(); + private XGW3000Configuration config; private @NonNullByDefault({}) MieleGatewayCommunicationController gatewayCommunication; private Set discoveryListeners = ConcurrentHashMap.newKeySet(); @@ -115,18 +112,25 @@ public class MieleBridgeHandler extends BaseBridgeHandler { public MieleBridgeHandler(Bridge bridge, HttpClient httpClient) { super(bridge); this.httpClient = httpClient; + + // Default configuration + this.config = new XGW3000Configuration(); } @Override public void initialize() { logger.debug("Initializing handler for bridge {}", getThing().getUID()); - if (!validateConfig(getConfig())) { + migrateMulticastInterfaceConfiguration(); + + config = getConfigAs(XGW3000Configuration.class); + + if (!validateConfig()) { return; } try { - gatewayCommunication = new MieleGatewayCommunicationController(httpClient, (String) getConfig().get(HOST)); + gatewayCommunication = new MieleGatewayCommunicationController(httpClient, config.ipAddress); } catch (URISyntaxException e) { updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.OFFLINE.CONFIGURATION_ERROR, e.getMessage()); return; @@ -142,30 +146,57 @@ public Collection> getServices() { return Set.of(MieleApplianceDiscoveryService.class); } - private boolean validateConfig(Configuration config) { - if (config.get(HOST) == null || ((String) config.get(HOST)).isBlank()) { - updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.OFFLINE.CONFIGURATION_ERROR, - "@text/offline.configuration-error.ip-address-not-set"); - return false; + private void migrateMulticastInterfaceConfiguration() { + Configuration config = editConfiguration(); + + String interfaceIpAddress = (String) config.get(LEGACY_INTERFACE_PARAMETER); + if (interfaceIpAddress == null || interfaceIpAddress.isBlank()) { + // Nothing to migrate + return; } - if (config.get(INTERFACE) == null || ((String) config.get(INTERFACE)).isBlank()) { - updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.OFFLINE.CONFIGURATION_ERROR, - "@text/offline.configuration-error.ip-multicast-interface-not-set"); - return false; + + String interfaceName = (String) config.get(MULTICAST_INTERFACE); + if (interfaceName != null && !interfaceName.isBlank()) { + // Already migrated + return; + } + + logger.debug("Attempting to migrate multicast interface IP address: '{}'", interfaceIpAddress); + try { + InetAddress addr = InetAddress.getByName(interfaceIpAddress); + NetworkInterface ni = NetworkInterface.getByInetAddress(addr); + if (ni == null) { + logger.warn("Failed to migrate network interface. No network interface found for IP address '{}'", + interfaceIpAddress); + return; + } + + interfaceName = ni.getName(); + + config.put(MULTICAST_INTERFACE, interfaceName); + config.remove(LEGACY_INTERFACE_PARAMETER); + updateConfiguration(config); + + logger.info( + "Migrated network interface '{}' from IP address '{}'. For unmanaged Things, please adapt configuration manually", + interfaceName, interfaceIpAddress); + } catch (SocketException | UnknownHostException e) { + logger.warn("Multicast interface migration failed: '{}'", e.getMessage()); } - if (!IP_PATTERN.matcher((String) config.get(INTERFACE)).matches()) { + } + + private boolean validateConfig() { + if (config.ipAddress.isBlank()) { updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.OFFLINE.CONFIGURATION_ERROR, - "@text/offline.configuration-error.invalid-ip-multicast-interface [\"" + config.get(INTERFACE) - + "\"]"); + "@text/offline.configuration-error.ip-address-not-set"); return false; } - String language = (String) config.get(LANGUAGE); - if (language != null && !language.isBlank()) { + if (!config.language.isBlank()) { try { - new Locale.Builder().setLanguageTag(language).build(); + new Locale.Builder().setLanguageTag(config.language).build(); } catch (IllformedLocaleException e) { updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.OFFLINE.CONFIGURATION_ERROR, - "@text/offline.configuration-error.invalid-language [\"" + language + "\"]"); + "@text/offline.configuration-error.invalid-language [\"" + config.language + "\"]"); return false; } } @@ -175,12 +206,11 @@ private boolean validateConfig(Configuration config) { private Runnable pollingRunnable = new Runnable() { @Override public void run() { - String host = (String) getConfig().get(HOST); try { List homeDevices = getHomeDevices(); if (!lastBridgeConnectionState) { - logger.debug("Connection to Miele Gateway {} established.", host); + logger.debug("Connection to Miele Gateway {} established.", config.ipAddress); lastBridgeConnectionState = true; } updateStatus(ThingStatus.ONLINE); @@ -231,7 +261,7 @@ public void run() { message); } if (lastBridgeConnectionState) { - logger.debug("Connection to Miele Gateway {} lost.", host); + logger.debug("Connection to Miele Gateway {} lost.", config.ipAddress); lastBridgeConnectionState = false; } updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.OFFLINE.COMMUNICATION_ERROR, message); @@ -324,9 +354,8 @@ private List getHomeDevices() throws MieleRpcException { } private Runnable eventListenerRunnable = () -> { - String interfaceIpAddress = (String) getConfig().get(INTERFACE); - if (!IP_PATTERN.matcher(interfaceIpAddress).matches()) { - logger.debug("Invalid IP address for the multicast interface: '{}'", interfaceIpAddress); + if (config.multicastInterface.isBlank()) { + logger.debug("No multicast interface configured"); return; } @@ -348,9 +377,9 @@ private List getHomeDevices() throws MieleRpcException { clientSocket = new MulticastSocket(JSON_RPC_PORT); clientSocket.setSoTimeout(MULTICAST_TIMEOUT_MILLIS); - NetworkInterface networkInterface = getMulticastInterface(interfaceIpAddress); + NetworkInterface networkInterface = NetworkInterface.getByName(config.multicastInterface); if (networkInterface == null) { - logger.warn("Unable to find network interface for address {}", interfaceIpAddress); + logger.warn("Unable to get network interface for '{}'", config.multicastInterface); return; } clientSocket.setNetworkInterface(networkInterface); @@ -442,30 +471,6 @@ private List getHomeDevices() throws MieleRpcException { } }; - private @Nullable NetworkInterface getMulticastInterface(String interfaceIpAddress) throws SocketException { - Enumeration networkInterfaces = NetworkInterface.getNetworkInterfaces(); - - @Nullable - NetworkInterface networkInterface; - while (networkInterfaces.hasMoreElements()) { - networkInterface = networkInterfaces.nextElement(); - if (networkInterface.isLoopback()) { - continue; - } - for (InterfaceAddress interfaceAddress : networkInterface.getInterfaceAddresses()) { - if (logger.isTraceEnabled()) { - logger.trace("Found interface address {} -> {}", interfaceAddress.toString(), - interfaceAddress.getAddress().toString()); - } - if (interfaceAddress.getAddress().toString().endsWith("/" + interfaceIpAddress)) { - return networkInterface; - } - } - } - - return null; - } - public JsonElement invokeOperation(String applianceId, String modelID, String methodName) throws MieleRpcException { if (getThing().getStatus() != ThingStatus.ONLINE) { throw new MieleRpcException("Bridge is offline, operations can not be invoked"); diff --git a/bundles/org.openhab.binding.miele/src/main/resources/OH-INF/i18n/miele.properties b/bundles/org.openhab.binding.miele/src/main/resources/OH-INF/i18n/miele.properties index 598ff5392e5eb..29f85cfffdafd 100644 --- a/bundles/org.openhab.binding.miele/src/main/resources/OH-INF/i18n/miele.properties +++ b/bundles/org.openhab.binding.miele/src/main/resources/OH-INF/i18n/miele.properties @@ -45,12 +45,12 @@ thing-type.miele.xgw3000.description = The Miele bridge represents the Miele@hom thing-type.config.miele.appliance.uid.label = ID thing-type.config.miele.appliance.uid.description = Unique identifier for specific appliance on the gateway. -thing-type.config.miele.xgw3000.interface.label = Network Address of the Multicast Interface -thing-type.config.miele.xgw3000.interface.description = Network address of openHAB host interface where the binding will listen for multicast events coming from the Miele@home gateway. thing-type.config.miele.xgw3000.ipAddress.label = Network Address thing-type.config.miele.xgw3000.ipAddress.description = Network address of the Miele@home gateway. thing-type.config.miele.xgw3000.language.label = Language thing-type.config.miele.xgw3000.language.description = Language for state, program and phase texts. Leave blank for system language. +thing-type.config.miele.xgw3000.multicastInterface.label = Name of the Multicast Interface +thing-type.config.miele.xgw3000.multicastInterface.description = Name of the openHAB host interface where the binding will listen for multicast events coming from the Miele@home gateway. thing-type.config.miele.xgw3000.password.label = Password thing-type.config.miele.xgw3000.password.description = Password for the registered Miele@home user. thing-type.config.miele.xgw3000.userName.label = Username @@ -134,8 +134,6 @@ channel-type.miele.water-consumption.description = Water consumption by the curr offline.configuration-error.bridge-missing = Bridge is missing offline.configuration-error.ip-address-not-set = Cannot connect to the Miele gateway: host IP address is not set. -offline.configuration-error.ip-multicast-interface-not-set = Cannot connect to the Miele gateway: multicast interface is not set. -offline.configuration-error.invalid-ip-multicast-interface = Invalid IP address for the multicast interface: {0} offline.configuration-error.invalid-language = Invalid language: {0} offline.configuration-error.uid-not-set = Appliance ID is not set diff --git a/bundles/org.openhab.binding.miele/src/main/resources/OH-INF/thing/xgw3000.xml b/bundles/org.openhab.binding.miele/src/main/resources/OH-INF/thing/xgw3000.xml index 016cd57736e1c..8a0e1a743a974 100644 --- a/bundles/org.openhab.binding.miele/src/main/resources/OH-INF/thing/xgw3000.xml +++ b/bundles/org.openhab.binding.miele/src/main/resources/OH-INF/thing/xgw3000.xml @@ -22,24 +22,22 @@ Network address of the Miele@home gateway. - - network-address - - Network address of openHAB host interface where the binding will listen for multicast events coming - from the Miele@home gateway. + + network-interface + + Name of the openHAB host interface where the binding will listen for multicast events coming from the + Miele@home gateway. - + - - Name of a registered Miele@home user. - + Name of a registered Miele@home user. - + password Password for the registered Miele@home user. - + Language for state, program and phase texts. Leave blank for system language. From 8711811c2e5375d6dd2a43bb7c805880d385f5fa Mon Sep 17 00:00:00 2001 From: Jacob Laursen Date: Thu, 23 Oct 2025 17:46:06 +0200 Subject: [PATCH 2/3] Determine multicast interface from gateway address Signed-off-by: Jacob Laursen --- bundles/org.openhab.binding.miele/README.md | 3 +- .../miele/internal/MieleBindingConstants.java | 1 - .../internal/config/XGW3000Configuration.java | 1 - .../MieleMDNSDiscoveryParticipant.java | 27 +------ .../internal/handler/MieleBridgeHandler.java | 71 +++++-------------- .../resources/OH-INF/i18n/miele.properties | 2 - .../main/resources/OH-INF/thing/xgw3000.xml | 6 -- 7 files changed, 22 insertions(+), 89 deletions(-) diff --git a/bundles/org.openhab.binding.miele/README.md b/bundles/org.openhab.binding.miele/README.md index 315414faa5955..d52552d47fd7f 100644 --- a/bundles/org.openhab.binding.miele/README.md +++ b/bundles/org.openhab.binding.miele/README.md @@ -48,7 +48,6 @@ discovery.miele:removalGracePeriod=30 | Configuration Parameter | Description | |-------------------------|----------------------------------------------------------------------------------------------------------------------------------| | ipAddress | Network address of the Miele@home gateway | -| multicastInterface | Name of the openHAB host interface where the binding will listen for multicast events coming from the Miele@home gateway. | | userName | Name of a registered Miele@home user. | | password | Password for the registered Miele@home user. | | language | Language for state, program and phase texts. Leave blank for system language. | @@ -423,7 +422,7 @@ See oven. ## things/miele.things ```java -Bridge miele:xgw3000:home [ipAddress="192.168.0.18", multicastInterface="eth0"] { +Bridge miele:xgw3000:home [ipAddress="192.168.0.18"] { Things: Thing fridgefreezer freezer [uid="00124b000424be44#2"] Thing hood hood [uid="001d63fffe020685#210"] diff --git a/bundles/org.openhab.binding.miele/src/main/java/org/openhab/binding/miele/internal/MieleBindingConstants.java b/bundles/org.openhab.binding.miele/src/main/java/org/openhab/binding/miele/internal/MieleBindingConstants.java index 26ff0c81d8a77..5a121c8264800 100644 --- a/bundles/org.openhab.binding.miele/src/main/java/org/openhab/binding/miele/internal/MieleBindingConstants.java +++ b/bundles/org.openhab.binding.miele/src/main/java/org/openhab/binding/miele/internal/MieleBindingConstants.java @@ -119,7 +119,6 @@ public class MieleBindingConstants { // Bridge config properties public static final String HOST = "ipAddress"; - public static final String MULTICAST_INTERFACE = "multicastInterface"; public static final String USER_NAME = "userName"; public static final String PASSWORD = "password"; public static final String LANGUAGE = "language"; diff --git a/bundles/org.openhab.binding.miele/src/main/java/org/openhab/binding/miele/internal/config/XGW3000Configuration.java b/bundles/org.openhab.binding.miele/src/main/java/org/openhab/binding/miele/internal/config/XGW3000Configuration.java index 01ba0aa181e48..ba2ff4c44abbc 100644 --- a/bundles/org.openhab.binding.miele/src/main/java/org/openhab/binding/miele/internal/config/XGW3000Configuration.java +++ b/bundles/org.openhab.binding.miele/src/main/java/org/openhab/binding/miele/internal/config/XGW3000Configuration.java @@ -22,7 +22,6 @@ @NonNullByDefault public class XGW3000Configuration { public String ipAddress = ""; - public String multicastInterface = ""; public String userName = ""; public String password = ""; public String language = ""; diff --git a/bundles/org.openhab.binding.miele/src/main/java/org/openhab/binding/miele/internal/discovery/MieleMDNSDiscoveryParticipant.java b/bundles/org.openhab.binding.miele/src/main/java/org/openhab/binding/miele/internal/discovery/MieleMDNSDiscoveryParticipant.java index f017b7f290091..b38976301d44e 100644 --- a/bundles/org.openhab.binding.miele/src/main/java/org/openhab/binding/miele/internal/discovery/MieleMDNSDiscoveryParticipant.java +++ b/bundles/org.openhab.binding.miele/src/main/java/org/openhab/binding/miele/internal/discovery/MieleMDNSDiscoveryParticipant.java @@ -12,11 +12,7 @@ */ package org.openhab.binding.miele.internal.discovery; -import java.io.IOException; import java.net.InetAddress; -import java.net.NetworkInterface; -import java.net.Socket; -import java.util.HashMap; import java.util.Map; import java.util.Set; @@ -117,27 +113,8 @@ public String getServiceType() { return null; } - String ipAddress = addresses[0].getHostAddress(); - Map properties = new HashMap<>(2); - properties.put(MieleBindingConstants.HOST, ipAddress); - - Socket socket = null; - try { - socket = new Socket(addresses[0], 80); - InetAddress ourAddress = socket.getLocalAddress(); - NetworkInterface ni = NetworkInterface.getByInetAddress(ourAddress); - String interfaceName = ni.getName(); - socket.close(); - - properties.put(MieleBindingConstants.MULTICAST_INTERFACE, interfaceName); - logger.debug("Discovered Miele@home gateway with IP address {} and multicast interface {}", ipAddress, - interfaceName); - } catch (IOException e) { - logger.warn("An exception occurred while connecting to the Miele Gateway: '{}'", e.getMessage()); - return null; - } - - return DiscoveryResultBuilder.create(uid).withProperties(properties) + return DiscoveryResultBuilder.create(uid) + .withProperty(MieleBindingConstants.HOST, addresses[0].getHostAddress()) .withRepresentationProperty(MieleBindingConstants.HOST).withLabel("@text/discovery.xgw3000.label") .build(); } diff --git a/bundles/org.openhab.binding.miele/src/main/java/org/openhab/binding/miele/internal/handler/MieleBridgeHandler.java b/bundles/org.openhab.binding.miele/src/main/java/org/openhab/binding/miele/internal/handler/MieleBridgeHandler.java index f544c88f8710c..7e06a5f6b5f8a 100644 --- a/bundles/org.openhab.binding.miele/src/main/java/org/openhab/binding/miele/internal/handler/MieleBridgeHandler.java +++ b/bundles/org.openhab.binding.miele/src/main/java/org/openhab/binding/miele/internal/handler/MieleBridgeHandler.java @@ -20,7 +20,7 @@ import java.net.InetSocketAddress; import java.net.MulticastSocket; import java.net.NetworkInterface; -import java.net.SocketException; +import java.net.Socket; import java.net.SocketTimeoutException; import java.net.URISyntaxException; import java.net.UnknownHostException; @@ -53,7 +53,6 @@ import org.openhab.binding.miele.internal.discovery.MieleApplianceDiscoveryService; import org.openhab.binding.miele.internal.exceptions.MieleRpcException; import org.openhab.core.common.NamedThreadFactory; -import org.openhab.core.config.core.Configuration; import org.openhab.core.thing.Bridge; import org.openhab.core.thing.ChannelUID; import org.openhab.core.thing.ThingStatus; @@ -89,7 +88,6 @@ public class MieleBridgeHandler extends BaseBridgeHandler { private static final String JSON_RPC_MULTICAST_IP2 = "224.255.68.139"; private static final int MULTICAST_TIMEOUT_MILLIS = 100; private static final int MULTICAST_SLEEP_MILLIS = 500; - private static final String LEGACY_INTERFACE_PARAMETER = "interface"; private final Logger logger = LoggerFactory.getLogger(MieleBridgeHandler.class); @@ -121,8 +119,6 @@ public MieleBridgeHandler(Bridge bridge, HttpClient httpClient) { public void initialize() { logger.debug("Initializing handler for bridge {}", getThing().getUID()); - migrateMulticastInterfaceConfiguration(); - config = getConfigAs(XGW3000Configuration.class); if (!validateConfig()) { @@ -146,45 +142,6 @@ public Collection> getServices() { return Set.of(MieleApplianceDiscoveryService.class); } - private void migrateMulticastInterfaceConfiguration() { - Configuration config = editConfiguration(); - - String interfaceIpAddress = (String) config.get(LEGACY_INTERFACE_PARAMETER); - if (interfaceIpAddress == null || interfaceIpAddress.isBlank()) { - // Nothing to migrate - return; - } - - String interfaceName = (String) config.get(MULTICAST_INTERFACE); - if (interfaceName != null && !interfaceName.isBlank()) { - // Already migrated - return; - } - - logger.debug("Attempting to migrate multicast interface IP address: '{}'", interfaceIpAddress); - try { - InetAddress addr = InetAddress.getByName(interfaceIpAddress); - NetworkInterface ni = NetworkInterface.getByInetAddress(addr); - if (ni == null) { - logger.warn("Failed to migrate network interface. No network interface found for IP address '{}'", - interfaceIpAddress); - return; - } - - interfaceName = ni.getName(); - - config.put(MULTICAST_INTERFACE, interfaceName); - config.remove(LEGACY_INTERFACE_PARAMETER); - updateConfiguration(config); - - logger.info( - "Migrated network interface '{}' from IP address '{}'. For unmanaged Things, please adapt configuration manually", - interfaceName, interfaceIpAddress); - } catch (SocketException | UnknownHostException e) { - logger.warn("Multicast interface migration failed: '{}'", e.getMessage()); - } - } - private boolean validateConfig() { if (config.ipAddress.isBlank()) { updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.OFFLINE.CONFIGURATION_ERROR, @@ -354,11 +311,14 @@ private List getHomeDevices() throws MieleRpcException { } private Runnable eventListenerRunnable = () -> { - if (config.multicastInterface.isBlank()) { - logger.debug("No multicast interface configured"); + NetworkInterface networkInterface = getMulticastNetworkInterface(config.ipAddress); + if (networkInterface == null) { + logger.warn("Unable to determine multicast network interface for gateway {}", config.ipAddress); return; } + logger.debug("Using multicast interface {} for gateway {}", networkInterface.getName(), config.ipAddress); + // Get the address that we are going to connect to. InetSocketAddress address1 = null; InetSocketAddress address2 = null; @@ -376,12 +336,6 @@ private List getHomeDevices() throws MieleRpcException { try { clientSocket = new MulticastSocket(JSON_RPC_PORT); clientSocket.setSoTimeout(MULTICAST_TIMEOUT_MILLIS); - - NetworkInterface networkInterface = NetworkInterface.getByName(config.multicastInterface); - if (networkInterface == null) { - logger.warn("Unable to get network interface for '{}'", config.multicastInterface); - return; - } clientSocket.setNetworkInterface(networkInterface); clientSocket.joinGroup(address1, null); clientSocket.joinGroup(address2, null); @@ -471,6 +425,19 @@ private List getHomeDevices() throws MieleRpcException { } }; + private @Nullable NetworkInterface getMulticastNetworkInterface(String address) { + Socket socket = null; + try { + socket = new Socket(address, 80); + InetAddress ourAddress = socket.getLocalAddress(); + NetworkInterface ni = NetworkInterface.getByInetAddress(ourAddress); + socket.close(); + return ni; + } catch (IOException e) { + return null; + } + } + public JsonElement invokeOperation(String applianceId, String modelID, String methodName) throws MieleRpcException { if (getThing().getStatus() != ThingStatus.ONLINE) { throw new MieleRpcException("Bridge is offline, operations can not be invoked"); diff --git a/bundles/org.openhab.binding.miele/src/main/resources/OH-INF/i18n/miele.properties b/bundles/org.openhab.binding.miele/src/main/resources/OH-INF/i18n/miele.properties index 29f85cfffdafd..abb011a6e4206 100644 --- a/bundles/org.openhab.binding.miele/src/main/resources/OH-INF/i18n/miele.properties +++ b/bundles/org.openhab.binding.miele/src/main/resources/OH-INF/i18n/miele.properties @@ -49,8 +49,6 @@ thing-type.config.miele.xgw3000.ipAddress.label = Network Address thing-type.config.miele.xgw3000.ipAddress.description = Network address of the Miele@home gateway. thing-type.config.miele.xgw3000.language.label = Language thing-type.config.miele.xgw3000.language.description = Language for state, program and phase texts. Leave blank for system language. -thing-type.config.miele.xgw3000.multicastInterface.label = Name of the Multicast Interface -thing-type.config.miele.xgw3000.multicastInterface.description = Name of the openHAB host interface where the binding will listen for multicast events coming from the Miele@home gateway. thing-type.config.miele.xgw3000.password.label = Password thing-type.config.miele.xgw3000.password.description = Password for the registered Miele@home user. thing-type.config.miele.xgw3000.userName.label = Username diff --git a/bundles/org.openhab.binding.miele/src/main/resources/OH-INF/thing/xgw3000.xml b/bundles/org.openhab.binding.miele/src/main/resources/OH-INF/thing/xgw3000.xml index 8a0e1a743a974..bc184ef97ac80 100644 --- a/bundles/org.openhab.binding.miele/src/main/resources/OH-INF/thing/xgw3000.xml +++ b/bundles/org.openhab.binding.miele/src/main/resources/OH-INF/thing/xgw3000.xml @@ -22,12 +22,6 @@ Network address of the Miele@home gateway. - - network-interface - - Name of the openHAB host interface where the binding will listen for multicast events coming from the - Miele@home gateway. - Name of a registered Miele@home user. From f2cbbef29e96ae2b2bc5fafbfc2cbf66c573ea05 Mon Sep 17 00:00:00 2001 From: Jacob Laursen Date: Thu, 23 Oct 2025 20:38:46 +0200 Subject: [PATCH 3/3] Determine outbound network interface without TCP connect Signed-off-by: Jacob Laursen --- .../internal/handler/MieleBridgeHandler.java | 27 +++++++++++-------- 1 file changed, 16 insertions(+), 11 deletions(-) diff --git a/bundles/org.openhab.binding.miele/src/main/java/org/openhab/binding/miele/internal/handler/MieleBridgeHandler.java b/bundles/org.openhab.binding.miele/src/main/java/org/openhab/binding/miele/internal/handler/MieleBridgeHandler.java index 7e06a5f6b5f8a..5542d2462f350 100644 --- a/bundles/org.openhab.binding.miele/src/main/java/org/openhab/binding/miele/internal/handler/MieleBridgeHandler.java +++ b/bundles/org.openhab.binding.miele/src/main/java/org/openhab/binding/miele/internal/handler/MieleBridgeHandler.java @@ -16,11 +16,12 @@ import java.io.IOException; import java.net.DatagramPacket; +import java.net.DatagramSocket; import java.net.InetAddress; import java.net.InetSocketAddress; import java.net.MulticastSocket; import java.net.NetworkInterface; -import java.net.Socket; +import java.net.SocketException; import java.net.SocketTimeoutException; import java.net.URISyntaxException; import java.net.UnknownHostException; @@ -313,7 +314,6 @@ private List getHomeDevices() throws MieleRpcException { private Runnable eventListenerRunnable = () -> { NetworkInterface networkInterface = getMulticastNetworkInterface(config.ipAddress); if (networkInterface == null) { - logger.warn("Unable to determine multicast network interface for gateway {}", config.ipAddress); return; } @@ -425,17 +425,22 @@ private List getHomeDevices() throws MieleRpcException { } }; - private @Nullable NetworkInterface getMulticastNetworkInterface(String address) { - Socket socket = null; + private @Nullable NetworkInterface getMulticastNetworkInterface(String gatewayIpAddress) { try { - socket = new Socket(address, 80); - InetAddress ourAddress = socket.getLocalAddress(); - NetworkInterface ni = NetworkInterface.getByInetAddress(ourAddress); - socket.close(); - return ni; - } catch (IOException e) { - return null; + InetAddress gatewayAddress = InetAddress.getByName(gatewayIpAddress); + + try (DatagramSocket socket = new DatagramSocket()) { + socket.connect(gatewayAddress, 80); + InetAddress localAddress = socket.getLocalAddress(); + return NetworkInterface.getByInetAddress(localAddress); + } + } catch (UnknownHostException e) { + logger.warn("Invalid gateway '{}': {}", gatewayIpAddress, e.getMessage()); + } catch (SocketException e) { + logger.warn("Failed to determine network interface for '{}': {}", gatewayIpAddress, e.getMessage()); } + + return null; } public JsonElement invokeOperation(String applianceId, String modelID, String methodName) throws MieleRpcException {