diff --git a/bundles/org.openhab.binding.homematic/README.md b/bundles/org.openhab.binding.homematic/README.md index 07f447eb48e..1849f809ad4 100644 --- a/bundles/org.openhab.binding.homematic/README.md +++ b/bundles/org.openhab.binding.homematic/README.md @@ -14,15 +14,28 @@ When the option "Restricted access" is used, some ports have to be added to the 2000; 2001; 2010; +8181; 8701; 9292; ``` Also the IP of the device running openHAB has to be set to the list of "IP addresses for restricted access". -Also under `Home page > Settings > Control panel` with the menu `Security` the option `Authentication` has to be disabled as the binding does not support the configuration of `username` and `password`for the XML-RPC API. +Also under `Home page > Settings > Control panel` with the menu `Security` the option `Authentication` has to be disabled if the option 'useAuthentication' is not set. +This option may be enabled if the option 'useAuthentication' is set and BIN-RPC is not used. +In this case, a user and password must be created. +This can be done under `Home page > Settings > Control panel` with the menu `User management`. +This can be done under `Home page > Settings > Control Panel` in the `User Management` menu. +The new user should have the following configuration: -If this is not done the binding will not be able to connect to the CCU and the CCU Thing will stay uninitialized and sets a timeout exception: +- User name - button for login: No +- Permission level: User +- Expert mode not visible: Yes +- Automatically confirm the device message: Yes + +The user and password must then be entered in the 'Username' and 'Password' settings. + +If this is not done the binding will not be able to connect to the CCU and the CCU Thing will stay uninitialized and sets a timeout exception or a authentication error ```text xxx-xx-xx xx:xx:xx.xxx [hingStatusInfoChangedEvent] - - 'homematic:bridge:xxx' changed from INITIALIZING to OFFLINE (COMMUNICATION_ERROR): java.net.SocketTimeoutException: Connect Timeout @@ -179,6 +192,15 @@ Due to the factory reset, the device will also be unpaired from the gateway, eve If a large number of devices are connected to the gateway, the default buffersize of 2048 kB may be too small for communication with the gateway. In this case, e.g. the discovery fails. With this setting the buffer size can be adjusted. The value is specified in kB. + +- **useAuthentication** +Username and password are send to the gateway to authenticate the access to the gateway. + +- **username** +Username for Authentication to the gateway. + +- **password** +Password for Authentication to the gateway. The syntax for a bridge is: diff --git a/bundles/org.openhab.binding.homematic/src/main/java/org/openhab/binding/homematic/internal/common/AuthenticationHandler.java b/bundles/org.openhab.binding.homematic/src/main/java/org/openhab/binding/homematic/internal/common/AuthenticationHandler.java new file mode 100644 index 00000000000..33152e38662 --- /dev/null +++ b/bundles/org.openhab.binding.homematic/src/main/java/org/openhab/binding/homematic/internal/common/AuthenticationHandler.java @@ -0,0 +1,53 @@ +/** + * Copyright (c) 2010-2024 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.homematic.internal.common; + +import java.util.Base64; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; +import org.eclipse.jetty.client.api.Request; +import org.eclipse.jetty.http.HttpHeader; +import org.openhab.core.i18n.ConfigurationException; + +/** + * Handles the authentication to Homematic server. + * + * @author Christian Kittel + */ +@NonNullByDefault +public class AuthenticationHandler { + + private Boolean useAuthentication; + private @Nullable String authValue; + + public AuthenticationHandler(HomematicConfig config) throws ConfigurationException { + this.useAuthentication = config.getUseAuthentication(); + if (!useAuthentication) { + return; + } + + if (config.getPassword() == null || config.getUserName() == null) { + throw new ConfigurationException("Username or password missing"); + } + this.authValue = "Basic " + + Base64.getEncoder().encodeToString((config.getUserName() + ":" + config.getPassword()).getBytes()); + } + + /** + * Add or remove the basic auth credentials th the request if needed. + */ + public Request updateAuthenticationInformation(final Request request) { + return useAuthentication ? request.header(HttpHeader.AUTHORIZATION, authValue) : request; + } +} diff --git a/bundles/org.openhab.binding.homematic/src/main/java/org/openhab/binding/homematic/internal/common/HomematicConfig.java b/bundles/org.openhab.binding.homematic/src/main/java/org/openhab/binding/homematic/internal/common/HomematicConfig.java index 9624a855c76..1f5cb73b2b5 100644 --- a/bundles/org.openhab.binding.homematic/src/main/java/org/openhab/binding/homematic/internal/common/HomematicConfig.java +++ b/bundles/org.openhab.binding.homematic/src/main/java/org/openhab/binding/homematic/internal/common/HomematicConfig.java @@ -60,6 +60,10 @@ public class HomematicConfig { private HmGatewayInfo gatewayInfo; private int callbackRegTimeout; + private boolean useAuthentication; + private String userName; + private String password; + /** * Returns the Homematic gateway address. */ @@ -272,6 +276,48 @@ public class HomematicConfig { return getRpcPort(channel.getDevice().getHmInterface()); } + /** + * Returns true if authorization for the gateway has to be used; otherwise false + */ + public boolean getUseAuthentication() { + return useAuthentication; + } + + /** + * Sets if authorization for the gateway has to be used + */ + public void setUseAuthentication(Boolean useAuthentication) { + this.useAuthentication = useAuthentication; + } + + /** + * Returns the user name for authorize against the gateway + */ + public String getUserName() { + return userName; + } + + /** + * Sets the user name for authorize against the gateway + */ + public void setUserName(String userName) { + this.userName = userName; + } + + /** + * Returns the password for authorize against the gateway + */ + public String getPassword() { + return password; + } + + /** + * Sets the password for authorize against the gateway + */ + public void setPassword(String password) { + this.password = password; + } + /** * Returns the Homematic gateway port of the interfaces. */ diff --git a/bundles/org.openhab.binding.homematic/src/main/java/org/openhab/binding/homematic/internal/communicator/AbstractHomematicGateway.java b/bundles/org.openhab.binding.homematic/src/main/java/org/openhab/binding/homematic/internal/communicator/AbstractHomematicGateway.java index 64023f8e818..7210e024352 100644 --- a/bundles/org.openhab.binding.homematic/src/main/java/org/openhab/binding/homematic/internal/communicator/AbstractHomematicGateway.java +++ b/bundles/org.openhab.binding.homematic/src/main/java/org/openhab/binding/homematic/internal/communicator/AbstractHomematicGateway.java @@ -12,7 +12,7 @@ */ package org.openhab.binding.homematic.internal.communicator; -import static org.openhab.binding.homematic.internal.HomematicBindingConstants.*; +import static org.openhab.binding.homematic.internal.HomematicBindingConstants.GATEWAY_POOL_NAME; import static org.openhab.binding.homematic.internal.misc.HomematicConstants.*; import java.io.IOException; diff --git a/bundles/org.openhab.binding.homematic/src/main/java/org/openhab/binding/homematic/internal/communicator/CcuGateway.java b/bundles/org.openhab.binding.homematic/src/main/java/org/openhab/binding/homematic/internal/communicator/CcuGateway.java index 7f43335a6f7..05d7d620872 100644 --- a/bundles/org.openhab.binding.homematic/src/main/java/org/openhab/binding/homematic/internal/communicator/CcuGateway.java +++ b/bundles/org.openhab.binding.homematic/src/main/java/org/openhab/binding/homematic/internal/communicator/CcuGateway.java @@ -22,10 +22,12 @@ import java.util.Map; import java.util.Objects; import java.util.concurrent.TimeUnit; +import org.eclipse.jdt.annotation.NonNull; import org.eclipse.jetty.client.HttpClient; import org.eclipse.jetty.client.api.ContentResponse; import org.eclipse.jetty.client.util.StringContentProvider; import org.eclipse.jetty.http.HttpHeader; +import org.openhab.binding.homematic.internal.common.AuthenticationHandler; import org.openhab.binding.homematic.internal.common.HomematicConfig; import org.openhab.binding.homematic.internal.communicator.client.UnknownParameterSetException; import org.openhab.binding.homematic.internal.communicator.client.UnknownRpcFailureException; @@ -41,6 +43,7 @@ import org.openhab.binding.homematic.internal.model.HmResult; import org.openhab.binding.homematic.internal.model.TclScript; import org.openhab.binding.homematic.internal.model.TclScriptDataList; import org.openhab.binding.homematic.internal.model.TclScriptList; +import org.openhab.core.i18n.ConfigurationException; import org.osgi.framework.Bundle; import org.osgi.framework.FrameworkUtil; import org.slf4j.Logger; @@ -60,10 +63,14 @@ public class CcuGateway extends AbstractHomematicGateway { private Map tclregaScripts; private XStream xStream = new XStream(new StaxDriver()); + private @NonNull AuthenticationHandler authenticationHandler; + protected CcuGateway(String id, HomematicConfig config, HomematicGatewayAdapter gatewayAdapter, - HttpClient httpClient) { + HttpClient httpClient) throws IOException, ConfigurationException { super(id, config, gatewayAdapter, httpClient); + this.authenticationHandler = new AuthenticationHandler(config); + xStream.allowTypesByWildcard(new String[] { HmDevice.class.getPackageName() + ".**" }); xStream.setClassLoader(CcuGateway.class.getClassLoader()); xStream.autodetectAnnotations(true); @@ -211,9 +218,9 @@ public class CcuGateway extends AbstractHomematicGateway { } StringContentProvider content = new StringContentProvider(script, config.getEncoding()); - ContentResponse response = httpClient.POST(config.getTclRegaUrl()).content(content) - .timeout(config.getTimeout(), TimeUnit.SECONDS) - .header(HttpHeader.CONTENT_TYPE, "text/plain;charset=" + config.getEncoding()).send(); + ContentResponse response = authenticationHandler.updateAuthenticationInformation(httpClient + .POST(config.getTclRegaUrl()).content(content).timeout(config.getTimeout(), TimeUnit.SECONDS) + .header(HttpHeader.CONTENT_TYPE, "text/plain;charset=" + config.getEncoding())).send(); String result = new String(response.getContent(), config.getEncoding()); int lastPos = result.lastIndexOf(""); diff --git a/bundles/org.openhab.binding.homematic/src/main/java/org/openhab/binding/homematic/internal/communicator/HomematicGatewayFactory.java b/bundles/org.openhab.binding.homematic/src/main/java/org/openhab/binding/homematic/internal/communicator/HomematicGatewayFactory.java index 9f2271ee51f..d50def0c80c 100644 --- a/bundles/org.openhab.binding.homematic/src/main/java/org/openhab/binding/homematic/internal/communicator/HomematicGatewayFactory.java +++ b/bundles/org.openhab.binding.homematic/src/main/java/org/openhab/binding/homematic/internal/communicator/HomematicGatewayFactory.java @@ -18,6 +18,7 @@ import org.eclipse.jetty.client.HttpClient; import org.openhab.binding.homematic.internal.common.HomematicConfig; import org.openhab.binding.homematic.internal.communicator.client.RpcClient; import org.openhab.binding.homematic.internal.communicator.client.XmlRpcClient; +import org.openhab.core.i18n.ConfigurationException; /** * Factory which evaluates the type of the Homematic gateway and instantiates the appropriate class. @@ -30,7 +31,7 @@ public class HomematicGatewayFactory { * Creates the HomematicGateway. */ public static HomematicGateway createGateway(String id, HomematicConfig config, - HomematicGatewayAdapter gatewayAdapter, HttpClient httpClient) throws IOException { + HomematicGatewayAdapter gatewayAdapter, HttpClient httpClient) throws IOException, ConfigurationException { loadGatewayInfo(config, id, httpClient); if (config.getGatewayInfo().isCCU()) { return new CcuGateway(id, config, gatewayAdapter, httpClient); diff --git a/bundles/org.openhab.binding.homematic/src/main/java/org/openhab/binding/homematic/internal/communicator/client/XmlRpcClient.java b/bundles/org.openhab.binding.homematic/src/main/java/org/openhab/binding/homematic/internal/communicator/client/XmlRpcClient.java index 3ea6d35f8c9..8776acc95e8 100644 --- a/bundles/org.openhab.binding.homematic/src/main/java/org/openhab/binding/homematic/internal/communicator/client/XmlRpcClient.java +++ b/bundles/org.openhab.binding.homematic/src/main/java/org/openhab/binding/homematic/internal/communicator/client/XmlRpcClient.java @@ -14,6 +14,8 @@ package org.openhab.binding.homematic.internal.communicator.client; import java.io.ByteArrayInputStream; import java.io.IOException; +import java.net.URI; +import java.net.URISyntaxException; import java.util.concurrent.ExecutionException; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; @@ -26,11 +28,14 @@ import org.eclipse.jetty.client.api.Request; import org.eclipse.jetty.client.util.BytesContentProvider; import org.eclipse.jetty.client.util.FutureResponseListener; import org.eclipse.jetty.http.HttpHeader; +import org.eclipse.jetty.http.HttpStatus; +import org.openhab.binding.homematic.internal.common.AuthenticationHandler; import org.openhab.binding.homematic.internal.common.HomematicConfig; import org.openhab.binding.homematic.internal.communicator.message.RpcRequest; import org.openhab.binding.homematic.internal.communicator.message.XmlRpcRequest; import org.openhab.binding.homematic.internal.communicator.message.XmlRpcResponse; import org.openhab.binding.homematic.internal.communicator.parser.RpcResponseParser; +import org.openhab.core.i18n.ConfigurationException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.xml.sax.SAXException; @@ -43,8 +48,9 @@ import org.xml.sax.SAXException; public class XmlRpcClient extends RpcClient { private final Logger logger = LoggerFactory.getLogger(XmlRpcClient.class); private HttpClient httpClient; + private AuthenticationHandler authenticationHandler; - public XmlRpcClient(HomematicConfig config, HttpClient httpClient) throws IOException { + public XmlRpcClient(HomematicConfig config, HttpClient httpClient) throws IOException, ConfigurationException { super(config); this.httpClient = httpClient; } @@ -103,11 +109,22 @@ public class XmlRpcClient extends RpcClient { if (port == config.getGroupPort()) { url += "/groups"; } - Request req = httpClient.POST(url).content(content).timeout(config.getTimeout(), TimeUnit.SECONDS) - .header(HttpHeader.CONTENT_TYPE, "text/xml;charset=" + config.getEncoding()); + if (authenticationHandler == null) { + authenticationHandler = new AuthenticationHandler(config); + } + + Request req = authenticationHandler.updateAuthenticationInformation( + httpClient.POST(new URI(url)).content(content).timeout(config.getTimeout(), TimeUnit.SECONDS) + .header(HttpHeader.CONTENT_TYPE, "text/xml;charset=" + config.getEncoding())); + FutureResponseListener listener = new FutureResponseListener(req, config.getBufferSize() * 1024); req.send(listener); ContentResponse response = listener.get(config.getTimeout(), TimeUnit.SECONDS); + + if (response.getStatus() == HttpStatus.UNAUTHORIZED_401) { + throw new IOException("Access to Homematic gateway unauthorized"); + } + ret = response.getContent(); if (ret == null || ret.length == 0) { throw new IOException("Received no data from the Homematic gateway"); @@ -116,7 +133,8 @@ public class XmlRpcClient extends RpcClient { String result = new String(ret, config.getEncoding()); logger.trace("Client XmlRpcResponse (port {}):\n{}", port, result); } - } catch (InterruptedException | ExecutionException | TimeoutException | IllegalArgumentException e) { + } catch (InterruptedException | ExecutionException | TimeoutException | IllegalArgumentException + | URISyntaxException | ConfigurationException e) { throw new IOException(e); } return ret; diff --git a/bundles/org.openhab.binding.homematic/src/main/java/org/openhab/binding/homematic/internal/handler/HomematicBridgeHandler.java b/bundles/org.openhab.binding.homematic/src/main/java/org/openhab/binding/homematic/internal/handler/HomematicBridgeHandler.java index 8d4f29fe9d3..5e0e358d1d4 100644 --- a/bundles/org.openhab.binding.homematic/src/main/java/org/openhab/binding/homematic/internal/handler/HomematicBridgeHandler.java +++ b/bundles/org.openhab.binding.homematic/src/main/java/org/openhab/binding/homematic/internal/handler/HomematicBridgeHandler.java @@ -36,6 +36,7 @@ import org.openhab.binding.homematic.internal.model.HmDevice; import org.openhab.binding.homematic.internal.model.HmGatewayInfo; import org.openhab.binding.homematic.internal.type.HomematicTypeGenerator; import org.openhab.binding.homematic.internal.type.UidUtils; +import org.openhab.core.i18n.ConfigurationException; import org.openhab.core.library.types.DecimalType; import org.openhab.core.thing.Bridge; import org.openhab.core.thing.Channel; @@ -132,6 +133,9 @@ public class HomematicBridgeHandler extends BaseBridgeHandler implements Homemat ex.getMessage(), ex); disposeInternal(); scheduleReinitialize(); + } catch (ConfigurationException ex) { + updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, ex.getMessage()); + disposeInternal(); } } } diff --git a/bundles/org.openhab.binding.homematic/src/main/resources/OH-INF/i18n/homematic.properties b/bundles/org.openhab.binding.homematic/src/main/resources/OH-INF/i18n/homematic.properties index 207b8dbd7ec..a78f4d6b481 100644 --- a/bundles/org.openhab.binding.homematic/src/main/resources/OH-INF/i18n/homematic.properties +++ b/bundles/org.openhab.binding.homematic/src/main/resources/OH-INF/i18n/homematic.properties @@ -37,6 +37,8 @@ thing-type.config.homematic.bridge.hmIpPort.label = HMIP Port thing-type.config.homematic.bridge.hmIpPort.description = The port number of the Homematic IP daemon thing-type.config.homematic.bridge.installModeDuration.label = Install Mode Duration thing-type.config.homematic.bridge.installModeDuration.description = Time in seconds that the controller will be in install mode when a device discovery is initiated +thing-type.config.homematic.bridge.password.label = Password +thing-type.config.homematic.bridge.password.description = Password for accessing the gateway if authenticaton is required. thing-type.config.homematic.bridge.rfPort.label = RF Port thing-type.config.homematic.bridge.rfPort.description = The port number of the RF daemon thing-type.config.homematic.bridge.socketMaxAlive.label = Socket MaxAlive @@ -45,6 +47,10 @@ thing-type.config.homematic.bridge.timeout.label = Timeout thing-type.config.homematic.bridge.timeout.description = The timeout in seconds for connections to a Homematic gateway thing-type.config.homematic.bridge.unpairOnDeletion.label = Unpair Devices On Deletion thing-type.config.homematic.bridge.unpairOnDeletion.description = If set to true, devices are unpaired from the gateway when their corresponding things are removed. The option "factoryResetOnDeletion" also unpairs a device, so in order to avoid unpairing on deletion, both options need to be set to false! +thing-type.config.homematic.bridge.useAuthentication.label = Use authentication +thing-type.config.homematic.bridge.useAuthentication.description = Use authentication to access the gateway. If set to true, username and password is required. +thing-type.config.homematic.bridge.userName.label = User name +thing-type.config.homematic.bridge.userName.description = User name for accessing the gateway if authenticaton is required. thing-type.config.homematic.bridge.wiredPort.label = Wired Port thing-type.config.homematic.bridge.wiredPort.description = The port number of the HS485 daemon thing-type.config.homematic.bridge.xmlCallbackPort.label = XML-RPC Callback Port diff --git a/bundles/org.openhab.binding.homematic/src/main/resources/OH-INF/thing/bridge.xml b/bundles/org.openhab.binding.homematic/src/main/resources/OH-INF/thing/bridge.xml index 343098ea1d9..42537ce61c8 100644 --- a/bundles/org.openhab.binding.homematic/src/main/resources/OH-INF/thing/bridge.xml +++ b/bundles/org.openhab.binding.homematic/src/main/resources/OH-INF/thing/bridge.xml @@ -127,6 +127,23 @@ 2048 true + + + Use authentication to access the gateway. If set to true, username and password is required. + true + false + + + + User name for accessing the gateway if authenticaton is required. + true + + + + Password for accessing the gateway if authenticaton is required. + true + password +