diff --git a/bundles/org.openhab.binding.miele/README.md b/bundles/org.openhab.binding.miele/README.md index 8d29af7444975..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 | -| interface | Network address of 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", interface="192.168.0.5"] { +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 fc18aa183d09d..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 INTERFACE = "interface"; 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..ba2ff4c44abbc --- /dev/null +++ b/bundles/org.openhab.binding.miele/src/main/java/org/openhab/binding/miele/internal/config/XGW3000Configuration.java @@ -0,0 +1,28 @@ +/* + * 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 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..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,10 +12,7 @@ */ package org.openhab.binding.miele.internal.discovery; -import java.io.IOException; import java.net.InetAddress; -import java.net.Socket; -import java.util.HashMap; import java.util.Map; import java.util.Set; @@ -116,26 +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(); - String interfaceIpAddress = ourAddress.getHostAddress(); - socket.close(); - - properties.put(MieleBindingConstants.INTERFACE, interfaceIpAddress); - logger.debug("Discovered Miele@home gateway with IP address {} and interface IP address {}", ipAddress, - interfaceIpAddress); - } 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 a3c081ba953c3..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,9 +16,9 @@ import java.io.IOException; import java.net.DatagramPacket; +import java.net.DatagramSocket; 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 +28,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 +41,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,10 +50,10 @@ 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; -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; @@ -85,9 +83,6 @@ 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"; @@ -101,6 +96,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 +111,23 @@ 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())) { + 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 +143,18 @@ public Collection> getServices() { return Set.of(MieleApplianceDiscoveryService.class); } - private boolean validateConfig(Configuration config) { - if (config.get(HOST) == null || ((String) config.get(HOST)).isBlank()) { + private boolean validateConfig() { + if (config.ipAddress.isBlank()) { updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.OFFLINE.CONFIGURATION_ERROR, "@text/offline.configuration-error.ip-address-not-set"); return false; } - 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; - } - if (!IP_PATTERN.matcher((String) config.get(INTERFACE)).matches()) { - updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.OFFLINE.CONFIGURATION_ERROR, - "@text/offline.configuration-error.invalid-ip-multicast-interface [\"" + config.get(INTERFACE) - + "\"]"); - 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 +164,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 +219,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,12 +312,13 @@ 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); + NetworkInterface networkInterface = getMulticastNetworkInterface(config.ipAddress); + if (networkInterface == null) { 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; @@ -347,12 +336,6 @@ private List getHomeDevices() throws MieleRpcException { try { clientSocket = new MulticastSocket(JSON_RPC_PORT); clientSocket.setSoTimeout(MULTICAST_TIMEOUT_MILLIS); - - NetworkInterface networkInterface = getMulticastInterface(interfaceIpAddress); - if (networkInterface == null) { - logger.warn("Unable to find network interface for address {}", interfaceIpAddress); - return; - } clientSocket.setNetworkInterface(networkInterface); clientSocket.joinGroup(address1, null); clientSocket.joinGroup(address2, null); @@ -442,25 +425,19 @@ private List getHomeDevices() throws MieleRpcException { } }; - private @Nullable NetworkInterface getMulticastInterface(String interfaceIpAddress) throws SocketException { - Enumeration networkInterfaces = NetworkInterface.getNetworkInterfaces(); + private @Nullable NetworkInterface getMulticastNetworkInterface(String gatewayIpAddress) { + try { + InetAddress gatewayAddress = InetAddress.getByName(gatewayIpAddress); - @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; - } + 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; 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..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 @@ -45,8 +45,6 @@ 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 @@ -134,8 +132,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..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,24 +22,16 @@ 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. - - + - - 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.