added migrated 2.x add-ons
Signed-off-by: Kai Kreuzer <kai@openhab.org>
This commit is contained in:
32
bundles/org.openhab.binding.groheondus/.classpath
Normal file
32
bundles/org.openhab.binding.groheondus/.classpath
Normal file
@@ -0,0 +1,32 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<classpath>
|
||||
<classpathentry kind="src" output="target/classes" path="src/main/java">
|
||||
<attributes>
|
||||
<attribute name="optional" value="true"/>
|
||||
<attribute name="maven.pomderived" value="true"/>
|
||||
</attributes>
|
||||
</classpathentry>
|
||||
<classpathentry excluding="**" kind="src" output="target/classes" path="src/main/resources">
|
||||
<attributes>
|
||||
<attribute name="maven.pomderived" value="true"/>
|
||||
</attributes>
|
||||
</classpathentry>
|
||||
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-11">
|
||||
<attributes>
|
||||
<attribute name="maven.pomderived" value="true"/>
|
||||
</attributes>
|
||||
</classpathentry>
|
||||
<classpathentry kind="con" path="org.eclipse.m2e.MAVEN2_CLASSPATH_CONTAINER">
|
||||
<attributes>
|
||||
<attribute name="maven.pomderived" value="true"/>
|
||||
</attributes>
|
||||
</classpathentry>
|
||||
<classpathentry kind="src" output="target/test-classes" path="src/test/java">
|
||||
<attributes>
|
||||
<attribute name="optional" value="true"/>
|
||||
<attribute name="maven.pomderived" value="true"/>
|
||||
<attribute name="test" value="true"/>
|
||||
</attributes>
|
||||
</classpathentry>
|
||||
<classpathentry kind="output" path="target/classes"/>
|
||||
</classpath>
|
||||
23
bundles/org.openhab.binding.groheondus/.project
Normal file
23
bundles/org.openhab.binding.groheondus/.project
Normal file
@@ -0,0 +1,23 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<projectDescription>
|
||||
<name>org.openhab.binding.groheondus</name>
|
||||
<comment></comment>
|
||||
<projects>
|
||||
</projects>
|
||||
<buildSpec>
|
||||
<buildCommand>
|
||||
<name>org.eclipse.jdt.core.javabuilder</name>
|
||||
<arguments>
|
||||
</arguments>
|
||||
</buildCommand>
|
||||
<buildCommand>
|
||||
<name>org.eclipse.m2e.core.maven2Builder</name>
|
||||
<arguments>
|
||||
</arguments>
|
||||
</buildCommand>
|
||||
</buildSpec>
|
||||
<natures>
|
||||
<nature>org.eclipse.jdt.core.javanature</nature>
|
||||
<nature>org.eclipse.m2e.core.maven2Nature</nature>
|
||||
</natures>
|
||||
</projectDescription>
|
||||
20
bundles/org.openhab.binding.groheondus/NOTICE
Normal file
20
bundles/org.openhab.binding.groheondus/NOTICE
Normal file
@@ -0,0 +1,20 @@
|
||||
This content is produced and maintained by the openHAB project.
|
||||
|
||||
* Project home: https://www.openhab.org
|
||||
|
||||
== Declared Project Licenses
|
||||
|
||||
This program and the accompanying materials are made available under the terms
|
||||
of the Eclipse Public License 2.0 which is available at
|
||||
https://www.eclipse.org/legal/epl-2.0/.
|
||||
|
||||
== Source Code
|
||||
|
||||
https://github.com/openhab/openhab-addons
|
||||
|
||||
== Third-party Content
|
||||
|
||||
grohe-ondus-api-java
|
||||
* License: MIT License
|
||||
* Project: https://github.com/FlorianSW/grohe-ondus-api-java
|
||||
* Source: https://github.com/FlorianSW/grohe-ondus-api-java
|
||||
139
bundles/org.openhab.binding.groheondus/README.md
Normal file
139
bundles/org.openhab.binding.groheondus/README.md
Normal file
@@ -0,0 +1,139 @@
|
||||
# GROHE ONDUS Binding
|
||||
|
||||
The GROHE ONDUS Binding provides access to data collected by a GROHE ONDUS appliance, such as an [GROHE Sense Guard](https://www.grohe.de/de_de/smarthome/grohe-sense-guard/).
|
||||
The binding uses the REST API interface (the same as used by the Android App) to retrieve the collected data.
|
||||
|
||||
## Supported Things
|
||||
|
||||
This binding should support all appliances from GROHE, however, only the GROHE Sense Guard is tested with it.
|
||||
|
||||
| Thing type | Name |
|
||||
|--------------------------|--------------------------|
|
||||
| account | GROHE ONDUS Account |
|
||||
| senseguard | GROHE SENSE Guard device |
|
||||
| sense | GROHE SENSE device |
|
||||
|
||||
## Discovery
|
||||
|
||||
The binding requires you to create at least one Account thing as a bridge manually.
|
||||
The discovery process will look through all locations and rooms of your configured GROHE account and adds each found appliance as a new thing automatically to the inbox.
|
||||
|
||||
## Binding Configuration
|
||||
|
||||
This binding does not require any configuration outside of things.
|
||||
|
||||
## Thing Configuration
|
||||
|
||||
There is only one thing and one bridge that needs to be configured together to get this binding to work, see the full example section for a self-explaining example.
|
||||
|
||||
### Account Bridge
|
||||
|
||||
The `groheondus:account` bridge is used to configure the API interface for a specific account, which is used to access the collected and saved data of your GROHE account.
|
||||
You can either use your username and password combination for logging in into your GROHE account, in which case both parameters, `username` as well as `password`, are required arguments and refer to the same login credentials you used during setting up your GROHE account or while logging into the app.
|
||||
Alternatively you can use a so called `refresh token` to grant openHAB access to your account without having to share your credentials with the system.
|
||||
For that you need to obtain such `refresh token` from the GROHE ONDUS Api (see more on that below) and paste this string into the respective input field on the account management page you can reach from `http://<your-openHAB-domain-and-port>/groheondus`.
|
||||
On this site you can also delete a previously saved `refresh token`.
|
||||
The GROHE ONDUS binding also refreshes this refresh token in order to ensure that you stay logged in.
|
||||
|
||||
### Appliance
|
||||
|
||||
The `groheondus:sense` and `groheondus:senseguard` things are used to retrieve information of a specific appliance from GROHE.
|
||||
This appliance needs to be connected with your GROHE ONDUS account as configured in the corresponding Account Bridge.
|
||||
The appliance needs to be configured with the unique appliance ID (with the `applianceId` configuration) as well as the `roomId`
|
||||
and the `locationId`. Once the account bridge is configured, the appliances in your account will be discovered as Appliance things.
|
||||
|
||||
| Configuration | Default value | Description |
|
||||
|--------------------------|--------------------------|-------------------------------------------------------|
|
||||
| applianceId | '' | Unique ID of the appliance in the GROHE ONDUS account |
|
||||
| roomId | '' | ID of the room the appliance is in |
|
||||
| locationId | '' | ID of the location (building) the appliance is in |
|
||||
| pollingInterval | Retrieved from API, | Interval in seconds to get new data from the API |
|
||||
| | usually 900 | The `sense` thing uses 900 by default |
|
||||
|
||||
#### Channels
|
||||
|
||||
##### senseguard
|
||||
|
||||
| Channel | Type | Description |
|
||||
|--------------------------|--------------------------|-------------------------------------------------------|
|
||||
| name | String | The name of the appliance |
|
||||
| pressure | Number:Pressure | The pressure of your water supply |
|
||||
| temperature_guard | Number:Temperature | The ambient temperature of the appliance |
|
||||
| valve_open | Switch | Valve switch |
|
||||
| waterconsumption | Number | The amount of water used in a specific timeframe |
|
||||
|
||||
##### sense
|
||||
|
||||
| Channel | Type | Description |
|
||||
|--------------------------|--------------------------|-------------------------------------------------------|
|
||||
| name | String | The name of the appliance |
|
||||
| humidity | Number:Dimensionless | The humidity measured by the appliance |
|
||||
| temperature | Number:Temperature | The ambient temperature of the appliance |
|
||||
| battery | Number | The battery level of the appliance |
|
||||
|
||||
## Full Example
|
||||
|
||||
Things file:
|
||||
|
||||
````
|
||||
Bridge groheondus:account:account1 [ username="user@example.com", password="YourStrongPasswordHere!" ] {
|
||||
groheondus:senseguard:550e8400-e29b-11d4-a716-446655440000 [ applianceId="550e8400-e29b-11d4-a716-446655440000", roomId=456, locationId=123 ] {
|
||||
Channels:
|
||||
Type number : waterconsumption [
|
||||
timeframe=3
|
||||
]
|
||||
}
|
||||
groheondus:sense:550e8400-e29b-11d4-a716-446655440000 [ applianceId="444e8400-e29b-11d4-a716-446655440000", roomId=456, locationId=123 ]
|
||||
}
|
||||
````
|
||||
|
||||
Items file:
|
||||
|
||||
````
|
||||
String Name_Sense_Guard "Appliance Name" {channel="groheondus:senseguard:groheondus:appliance:550e8400-e29b-11d4-a716-446655440000:name"}
|
||||
Number:Pressure Pressure_Sense_Guard "Pressure [%.1f %unit%]" {channel="groheondus:senseguard:groheondus:appliance:550e8400-e29b-11d4-a716-446655440000:pressure"}
|
||||
Number:Temperature Temperature_Sense_Guard "Temperature [%.1f %unit%]" {channel="groheondus:senseguard:groheondus:appliance:550e8400-e29b-11d4-a716-446655440000:temperature_guard"}
|
||||
|
||||
String Name_Sense "Temperature [%.1f %unit%]" {channel="groheondus:sense:groheondus:appliance:444e8400-e29b-11d4-a716-446655440000:name"}
|
||||
Number:Temperature Temperature_Sense "Temperature [%.1f %unit%]" {channel="groheondus:sense:groheondus:appliance:444e8400-e29b-11d4-a716-446655440000:temperature"}
|
||||
Number Humidity_Sense "Humidity [%.1f %unit%]" {channel="groheondus:sense:groheondus:appliance:444e8400-e29b-11d4-a716-446655440000:humidity"}
|
||||
````
|
||||
|
||||
## Obtaining a `refresh token`
|
||||
|
||||
Actually obtaining a `refresh token` from the GROHE ONDUS Api requires some manual steps.
|
||||
In order to more deeply understand what is happening during the process, you can read more information about the OAuth2/OIDC (OpenID Connect) login flow by searching for these terms in your favorite search engine.
|
||||
Here is a short step-by-step guide on how to obtain a refresh token:
|
||||
|
||||
1. Open a new tab in your Internet browser
|
||||
2. Open the developer console of your browser (mostly possible by pressing F12)
|
||||
3. Select the network tab of the developer console (which shows you the network request done by the browser)
|
||||
4. Open the following URL: https://idp2-apigw.cloud.grohe.com/v3/iot/oidc/login
|
||||
5. You will automatically being redirected to the GROHE ONDUS login page, login there
|
||||
6. After logging in successfully, nothing should happen, except a failed request to a page starting with `token?`
|
||||
7. Click on this request (the URL in the request overview should start with `ondus://idp2-apigw.cloud.grohe.com/v3/iot/oidc/token?` or something like that
|
||||
8. Copy the whole request URL (which should contain a lot of stuff, like a `state` parameter and so on)
|
||||
9. Open a new tab in your Internet browser and paste the URL into the address bar (do not hit ENTER or start the navigation to this page, yet)
|
||||
10. Replace the `ondus://` part of the URL with `https://` and hit ENTER
|
||||
11. The response of the page should be plain text with a so called `JSON object`. Somewhere in the text should be a `refresh_token` string, select the string after this `refresh_token` text, which is encapsulated with `"`.
|
||||
|
||||
E.g.: If the response of the page looks like this:
|
||||
|
||||
````
|
||||
{
|
||||
"access_token": "the_access_token",
|
||||
"expires_in":3600,
|
||||
"refresh_expires_in":15552000,
|
||||
"refresh_token":"the_refresh_token",
|
||||
"token_type":"bearer",
|
||||
"id_token":"the_id_token",
|
||||
"not-before-policy":0,
|
||||
"session_state":"a-state",
|
||||
"scope":"",
|
||||
"tandc_accepted":true,
|
||||
"partialLogin":false
|
||||
}
|
||||
````
|
||||
|
||||
Then the `refresh_token` value you should copy would be: `the_refresh_token`.
|
||||
This value is the `refresh token` you should save as described above.
|
||||
42
bundles/org.openhab.binding.groheondus/pom.xml
Normal file
42
bundles/org.openhab.binding.groheondus/pom.xml
Normal file
@@ -0,0 +1,42 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<parent>
|
||||
<groupId>org.openhab.addons.bundles</groupId>
|
||||
<artifactId>org.openhab.addons.reactor.bundles</artifactId>
|
||||
<version>3.0.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>org.openhab.binding.groheondus</artifactId>
|
||||
|
||||
<name>openHAB Add-ons :: Bundles :: GROHE ONDUS Binding</name>
|
||||
|
||||
<properties>
|
||||
<dep.noembedding>commons-text,commons-lang3</dep.noembedding>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.grohe</groupId>
|
||||
<artifactId>ondus-api</artifactId>
|
||||
<version>1.0.0</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.commons</groupId>
|
||||
<artifactId>commons-text</artifactId>
|
||||
<version>1.6</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.commons</groupId>
|
||||
<artifactId>commons-lang3</artifactId>
|
||||
<version>3.8.1</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
</project>
|
||||
@@ -0,0 +1,12 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<features name="org.openhab.binding.groheondus-${project.version}" xmlns="http://karaf.apache.org/xmlns/features/v1.4.0">
|
||||
<repository>mvn:org.openhab.core.features.karaf/org.openhab.core.features.karaf.openhab-core/${ohc.version}/xml/features</repository>
|
||||
|
||||
<feature name="openhab-binding-groheondus" description="GROHE ONDUS Binding" version="${project.version}">
|
||||
<feature>openhab-runtime-base</feature>
|
||||
<feature dependency="true">openhab.tp-jackson</feature>
|
||||
<bundle dependency="true">mvn:org.apache.commons/commons-text/1.6</bundle>
|
||||
<bundle dependency="true">mvn:org.apache.commons/commons-lang3/3.8.1</bundle>
|
||||
<bundle start-level="80">mvn:org.openhab.addons.bundles/org.openhab.binding.groheondus/${project.version}</bundle>
|
||||
</feature>
|
||||
</features>
|
||||
@@ -0,0 +1,139 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2020 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.groheondus.internal;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.net.URLEncoder;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.Map;
|
||||
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.http.HttpServlet;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
import org.eclipse.jetty.http.HttpStatus;
|
||||
import org.openhab.binding.groheondus.internal.handler.GroheOndusAccountHandler;
|
||||
import org.osgi.service.http.HttpService;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* @author Florian Schmidt - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class AccountServlet extends HttpServlet {
|
||||
private static final long serialVersionUID = -6321196284331950479L;
|
||||
private final Logger logger = LoggerFactory.getLogger(AccountServlet.class);
|
||||
|
||||
private HttpService httpService;
|
||||
private String bridgeId;
|
||||
private GroheOndusAccountHandler accountHandler;
|
||||
|
||||
public AccountServlet(HttpService httpService, String bridgeId, GroheOndusAccountHandler accountHandler) {
|
||||
this.httpService = httpService;
|
||||
this.bridgeId = bridgeId;
|
||||
this.accountHandler = accountHandler;
|
||||
|
||||
try {
|
||||
httpService.registerServlet(servletUrl(), this, null, httpService.createDefaultHttpContext());
|
||||
} catch (Exception e) {
|
||||
logger.warn("Register servlet fails", e);
|
||||
}
|
||||
}
|
||||
|
||||
private String servletUrl() throws UnsupportedEncodingException {
|
||||
return "/groheondus/" + URLEncoder.encode(bridgeId, StandardCharsets.UTF_8.name());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doGet(@Nullable HttpServletRequest req, @Nullable HttpServletResponse resp)
|
||||
throws ServletException, IOException {
|
||||
if (req == null || resp == null) {
|
||||
return;
|
||||
}
|
||||
resp.addHeader("content-type", "text/html;charset=UTF-8");
|
||||
StringBuilder htmlString = new StringBuilder();
|
||||
htmlString.append("<html>");
|
||||
htmlString.append("<head>");
|
||||
htmlString.append("<title>Set refresh token</title>");
|
||||
htmlString.append("</head>");
|
||||
htmlString.append("<body>");
|
||||
htmlString.append("<header>");
|
||||
htmlString.append("<h1>Set refresh token for accout: ");
|
||||
htmlString.append(bridgeId);
|
||||
htmlString.append("</h1>");
|
||||
htmlString.append("</header>");
|
||||
htmlString.append("<div>Has refresh token: ");
|
||||
if (this.accountHandler.hasRefreshToken()) {
|
||||
htmlString.append("yes");
|
||||
htmlString.append(
|
||||
"<input type=\"submit\" value=\"Delete\" onclick=\"fetch(window.location.href, {method: 'DELETE'}).then(window.location.reload())\">");
|
||||
} else {
|
||||
htmlString.append("no");
|
||||
}
|
||||
htmlString.append("</div>");
|
||||
htmlString.append("<form method=\"post\">");
|
||||
htmlString.append("<label for=\"refreshToken\">Refresh Token: </label>");
|
||||
htmlString.append("<input type=\"text\" id=\"refreshToken\" autocomplete=\"off\" name=\"refreshToken\">");
|
||||
htmlString.append("<input type=\"submit\" value=\"Save\">");
|
||||
htmlString.append("</form>");
|
||||
htmlString.append("</body>");
|
||||
htmlString.append("</html>");
|
||||
|
||||
resp.getWriter().write(htmlString.toString());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doPost(@Nullable HttpServletRequest req, @Nullable HttpServletResponse resp)
|
||||
throws ServletException, IOException {
|
||||
if (req == null) {
|
||||
return;
|
||||
}
|
||||
if (resp == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
Map<String, String[]> map = req.getParameterMap();
|
||||
this.accountHandler.setRefreshToken(map.get("refreshToken")[0]);
|
||||
|
||||
resp.addHeader("Location", "/groheondus");
|
||||
resp.setStatus(HttpStatus.MOVED_TEMPORARILY_302);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doDelete(@Nullable HttpServletRequest req, @Nullable HttpServletResponse resp)
|
||||
throws ServletException, IOException {
|
||||
if (req == null) {
|
||||
return;
|
||||
}
|
||||
if (resp == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.accountHandler.deleteRefreshToken();
|
||||
|
||||
resp.setStatus(HttpStatus.OK_200);
|
||||
}
|
||||
|
||||
public void dispose() {
|
||||
try {
|
||||
httpService.unregister(servletUrl());
|
||||
} catch (UnsupportedEncodingException e) {
|
||||
logger.warn("Unregistration of servlet failed", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,114 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2020 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.groheondus.internal;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.http.HttpServlet;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
import org.eclipse.jetty.http.HttpStatus;
|
||||
import org.openhab.core.thing.Thing;
|
||||
import org.osgi.service.component.annotations.Activate;
|
||||
import org.osgi.service.component.annotations.Component;
|
||||
import org.osgi.service.component.annotations.Reference;
|
||||
import org.osgi.service.component.annotations.ServiceScope;
|
||||
import org.osgi.service.http.HttpService;
|
||||
import org.osgi.service.http.NamespaceException;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* @author Florian Schmidt - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
@Component(service = AccountsServlet.class, scope = ServiceScope.SINGLETON)
|
||||
public class AccountsServlet extends HttpServlet {
|
||||
private static final long serialVersionUID = -9183159739446995608L;
|
||||
|
||||
private static final String SERVLET_URL = "/groheondus";
|
||||
|
||||
private final Logger logger = LoggerFactory.getLogger(AccountsServlet.class);
|
||||
private final List<Thing> accounts = new ArrayList<>();
|
||||
private HttpService httpService;
|
||||
|
||||
@Activate
|
||||
public AccountsServlet(@Reference HttpService httpService) {
|
||||
this.httpService = httpService;
|
||||
|
||||
try {
|
||||
httpService.registerServlet(SERVLET_URL, this, null, httpService.createDefaultHttpContext());
|
||||
} catch (ServletException | NamespaceException e) {
|
||||
logger.warn("Register servlet fails", e);
|
||||
}
|
||||
}
|
||||
|
||||
public void addAccount(Thing accountThing) {
|
||||
accounts.add(accountThing);
|
||||
}
|
||||
|
||||
public void removeAccount(Thing accountThing) {
|
||||
accounts.remove(accountThing);
|
||||
}
|
||||
|
||||
public void deactivate() {
|
||||
httpService.unregister(SERVLET_URL);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doGet(@Nullable HttpServletRequest req, @Nullable HttpServletResponse resp)
|
||||
throws ServletException, IOException {
|
||||
if (req == null || resp == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
StringBuilder htmlString = new StringBuilder();
|
||||
htmlString.append("<html>");
|
||||
htmlString.append("<head>");
|
||||
htmlString.append("<title>GROHE Ondus Account login</title>");
|
||||
htmlString.append("</head>");
|
||||
htmlString.append("<body>");
|
||||
if (accounts.isEmpty()) {
|
||||
htmlString.append(
|
||||
"Please first create an GROHE ONDUS account thing in openHAB in order to log into this account.");
|
||||
} else {
|
||||
htmlString.append(
|
||||
"You've the following GROHE ONDUS account things, click on the one you want to manage:<br />");
|
||||
htmlString.append("<ul>");
|
||||
accounts.forEach(account -> {
|
||||
String accountId = account.getUID().getId();
|
||||
htmlString.append("<li>");
|
||||
htmlString.append("<a href=\"");
|
||||
htmlString.append(SERVLET_URL);
|
||||
htmlString.append("/");
|
||||
htmlString.append(accountId);
|
||||
htmlString.append("\">");
|
||||
htmlString.append(accountId);
|
||||
htmlString.append("</a>");
|
||||
htmlString.append("</li>");
|
||||
});
|
||||
htmlString.append("</ul>");
|
||||
}
|
||||
htmlString.append("</body>");
|
||||
htmlString.append("</html>");
|
||||
|
||||
resp.setStatus(HttpStatus.OK_200);
|
||||
resp.getWriter().write(htmlString.toString());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2020 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.groheondus.internal;
|
||||
|
||||
/**
|
||||
* @author Florian Schmidt and Arne Wohlert - Initial contribution
|
||||
*/
|
||||
public class GroheOndusAccountConfiguration {
|
||||
|
||||
public String username;
|
||||
public String password;
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2020 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.groheondus.internal;
|
||||
|
||||
/**
|
||||
* @author Florian Schmidt and Arne Wohlert - Initial contribution
|
||||
*/
|
||||
public class GroheOndusApplianceConfiguration {
|
||||
|
||||
public String applianceId;
|
||||
public int roomId;
|
||||
public int locationId;
|
||||
public int pollingInterval;
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2020 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.groheondus.internal;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.openhab.core.thing.ThingTypeUID;
|
||||
|
||||
/**
|
||||
* @author Florian Schmidt and Arne Wohlert - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class GroheOndusBindingConstants {
|
||||
|
||||
private static final String BINDING_ID = "groheondus";
|
||||
|
||||
public static final ThingTypeUID THING_TYPE_BRIDGE_ACCOUNT = new ThingTypeUID(BINDING_ID, "account");
|
||||
public static final ThingTypeUID THING_TYPE_SENSEGUARD = new ThingTypeUID(BINDING_ID, "senseguard");
|
||||
public static final ThingTypeUID THING_TYPE_SENSE = new ThingTypeUID(BINDING_ID, "sense");
|
||||
|
||||
public static final String CHANNEL_NAME = "name";
|
||||
public static final String CHANNEL_PRESSURE = "pressure";
|
||||
public static final String CHANNEL_TEMPERATURE_GUARD = "temperature_guard";
|
||||
public static final String CHANNEL_VALVE_OPEN = "valve_open";
|
||||
public static final String CHANNEL_WATERCONSUMPTION = "waterconsumption";
|
||||
public static final String CHANNEL_TEMPERATURE = "temperature";
|
||||
public static final String CHANNEL_HUMIDITY = "humidity";
|
||||
public static final String CHANNEL_BATTERY = "battery";
|
||||
|
||||
public static final String CHANNEL_CONFIG_TIMEFRAME = "timeframe";
|
||||
}
|
||||
@@ -0,0 +1,103 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2020 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.groheondus.internal.discovery;
|
||||
|
||||
import static org.openhab.binding.groheondus.internal.GroheOndusBindingConstants.*;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.grohe.ondus.api.OndusService;
|
||||
import org.grohe.ondus.api.model.BaseAppliance;
|
||||
import org.openhab.binding.groheondus.internal.handler.GroheOndusAccountHandler;
|
||||
import org.openhab.core.config.discovery.AbstractDiscoveryService;
|
||||
import org.openhab.core.config.discovery.DiscoveryResult;
|
||||
import org.openhab.core.config.discovery.DiscoveryResultBuilder;
|
||||
import org.openhab.core.thing.ThingUID;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* @author Florian Schmidt - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class GroheOndusDiscoveryService extends AbstractDiscoveryService {
|
||||
private static final String PROPERTY_APPLIANCE_ID = "applianceId";
|
||||
|
||||
private static final String PROPERTY_ROOM_ID = "roomId";
|
||||
|
||||
private static final String PROPERTY_LOCATION_ID = "locationId";
|
||||
|
||||
private final Logger logger = LoggerFactory.getLogger(GroheOndusDiscoveryService.class);
|
||||
|
||||
private final GroheOndusAccountHandler bridgeHandler;
|
||||
|
||||
public GroheOndusDiscoveryService(GroheOndusAccountHandler bridgeHandler) {
|
||||
super(Collections
|
||||
.unmodifiableSet(Stream.of(THING_TYPE_SENSE, THING_TYPE_SENSEGUARD).collect(Collectors.toSet())), 30);
|
||||
logger.debug("initialize discovery service");
|
||||
this.bridgeHandler = bridgeHandler;
|
||||
this.activate(null);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void startScan() {
|
||||
OndusService service;
|
||||
try {
|
||||
service = bridgeHandler.getService();
|
||||
} catch (IllegalStateException e) {
|
||||
logger.debug("No instance of OndusService given.", e);
|
||||
return;
|
||||
}
|
||||
List<BaseAppliance> discoveredAppliances = new ArrayList<>();
|
||||
try {
|
||||
discoveredAppliances = service.appliances();
|
||||
} catch (IOException e) {
|
||||
logger.debug("Could not discover appliances.", e);
|
||||
return;
|
||||
}
|
||||
|
||||
discoveredAppliances.forEach(appliance -> {
|
||||
ThingUID bridgeUID = bridgeHandler.getThing().getUID();
|
||||
ThingUID thingUID = null;
|
||||
switch (appliance.getType()) {
|
||||
case org.grohe.ondus.api.model.guard.Appliance.TYPE:
|
||||
thingUID = new ThingUID(THING_TYPE_SENSEGUARD, bridgeUID, appliance.getApplianceId());
|
||||
break;
|
||||
case org.grohe.ondus.api.model.sense.Appliance.TYPE:
|
||||
thingUID = new ThingUID(THING_TYPE_SENSE, bridgeUID, appliance.getApplianceId());
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
|
||||
Map<String, Object> properties = new HashMap<>();
|
||||
properties.put(PROPERTY_LOCATION_ID, appliance.getRoom().getLocation().getId());
|
||||
properties.put(PROPERTY_ROOM_ID, appliance.getRoom().getId());
|
||||
properties.put(PROPERTY_APPLIANCE_ID, appliance.getApplianceId());
|
||||
|
||||
DiscoveryResult discoveryResult = DiscoveryResultBuilder.create(thingUID).withProperties(properties)
|
||||
.withBridge(bridgeUID).withLabel(appliance.getName())
|
||||
.withRepresentationProperty(PROPERTY_APPLIANCE_ID).build();
|
||||
|
||||
thingDiscovered(discoveryResult);
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,165 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2020 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.groheondus.internal.handler;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.time.Duration;
|
||||
import java.time.Instant;
|
||||
import java.util.concurrent.ScheduledFuture;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import javax.security.auth.login.LoginException;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
import org.grohe.ondus.api.OndusService;
|
||||
import org.openhab.binding.groheondus.internal.AccountServlet;
|
||||
import org.openhab.binding.groheondus.internal.GroheOndusAccountConfiguration;
|
||||
import org.openhab.core.storage.Storage;
|
||||
import org.openhab.core.thing.Bridge;
|
||||
import org.openhab.core.thing.ChannelUID;
|
||||
import org.openhab.core.thing.ThingStatus;
|
||||
import org.openhab.core.thing.ThingStatusDetail;
|
||||
import org.openhab.core.thing.binding.BaseBridgeHandler;
|
||||
import org.openhab.core.types.Command;
|
||||
import org.osgi.service.http.HttpService;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* @author Florian Schmidt and Arne Wohlert - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class GroheOndusAccountHandler extends BaseBridgeHandler {
|
||||
|
||||
private static final String STORAGE_KEY_REFRESH_TOKEN = "refreshToken";
|
||||
|
||||
private final Logger logger = LoggerFactory.getLogger(GroheOndusAccountHandler.class);
|
||||
|
||||
private HttpService httpService;
|
||||
private Storage<String> storage;
|
||||
private @Nullable AccountServlet accountServlet;
|
||||
private @Nullable OndusService ondusService;
|
||||
private @Nullable ScheduledFuture<?> refreshTokenFuture;
|
||||
|
||||
public GroheOndusAccountHandler(Bridge bridge, HttpService httpService, Storage<String> storage) {
|
||||
super(bridge);
|
||||
this.httpService = httpService;
|
||||
this.storage = storage;
|
||||
}
|
||||
|
||||
public OndusService getService() {
|
||||
OndusService ret = this.ondusService;
|
||||
if (ret == null) {
|
||||
throw new IllegalStateException("OndusService requested, which is null (UNINITIALIZED)");
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
public void deleteRefreshToken() {
|
||||
this.storage.remove(STORAGE_KEY_REFRESH_TOKEN);
|
||||
this.initialize();
|
||||
|
||||
if (refreshTokenFuture != null) {
|
||||
refreshTokenFuture.cancel(true);
|
||||
}
|
||||
}
|
||||
|
||||
public void setRefreshToken(String refreshToken) {
|
||||
this.storage.put(STORAGE_KEY_REFRESH_TOKEN, refreshToken);
|
||||
this.initialize();
|
||||
}
|
||||
|
||||
private void scheduleTokenRefresh() {
|
||||
if (ondusService != null) {
|
||||
Instant expiresAt = ondusService.authorizationExpiresAt();
|
||||
Duration between = Duration.between(Instant.now(), expiresAt);
|
||||
refreshTokenFuture = scheduler.schedule(() -> {
|
||||
OndusService ondusService = this.ondusService;
|
||||
if (ondusService == null) {
|
||||
logger.warn("Trying to refresh Ondus account without a service being present.");
|
||||
return;
|
||||
}
|
||||
try {
|
||||
setRefreshToken(ondusService.refreshAuthorization());
|
||||
} catch (Exception e) {
|
||||
logger.warn("Could not refresh authorization for GROHE ONDUS account", e);
|
||||
}
|
||||
}, between.getSeconds(), TimeUnit.SECONDS);
|
||||
}
|
||||
}
|
||||
|
||||
public boolean hasRefreshToken() {
|
||||
return this.storage.containsKey(STORAGE_KEY_REFRESH_TOKEN);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleCommand(ChannelUID channelUID, Command command) {
|
||||
// Nothing to do for bridge
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dispose() {
|
||||
super.dispose();
|
||||
if (ondusService != null) {
|
||||
ondusService = null;
|
||||
}
|
||||
if (accountServlet != null) {
|
||||
accountServlet.dispose();
|
||||
}
|
||||
if (refreshTokenFuture != null) {
|
||||
refreshTokenFuture.cancel(true);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initialize() {
|
||||
GroheOndusAccountConfiguration config = getConfigAs(GroheOndusAccountConfiguration.class);
|
||||
|
||||
if (this.accountServlet == null) {
|
||||
this.accountServlet = new AccountServlet(httpService, this.getThing().getUID().getId(), this);
|
||||
}
|
||||
|
||||
if ((config.username == null || config.password == null) && !this.hasRefreshToken()) {
|
||||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_PENDING,
|
||||
"Need username/password or refreshToken");
|
||||
return;
|
||||
}
|
||||
|
||||
updateStatus(ThingStatus.UNKNOWN);
|
||||
try {
|
||||
if (storage.containsKey(STORAGE_KEY_REFRESH_TOKEN)) {
|
||||
ondusService = OndusService.login(storage.get(STORAGE_KEY_REFRESH_TOKEN));
|
||||
scheduleTokenRefresh();
|
||||
} else {
|
||||
// TODO: That's probably really inefficient, internally the loginWebform method acquires a refresh
|
||||
// token, maybe there should be a way to obtain this token here, somehow.
|
||||
ondusService = OndusService.loginWebform(config.username, config.password);
|
||||
}
|
||||
updateStatus(ThingStatus.ONLINE);
|
||||
|
||||
scheduler.submit(() -> getThing().getThings().forEach(thing -> {
|
||||
GroheOndusBaseHandler thingHandler = (GroheOndusBaseHandler) thing.getHandler();
|
||||
if (thingHandler != null) {
|
||||
thingHandler.updateChannels();
|
||||
}
|
||||
}));
|
||||
} catch (LoginException e) {
|
||||
logger.debug("Grohe api login failed", e);
|
||||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, "Login failed");
|
||||
} catch (IOException e) {
|
||||
logger.debug("Communication error while logging into the grohe api", e);
|
||||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,163 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2020 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.groheondus.internal.handler;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
import org.grohe.ondus.api.OndusService;
|
||||
import org.grohe.ondus.api.model.BaseAppliance;
|
||||
import org.grohe.ondus.api.model.Location;
|
||||
import org.grohe.ondus.api.model.Room;
|
||||
import org.openhab.binding.groheondus.internal.GroheOndusApplianceConfiguration;
|
||||
import org.openhab.core.thing.Bridge;
|
||||
import org.openhab.core.thing.ChannelUID;
|
||||
import org.openhab.core.thing.Thing;
|
||||
import org.openhab.core.thing.ThingStatus;
|
||||
import org.openhab.core.thing.ThingStatusDetail;
|
||||
import org.openhab.core.thing.binding.BaseThingHandler;
|
||||
import org.openhab.core.thing.binding.BridgeHandler;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* @author Florian Schmidt - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public abstract class GroheOndusBaseHandler<T extends BaseAppliance, M> extends BaseThingHandler {
|
||||
private final Logger logger = LoggerFactory.getLogger(GroheOndusBaseHandler.class);
|
||||
|
||||
protected @Nullable GroheOndusApplianceConfiguration config;
|
||||
|
||||
private final int applianceType;
|
||||
|
||||
public GroheOndusBaseHandler(Thing thing, int applianceType) {
|
||||
super(thing);
|
||||
this.applianceType = applianceType;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initialize() {
|
||||
config = getConfigAs(GroheOndusApplianceConfiguration.class);
|
||||
|
||||
OndusService ondusService = getOndusService();
|
||||
if (ondusService == null) {
|
||||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.BRIDGE_OFFLINE,
|
||||
"No initialized OndusService available from bridge.");
|
||||
return;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
T appliance = getAppliance(ondusService);
|
||||
if (appliance == null) {
|
||||
updateStatus(ThingStatus.UNKNOWN, ThingStatusDetail.COMMUNICATION_ERROR, "Could not load appliance");
|
||||
return;
|
||||
}
|
||||
int pollingInterval = getPollingInterval(appliance);
|
||||
scheduler.scheduleWithFixedDelay(this::updateChannels, 0, pollingInterval, TimeUnit.SECONDS);
|
||||
|
||||
updateStatus(ThingStatus.UNKNOWN);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void channelLinked(ChannelUID channelUID) {
|
||||
super.channelLinked(channelUID);
|
||||
|
||||
OndusService ondusService = getOndusService();
|
||||
if (ondusService == null) {
|
||||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.BRIDGE_OFFLINE,
|
||||
"No initialized OndusService available from bridge.");
|
||||
return;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
T appliance = getAppliance(ondusService);
|
||||
if (appliance == null) {
|
||||
return;
|
||||
}
|
||||
updateChannel(channelUID, appliance, getLastDataPoint(appliance));
|
||||
}
|
||||
|
||||
public void updateChannels() {
|
||||
OndusService ondusService = getOndusService();
|
||||
if (ondusService == null) {
|
||||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.BRIDGE_OFFLINE,
|
||||
"No initialized OndusService available from bridge.");
|
||||
return;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
T appliance = getAppliance(ondusService);
|
||||
if (appliance == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
M measurement = getLastDataPoint(appliance);
|
||||
getThing().getChannels().forEach(channel -> updateChannel(channel.getUID(), appliance, measurement));
|
||||
|
||||
updateStatus(ThingStatus.ONLINE);
|
||||
}
|
||||
|
||||
protected abstract M getLastDataPoint(T appliance);
|
||||
|
||||
protected abstract void updateChannel(ChannelUID channelUID, T appliance, M measurement);
|
||||
|
||||
public @Nullable OndusService getOndusService() {
|
||||
Bridge bridge = getBridge();
|
||||
if (bridge == null) {
|
||||
return null;
|
||||
}
|
||||
BridgeHandler handler = bridge.getHandler();
|
||||
if (!(handler instanceof GroheOndusAccountHandler)) {
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
return ((GroheOndusAccountHandler) handler).getService();
|
||||
} catch (IllegalStateException e) {
|
||||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.BRIDGE_OFFLINE);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
protected Room getRoom() {
|
||||
return new Room(config.roomId, getLocation());
|
||||
}
|
||||
|
||||
protected Location getLocation() {
|
||||
return new Location(config.locationId);
|
||||
}
|
||||
|
||||
protected @Nullable T getAppliance(OndusService ondusService) {
|
||||
try {
|
||||
BaseAppliance appliance = ondusService.getAppliance(getRoom(), config.applianceId).orElse(null);
|
||||
if (appliance.getType() != getType()) {
|
||||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR,
|
||||
"Thing is not a GROHE SENSE Guard device.");
|
||||
return null;
|
||||
}
|
||||
return (T) appliance;
|
||||
} catch (IOException e) {
|
||||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, e.getMessage());
|
||||
logger.debug("Could not load appliance", e);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
protected abstract int getPollingInterval(T appliance);
|
||||
|
||||
private int getType() {
|
||||
return this.applianceType;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,114 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2020 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.groheondus.internal.handler;
|
||||
|
||||
import static org.openhab.binding.groheondus.internal.GroheOndusBindingConstants.*;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.Hashtable;
|
||||
import java.util.Map;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
import org.openhab.binding.groheondus.internal.AccountsServlet;
|
||||
import org.openhab.binding.groheondus.internal.discovery.GroheOndusDiscoveryService;
|
||||
import org.openhab.core.config.discovery.DiscoveryService;
|
||||
import org.openhab.core.storage.StorageService;
|
||||
import org.openhab.core.thing.Bridge;
|
||||
import org.openhab.core.thing.Thing;
|
||||
import org.openhab.core.thing.ThingTypeUID;
|
||||
import org.openhab.core.thing.ThingUID;
|
||||
import org.openhab.core.thing.binding.BaseThingHandlerFactory;
|
||||
import org.openhab.core.thing.binding.ThingHandler;
|
||||
import org.openhab.core.thing.binding.ThingHandlerFactory;
|
||||
import org.osgi.framework.FrameworkUtil;
|
||||
import org.osgi.framework.ServiceRegistration;
|
||||
import org.osgi.framework.wiring.BundleWiring;
|
||||
import org.osgi.service.component.annotations.Activate;
|
||||
import org.osgi.service.component.annotations.Component;
|
||||
import org.osgi.service.component.annotations.Reference;
|
||||
import org.osgi.service.http.HttpService;
|
||||
|
||||
/**
|
||||
* @author Florian Schmidt and Arne Wohlert - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
@Component(configurationPid = "binding.groheondus", service = ThingHandlerFactory.class)
|
||||
public class GroheOndusHandlerFactory extends BaseThingHandlerFactory {
|
||||
|
||||
private final Map<ThingUID, @Nullable ServiceRegistration<?>> discoveryServiceRegs = new HashMap<>();
|
||||
|
||||
private HttpService httpService;
|
||||
private StorageService storageService;
|
||||
private AccountsServlet accountsServlet;
|
||||
|
||||
@Activate
|
||||
public GroheOndusHandlerFactory(@Reference HttpService httpService, @Reference StorageService storageService,
|
||||
@Reference AccountsServlet accountsServlet) {
|
||||
this.httpService = httpService;
|
||||
this.storageService = storageService;
|
||||
this.accountsServlet = accountsServlet;
|
||||
}
|
||||
|
||||
private static final Collection<ThingTypeUID> SUPPORTED_THING_TYPES_UIDS = Arrays.asList(THING_TYPE_SENSEGUARD,
|
||||
THING_TYPE_SENSE, THING_TYPE_BRIDGE_ACCOUNT);
|
||||
|
||||
@Override
|
||||
public boolean supportsThingType(ThingTypeUID thingTypeUID) {
|
||||
return SUPPORTED_THING_TYPES_UIDS.contains(thingTypeUID);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected @Nullable ThingHandler createHandler(Thing thing) {
|
||||
ThingTypeUID thingTypeUID = thing.getThingTypeUID();
|
||||
|
||||
if (THING_TYPE_BRIDGE_ACCOUNT.equals(thingTypeUID)) {
|
||||
GroheOndusAccountHandler handler = new GroheOndusAccountHandler((Bridge) thing, httpService,
|
||||
storageService.getStorage(thing.getUID().toString(),
|
||||
FrameworkUtil.getBundle(getClass()).adapt(BundleWiring.class).getClassLoader()));
|
||||
onAccountCreated(thing, handler);
|
||||
return handler;
|
||||
} else if (THING_TYPE_SENSEGUARD.equals(thingTypeUID)) {
|
||||
return new GroheOndusSenseGuardHandler(thing);
|
||||
} else if (THING_TYPE_SENSE.equals(thingTypeUID)) {
|
||||
return new GroheOndusSenseHandler(thing);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private void onAccountCreated(Thing thing, GroheOndusAccountHandler handler) {
|
||||
registerDeviceDiscoveryService(handler);
|
||||
if (this.accountsServlet != null) {
|
||||
this.accountsServlet.addAccount(thing);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected synchronized void removeHandler(ThingHandler thingHandler) {
|
||||
if (thingHandler instanceof GroheOndusAccountHandler) {
|
||||
ServiceRegistration<?> serviceReg = discoveryServiceRegs.remove(thingHandler.getThing().getUID());
|
||||
if (serviceReg != null) {
|
||||
serviceReg.unregister();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private synchronized void registerDeviceDiscoveryService(GroheOndusAccountHandler handler) {
|
||||
GroheOndusDiscoveryService discoveryService = new GroheOndusDiscoveryService(handler);
|
||||
discoveryServiceRegs.put(handler.getThing().getUID(),
|
||||
bundleContext.registerService(DiscoveryService.class.getName(), discoveryService, new Hashtable<>()));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,225 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2020 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.groheondus.internal.handler;
|
||||
|
||||
import static org.openhab.binding.groheondus.internal.GroheOndusBindingConstants.*;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.math.BigDecimal;
|
||||
import java.time.Instant;
|
||||
import java.time.temporal.ChronoUnit;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
import org.grohe.ondus.api.OndusService;
|
||||
import org.grohe.ondus.api.model.BaseApplianceCommand;
|
||||
import org.grohe.ondus.api.model.BaseApplianceData;
|
||||
import org.grohe.ondus.api.model.guard.Appliance;
|
||||
import org.grohe.ondus.api.model.guard.ApplianceCommand;
|
||||
import org.grohe.ondus.api.model.guard.ApplianceData;
|
||||
import org.grohe.ondus.api.model.guard.ApplianceData.Data;
|
||||
import org.grohe.ondus.api.model.guard.ApplianceData.Measurement;
|
||||
import org.openhab.core.library.types.DecimalType;
|
||||
import org.openhab.core.library.types.OnOffType;
|
||||
import org.openhab.core.library.types.QuantityType;
|
||||
import org.openhab.core.library.types.StringType;
|
||||
import org.openhab.core.library.unit.SIUnits;
|
||||
import org.openhab.core.library.unit.SmartHomeUnits;
|
||||
import org.openhab.core.thing.Channel;
|
||||
import org.openhab.core.thing.ChannelUID;
|
||||
import org.openhab.core.thing.Thing;
|
||||
import org.openhab.core.thing.ThingStatus;
|
||||
import org.openhab.core.thing.ThingStatusDetail;
|
||||
import org.openhab.core.types.Command;
|
||||
import org.openhab.core.types.State;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* @author Florian Schmidt and Arne Wohlert - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class GroheOndusSenseGuardHandler<T, M> extends GroheOndusBaseHandler<Appliance, Data> {
|
||||
private static final int MIN_API_TIMEFRAME_DAYS = 1;
|
||||
private static final int MAX_API_TIMEFRAME_DAYS = 90;
|
||||
private static final int DEFAULT_TIMEFRAME_DAYS = 1;
|
||||
|
||||
private final Logger logger = LoggerFactory.getLogger(GroheOndusSenseGuardHandler.class);
|
||||
|
||||
public GroheOndusSenseGuardHandler(Thing thing) {
|
||||
super(thing, Appliance.TYPE);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int getPollingInterval(Appliance appliance) {
|
||||
if (config.pollingInterval > 0) {
|
||||
return config.pollingInterval;
|
||||
}
|
||||
return appliance.getConfig().getMeasurementTransmissionIntervall();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void updateChannel(ChannelUID channelUID, Appliance appliance, Data dataPoint) {
|
||||
String channelId = channelUID.getIdWithoutGroup();
|
||||
State newState;
|
||||
switch (channelId) {
|
||||
case CHANNEL_NAME:
|
||||
newState = new StringType(appliance.getName());
|
||||
break;
|
||||
case CHANNEL_PRESSURE:
|
||||
newState = new QuantityType<>(getLastMeasurement(dataPoint).getPressure(), SmartHomeUnits.BAR);
|
||||
break;
|
||||
case CHANNEL_TEMPERATURE_GUARD:
|
||||
newState = new QuantityType<>(getLastMeasurement(dataPoint).getTemperatureGuard(), SIUnits.CELSIUS);
|
||||
break;
|
||||
case CHANNEL_VALVE_OPEN:
|
||||
newState = getValveOpenType(appliance);
|
||||
break;
|
||||
case CHANNEL_WATERCONSUMPTION:
|
||||
newState = sumWaterCosumption(dataPoint);
|
||||
break;
|
||||
default:
|
||||
throw new IllegalArgumentException("Channel " + channelUID + " not supported.");
|
||||
}
|
||||
if (newState != null) {
|
||||
updateState(channelUID, newState);
|
||||
}
|
||||
}
|
||||
|
||||
private DecimalType sumWaterCosumption(Data dataPoint) {
|
||||
Double waterConsumption = dataPoint.getWithdrawals().stream()
|
||||
.mapToDouble(withdrawal -> withdrawal.getWaterconsumption()).sum();
|
||||
return new DecimalType(waterConsumption);
|
||||
}
|
||||
|
||||
private Measurement getLastMeasurement(Data dataPoint) {
|
||||
List<Measurement> measurementList = dataPoint.getMeasurement();
|
||||
return measurementList.isEmpty() ? new Measurement() : measurementList.get(measurementList.size() - 1);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private OnOffType getValveOpenType(Appliance appliance) {
|
||||
OndusService service = getOndusService();
|
||||
if (service == null) {
|
||||
return null;
|
||||
}
|
||||
Optional<BaseApplianceCommand> commandOptional;
|
||||
try {
|
||||
commandOptional = service.applianceCommand(appliance);
|
||||
} catch (IOException e) {
|
||||
logger.debug("Could not get appliance command", e);
|
||||
return null;
|
||||
}
|
||||
if (!commandOptional.isPresent()) {
|
||||
return null;
|
||||
}
|
||||
if (commandOptional.get().getType() != Appliance.TYPE) {
|
||||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR,
|
||||
"Thing is not a GROHE SENSE Guard device.");
|
||||
return null;
|
||||
}
|
||||
return ((ApplianceCommand) commandOptional.get()).getCommand().getValveOpen() ? OnOffType.ON : OnOffType.OFF;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Data getLastDataPoint(Appliance appliance) {
|
||||
if (getOndusService() == null) {
|
||||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.BRIDGE_OFFLINE,
|
||||
"No initialized OndusService available from bridge.");
|
||||
return new Data();
|
||||
}
|
||||
|
||||
ApplianceData applianceData = getApplianceData(appliance);
|
||||
if (applianceData == null) {
|
||||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, "Could not load data from API.");
|
||||
return new Data();
|
||||
}
|
||||
return applianceData.getData();
|
||||
}
|
||||
|
||||
private @Nullable ApplianceData getApplianceData(Appliance appliance) {
|
||||
Instant from = fromTime();
|
||||
Instant to = Instant.now();
|
||||
|
||||
OndusService service = getOndusService();
|
||||
if (service == null) {
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
BaseApplianceData applianceData = service.applianceData(appliance, from, to).orElse(null);
|
||||
if (applianceData.getType() != Appliance.TYPE) {
|
||||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR,
|
||||
"Thing is not a GROHE SENSE Guard device.");
|
||||
return null;
|
||||
}
|
||||
return (ApplianceData) applianceData;
|
||||
} catch (IOException e) {
|
||||
logger.debug("Could not load appliance data", e);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private Instant fromTime() {
|
||||
Instant from = Instant.now().minus(DEFAULT_TIMEFRAME_DAYS, ChronoUnit.DAYS);
|
||||
Channel waterconsumptionChannel = this.thing.getChannel(CHANNEL_WATERCONSUMPTION);
|
||||
if (waterconsumptionChannel == null) {
|
||||
return from;
|
||||
}
|
||||
|
||||
Object timeframeConfig = waterconsumptionChannel.getConfiguration().get(CHANNEL_CONFIG_TIMEFRAME);
|
||||
if (!(timeframeConfig instanceof BigDecimal)) {
|
||||
return from;
|
||||
}
|
||||
|
||||
int timeframe = ((BigDecimal) timeframeConfig).intValue();
|
||||
if (timeframe < MIN_API_TIMEFRAME_DAYS && timeframe > MAX_API_TIMEFRAME_DAYS) {
|
||||
logger.info(
|
||||
"timeframe configuration of waterconsumption channel needs to be a number between 1 to 90, got {}",
|
||||
timeframe);
|
||||
return from;
|
||||
}
|
||||
|
||||
return Instant.now().minus(timeframe, ChronoUnit.DAYS);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleCommand(ChannelUID channelUID, Command command) {
|
||||
if (!CHANNEL_VALVE_OPEN.equals(channelUID.getIdWithoutGroup())) {
|
||||
return;
|
||||
}
|
||||
if (!(command instanceof OnOffType)) {
|
||||
logger.debug("Invalid command received for channel. Expected OnOffType, received {}.",
|
||||
command.getClass().getName());
|
||||
return;
|
||||
}
|
||||
OnOffType openClosedCommand = (OnOffType) command;
|
||||
boolean openState = openClosedCommand == OnOffType.ON;
|
||||
|
||||
OndusService service = getOndusService();
|
||||
if (service == null) {
|
||||
return;
|
||||
}
|
||||
Appliance appliance = getAppliance(service);
|
||||
if (appliance == null) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
service.setValveOpen(appliance, openState);
|
||||
updateChannels();
|
||||
} catch (IOException e) {
|
||||
logger.debug("Could not update valve open state", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,158 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2020 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.groheondus.internal.handler;
|
||||
|
||||
import static org.openhab.binding.groheondus.internal.GroheOndusBindingConstants.*;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.time.Instant;
|
||||
import java.time.temporal.ChronoUnit;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
import org.grohe.ondus.api.OndusService;
|
||||
import org.grohe.ondus.api.model.ApplianceStatus;
|
||||
import org.grohe.ondus.api.model.BaseApplianceData;
|
||||
import org.grohe.ondus.api.model.sense.Appliance;
|
||||
import org.grohe.ondus.api.model.sense.ApplianceData;
|
||||
import org.grohe.ondus.api.model.sense.ApplianceData.Measurement;
|
||||
import org.openhab.core.library.types.DecimalType;
|
||||
import org.openhab.core.library.types.QuantityType;
|
||||
import org.openhab.core.library.types.StringType;
|
||||
import org.openhab.core.library.unit.SIUnits;
|
||||
import org.openhab.core.library.unit.SmartHomeUnits;
|
||||
import org.openhab.core.thing.ChannelUID;
|
||||
import org.openhab.core.thing.Thing;
|
||||
import org.openhab.core.thing.ThingStatus;
|
||||
import org.openhab.core.thing.ThingStatusDetail;
|
||||
import org.openhab.core.types.Command;
|
||||
import org.openhab.core.types.State;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* @author Florian Schmidt - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class GroheOndusSenseHandler<T, M> extends GroheOndusBaseHandler<Appliance, Measurement> {
|
||||
|
||||
private static final int DEFAULT_POLLING_INTERVAL = 900;
|
||||
|
||||
private final Logger logger = LoggerFactory.getLogger(GroheOndusSenseHandler.class);
|
||||
|
||||
public GroheOndusSenseHandler(Thing thing) {
|
||||
super(thing, Appliance.TYPE);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int getPollingInterval(Appliance appliance) {
|
||||
if (config.pollingInterval > 0) {
|
||||
return config.pollingInterval;
|
||||
}
|
||||
return DEFAULT_POLLING_INTERVAL;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void updateChannel(ChannelUID channelUID, Appliance appliance, Measurement measurement) {
|
||||
String channelId = channelUID.getIdWithoutGroup();
|
||||
State newState;
|
||||
switch (channelId) {
|
||||
case CHANNEL_NAME:
|
||||
newState = new StringType(appliance.getName());
|
||||
break;
|
||||
case CHANNEL_TEMPERATURE:
|
||||
newState = new QuantityType<>(measurement.getTemperature(), SIUnits.CELSIUS);
|
||||
break;
|
||||
case CHANNEL_HUMIDITY:
|
||||
newState = new QuantityType<>(measurement.getHumidity(), SmartHomeUnits.PERCENT);
|
||||
break;
|
||||
case CHANNEL_BATTERY:
|
||||
newState = new DecimalType(getBatteryStatus(appliance));
|
||||
break;
|
||||
default:
|
||||
throw new IllegalArgumentException("Channel " + channelUID + " not supported.");
|
||||
}
|
||||
if (newState != null) {
|
||||
updateState(channelUID, newState);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Measurement getLastDataPoint(Appliance appliance) {
|
||||
if (getOndusService() == null) {
|
||||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.BRIDGE_OFFLINE,
|
||||
"No initialized OndusService available from bridge.");
|
||||
return new Measurement();
|
||||
}
|
||||
|
||||
ApplianceData applianceData = getApplianceData(appliance);
|
||||
if (applianceData == null) {
|
||||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, "Could not load data from API.");
|
||||
return new Measurement();
|
||||
}
|
||||
List<Measurement> measurementList = applianceData.getData().getMeasurement();
|
||||
|
||||
return measurementList.isEmpty() ? new Measurement() : measurementList.get(measurementList.size() - 1);
|
||||
}
|
||||
|
||||
private int getBatteryStatus(Appliance appliance) {
|
||||
OndusService ondusService = getOndusService();
|
||||
if (ondusService == null) {
|
||||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.BRIDGE_OFFLINE,
|
||||
"No initialized OndusService available from bridge.");
|
||||
return -1;
|
||||
}
|
||||
|
||||
Optional<ApplianceStatus> applianceStatusOptional;
|
||||
try {
|
||||
applianceStatusOptional = ondusService.applianceStatus(appliance);
|
||||
if (!applianceStatusOptional.isPresent()) {
|
||||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR,
|
||||
"Could not load data from API.");
|
||||
return -1;
|
||||
}
|
||||
|
||||
return applianceStatusOptional.get().getBatteryStatus();
|
||||
} catch (IOException e) {
|
||||
logger.debug("Could not load appliance status", e);
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
private @Nullable ApplianceData getApplianceData(Appliance appliance) {
|
||||
Instant yesterday = Instant.now().minus(1, ChronoUnit.DAYS);
|
||||
Instant today = Instant.now();
|
||||
OndusService service = getOndusService();
|
||||
if (service == null) {
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
BaseApplianceData applianceData = service.applianceData(appliance, yesterday, today).orElse(null);
|
||||
if (applianceData.getType() != Appliance.TYPE) {
|
||||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR,
|
||||
"Thing is not a GROHE SENSE device.");
|
||||
return null;
|
||||
}
|
||||
return (ApplianceData) applianceData;
|
||||
} catch (IOException e) {
|
||||
logger.debug("Could not load appliance data", e);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleCommand(ChannelUID channelUID, Command command) {
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<binding:binding id="groheondus" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xmlns:binding="https://openhab.org/schemas/binding/v1.0.0"
|
||||
xsi:schemaLocation="https://openhab.org/schemas/binding/v1.0.0 https://openhab.org/schemas/binding-1.0.0.xsd">
|
||||
|
||||
<name>GROHE ONDUS Binding</name>
|
||||
<description>Provides an integration for GROHE Appliances in openHAB</description>
|
||||
<author>Florian Schmidt and Arne Wohlert</author>
|
||||
|
||||
</binding:binding>
|
||||
@@ -0,0 +1,11 @@
|
||||
# binding
|
||||
binding.groheondus.name = GROHE ONDUS
|
||||
binding.groheondus.description = Stellt eine Integration mit Geräten von GROHE in openHAB zur Verfügung.
|
||||
|
||||
# thing types
|
||||
thing-type.groheondus.senseguard.label = GROHE SENSE Guard Gerät
|
||||
thing-type.groheondus.sense.label = GROHE SENSE Gerät
|
||||
thing-type.groheondus.senseguard.description = Ein GROHE SENSE Guard
|
||||
thing-type.groheondus.sense.description = Ein GROHE SENSE
|
||||
thing-type.groheondus.account.label = GROHE ONDUS Account
|
||||
thing-type.groheondus.account.description = Dies ist die Schnittstelle zum GROHE ONDUS Account wie sie von der App verwendet wird.
|
||||
@@ -0,0 +1,149 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<thing:thing-descriptions bindingId="groheondus"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xmlns:thing="https://openhab.org/schemas/thing-description/v1.0.0"
|
||||
xsi:schemaLocation="https://openhab.org/schemas/thing-description/v1.0.0 https://openhab.org/schemas/thing-description-1.0.0.xsd">
|
||||
|
||||
<bridge-type id="account">
|
||||
<label>GROHE ONDUS Account</label>
|
||||
<description>This is an interface to the GROHE ONDUS Account as it is used by the app. If username and password are
|
||||
not set, you can configure to use a `refreshToken` to login. Read the README to get more info.</description>
|
||||
|
||||
<config-description>
|
||||
<parameter name="username" type="text" required="false">
|
||||
<label>Username</label>
|
||||
<description>Username as used in the GROHE ONDUS App, usually your e-mail address.</description>
|
||||
<required>true</required>
|
||||
</parameter>
|
||||
<parameter name="password" type="text" required="false">
|
||||
<label>Password</label>
|
||||
<required>true</required>
|
||||
<context>password</context>
|
||||
<description>Password as used in the GROHE ONDUS App.</description>
|
||||
</parameter>
|
||||
</config-description>
|
||||
</bridge-type>
|
||||
|
||||
<thing-type id="senseguard">
|
||||
<supported-bridge-type-refs>
|
||||
<bridge-type-ref id="account"/>
|
||||
</supported-bridge-type-refs>
|
||||
|
||||
<label>GROHE SENSE GUARD Appliance</label>
|
||||
<description>A SENSE GUARD device</description>
|
||||
|
||||
<channels>
|
||||
<channel id="name" typeId="name"/>
|
||||
<channel id="pressure" typeId="pressure"/>
|
||||
<channel id="temperature_guard" typeId="temperature_guard"/>
|
||||
<channel id="waterconsumption" typeId="waterconsumption"/>
|
||||
<channel id="valve_open" typeId="valve_open"/>
|
||||
</channels>
|
||||
|
||||
<representation-property>applianceId</representation-property>
|
||||
|
||||
<config-description>
|
||||
<parameter name="applianceId" type="text" required="true">
|
||||
<label>Appliance ID</label>
|
||||
<description>The UUID of the appliance as retrieved from the GROHE ONDUS API.</description>
|
||||
</parameter>
|
||||
<parameter name="roomId" type="integer" required="true">
|
||||
<label>Room ID</label>
|
||||
<description>The ID of the room the appliance is in as retrieved from the GROHE ONDUS API.</description>
|
||||
</parameter>
|
||||
<parameter name="locationId" type="integer" required="true">
|
||||
<label>Location ID</label>
|
||||
<description>The ID of the location the room is in as retrieved from the GROHE ONDUS API.</description>
|
||||
</parameter>
|
||||
<parameter name="pollingInterval" type="integer" required="false">
|
||||
<label>Polling Interval</label>
|
||||
<description>The interval in seconds used to poll the API for new data. Defaults to the configuration of the
|
||||
appliance itself as retrieved from the API, usually 15 minutes.</description>
|
||||
</parameter>
|
||||
</config-description>
|
||||
</thing-type>
|
||||
|
||||
<thing-type id="sense">
|
||||
<supported-bridge-type-refs>
|
||||
<bridge-type-ref id="account"/>
|
||||
</supported-bridge-type-refs>
|
||||
|
||||
<label>GROHE SENSE Appliance</label>
|
||||
<description>A SENSE device</description>
|
||||
|
||||
<channels>
|
||||
<channel id="name" typeId="name"/>
|
||||
<channel id="humidity" typeId="humidity"/>
|
||||
<channel id="temperature" typeId="temperature"/>
|
||||
<channel id="battery" typeId="system.battery-level"/>
|
||||
</channels>
|
||||
|
||||
<representation-property>applianceId</representation-property>
|
||||
|
||||
<config-description>
|
||||
<parameter name="applianceId" type="text" required="true">
|
||||
<label>Appliance ID</label>
|
||||
<description>The UUID of the appliance as retrieved from the GROHE ONDUS API.</description>
|
||||
</parameter>
|
||||
<parameter name="roomId" type="integer" required="true">
|
||||
<label>Room ID</label>
|
||||
<description>The ID of the room the appliance is in as retrieved from the GROHE ONDUS API.</description>
|
||||
</parameter>
|
||||
<parameter name="locationId" type="integer" required="true">
|
||||
<label>Location ID</label>
|
||||
<description>The ID of the location the room is in as retrieved from the GROHE ONDUS API.</description>
|
||||
</parameter>
|
||||
<parameter name="pollingInterval" type="integer" required="false">
|
||||
<label>Polling Interval</label>
|
||||
<description>The interval in seconds used to poll the API for new data.</description>
|
||||
</parameter>
|
||||
</config-description>
|
||||
</thing-type>
|
||||
|
||||
<channel-type id="name">
|
||||
<item-type>String</item-type>
|
||||
<label>Appliance Name</label>
|
||||
<description>The name of the appliance</description>
|
||||
</channel-type>
|
||||
<channel-type id="pressure">
|
||||
<item-type>Number:Pressure</item-type>
|
||||
<label>Pressure</label>
|
||||
<description>The pressure of your water supply</description>
|
||||
</channel-type>
|
||||
<channel-type id="temperature_guard">
|
||||
<item-type>Number:Temperature</item-type>
|
||||
<label>Temperature</label>
|
||||
<description>The ambient temperature of the appliance</description>
|
||||
<state readOnly="true" pattern="%.1f %unit%"/>
|
||||
</channel-type>
|
||||
<channel-type id="valve_open">
|
||||
<item-type>Switch</item-type>
|
||||
<label>Valve Open</label>
|
||||
<description>Valve switch</description>
|
||||
</channel-type>
|
||||
<channel-type id="humidity">
|
||||
<item-type>Number:Dimensionless</item-type>
|
||||
<label>Humidity</label>
|
||||
<description>The humidity reported by the device</description>
|
||||
<state readOnly="true" pattern="%.1f %unit%"/>
|
||||
</channel-type>
|
||||
<channel-type id="temperature">
|
||||
<item-type>Number:Temperature</item-type>
|
||||
<label>Temperature</label>
|
||||
<description>The temperature reported by the device</description>
|
||||
<state readOnly="true" pattern="%.1f %unit%"/>
|
||||
</channel-type>
|
||||
<channel-type id="waterconsumption">
|
||||
<item-type>Number</item-type>
|
||||
<label>Water Consumption</label>
|
||||
<description>The amount of water consumed in the given time period.</description>
|
||||
<state readOnly="true"/>
|
||||
<config-description>
|
||||
<parameter name="timeframe" type="integer" min="1" max="90" step="1" required="true">
|
||||
<label>Timeframe</label>
|
||||
<description>The timeframe in days to get the water consumption of</description>
|
||||
<default>1</default>
|
||||
</parameter>
|
||||
</config-description>
|
||||
</channel-type>
|
||||
</thing:thing-descriptions>
|
||||
Reference in New Issue
Block a user