[smgw] Initial contribution (#16017)
Signed-off-by: Jan N. Klug <github@klug.nrw>
This commit is contained in:
parent
e4c4d03167
commit
3e0deab3ba
|
@ -316,6 +316,7 @@
|
||||||
/bundles/org.openhab.binding.smaenergymeter/ @monnimeter
|
/bundles/org.openhab.binding.smaenergymeter/ @monnimeter
|
||||||
/bundles/org.openhab.binding.smartmeter/ @msteigenberger
|
/bundles/org.openhab.binding.smartmeter/ @msteigenberger
|
||||||
/bundles/org.openhab.binding.smartthings/ @BobRak
|
/bundles/org.openhab.binding.smartthings/ @BobRak
|
||||||
|
/bundles/org.openhab.binding.smgw/ @J-N-K
|
||||||
/bundles/org.openhab.binding.smhi/ @pacive
|
/bundles/org.openhab.binding.smhi/ @pacive
|
||||||
/bundles/org.openhab.binding.smsmodem/ @dalgwen
|
/bundles/org.openhab.binding.smsmodem/ @dalgwen
|
||||||
/bundles/org.openhab.binding.sncf/ @clinique
|
/bundles/org.openhab.binding.sncf/ @clinique
|
||||||
|
|
|
@ -1566,6 +1566,11 @@
|
||||||
<artifactId>org.openhab.binding.smartthings</artifactId>
|
<artifactId>org.openhab.binding.smartthings</artifactId>
|
||||||
<version>${project.version}</version>
|
<version>${project.version}</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.openhab.addons.bundles</groupId>
|
||||||
|
<artifactId>org.openhab.binding.smgw</artifactId>
|
||||||
|
<version>${project.version}</version>
|
||||||
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.openhab.addons.bundles</groupId>
|
<groupId>org.openhab.addons.bundles</groupId>
|
||||||
<artifactId>org.openhab.binding.smhi</artifactId>
|
<artifactId>org.openhab.binding.smhi</artifactId>
|
||||||
|
|
|
@ -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
|
||||||
|
|
||||||
|
jsoup
|
||||||
|
* License: MIT License
|
||||||
|
* Project: https://jsoup.org/
|
||||||
|
* Source: https://github.com/jhy/jsoup
|
|
@ -0,0 +1,36 @@
|
||||||
|
# PPC SMGW Binding
|
||||||
|
|
||||||
|
The PPC SMGW binding adds support for PPC Smart Meter Gateways.
|
||||||
|
The gateway is commonly installed by the network operator to allow remote access to a smart meter.
|
||||||
|
It also provides a HAN (home area network) interface for local access.
|
||||||
|
|
||||||
|
To use the HAN interface you need to connect it to your local network with an ethernet cable.
|
||||||
|
|
||||||
|
## Supported Things
|
||||||
|
|
||||||
|
- `smgw`: A smart meter gateway device.
|
||||||
|
|
||||||
|
## Thing Configuration
|
||||||
|
|
||||||
|
### `smgw` Thing Configuration
|
||||||
|
|
||||||
|
| Name | Type | Description | Default | Required | Advanced |
|
||||||
|
|------------|------|--------------------------------------|----------------|----------|----------|
|
||||||
|
| `hostname` | text | Hostname or IP address of the device | `192.168.1.200 | no | no |
|
||||||
|
| `username` | text | Username to access the device | N/A | yes | no |
|
||||||
|
| `password` | text | Password to access the device | N/A | yes | no |
|
||||||
|
|
||||||
|
The default value for the hostname matches the default value according to PPC's documentation.
|
||||||
|
Check with your network operator's documentation if DHCP has been enabled or a different fixed address has been set.
|
||||||
|
|
||||||
|
Username and password are typically supplied by the network operator.
|
||||||
|
Login with certificate is not supported.
|
||||||
|
|
||||||
|
## Channels
|
||||||
|
|
||||||
|
| Channel | Type | Read/Write | Description |
|
||||||
|
|-------------|---------------|------------|------------------------------------------------|
|
||||||
|
| `meter` | Number:Energy | R | The meter reading of the smart meter. |
|
||||||
|
| `timestamp` | DateTime | R | The date and time for which the meter reading. |
|
||||||
|
|
||||||
|
Channels are refreshed every 900s.
|
|
@ -0,0 +1,30 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://maven.apache.org/POM/4.0.0"
|
||||||
|
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://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>4.1.0-SNAPSHOT</version>
|
||||||
|
</parent>
|
||||||
|
|
||||||
|
<artifactId>org.openhab.binding.smgw</artifactId>
|
||||||
|
|
||||||
|
<name>openHAB Add-ons :: Bundles :: PPC SMGW Binding</name>
|
||||||
|
|
||||||
|
<properties>
|
||||||
|
<jsoup.version>1.15.3</jsoup.version>
|
||||||
|
</properties>
|
||||||
|
|
||||||
|
<dependencies>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.jsoup</groupId>
|
||||||
|
<artifactId>jsoup</artifactId>
|
||||||
|
<version>${jsoup.version}</version>
|
||||||
|
<scope>provided</scope>
|
||||||
|
</dependency>
|
||||||
|
</dependencies>
|
||||||
|
|
||||||
|
</project>
|
|
@ -0,0 +1,10 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<features name="org.openhab.binding.smgw-${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-smgw" description="PPC SMGW Binding" version="${project.version}">
|
||||||
|
<feature>openhab-runtime-base</feature>
|
||||||
|
<bundle dependency="true">mvn:org.jsoup/jsoup/1.15.3</bundle>
|
||||||
|
<bundle start-level="80">mvn:org.openhab.addons.bundles/org.openhab.binding.smgw/${project.version}</bundle>
|
||||||
|
</feature>
|
||||||
|
</features>
|
|
@ -0,0 +1,32 @@
|
||||||
|
/**
|
||||||
|
* Copyright (c) 2010-2023 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.smgw.internal;
|
||||||
|
|
||||||
|
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||||
|
import org.openhab.core.thing.ThingTypeUID;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The {@link SmgwBindingConstants} class defines common constants, which are
|
||||||
|
* used across the whole binding.
|
||||||
|
*
|
||||||
|
* @author Jan N. Klug - Initial contribution
|
||||||
|
*/
|
||||||
|
@NonNullByDefault
|
||||||
|
public class SmgwBindingConstants {
|
||||||
|
private static final String BINDING_ID = "smgw";
|
||||||
|
|
||||||
|
public static final ThingTypeUID THING_TYPE_SMGW = new ThingTypeUID(BINDING_ID, "smgw");
|
||||||
|
|
||||||
|
public static final String CHANNEL_METER = "meter";
|
||||||
|
public static final String CHANNEL_TIMESTAMP = "timestamp";
|
||||||
|
}
|
|
@ -0,0 +1,27 @@
|
||||||
|
/**
|
||||||
|
* Copyright (c) 2010-2023 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.smgw.internal;
|
||||||
|
|
||||||
|
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The {@link SmgwConfiguration} class contains fields mapping thing configuration parameters.
|
||||||
|
*
|
||||||
|
* @author Jan N. Klug - Initial contribution
|
||||||
|
*/
|
||||||
|
@NonNullByDefault
|
||||||
|
public class SmgwConfiguration {
|
||||||
|
public String hostname = "192.168.1.200";
|
||||||
|
public String username = "";
|
||||||
|
public String password = "";
|
||||||
|
}
|
|
@ -0,0 +1,232 @@
|
||||||
|
/**
|
||||||
|
* Copyright (c) 2010-2023 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.smgw.internal;
|
||||||
|
|
||||||
|
import static org.openhab.binding.smgw.internal.SmgwBindingConstants.CHANNEL_METER;
|
||||||
|
import static org.openhab.binding.smgw.internal.SmgwBindingConstants.CHANNEL_TIMESTAMP;
|
||||||
|
|
||||||
|
import java.net.CookieStore;
|
||||||
|
import java.net.HttpCookie;
|
||||||
|
import java.net.URI;
|
||||||
|
import java.net.URISyntaxException;
|
||||||
|
import java.time.LocalDate;
|
||||||
|
import java.time.format.DateTimeFormatter;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.concurrent.CompletableFuture;
|
||||||
|
|
||||||
|
import javax.measure.quantity.Energy;
|
||||||
|
|
||||||
|
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||||
|
import org.eclipse.jdt.annotation.Nullable;
|
||||||
|
import org.eclipse.jetty.client.HttpClient;
|
||||||
|
import org.eclipse.jetty.client.api.Authentication;
|
||||||
|
import org.eclipse.jetty.client.api.AuthenticationStore;
|
||||||
|
import org.eclipse.jetty.client.api.Response;
|
||||||
|
import org.eclipse.jetty.client.api.Result;
|
||||||
|
import org.eclipse.jetty.client.util.BufferingResponseListener;
|
||||||
|
import org.eclipse.jetty.client.util.DigestAuthentication;
|
||||||
|
import org.eclipse.jetty.client.util.StringContentProvider;
|
||||||
|
import org.eclipse.jetty.http.HttpHeader;
|
||||||
|
import org.eclipse.jetty.http.HttpStatus;
|
||||||
|
import org.jsoup.Jsoup;
|
||||||
|
import org.jsoup.nodes.Document;
|
||||||
|
import org.jsoup.nodes.Element;
|
||||||
|
import org.openhab.core.library.types.DateTimeType;
|
||||||
|
import org.openhab.core.library.types.QuantityType;
|
||||||
|
import org.openhab.core.scheduler.CronScheduler;
|
||||||
|
import org.openhab.core.scheduler.ScheduledCompletableFuture;
|
||||||
|
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.types.Command;
|
||||||
|
import org.openhab.core.types.RefreshType;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The {@link SmgwHandler} is responsible for refreshing the smart meter's data and handling REFRESH commands.
|
||||||
|
*
|
||||||
|
* @author Jan N. Klug - Initial contribution
|
||||||
|
*/
|
||||||
|
@NonNullByDefault
|
||||||
|
public class SmgwHandler extends BaseThingHandler {
|
||||||
|
private static final URI URI_NOT_SET = URI.create("");
|
||||||
|
private final Logger logger = LoggerFactory.getLogger(SmgwHandler.class);
|
||||||
|
private final HttpClient httpClient;
|
||||||
|
private final CronScheduler cronScheduler;
|
||||||
|
private SmgwConfiguration config = new SmgwConfiguration();
|
||||||
|
private URI uri = URI_NOT_SET;
|
||||||
|
private @Nullable ScheduledCompletableFuture<?> cronJob;
|
||||||
|
|
||||||
|
public SmgwHandler(Thing thing, HttpClient httpClient, CronScheduler cronScheduler) {
|
||||||
|
super(thing);
|
||||||
|
this.httpClient = httpClient;
|
||||||
|
this.cronScheduler = cronScheduler;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void handleCommand(ChannelUID channelUID, Command command) {
|
||||||
|
if (command instanceof RefreshType && !URI_NOT_SET.equals(uri)) {
|
||||||
|
cancelRefreshJob();
|
||||||
|
getData();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void initialize() {
|
||||||
|
config = getConfigAs(SmgwConfiguration.class);
|
||||||
|
try {
|
||||||
|
uri = new URI("https://" + config.hostname + "/cgi-bin/hanservice.cgi");
|
||||||
|
} catch (URISyntaxException e) {
|
||||||
|
uri = URI_NOT_SET;
|
||||||
|
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR,
|
||||||
|
"Could not create URI from given hostname");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
updateStatus(ThingStatus.UNKNOWN);
|
||||||
|
getData();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void dispose() {
|
||||||
|
cancelRefreshJob();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void cancelRefreshJob() {
|
||||||
|
ScheduledCompletableFuture<?> cronJob = this.cronJob;
|
||||||
|
if (cronJob != null) {
|
||||||
|
cronJob.cancel(true);
|
||||||
|
this.cronJob = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void getData() {
|
||||||
|
if (URI_NOT_SET.equals(uri)) {
|
||||||
|
logger.warn("getData() called, but URI is not set. Please describe what happened and report a bug.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// clear cookies
|
||||||
|
CookieStore cookieStore = httpClient.getCookieStore();
|
||||||
|
List<HttpCookie> cookies = cookieStore.get(uri);
|
||||||
|
cookies.forEach(cookie -> cookieStore.remove(uri, cookie));
|
||||||
|
|
||||||
|
// clear auth
|
||||||
|
AuthenticationStore authStore = httpClient.getAuthenticationStore();
|
||||||
|
Authentication.Result authResult = authStore.findAuthenticationResult(uri);
|
||||||
|
if (authResult != null) {
|
||||||
|
authStore.removeAuthenticationResult(authResult);
|
||||||
|
}
|
||||||
|
Authentication authentication = authStore.findAuthentication("Digest", uri, Authentication.ANY_REALM);
|
||||||
|
if (authentication != null) {
|
||||||
|
authStore.removeAuthentication(authentication);
|
||||||
|
}
|
||||||
|
|
||||||
|
// add new auth
|
||||||
|
authStore.addAuthentication(
|
||||||
|
new DigestAuthentication(uri, Authentication.ANY_REALM, config.username, config.password));
|
||||||
|
|
||||||
|
CompletableFuture<SmgwResponse> future = new CompletableFuture<>();
|
||||||
|
httpClient.newRequest(uri).send(new ResponseListener(future));
|
||||||
|
future.thenCompose(this::onLoginSuccess).thenCompose(this::onMeterForm).handle(this::onShowMeterValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
private CompletableFuture<SmgwResponse> onLoginSuccess(SmgwResponse response) {
|
||||||
|
Element tknElement = response.document().selectFirst("input[name='tkn']");
|
||||||
|
if (tknElement == null) {
|
||||||
|
return CompletableFuture.failedFuture(new IllegalStateException("Could not determine tkn"));
|
||||||
|
}
|
||||||
|
String tkn = tknElement.val();
|
||||||
|
String showMeterValuesForm = "tkn=" + tkn + "&action=meterform";
|
||||||
|
CompletableFuture<SmgwResponse> future = new CompletableFuture<>();
|
||||||
|
httpClient.POST(uri).content(new StringContentProvider(showMeterValuesForm)).send(new ResponseListener(future));
|
||||||
|
return future;
|
||||||
|
}
|
||||||
|
|
||||||
|
private CompletableFuture<SmgwResponse> onMeterForm(SmgwResponse response) {
|
||||||
|
Element tknElement = response.document().selectFirst("input[name='tkn']");
|
||||||
|
Element midElement = response.document().selectFirst("select[name='mid'] option");
|
||||||
|
if (tknElement == null || midElement == null) {
|
||||||
|
return CompletableFuture.failedFuture(new IllegalStateException("Could not determine mid or tkn"));
|
||||||
|
}
|
||||||
|
String tkn = tknElement.val();
|
||||||
|
String mid = midElement.val();
|
||||||
|
String localDate = LocalDate.now().format(DateTimeFormatter.ISO_LOCAL_DATE);
|
||||||
|
|
||||||
|
String showMeterValues = "tkn=" + tkn + "&mid=" + mid + "&action=showMeterValues&from=" + localDate + "&to="
|
||||||
|
+ localDate;
|
||||||
|
|
||||||
|
CompletableFuture<SmgwResponse> future = new CompletableFuture<>();
|
||||||
|
httpClient.POST(uri).content(new StringContentProvider(showMeterValues))
|
||||||
|
.header(HttpHeader.COOKIE, response.cookies()).send(new ResponseListener(future));
|
||||||
|
|
||||||
|
return future;
|
||||||
|
}
|
||||||
|
|
||||||
|
private @Nullable Object onShowMeterValue(@Nullable SmgwResponse response, @Nullable Throwable t) {
|
||||||
|
if (t != null || response == null) {
|
||||||
|
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR);
|
||||||
|
} else {
|
||||||
|
Element valueElement = response.document().selectFirst("#table_metervalues_col_wert");
|
||||||
|
Element unitElement = response.document().selectFirst("#table_metervalues_col_einheit");
|
||||||
|
Element dateTimeElement = response.document().selectFirst("#table_metervalues_col_timestamp");
|
||||||
|
if (valueElement == null || unitElement == null || dateTimeElement == null) {
|
||||||
|
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR);
|
||||||
|
} else {
|
||||||
|
QuantityType<Energy> value = new QuantityType<>(valueElement.text() + " " + unitElement.text());
|
||||||
|
DateTimeType dateTime = DateTimeType.valueOf(dateTimeElement.text().replace(" ", "T"));
|
||||||
|
|
||||||
|
updateState(CHANNEL_METER, value);
|
||||||
|
updateState(CHANNEL_TIMESTAMP, dateTime);
|
||||||
|
updateStatus(ThingStatus.ONLINE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ScheduledCompletableFuture<?> cronJob = this.cronJob;
|
||||||
|
if (cronJob == null || cronJob.isDone()) {
|
||||||
|
this.cronJob = cronScheduler.schedule(this::getData, "5 0/15 * * * ? *");
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class ResponseListener extends BufferingResponseListener {
|
||||||
|
private final Logger logger = LoggerFactory.getLogger(ResponseListener.class);
|
||||||
|
private final CompletableFuture<SmgwResponse> resultFuture;
|
||||||
|
|
||||||
|
public ResponseListener(CompletableFuture<SmgwResponse> resultFuture) {
|
||||||
|
this.resultFuture = resultFuture;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onComplete(@NonNullByDefault({}) Result result) {
|
||||||
|
if (result.isSucceeded()) {
|
||||||
|
Response response = result.getResponse();
|
||||||
|
int status = response.getStatus();
|
||||||
|
if (HttpStatus.isSuccess(status)) {
|
||||||
|
String setCookies = response.getHeaders().get(HttpHeader.SET_COOKIE);
|
||||||
|
String cookies = setCookies != null ? setCookies
|
||||||
|
: result.getRequest().getHeaders().get(HttpHeader.COOKIE);
|
||||||
|
Document doc = Jsoup.parse(getContentAsString());
|
||||||
|
resultFuture.complete(new SmgwResponse(cookies, doc));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
logger.warn("Failed to request {}", result.getRequest().getURI());
|
||||||
|
resultFuture.completeExceptionally(new IllegalStateException());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private record SmgwResponse(String cookies, Document document) {
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,89 @@
|
||||||
|
/**
|
||||||
|
* Copyright (c) 2010-2023 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.smgw.internal;
|
||||||
|
|
||||||
|
import static org.openhab.binding.smgw.internal.SmgwBindingConstants.*;
|
||||||
|
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||||
|
import org.eclipse.jdt.annotation.Nullable;
|
||||||
|
import org.eclipse.jetty.client.HttpClient;
|
||||||
|
import org.eclipse.jetty.util.ssl.SslContextFactory;
|
||||||
|
import org.openhab.core.io.net.http.HttpClientFactory;
|
||||||
|
import org.openhab.core.scheduler.CronScheduler;
|
||||||
|
import org.openhab.core.thing.Thing;
|
||||||
|
import org.openhab.core.thing.ThingTypeUID;
|
||||||
|
import org.openhab.core.thing.binding.BaseThingHandlerFactory;
|
||||||
|
import org.openhab.core.thing.binding.ThingHandler;
|
||||||
|
import org.openhab.core.thing.binding.ThingHandlerFactory;
|
||||||
|
import org.osgi.service.component.annotations.Activate;
|
||||||
|
import org.osgi.service.component.annotations.Component;
|
||||||
|
import org.osgi.service.component.annotations.Deactivate;
|
||||||
|
import org.osgi.service.component.annotations.Reference;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The {@link SmgwHandlerFactory} is responsible for creating things and thing
|
||||||
|
* handlers.
|
||||||
|
*
|
||||||
|
* @author Jan N. Klug - Initial contribution
|
||||||
|
*/
|
||||||
|
@NonNullByDefault
|
||||||
|
@Component(service = ThingHandlerFactory.class)
|
||||||
|
public class SmgwHandlerFactory extends BaseThingHandlerFactory {
|
||||||
|
private static final Set<ThingTypeUID> SUPPORTED_THING_TYPES_UIDS = Set.of(THING_TYPE_SMGW);
|
||||||
|
private final Logger logger = LoggerFactory.getLogger(SmgwHandlerFactory.class);
|
||||||
|
|
||||||
|
private final HttpClient httpClient;
|
||||||
|
private final CronScheduler cronScheduler;
|
||||||
|
|
||||||
|
@Activate
|
||||||
|
public SmgwHandlerFactory(@Reference HttpClientFactory clientFactory, @Reference CronScheduler cronScheduler) {
|
||||||
|
this.cronScheduler = cronScheduler;
|
||||||
|
this.httpClient = clientFactory.createHttpClient("smgw", new SslContextFactory.Client(true));
|
||||||
|
try {
|
||||||
|
this.httpClient.start();
|
||||||
|
} catch (Exception e) {
|
||||||
|
// catching exception is necessary due to the signature of HttpClient.start()
|
||||||
|
logger.warn("Failed to start http client: {}", e.getMessage());
|
||||||
|
throw new IllegalStateException("Could not create HttpClient");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Deactivate
|
||||||
|
public void deactivate() {
|
||||||
|
try {
|
||||||
|
httpClient.stop();
|
||||||
|
} catch (Exception e) {
|
||||||
|
logger.warn("Failed to stop http client: {}", e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@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_SMGW.equals(thingTypeUID)) {
|
||||||
|
return new SmgwHandler(thing, httpClient, cronScheduler);
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,12 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<addon:addon id="smgw" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
xmlns:addon="https://openhab.org/schemas/addon/v1.0.0"
|
||||||
|
xsi:schemaLocation="https://openhab.org/schemas/addon/v1.0.0 https://openhab.org/schemas/addon-1.0.0.xsd">
|
||||||
|
|
||||||
|
<type>binding</type>
|
||||||
|
<name>PPC SMGW Binding</name>
|
||||||
|
<description>This integrates the PPC Smart Meter Gateways.</description>
|
||||||
|
|
||||||
|
<connection>local</connection>
|
||||||
|
|
||||||
|
</addon:addon>
|
|
@ -0,0 +1,22 @@
|
||||||
|
# add-on
|
||||||
|
|
||||||
|
addon.smgw.name = PPC SMGW Binding
|
||||||
|
addon.smgw.description = This integrates the PPC Smart Meter Gateways.
|
||||||
|
|
||||||
|
# thing types
|
||||||
|
|
||||||
|
thing-type.smgw.smgw.label = Smartmeter Gateway
|
||||||
|
thing-type.smgw.smgw.description = A Smartmeter Gateway
|
||||||
|
thing-type.smgw.smgw.channel.meter.label = Meter Reading
|
||||||
|
|
||||||
|
# thing types config
|
||||||
|
|
||||||
|
thing-type.config.smgw.smgw.hostname.label = Hostname
|
||||||
|
thing-type.config.smgw.smgw.hostname.description = Hostname or IP address of the device
|
||||||
|
thing-type.config.smgw.smgw.password.label = Password
|
||||||
|
thing-type.config.smgw.smgw.username.label = Username
|
||||||
|
|
||||||
|
# channel types
|
||||||
|
|
||||||
|
channel-type.smgw.timestamp.label = Timestamp
|
||||||
|
channel-type.smgw.timestamp.description = The timestamp of the meter reading.
|
|
@ -0,0 +1,45 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<thing:thing-descriptions bindingId="smgw"
|
||||||
|
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">
|
||||||
|
|
||||||
|
<!-- Sample Thing Type -->
|
||||||
|
<thing-type id="smgw">
|
||||||
|
<label>Smartmeter Gateway</label>
|
||||||
|
<description>A Smartmeter Gateway</description>
|
||||||
|
|
||||||
|
<channels>
|
||||||
|
<channel id="meter" typeId="system.electric-energy">
|
||||||
|
<label>Meter Reading</label>
|
||||||
|
</channel>
|
||||||
|
<channel id="timestamp" typeId="timestamp">
|
||||||
|
</channel>
|
||||||
|
</channels>
|
||||||
|
|
||||||
|
<config-description>
|
||||||
|
<parameter name="hostname" type="text">
|
||||||
|
<context>network-address</context>
|
||||||
|
<label>Hostname</label>
|
||||||
|
<description>Hostname or IP address of the device</description>
|
||||||
|
<default>192.168.1.200</default>
|
||||||
|
</parameter>
|
||||||
|
<parameter name="username" type="text" required="true">
|
||||||
|
<label>Username</label>
|
||||||
|
</parameter>
|
||||||
|
<parameter name="password" type="text" required="true">
|
||||||
|
<context>password</context>
|
||||||
|
<label>Password</label>
|
||||||
|
</parameter>
|
||||||
|
</config-description>
|
||||||
|
|
||||||
|
</thing-type>
|
||||||
|
|
||||||
|
<channel-type id="timestamp">
|
||||||
|
<item-type>DateTime</item-type>
|
||||||
|
<label>Timestamp</label>
|
||||||
|
<description>The timestamp of the meter reading.</description>
|
||||||
|
<state readOnly="true"/>
|
||||||
|
</channel-type>
|
||||||
|
|
||||||
|
</thing:thing-descriptions>
|
|
@ -348,6 +348,7 @@
|
||||||
<module>org.openhab.binding.smaenergymeter</module>
|
<module>org.openhab.binding.smaenergymeter</module>
|
||||||
<module>org.openhab.binding.smartmeter</module>
|
<module>org.openhab.binding.smartmeter</module>
|
||||||
<module>org.openhab.binding.smartthings</module>
|
<module>org.openhab.binding.smartthings</module>
|
||||||
|
<module>org.openhab.binding.smgw</module>
|
||||||
<module>org.openhab.binding.smhi</module>
|
<module>org.openhab.binding.smhi</module>
|
||||||
<module>org.openhab.binding.smsmodem</module>
|
<module>org.openhab.binding.smsmodem</module>
|
||||||
<module>org.openhab.binding.sncf</module>
|
<module>org.openhab.binding.sncf</module>
|
||||||
|
|
Loading…
Reference in New Issue