mirror of
https://github.com/openhab/openhab-addons.git
synced 2025-01-10 15:11:59 +01:00
[ecowatt] Initial contribution (#13404)
* [ecowatt] Initial contribution This binding uses the Ecowatt API to expose clear signals to adopt the right gestures and to ensure a good supply of electricity for all in France. Close #13351 Signed-off-by: Laurent Garnier <lg.hc@free.fr> * Update bundles/org.openhab.binding.ecowatt/src/main/resources/OH-INF/thing/thing-types.xml Signed-off-by: Fabian Wolter <github@fabian-wolter.de> Signed-off-by: Laurent Garnier <lg.hc@free.fr> Signed-off-by: Fabian Wolter <github@fabian-wolter.de> Co-authored-by: Fabian Wolter <github@fabian-wolter.de>
This commit is contained in:
parent
798b3ede04
commit
6ebf20f183
@ -79,6 +79,7 @@
|
|||||||
/bundles/org.openhab.binding.dwdunwetter/ @limdul79
|
/bundles/org.openhab.binding.dwdunwetter/ @limdul79
|
||||||
/bundles/org.openhab.binding.ecobee/ @mhilbush
|
/bundles/org.openhab.binding.ecobee/ @mhilbush
|
||||||
/bundles/org.openhab.binding.ecotouch/ @sibbi77
|
/bundles/org.openhab.binding.ecotouch/ @sibbi77
|
||||||
|
/bundles/org.openhab.binding.ecowatt/ @lolodomo
|
||||||
/bundles/org.openhab.binding.ekey/ @hmerk
|
/bundles/org.openhab.binding.ekey/ @hmerk
|
||||||
/bundles/org.openhab.binding.electroluxair/ @jannegpriv
|
/bundles/org.openhab.binding.electroluxair/ @jannegpriv
|
||||||
/bundles/org.openhab.binding.elerotransmitterstick/ @vbier
|
/bundles/org.openhab.binding.elerotransmitterstick/ @vbier
|
||||||
|
@ -391,6 +391,11 @@
|
|||||||
<artifactId>org.openhab.binding.ecotouch</artifactId>
|
<artifactId>org.openhab.binding.ecotouch</artifactId>
|
||||||
<version>${project.version}</version>
|
<version>${project.version}</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.openhab.addons.bundles</groupId>
|
||||||
|
<artifactId>org.openhab.binding.ecowatt</artifactId>
|
||||||
|
<version>${project.version}</version>
|
||||||
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.openhab.addons.bundles</groupId>
|
<groupId>org.openhab.addons.bundles</groupId>
|
||||||
<artifactId>org.openhab.binding.ekey</artifactId>
|
<artifactId>org.openhab.binding.ekey</artifactId>
|
||||||
|
13
bundles/org.openhab.binding.ecowatt/NOTICE
Normal file
13
bundles/org.openhab.binding.ecowatt/NOTICE
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
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
|
71
bundles/org.openhab.binding.ecowatt/README.md
Normal file
71
bundles/org.openhab.binding.ecowatt/README.md
Normal file
@ -0,0 +1,71 @@
|
|||||||
|
# Ecowatt Binding
|
||||||
|
|
||||||
|
This binding uses the Ecowatt API to expose clear signals to adopt the right gestures and to ensure a good supply of electricity for all in France.
|
||||||
|
|
||||||
|
You can find more information about Ecowatt on this [site](https://www.monecowatt.fr).
|
||||||
|
|
||||||
|
## Supported Things
|
||||||
|
|
||||||
|
This binding supports only one thing type: `signals`.
|
||||||
|
|
||||||
|
## Discovery
|
||||||
|
|
||||||
|
Discovery is not supported.
|
||||||
|
You have to add the thing manually.
|
||||||
|
|
||||||
|
## Prerequisites before configuration
|
||||||
|
|
||||||
|
You must create an account and an application on the RTE portal to obtain the OAuth2 credentials required to access the API.
|
||||||
|
|
||||||
|
1. Open this [page](https://data.rte-france.com/catalog/-/api/consumption/Ecowatt/v4.0), find the "Ecowatt" tile and click on the "Abonnez-vous à l'API" button.
|
||||||
|
2. Create an account by following the instructions (you will receive an email to validate your new account).
|
||||||
|
3. Once logged in, create an application by entering a name (for example "openHAB Integration"), choosing "Web Server" as type, entering any description of your choice and finally clicking on the "Valider" button.
|
||||||
|
4. You will then see your application details, in particular the "ID client" and "ID Secret" information which you will need later to set up your binding thing.
|
||||||
|
|
||||||
|
## Binding Configuration
|
||||||
|
|
||||||
|
There are no overall binding configuration settings that need to be set.
|
||||||
|
All settings are through thing configuration parameters.
|
||||||
|
|
||||||
|
## Thing Configuration
|
||||||
|
|
||||||
|
| Name | Type | Description | Required |
|
||||||
|
|-----------|---------|-----------------------------------------------------------------------|----------|
|
||||||
|
| idClient | text | ID client provided with the application you created in the RTE portal | yes |
|
||||||
|
| idSecret | text | ID secret provided with the application you created in the RTE portal | yes |
|
||||||
|
|
||||||
|
## Channels
|
||||||
|
|
||||||
|
All channels are read-only.
|
||||||
|
|
||||||
|
| Channel | Type | Description |
|
||||||
|
|-------------------|--------|------------------------------------------------------------------|
|
||||||
|
| todaySignal | Number | The signal relating to the forecast consumption level for today. Values are 1 for normal consumption, 2 for strained electrical system and 3 for very strained electrical system. |
|
||||||
|
| tomorrowSignal | Number | The signal relating to the forecast consumption level for tomorrow. Values are 1 for normal consumption, 2 for strained electrical system and 3 for very strained electrical system. |
|
||||||
|
| currentHourSignal | Number | The signal relating to the forecast consumption level for the current hour. Values are 1 for normal consumption, 2 for strained electrical system and 3 for very strained electrical system. |
|
||||||
|
|
||||||
|
## Full Example
|
||||||
|
|
||||||
|
example.things:
|
||||||
|
|
||||||
|
```
|
||||||
|
Thing ecowatt:signals:signals "Ecowatt Signals" [ idClient="xxxxx", idSecret="yyyyy"]
|
||||||
|
```
|
||||||
|
|
||||||
|
example.items:
|
||||||
|
|
||||||
|
```
|
||||||
|
Number TodaySignal "Today [%s]" { channel="ecowatt:signals:signals:todaySignal" }
|
||||||
|
Number TomorrowSignal "Tomorrow [%s]" { channel="ecowatt:signals:signals:tomorrowSignal" }
|
||||||
|
Number CurrentHourSignal "Current hour [%s]" { channel="ecowatt:signals:signals:currentHourSignal" }
|
||||||
|
```
|
||||||
|
|
||||||
|
example.sitemap:
|
||||||
|
|
||||||
|
```
|
||||||
|
Frame label="Ecowatt" {
|
||||||
|
Default item=TodaySignal
|
||||||
|
Default item=TomorrowSignal
|
||||||
|
Default item=CurrentHourSignal
|
||||||
|
}
|
||||||
|
```
|
17
bundles/org.openhab.binding.ecowatt/pom.xml
Normal file
17
bundles/org.openhab.binding.ecowatt/pom.xml
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
<?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>3.4.0-SNAPSHOT</version>
|
||||||
|
</parent>
|
||||||
|
|
||||||
|
<artifactId>org.openhab.binding.ecowatt</artifactId>
|
||||||
|
|
||||||
|
<name>openHAB Add-ons :: Bundles :: Ecowatt Binding</name>
|
||||||
|
|
||||||
|
</project>
|
@ -0,0 +1,9 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<features name="org.openhab.binding.ecowatt-${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-ecowatt" description="Ecowatt Binding" version="${project.version}">
|
||||||
|
<feature>openhab-runtime-base</feature>
|
||||||
|
<bundle start-level="80">mvn:org.openhab.addons.bundles/org.openhab.binding.ecowatt/${project.version}</bundle>
|
||||||
|
</feature>
|
||||||
|
</features>
|
@ -0,0 +1,36 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) 2010-2022 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.ecowatt.internal;
|
||||||
|
|
||||||
|
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||||
|
import org.openhab.core.thing.ThingTypeUID;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The {@link EcowattBindingConstants} class defines common constants, which are
|
||||||
|
* used across the whole binding.
|
||||||
|
*
|
||||||
|
* @author Laurent Garnier - Initial contribution
|
||||||
|
*/
|
||||||
|
@NonNullByDefault
|
||||||
|
public class EcowattBindingConstants {
|
||||||
|
|
||||||
|
private static final String BINDING_ID = "ecowatt";
|
||||||
|
|
||||||
|
// List of all Thing Type UIDs
|
||||||
|
public static final ThingTypeUID THING_TYPE_SIGNALS = new ThingTypeUID(BINDING_ID, "signals");
|
||||||
|
|
||||||
|
// List of all Channel ids
|
||||||
|
public static final String CHANNEL_TODAY_SIGNAL = "todaySignal";
|
||||||
|
public static final String CHANNEL_TOMORROW_SIGNAL = "tomorrowSignal";
|
||||||
|
public static final String CHANNEL_CURRENT_HOUR_SIGNAL = "currentHourSignal";
|
||||||
|
}
|
@ -0,0 +1,77 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) 2010-2022 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.ecowatt.internal;
|
||||||
|
|
||||||
|
import static org.openhab.binding.ecowatt.internal.EcowattBindingConstants.THING_TYPE_SIGNALS;
|
||||||
|
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||||
|
import org.eclipse.jdt.annotation.Nullable;
|
||||||
|
import org.eclipse.jetty.client.HttpClient;
|
||||||
|
import org.openhab.binding.ecowatt.internal.handler.EcowattHandler;
|
||||||
|
import org.openhab.core.auth.client.oauth2.OAuthFactory;
|
||||||
|
import org.openhab.core.i18n.TimeZoneProvider;
|
||||||
|
import org.openhab.core.i18n.TranslationProvider;
|
||||||
|
import org.openhab.core.io.net.http.HttpClientFactory;
|
||||||
|
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.Reference;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The {@link EcowattHandlerFactory} is responsible for creating things and thing
|
||||||
|
* handlers.
|
||||||
|
*
|
||||||
|
* @author Laurent Garnier - Initial contribution
|
||||||
|
*/
|
||||||
|
@NonNullByDefault
|
||||||
|
@Component(configurationPid = "binding.ecowatt", service = ThingHandlerFactory.class)
|
||||||
|
public class EcowattHandlerFactory extends BaseThingHandlerFactory {
|
||||||
|
|
||||||
|
private static final Set<ThingTypeUID> SUPPORTED_THING_TYPES_UIDS = Set.of(THING_TYPE_SIGNALS);
|
||||||
|
|
||||||
|
private final OAuthFactory oAuthFactory;
|
||||||
|
private final HttpClient httpClient;
|
||||||
|
private final TranslationProvider i18nProvider;
|
||||||
|
private final TimeZoneProvider timeZoneProvider;
|
||||||
|
|
||||||
|
@Activate
|
||||||
|
public EcowattHandlerFactory(@Reference OAuthFactory oAuthFactory, @Reference HttpClientFactory httpClientFactory,
|
||||||
|
final @Reference TranslationProvider i18nProvider, final @Reference TimeZoneProvider timeZoneProvider) {
|
||||||
|
this.oAuthFactory = oAuthFactory;
|
||||||
|
this.httpClient = httpClientFactory.getCommonHttpClient();
|
||||||
|
this.i18nProvider = i18nProvider;
|
||||||
|
this.timeZoneProvider = timeZoneProvider;
|
||||||
|
}
|
||||||
|
|
||||||
|
@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_SIGNALS.equals(thingTypeUID)) {
|
||||||
|
return new EcowattHandler(thing, oAuthFactory, httpClient, i18nProvider, timeZoneProvider);
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,27 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) 2010-2022 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.ecowatt.internal.configuration;
|
||||||
|
|
||||||
|
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The {@link EcowattConfiguration} class contains fields mapping thing configuration parameters.
|
||||||
|
*
|
||||||
|
* @author Laurent Garnier - Initial contribution
|
||||||
|
*/
|
||||||
|
@NonNullByDefault
|
||||||
|
public class EcowattConfiguration {
|
||||||
|
|
||||||
|
public String idClient = "";
|
||||||
|
public String idSecret = "";
|
||||||
|
}
|
@ -0,0 +1,37 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) 2010-2022 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.ecowatt.internal.exception;
|
||||||
|
|
||||||
|
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||||
|
import org.eclipse.jdt.annotation.Nullable;
|
||||||
|
import org.openhab.core.i18n.CommunicationException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An exception used when the API limit is reached
|
||||||
|
*
|
||||||
|
* @author Laurent Garnier - Initial contribution
|
||||||
|
*/
|
||||||
|
@NonNullByDefault
|
||||||
|
public class EcowattApiLimitException extends CommunicationException {
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
private int retryAfter;
|
||||||
|
|
||||||
|
public EcowattApiLimitException(int retryAfter, String message, @Nullable Object @Nullable... msgParams) {
|
||||||
|
super(message, msgParams);
|
||||||
|
this.retryAfter = retryAfter;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getRetryAfter() {
|
||||||
|
return retryAfter;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,245 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) 2010-2022 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.ecowatt.internal.handler;
|
||||||
|
|
||||||
|
import static org.openhab.binding.ecowatt.internal.EcowattBindingConstants.*;
|
||||||
|
|
||||||
|
import java.time.Duration;
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
import java.time.ZonedDateTime;
|
||||||
|
import java.time.format.DateTimeFormatter;
|
||||||
|
import java.time.temporal.ChronoUnit;
|
||||||
|
import java.util.concurrent.ScheduledFuture;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
|
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||||
|
import org.eclipse.jdt.annotation.Nullable;
|
||||||
|
import org.eclipse.jetty.client.HttpClient;
|
||||||
|
import org.openhab.binding.ecowatt.internal.configuration.EcowattConfiguration;
|
||||||
|
import org.openhab.binding.ecowatt.internal.exception.EcowattApiLimitException;
|
||||||
|
import org.openhab.binding.ecowatt.internal.restapi.EcowattApiResponse;
|
||||||
|
import org.openhab.binding.ecowatt.internal.restapi.EcowattDaySignals;
|
||||||
|
import org.openhab.binding.ecowatt.internal.restapi.EcowattRestApi;
|
||||||
|
import org.openhab.core.auth.client.oauth2.OAuthFactory;
|
||||||
|
import org.openhab.core.cache.ExpiringCache;
|
||||||
|
import org.openhab.core.i18n.CommunicationException;
|
||||||
|
import org.openhab.core.i18n.TimeZoneProvider;
|
||||||
|
import org.openhab.core.i18n.TranslationProvider;
|
||||||
|
import org.openhab.core.library.types.DecimalType;
|
||||||
|
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.openhab.core.types.State;
|
||||||
|
import org.openhab.core.types.UnDefType;
|
||||||
|
import org.osgi.framework.Bundle;
|
||||||
|
import org.osgi.framework.FrameworkUtil;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The {@link EcowattHandler} is responsible for updating the state of the channels
|
||||||
|
*
|
||||||
|
* @author Laurent Garnier - Initial contribution
|
||||||
|
*/
|
||||||
|
@NonNullByDefault
|
||||||
|
public class EcowattHandler extends BaseThingHandler {
|
||||||
|
|
||||||
|
private final Logger logger = LoggerFactory.getLogger(EcowattHandler.class);
|
||||||
|
|
||||||
|
private final OAuthFactory oAuthFactory;
|
||||||
|
private final HttpClient httpClient;
|
||||||
|
private final TranslationProvider i18nProvider;
|
||||||
|
private final TimeZoneProvider timeZoneProvider;
|
||||||
|
private final Bundle bundle;
|
||||||
|
|
||||||
|
private @Nullable EcowattRestApi api;
|
||||||
|
private ExpiringCache<EcowattApiResponse> cachedApiResponse = new ExpiringCache<>(Duration.ofHours(4),
|
||||||
|
this::getApiResponse); // cache the API response during 4 hours
|
||||||
|
|
||||||
|
private @Nullable ScheduledFuture<?> updateJob;
|
||||||
|
|
||||||
|
public EcowattHandler(Thing thing, OAuthFactory oAuthFactory, HttpClient httpClient,
|
||||||
|
TranslationProvider i18nProvider, TimeZoneProvider timeZoneProvider) {
|
||||||
|
super(thing);
|
||||||
|
this.oAuthFactory = oAuthFactory;
|
||||||
|
this.httpClient = httpClient;
|
||||||
|
this.i18nProvider = i18nProvider;
|
||||||
|
this.timeZoneProvider = timeZoneProvider;
|
||||||
|
this.bundle = FrameworkUtil.getBundle(this.getClass());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void handleCommand(ChannelUID channelUID, Command command) {
|
||||||
|
if (command == RefreshType.REFRESH) {
|
||||||
|
updateChannel(channelUID.getId());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void initialize() {
|
||||||
|
EcowattConfiguration config = getConfigAs(EcowattConfiguration.class);
|
||||||
|
|
||||||
|
final String idClient = config.idClient;
|
||||||
|
final String idSecret = config.idSecret;
|
||||||
|
|
||||||
|
if (idClient.isBlank() || idSecret.isBlank()) {
|
||||||
|
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR,
|
||||||
|
"@text/offline.config-error-unset-parameters");
|
||||||
|
} else {
|
||||||
|
api = new EcowattRestApi(oAuthFactory, httpClient, thing.getUID().getAsString(), idClient, idSecret);
|
||||||
|
updateStatus(ThingStatus.UNKNOWN);
|
||||||
|
scheduleNextUpdate(0, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void dispose() {
|
||||||
|
stopScheduledJob();
|
||||||
|
EcowattRestApi localApi = api;
|
||||||
|
if (localApi != null) {
|
||||||
|
localApi.dispose();
|
||||||
|
api = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Schedule the next update of channels.
|
||||||
|
*
|
||||||
|
* After this update is run, a new update will be rescheduled, either just after the API is reachable again or at
|
||||||
|
* the beginning of the following hour.
|
||||||
|
*
|
||||||
|
* @param delayInSeconds the delay in seconds before running the next update
|
||||||
|
* @param retryIfApiLimitReached true if a retry is expected when the update fails due to reached API limit
|
||||||
|
*/
|
||||||
|
private void scheduleNextUpdate(long delayInSeconds, boolean retryIfApiLimitReached) {
|
||||||
|
logger.debug("scheduleNextUpdate delay={}s retryIfLimitReached={}", delayInSeconds, retryIfApiLimitReached);
|
||||||
|
updateJob = scheduler.schedule(() -> {
|
||||||
|
int retryDelay = updateChannels(retryIfApiLimitReached);
|
||||||
|
long delayNextUpdate;
|
||||||
|
if (retryDelay > 0) {
|
||||||
|
// Schedule a new update just after the API is reachable again
|
||||||
|
logger.debug("retryDelay {}", retryDelay);
|
||||||
|
delayNextUpdate = retryDelay;
|
||||||
|
} else {
|
||||||
|
// Schedule a new update at the beginning of the following hour
|
||||||
|
final LocalDateTime now = LocalDateTime.now();
|
||||||
|
final LocalDateTime beginningNextHour = now.plusHours(1).truncatedTo(ChronoUnit.HOURS);
|
||||||
|
delayNextUpdate = ChronoUnit.SECONDS.between(now, beginningNextHour);
|
||||||
|
}
|
||||||
|
// Add 3s of additional delay for security...
|
||||||
|
delayNextUpdate += 3;
|
||||||
|
scheduleNextUpdate(delayNextUpdate, retryDelay == 0);
|
||||||
|
}, delayInSeconds, TimeUnit.SECONDS);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void stopScheduledJob() {
|
||||||
|
ScheduledFuture<?> job = updateJob;
|
||||||
|
if (job != null) {
|
||||||
|
job.cancel(true);
|
||||||
|
updateJob = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private EcowattApiResponse getApiResponse() {
|
||||||
|
EcowattRestApi localApi = api;
|
||||||
|
if (localApi == null) {
|
||||||
|
return new EcowattApiResponse();
|
||||||
|
}
|
||||||
|
|
||||||
|
EcowattApiResponse response;
|
||||||
|
try {
|
||||||
|
response = localApi.getSignals();
|
||||||
|
} catch (CommunicationException e) {
|
||||||
|
Throwable cause = e.getCause();
|
||||||
|
if (cause != null) {
|
||||||
|
logger.warn("{}: {}", e.getMessage(bundle, i18nProvider), cause.getMessage());
|
||||||
|
} else {
|
||||||
|
logger.warn("{}", e.getMessage(bundle, i18nProvider));
|
||||||
|
}
|
||||||
|
response = new EcowattApiResponse(e);
|
||||||
|
}
|
||||||
|
return response;
|
||||||
|
}
|
||||||
|
|
||||||
|
private int updateChannels(boolean retryIfApiLimitReached) {
|
||||||
|
return updateChannel(null, retryIfApiLimitReached);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void updateChannel(String channelId) {
|
||||||
|
updateChannel(channelId, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
private synchronized int updateChannel(@Nullable String channelId, boolean retryIfApiLimitReached) {
|
||||||
|
logger.debug("updateChannel channelId={}, retryIfApiLimitReached={}", channelId, retryIfApiLimitReached);
|
||||||
|
int retryDelay = 0;
|
||||||
|
EcowattApiResponse response = cachedApiResponse.getValue();
|
||||||
|
if (response == null || !response.succeeded()) {
|
||||||
|
CommunicationException exception = response == null ? null : response.getException();
|
||||||
|
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR,
|
||||||
|
exception == null ? null : exception.getRawMessage());
|
||||||
|
|
||||||
|
// Invalidate the cache to be sure the next request will trigger the API
|
||||||
|
cachedApiResponse.invalidateValue();
|
||||||
|
|
||||||
|
if (retryIfApiLimitReached && exception instanceof EcowattApiLimitException
|
||||||
|
&& ((EcowattApiLimitException) exception).getRetryAfter() > 0) {
|
||||||
|
// Will retry when the API is available again (just after the limit expired)
|
||||||
|
retryDelay = ((EcowattApiLimitException) exception).getRetryAfter();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
updateStatus(ThingStatus.ONLINE);
|
||||||
|
}
|
||||||
|
|
||||||
|
ZonedDateTime now = ZonedDateTime.now(timeZoneProvider.getTimeZone());
|
||||||
|
logger.debug("now {}", now.format(DateTimeFormatter.ISO_ZONED_DATE_TIME));
|
||||||
|
if ((channelId == null || CHANNEL_TODAY_SIGNAL.equals(channelId)) && isLinked(CHANNEL_TODAY_SIGNAL)) {
|
||||||
|
updateState(CHANNEL_TODAY_SIGNAL, getDaySignalState(response, now));
|
||||||
|
}
|
||||||
|
if ((channelId == null || CHANNEL_TOMORROW_SIGNAL.equals(channelId)) && isLinked(CHANNEL_TOMORROW_SIGNAL)) {
|
||||||
|
updateState(CHANNEL_TOMORROW_SIGNAL, getDaySignalState(response, now.plusDays(1)));
|
||||||
|
}
|
||||||
|
if ((channelId == null || CHANNEL_CURRENT_HOUR_SIGNAL.equals(channelId))
|
||||||
|
&& isLinked(CHANNEL_CURRENT_HOUR_SIGNAL)) {
|
||||||
|
updateState(CHANNEL_CURRENT_HOUR_SIGNAL, getHourSignalState(response, now));
|
||||||
|
}
|
||||||
|
|
||||||
|
return retryDelay;
|
||||||
|
}
|
||||||
|
|
||||||
|
private State getDaySignalState(@Nullable EcowattApiResponse response, ZonedDateTime dateTime) {
|
||||||
|
EcowattDaySignals signals = response == null ? null : response.getDaySignals(dateTime);
|
||||||
|
return signals != null && signals.getDaySignal() >= 1 && signals.getDaySignal() <= 3
|
||||||
|
? new DecimalType(signals.getDaySignal())
|
||||||
|
: UnDefType.UNDEF;
|
||||||
|
}
|
||||||
|
|
||||||
|
private State getHourSignalState(@Nullable EcowattApiResponse response, ZonedDateTime dateTime) {
|
||||||
|
EcowattDaySignals signals = response == null ? null : response.getDaySignals(dateTime);
|
||||||
|
ZonedDateTime day = signals == null ? null : signals.getDay();
|
||||||
|
if (signals != null && day != null) {
|
||||||
|
// Move the current time to the same offset as the data returned by the API to get and use the right current
|
||||||
|
// hour index in these data
|
||||||
|
int hour = dateTime.withZoneSameInstant(day.getZone()).getHour();
|
||||||
|
int value = signals.getHourSignal(hour);
|
||||||
|
logger.debug("hour {} value {}", hour, value);
|
||||||
|
if (value >= 1 && value <= 3) {
|
||||||
|
return new DecimalType(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return UnDefType.UNDEF;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,93 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) 2010-2022 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.ecowatt.internal.restapi;
|
||||||
|
|
||||||
|
import java.time.ZonedDateTime;
|
||||||
|
import java.time.format.DateTimeFormatter;
|
||||||
|
import java.time.temporal.ChronoUnit;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||||
|
import org.eclipse.jdt.annotation.Nullable;
|
||||||
|
import org.openhab.core.i18n.CommunicationException;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The {@link EcowattApiResponse} class contains fields mapping the response to the Ecowatt API request /signals.
|
||||||
|
*
|
||||||
|
* It also includes an exception field to be set in case the API request fails.
|
||||||
|
*
|
||||||
|
* @author Laurent Garnier - Initial contribution
|
||||||
|
*/
|
||||||
|
@NonNullByDefault
|
||||||
|
public class EcowattApiResponse {
|
||||||
|
private final Logger logger = LoggerFactory.getLogger(EcowattApiResponse.class);
|
||||||
|
|
||||||
|
public @Nullable List<EcowattDaySignals> signals;
|
||||||
|
private @Nullable CommunicationException exception;
|
||||||
|
|
||||||
|
public EcowattApiResponse() {
|
||||||
|
this.signals = null;
|
||||||
|
this.exception = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public EcowattApiResponse(@Nullable List<EcowattDaySignals> signals) {
|
||||||
|
this.signals = signals;
|
||||||
|
this.exception = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public EcowattApiResponse(CommunicationException exception) {
|
||||||
|
this.signals = null;
|
||||||
|
this.exception = exception;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Search the data for the day of the given date and time
|
||||||
|
*
|
||||||
|
* @param dateTime a date and time
|
||||||
|
* @return the data for the searched day or null if no data is found for this day
|
||||||
|
*/
|
||||||
|
public @Nullable EcowattDaySignals getDaySignals(ZonedDateTime dateTime) {
|
||||||
|
List<EcowattDaySignals> localSignals = signals;
|
||||||
|
if (localSignals != null) {
|
||||||
|
for (EcowattDaySignals daySignals : localSignals) {
|
||||||
|
ZonedDateTime zdt = daySignals.getDay();
|
||||||
|
if (zdt != null) {
|
||||||
|
// Adjust date/times to the same offset/zone
|
||||||
|
ZonedDateTime dateTime2 = dateTime.withZoneSameInstant(zdt.getZone());
|
||||||
|
logger.trace("zdt {} offset {} - dateTime2 {} offset {}",
|
||||||
|
zdt.format(DateTimeFormatter.ISO_ZONED_DATE_TIME), zdt.getOffset(),
|
||||||
|
dateTime2.format(DateTimeFormatter.ISO_ZONED_DATE_TIME), dateTime2.getOffset());
|
||||||
|
// Check if the two date/times are in the same day
|
||||||
|
if (zdt.truncatedTo(ChronoUnit.DAYS).toInstant()
|
||||||
|
.equals(dateTime2.truncatedTo(ChronoUnit.DAYS).toInstant())) {
|
||||||
|
logger.debug("getDaySignals for {} returns signal {} : {} ( {} )",
|
||||||
|
dateTime.format(DateTimeFormatter.ISO_ZONED_DATE_TIME), daySignals.getDaySignal(),
|
||||||
|
daySignals.getDayMessage(), zdt.format(DateTimeFormatter.ISO_ZONED_DATE_TIME));
|
||||||
|
return daySignals;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean succeeded() {
|
||||||
|
return signals != null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public @Nullable CommunicationException getException() {
|
||||||
|
return exception;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,63 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) 2010-2022 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.ecowatt.internal.restapi;
|
||||||
|
|
||||||
|
import java.time.ZonedDateTime;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||||
|
import org.eclipse.jdt.annotation.Nullable;
|
||||||
|
|
||||||
|
import com.google.gson.annotations.SerializedName;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The {@link EcowattDaySignals} class contains fields mapping the content of each value of JSON table "signals" inside
|
||||||
|
* the API response
|
||||||
|
*
|
||||||
|
* @author Laurent Garnier - Initial contribution
|
||||||
|
*/
|
||||||
|
@NonNullByDefault
|
||||||
|
public class EcowattDaySignals {
|
||||||
|
@SerializedName("GenerationFichier")
|
||||||
|
public @Nullable ZonedDateTime fileTimestamp;
|
||||||
|
@SerializedName("jour")
|
||||||
|
public @Nullable ZonedDateTime day;
|
||||||
|
@SerializedName("dvalue")
|
||||||
|
public int value;
|
||||||
|
public @Nullable String message;
|
||||||
|
public @Nullable List<EcowattHourSignal> values;
|
||||||
|
|
||||||
|
public @Nullable ZonedDateTime getDay() {
|
||||||
|
return day;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getDaySignal() {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public @Nullable String getDayMessage() {
|
||||||
|
return message;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getHourSignal(int hour) {
|
||||||
|
List<EcowattHourSignal> localValues = values;
|
||||||
|
if (localValues != null) {
|
||||||
|
for (EcowattHourSignal hourSignal : localValues) {
|
||||||
|
if (hourSignal.hour == hour) {
|
||||||
|
return hourSignal.value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,31 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) 2010-2022 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.ecowatt.internal.restapi;
|
||||||
|
|
||||||
|
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||||
|
|
||||||
|
import com.google.gson.annotations.SerializedName;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The {@link EcowattHourSignal} class contains fields mapping the content of each value of JSON table "values" inside
|
||||||
|
* the API response
|
||||||
|
*
|
||||||
|
* @author Laurent Garnier - Initial contribution
|
||||||
|
*/
|
||||||
|
@NonNullByDefault
|
||||||
|
public class EcowattHourSignal {
|
||||||
|
@SerializedName("pas")
|
||||||
|
public int hour = -1;
|
||||||
|
@SerializedName("hvalue")
|
||||||
|
public int value;
|
||||||
|
}
|
@ -0,0 +1,141 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) 2010-2022 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.ecowatt.internal.restapi;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.time.Instant;
|
||||||
|
import java.time.OffsetDateTime;
|
||||||
|
import java.time.ZonedDateTime;
|
||||||
|
import java.util.concurrent.ExecutionException;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
import java.util.concurrent.TimeoutException;
|
||||||
|
|
||||||
|
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||||
|
import org.eclipse.jetty.client.HttpClient;
|
||||||
|
import org.eclipse.jetty.client.api.ContentResponse;
|
||||||
|
import org.eclipse.jetty.client.api.Request;
|
||||||
|
import org.eclipse.jetty.http.HttpHeader;
|
||||||
|
import org.eclipse.jetty.http.HttpMethod;
|
||||||
|
import org.eclipse.jetty.http.HttpStatus;
|
||||||
|
import org.openhab.binding.ecowatt.internal.exception.EcowattApiLimitException;
|
||||||
|
import org.openhab.core.auth.client.oauth2.AccessTokenResponse;
|
||||||
|
import org.openhab.core.auth.client.oauth2.OAuthClientService;
|
||||||
|
import org.openhab.core.auth.client.oauth2.OAuthException;
|
||||||
|
import org.openhab.core.auth.client.oauth2.OAuthFactory;
|
||||||
|
import org.openhab.core.auth.client.oauth2.OAuthResponseException;
|
||||||
|
import org.openhab.core.i18n.CommunicationException;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
import com.google.gson.Gson;
|
||||||
|
import com.google.gson.GsonBuilder;
|
||||||
|
import com.google.gson.JsonDeserializer;
|
||||||
|
import com.google.gson.JsonSyntaxException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The {@link EcowattRestApi} is responsible for handling all communication with the Ecowatt REST API
|
||||||
|
*
|
||||||
|
* @author Laurent Garnier - Initial contribution
|
||||||
|
*/
|
||||||
|
@NonNullByDefault
|
||||||
|
public class EcowattRestApi {
|
||||||
|
|
||||||
|
private static final String ECOWATT_API_TOKEN_URL = "https://digital.iservices.rte-france.com/token/oauth/";
|
||||||
|
private static final String ECOWATT_API_GET_SIGNALS_URL = "https://digital.iservices.rte-france.com/open_api/ecowatt/v4/signals";
|
||||||
|
|
||||||
|
private final Logger logger = LoggerFactory.getLogger(EcowattRestApi.class);
|
||||||
|
|
||||||
|
private final OAuthFactory oAuthFactory;
|
||||||
|
private final HttpClient httpClient;
|
||||||
|
private final Gson gson;
|
||||||
|
private OAuthClientService authService;
|
||||||
|
private String authServiceHandle;
|
||||||
|
|
||||||
|
public EcowattRestApi(OAuthFactory oAuthFactory, HttpClient httpClient, String authServiceHandle, String idClient,
|
||||||
|
String idSecret) {
|
||||||
|
this.oAuthFactory = oAuthFactory;
|
||||||
|
this.httpClient = httpClient;
|
||||||
|
GsonBuilder gsonBuilder = new GsonBuilder();
|
||||||
|
gson = gsonBuilder.registerTypeAdapter(ZonedDateTime.class,
|
||||||
|
(JsonDeserializer<ZonedDateTime>) (json, type, jsonDeserializationContext) -> OffsetDateTime
|
||||||
|
.parse(json.getAsJsonPrimitive().getAsString()).toZonedDateTime())
|
||||||
|
.create();
|
||||||
|
this.authService = oAuthFactory.createOAuthClientService(authServiceHandle, ECOWATT_API_TOKEN_URL, null,
|
||||||
|
idClient, idSecret, null, true);
|
||||||
|
this.authServiceHandle = authServiceHandle;
|
||||||
|
}
|
||||||
|
|
||||||
|
public EcowattApiResponse getSignals() throws CommunicationException, EcowattApiLimitException {
|
||||||
|
logger.debug("API request signals");
|
||||||
|
String token = authenticate().getAccessToken();
|
||||||
|
|
||||||
|
final Request request = httpClient.newRequest(ECOWATT_API_GET_SIGNALS_URL).method(HttpMethod.GET)
|
||||||
|
.header(HttpHeader.AUTHORIZATION, "Bearer " + token).timeout(10, TimeUnit.SECONDS);
|
||||||
|
|
||||||
|
ContentResponse response;
|
||||||
|
try {
|
||||||
|
response = request.send();
|
||||||
|
} catch (TimeoutException | ExecutionException e) {
|
||||||
|
throw new CommunicationException("@text/exception.api-request-failed", e);
|
||||||
|
} catch (InterruptedException e) {
|
||||||
|
Thread.currentThread().interrupt();
|
||||||
|
throw new CommunicationException("@text/exception.api-request-failed", e);
|
||||||
|
}
|
||||||
|
|
||||||
|
int statusCode = response.getStatus();
|
||||||
|
|
||||||
|
if (statusCode == HttpStatus.TOO_MANY_REQUESTS_429) {
|
||||||
|
int retryAfter = -1;
|
||||||
|
if (response.getHeaders().contains(HttpHeader.RETRY_AFTER)) {
|
||||||
|
try {
|
||||||
|
retryAfter = Integer.parseInt(response.getHeaders().get(HttpHeader.RETRY_AFTER));
|
||||||
|
} catch (NumberFormatException e) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
throw new EcowattApiLimitException(retryAfter, "@text/exception.api-limit-reached");
|
||||||
|
} else if (statusCode != HttpStatus.OK_200) {
|
||||||
|
throw new CommunicationException("@text/exception.api-request-failed-params", statusCode,
|
||||||
|
response.getContentAsString());
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
EcowattApiResponse deserializedResp = gson.fromJson(response.getContentAsString(),
|
||||||
|
EcowattApiResponse.class);
|
||||||
|
if (deserializedResp == null) {
|
||||||
|
throw new CommunicationException("@text/exception.empty-api-response");
|
||||||
|
}
|
||||||
|
return deserializedResp;
|
||||||
|
} catch (JsonSyntaxException e) {
|
||||||
|
throw new CommunicationException("@text/exception.parsing-api-response-failed", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private AccessTokenResponse authenticate() throws CommunicationException {
|
||||||
|
try {
|
||||||
|
AccessTokenResponse result = authService.getAccessTokenResponse();
|
||||||
|
if (result == null || result.isExpired(Instant.now(), 120)) {
|
||||||
|
logger.debug("Authentication required");
|
||||||
|
result = authService.getAccessTokenByClientCredentials(null);
|
||||||
|
}
|
||||||
|
logger.debug("Token {} of type {} created on {} expiring after {} seconds", result.getAccessToken(),
|
||||||
|
result.getTokenType(), result.getCreatedOn(), result.getExpiresIn());
|
||||||
|
return result;
|
||||||
|
} catch (OAuthException | IOException | OAuthResponseException e) {
|
||||||
|
throw new CommunicationException("@text/exception.authentication-failed", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void dispose() {
|
||||||
|
oAuthFactory.ungetOAuthService(authServiceHandle);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,10 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<binding:binding id="ecowatt" 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>Ecowatt Binding</name>
|
||||||
|
<description>This binding uses the Ecowatt API to expose clear signals to adopt the right gestures and to ensure a good
|
||||||
|
supply of electricity for all in Frances.</description>
|
||||||
|
|
||||||
|
</binding:binding>
|
@ -0,0 +1,43 @@
|
|||||||
|
# binding
|
||||||
|
|
||||||
|
binding.ecowatt.name = Ecowatt Binding
|
||||||
|
binding.ecowatt.description = This binding uses the Ecowatt API to expose clear signals to adopt the right gestures and to ensure a good supply of electricity for all in Frances.
|
||||||
|
|
||||||
|
# thing types
|
||||||
|
|
||||||
|
thing-type.ecowatt.signals.label = Electricity Forecast
|
||||||
|
thing-type.ecowatt.signals.description = The French electricity consumption forecasts
|
||||||
|
thing-type.ecowatt.signals.channel.currentHourSignal.label = Current Hour Signal
|
||||||
|
thing-type.ecowatt.signals.channel.currentHourSignal.description = The signal relating to the forecast consumption level for the current hour. Values are 1 for normal consumption, 2 for strained electrical system and 3 for very strained electrical system.
|
||||||
|
thing-type.ecowatt.signals.channel.todaySignal.label = Today Signal
|
||||||
|
thing-type.ecowatt.signals.channel.todaySignal.description = The signal relating to the forecast consumption level for today. Values are 1 for normal consumption, 2 for strained electrical system and 3 for very strained electrical system.
|
||||||
|
thing-type.ecowatt.signals.channel.tomorrowSignal.label = Tomorrow Signal
|
||||||
|
thing-type.ecowatt.signals.channel.tomorrowSignal.description = The signal relating to the forecast consumption level for tomorrow. Values are 1 for normal consumption, 2 for strained electrical system and 3 for very strained electrical system.
|
||||||
|
|
||||||
|
# thing types config
|
||||||
|
|
||||||
|
thing-type.config.ecowatt.signals.idClient.label = ID Client
|
||||||
|
thing-type.config.ecowatt.signals.idClient.description = ID client provided with the application you created in the RTE portal.
|
||||||
|
thing-type.config.ecowatt.signals.idSecret.label = ID Secret
|
||||||
|
thing-type.config.ecowatt.signals.idSecret.description = ID secret provided with the application you created in the RTE portal.
|
||||||
|
|
||||||
|
# channel types
|
||||||
|
|
||||||
|
channel-type.ecowatt.signal.label = Consumption Signal
|
||||||
|
channel-type.ecowatt.signal.description = The signal relating to the forecast consumption level. Values are 1 for normal consumption, 2 for strained electrical system and 3 for very strained electrical system.
|
||||||
|
channel-type.ecowatt.signal.state.option.1 = Green (normal consumption)
|
||||||
|
channel-type.ecowatt.signal.state.option.2 = Orange (strained electrical system)
|
||||||
|
channel-type.ecowatt.signal.state.option.3 = Red (very strained electrical system)
|
||||||
|
|
||||||
|
# thing status descriptions
|
||||||
|
|
||||||
|
offline.config-error-unset-parameters = Id client and/or id secret configuration parameters is not set
|
||||||
|
|
||||||
|
# exceptions
|
||||||
|
|
||||||
|
exception.authentication-failed = Authentication to the API failed
|
||||||
|
exception.api-request-failed = REST API request failed
|
||||||
|
exception.api-request-failed-params = REST API request failed: statusCode={0}, message={1}
|
||||||
|
exception.empty-api-response = API response is empty
|
||||||
|
exception.parsing-api-response-failed = Parsing of the API response failed
|
||||||
|
exception.api-limit-reached = API limit reached; will retry later
|
@ -0,0 +1,55 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<thing:thing-descriptions bindingId="ecowatt"
|
||||||
|
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">
|
||||||
|
|
||||||
|
<thing-type id="signals">
|
||||||
|
<label>Electricity Forecast</label>
|
||||||
|
<description>The French electricity consumption forecasts</description>
|
||||||
|
|
||||||
|
<channels>
|
||||||
|
<channel id="todaySignal" typeId="signal">
|
||||||
|
<label>Today Signal</label>
|
||||||
|
<description>The signal relating to the forecast consumption level for today. Values are 1 for normal consumption, 2
|
||||||
|
for strained electrical system and 3 for very strained electrical system.</description>
|
||||||
|
</channel>
|
||||||
|
<channel id="tomorrowSignal" typeId="signal">
|
||||||
|
<label>Tomorrow Signal</label>
|
||||||
|
<description>The signal relating to the forecast consumption level for tomorrow. Values are 1 for normal
|
||||||
|
consumption, 2 for strained electrical system and 3 for very strained electrical system.</description>
|
||||||
|
</channel>
|
||||||
|
<channel id="currentHourSignal" typeId="signal">
|
||||||
|
<label>Current Hour Signal</label>
|
||||||
|
<description>The signal relating to the forecast consumption level for the current hour. Values are 1 for normal
|
||||||
|
consumption, 2 for strained electrical system and 3 for very strained electrical system.</description>
|
||||||
|
</channel>
|
||||||
|
</channels>
|
||||||
|
|
||||||
|
<config-description>
|
||||||
|
<parameter name="idClient" type="text" required="true">
|
||||||
|
<label>ID Client</label>
|
||||||
|
<description>ID client provided with the application you created in the RTE portal.</description>
|
||||||
|
</parameter>
|
||||||
|
<parameter name="idSecret" type="text" required="true">
|
||||||
|
<context>password</context>
|
||||||
|
<label>ID Secret</label>
|
||||||
|
<description>ID secret provided with the application you created in the RTE portal.</description>
|
||||||
|
</parameter>
|
||||||
|
</config-description>
|
||||||
|
</thing-type>
|
||||||
|
|
||||||
|
<channel-type id="signal">
|
||||||
|
<item-type>Number</item-type>
|
||||||
|
<label>Consumption Signal</label>
|
||||||
|
<description>The signal relating to the forecast consumption level. Values are 1 for normal consumption, 2 for
|
||||||
|
strained electrical system and 3 for very strained electrical system.</description>
|
||||||
|
<state readOnly="true">
|
||||||
|
<options>
|
||||||
|
<option value="1">Green (normal consumption)</option>
|
||||||
|
<option value="2">Orange (strained electrical system)</option>
|
||||||
|
<option value="3">Red (very strained electrical system)</option>
|
||||||
|
</options>
|
||||||
|
</state>
|
||||||
|
</channel-type>
|
||||||
|
</thing:thing-descriptions>
|
@ -112,6 +112,7 @@
|
|||||||
<module>org.openhab.binding.easee</module>
|
<module>org.openhab.binding.easee</module>
|
||||||
<module>org.openhab.binding.ecobee</module>
|
<module>org.openhab.binding.ecobee</module>
|
||||||
<module>org.openhab.binding.ecotouch</module>
|
<module>org.openhab.binding.ecotouch</module>
|
||||||
|
<module>org.openhab.binding.ecowatt</module>
|
||||||
<module>org.openhab.binding.ekey</module>
|
<module>org.openhab.binding.ekey</module>
|
||||||
<module>org.openhab.binding.electroluxair</module>
|
<module>org.openhab.binding.electroluxair</module>
|
||||||
<module>org.openhab.binding.elerotransmitterstick</module>
|
<module>org.openhab.binding.elerotransmitterstick</module>
|
||||||
|
Loading…
Reference in New Issue
Block a user