From 9b55bfba58cf06982c306e3f565286d3d9849101 Mon Sep 17 00:00:00 2001 From: lolodomo Date: Sun, 26 Nov 2023 23:34:45 +0100 Subject: [PATCH 1/7] [ecowatt] Add support for API version 5 (#15953) A new thing parameter to define the API version has been added. Its default value is 4 for backward compatibility even if this version is now deprecated. The documentation explains what to do to switch from a version to another. Signed-off-by: Laurent Garnier --- bundles/org.openhab.binding.ecowatt/README.md | 22 +++++++++++++------ .../configuration/EcowattConfiguration.java | 2 ++ .../internal/handler/EcowattHandler.java | 6 +++-- .../internal/restapi/EcowattDaySignals.java | 2 +- .../internal/restapi/EcowattRestApi.java | 11 ++++++---- .../resources/OH-INF/i18n/ecowatt.properties | 9 ++++++-- .../resources/OH-INF/thing/thing-types.xml | 20 +++++++++++++---- .../internal/EcowattApiResponseTest.java | 9 ++++++++ .../src/test/resources/ApiResponse.json | 2 +- 9 files changed, 62 insertions(+), 21 deletions(-) diff --git a/bundles/org.openhab.binding.ecowatt/README.md b/bundles/org.openhab.binding.ecowatt/README.md index 7a5b3d79356cb..23a41ce69480e 100644 --- a/bundles/org.openhab.binding.ecowatt/README.md +++ b/bundles/org.openhab.binding.ecowatt/README.md @@ -17,11 +17,16 @@ You have to add the thing manually. 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. +1. Open this [page](https://data.rte-france.com/catalog/-/api/consumption/Ecowatt/v5.0), find the "Ecowatt" tile and click on the "Abonnez-vous à l'API" button. 1. Create an account by following the instructions (you will receive an email to validate your new account). 1. 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. 1. 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. +Note that you are subscribed to a particular version of the API. +When a new version of the API is released, you will have to subscribe to this new version and create a new application. +You will then get new information "ID client" and "ID Secret" and you will have to update your thing configuration in openHAB. +After changing version, you will have to wait for your authentication token to be renewed (max 2 hours) to get a successful response from the API. + ## Binding Configuration There are no overall binding configuration settings that need to be set. @@ -29,10 +34,13 @@ 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 | +| Name | Type | Description | Required | Default | +|------------|---------|---------------------------------------------------------------------------|----------|---------| +| apiVersion | integer | The version of the Ecowatt tile to which you subscribed in the RTE portal | no | 4 | +| 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 | | + +Take care to select the API version corresponding to the one to which you subscribed in the RTE portal. ## Channels @@ -42,14 +50,14 @@ All channels are read-only. |-------------------|--------|------------------------------------------------------------------| | todaySignal | Number | The signal relating to the forecast consumption level for today. Values are 1 for normal consumption (green), 2 for strained electrical system (orange) and 3 for very strained electrical system (red). | | tomorrowSignal | Number | The signal relating to the forecast consumption level for tomorrow. Values are 1 for normal consumption (green), 2 for strained electrical system (orange) and 3 for very strained electrical system (red). | -| currentHourSignal | Number | The signal relating to the forecast consumption level for the current hour. Values are 1 for normal consumption (green), 2 for strained electrical system (orange) and 3 for very strained electrical system (red). | +| currentHourSignal | Number | The signal relating to the forecast consumption level for the current hour. Values are 0 for normal consumption (green) and carbon-free production, 1 for normal consumption (green), 2 for strained electrical system (orange) and 3 for very strained electrical system (red). | ## Full Example example.things: ```java -Thing ecowatt:signals:signals "Ecowatt Signals" [ idClient="xxxxx", idSecret="yyyyy"] +Thing ecowatt:signals:signals "Ecowatt Signals" [ apiVersion=4, idClient="xxxxx", idSecret="yyyyy"] ``` example.items: diff --git a/bundles/org.openhab.binding.ecowatt/src/main/java/org/openhab/binding/ecowatt/internal/configuration/EcowattConfiguration.java b/bundles/org.openhab.binding.ecowatt/src/main/java/org/openhab/binding/ecowatt/internal/configuration/EcowattConfiguration.java index 61e037919dd12..f9b8e3bbff802 100644 --- a/bundles/org.openhab.binding.ecowatt/src/main/java/org/openhab/binding/ecowatt/internal/configuration/EcowattConfiguration.java +++ b/bundles/org.openhab.binding.ecowatt/src/main/java/org/openhab/binding/ecowatt/internal/configuration/EcowattConfiguration.java @@ -18,10 +18,12 @@ * The {@link EcowattConfiguration} class contains fields mapping thing configuration parameters. * * @author Laurent Garnier - Initial contribution + * @author Laurent Garnier - New parameter apiVersion */ @NonNullByDefault public class EcowattConfiguration { + public int apiVersion = 4; public String idClient = ""; public String idSecret = ""; } diff --git a/bundles/org.openhab.binding.ecowatt/src/main/java/org/openhab/binding/ecowatt/internal/handler/EcowattHandler.java b/bundles/org.openhab.binding.ecowatt/src/main/java/org/openhab/binding/ecowatt/internal/handler/EcowattHandler.java index 365d65c67e2b0..912e93a9e2b4d 100644 --- a/bundles/org.openhab.binding.ecowatt/src/main/java/org/openhab/binding/ecowatt/internal/handler/EcowattHandler.java +++ b/bundles/org.openhab.binding.ecowatt/src/main/java/org/openhab/binding/ecowatt/internal/handler/EcowattHandler.java @@ -54,6 +54,7 @@ * The {@link EcowattHandler} is responsible for updating the state of the channels * * @author Laurent Garnier - Initial contribution + * @author Laurent Garnier - Add support for API version 5 */ @NonNullByDefault public class EcowattHandler extends BaseThingHandler { @@ -100,7 +101,8 @@ public void initialize() { updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, "@text/offline.config-error-unset-parameters"); } else { - api = new EcowattRestApi(oAuthFactory, httpClient, thing.getUID().getAsString(), idClient, idSecret); + api = new EcowattRestApi(oAuthFactory, httpClient, thing.getUID().getAsString(), idClient, idSecret, + config.apiVersion); updateStatus(ThingStatus.UNKNOWN); scheduleNextUpdate(0, true); } @@ -264,7 +266,7 @@ public static State getHourSignalState(@Nullable EcowattApiResponse response, Zo int hour = dateTime.withZoneSameInstant(day.getZone()).getHour(); int value = signals.getHourSignal(hour); LoggerFactory.getLogger(EcowattHandler.class).debug("hour {} value {}", hour, value); - if (value >= 1 && value <= 3) { + if (value >= 0 && value <= 3) { return new DecimalType(value); } } diff --git a/bundles/org.openhab.binding.ecowatt/src/main/java/org/openhab/binding/ecowatt/internal/restapi/EcowattDaySignals.java b/bundles/org.openhab.binding.ecowatt/src/main/java/org/openhab/binding/ecowatt/internal/restapi/EcowattDaySignals.java index 10dbf317ca6f1..e27ed283f7b80 100644 --- a/bundles/org.openhab.binding.ecowatt/src/main/java/org/openhab/binding/ecowatt/internal/restapi/EcowattDaySignals.java +++ b/bundles/org.openhab.binding.ecowatt/src/main/java/org/openhab/binding/ecowatt/internal/restapi/EcowattDaySignals.java @@ -58,6 +58,6 @@ public int getHourSignal(int hour) { } } } - return 0; + return -1; } } diff --git a/bundles/org.openhab.binding.ecowatt/src/main/java/org/openhab/binding/ecowatt/internal/restapi/EcowattRestApi.java b/bundles/org.openhab.binding.ecowatt/src/main/java/org/openhab/binding/ecowatt/internal/restapi/EcowattRestApi.java index 7da333721b3cf..37a68fabfd448 100644 --- a/bundles/org.openhab.binding.ecowatt/src/main/java/org/openhab/binding/ecowatt/internal/restapi/EcowattRestApi.java +++ b/bundles/org.openhab.binding.ecowatt/src/main/java/org/openhab/binding/ecowatt/internal/restapi/EcowattRestApi.java @@ -46,23 +46,25 @@ * The {@link EcowattRestApi} is responsible for handling all communication with the Ecowatt REST API * * @author Laurent Garnier - Initial contribution + * @author Laurent Garnier - Add support for different API versions */ @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 static final String ECOWATT_API_GET_SIGNALS_URL = "https://digital.iservices.rte-france.com/open_api/ecowatt/v%d/signals"; private final Logger logger = LoggerFactory.getLogger(EcowattRestApi.class); private final OAuthFactory oAuthFactory; private final HttpClient httpClient; private final Gson gson; + private final String apiUrl; private OAuthClientService authService; private String authServiceHandle; public EcowattRestApi(OAuthFactory oAuthFactory, HttpClient httpClient, String authServiceHandle, String idClient, - String idSecret) { + String idSecret, int apiVersion) { this.oAuthFactory = oAuthFactory; this.httpClient = httpClient; GsonBuilder gsonBuilder = new GsonBuilder(); @@ -73,13 +75,14 @@ public EcowattRestApi(OAuthFactory oAuthFactory, HttpClient httpClient, String a this.authService = oAuthFactory.createOAuthClientService(authServiceHandle, ECOWATT_API_TOKEN_URL, null, idClient, idSecret, null, true); this.authServiceHandle = authServiceHandle; + this.apiUrl = ECOWATT_API_GET_SIGNALS_URL.formatted(apiVersion); } public EcowattApiResponse getSignals() throws CommunicationException, EcowattApiLimitException { - logger.debug("API request signals"); + logger.debug("API request {}", apiUrl); String token = authenticate().getAccessToken(); - final Request request = httpClient.newRequest(ECOWATT_API_GET_SIGNALS_URL).method(HttpMethod.GET) + final Request request = httpClient.newRequest(apiUrl).method(HttpMethod.GET) .header(HttpHeader.AUTHORIZATION, "Bearer " + token).timeout(10, TimeUnit.SECONDS); ContentResponse response; diff --git a/bundles/org.openhab.binding.ecowatt/src/main/resources/OH-INF/i18n/ecowatt.properties b/bundles/org.openhab.binding.ecowatt/src/main/resources/OH-INF/i18n/ecowatt.properties index 690a286019837..832341843ce3b 100644 --- a/bundles/org.openhab.binding.ecowatt/src/main/resources/OH-INF/i18n/ecowatt.properties +++ b/bundles/org.openhab.binding.ecowatt/src/main/resources/OH-INF/i18n/ecowatt.properties @@ -8,7 +8,7 @@ addon.ecowatt.description = This binding uses the Ecowatt API to expose clear si 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 (green), 2 for strained electrical system (orange) and 3 for very strained electrical system (red). +thing-type.ecowatt.signals.channel.currentHourSignal.description = The signal relating to the forecast consumption level for the current hour. Values are 0 for normal consumption (green) and carbon-free production, 1 for normal consumption (green), 2 for strained electrical system (orange) and 3 for very strained electrical system (red). thing-type.ecowatt.signals.channel.inThreeDaysSignal.label = In Three Days Signal thing-type.ecowatt.signals.channel.inThreeDaysSignal.description = The signal relating to the forecast consumption level in three days. Values are 1 for normal consumption (green), 2 for strained electrical system (orange) and 3 for very strained electrical system (red). thing-type.ecowatt.signals.channel.inTwoDaysSignal.label = In Two Days Signal @@ -20,6 +20,10 @@ thing-type.ecowatt.signals.channel.tomorrowSignal.description = The signal relat # thing types config +thing-type.config.ecowatt.signals.apiVersion.label = API Version +thing-type.config.ecowatt.signals.apiVersion.description = The version of the Ecowatt tile to which you subscribed in the RTE portal. +thing-type.config.ecowatt.signals.apiVersion.option.4 = V4.0 (deprecated) +thing-type.config.ecowatt.signals.apiVersion.option.5 = V5.0 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 @@ -28,7 +32,8 @@ thing-type.config.ecowatt.signals.idSecret.description = ID secret provided with # 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 (green), 2 for strained electrical system (orange) and 3 for very strained electrical system (red). +channel-type.ecowatt.signal.description = The signal relating to the forecast consumption level. Values are 0 for normal consumption (green) and carbon-free production, 1 for normal consumption (green), 2 for strained electrical system (orange) and 3 for very strained electrical system (red). +channel-type.ecowatt.signal.state.option.0 = Green + carbon-free production channel-type.ecowatt.signal.state.option.1 = Green channel-type.ecowatt.signal.state.option.2 = Orange channel-type.ecowatt.signal.state.option.3 = Red diff --git a/bundles/org.openhab.binding.ecowatt/src/main/resources/OH-INF/thing/thing-types.xml b/bundles/org.openhab.binding.ecowatt/src/main/resources/OH-INF/thing/thing-types.xml index b1d7b43504dbc..1dbd4ac7e614a 100644 --- a/bundles/org.openhab.binding.ecowatt/src/main/resources/OH-INF/thing/thing-types.xml +++ b/bundles/org.openhab.binding.ecowatt/src/main/resources/OH-INF/thing/thing-types.xml @@ -34,12 +34,22 @@ - The signal relating to the forecast consumption level for the current hour. Values are 1 for normal - consumption (green), 2 for strained electrical system (orange) and 3 for very strained electrical system (red). + The signal relating to the forecast consumption level for the current hour. Values are 0 for normal + consumption (green) and carbon-free production, 1 for normal consumption (green), 2 for strained electrical system + (orange) and 3 for very strained electrical system (red). + + + The version of the Ecowatt tile to which you subscribed in the RTE portal. + + + + + 4 + ID client provided with the application you created in the RTE portal. @@ -55,10 +65,12 @@ Number - The signal relating to the forecast consumption level. Values are 1 for normal consumption (green), 2 for - strained electrical system (orange) and 3 for very strained electrical system (red). + The signal relating to the forecast consumption level. Values are 0 for normal consumption (green) and + carbon-free production, 1 for normal consumption (green), 2 for strained electrical system (orange) and 3 for very + strained electrical system (red). + diff --git a/bundles/org.openhab.binding.ecowatt/src/test/java/org/openhab/binding/ecowatt/internal/EcowattApiResponseTest.java b/bundles/org.openhab.binding.ecowatt/src/test/java/org/openhab/binding/ecowatt/internal/EcowattApiResponseTest.java index f2dbba608b041..b3b614adad092 100644 --- a/bundles/org.openhab.binding.ecowatt/src/test/java/org/openhab/binding/ecowatt/internal/EcowattApiResponseTest.java +++ b/bundles/org.openhab.binding.ecowatt/src/test/java/org/openhab/binding/ecowatt/internal/EcowattApiResponseTest.java @@ -42,6 +42,7 @@ @NonNullByDefault public class EcowattApiResponseTest { + private static final DecimalType STATE_ZERO = new DecimalType(0); private static final DecimalType STATE_ONE = new DecimalType(1); private static final DecimalType STATE_TWO = new DecimalType(2); private static final DecimalType STATE_THREE = new DecimalType(3); @@ -104,6 +105,10 @@ public void getHourSignalStateWithSameOffset() { State expectedState; for (int h = 0; h < 24; h++) { switch (h) { + case 2: + case 3: + expectedState = STATE_ZERO; + break; case 7: case 11: case 19: @@ -155,6 +160,10 @@ public void getHourSignalStateWithOtherOffset() { case 2: expectedState = UnDefType.UNDEF; break; + case 5: + case 6: + expectedState = STATE_ZERO; + break; case 10: case 14: case 22: diff --git a/bundles/org.openhab.binding.ecowatt/src/test/resources/ApiResponse.json b/bundles/org.openhab.binding.ecowatt/src/test/resources/ApiResponse.json index c5ac41b743499..a7b65bc5fd787 100644 --- a/bundles/org.openhab.binding.ecowatt/src/test/resources/ApiResponse.json +++ b/bundles/org.openhab.binding.ecowatt/src/test/resources/ApiResponse.json @@ -1,4 +1,4 @@ {"signals":[{"GenerationFichier":"2022-09-18T22:00:00+02:00","jour":"2022-09-22T00:00:00+02:00","dvalue":1,"message":"Notre consommation est raisonnable.","values":[{"pas":0,"hvalue":1},{"pas":1,"hvalue":1},{"pas":2,"hvalue":1},{"pas":3,"hvalue":1},{"pas":4,"hvalue":1},{"pas":5,"hvalue":1},{"pas":6,"hvalue":1},{"pas":7,"hvalue":1},{"pas":8,"hvalue":1},{"pas":9,"hvalue":1},{"pas":10,"hvalue":1},{"pas":11,"hvalue":1},{"pas":12,"hvalue":1},{"pas":13,"hvalue":1},{"pas":14,"hvalue":1},{"pas":15,"hvalue":1},{"pas":16,"hvalue":1},{"pas":17,"hvalue":1},{"pas":18,"hvalue":1},{"pas":19,"hvalue":1},{"pas":20,"hvalue":1},{"pas":21,"hvalue":1},{"pas":22,"hvalue":1}]}, {"GenerationFichier":"2022-09-18T22:00:00+02:00","jour":"2022-09-20T00:00:00+02:00","dvalue":2,"message":"Notre consommation est raisonnable.","values":[{"pas":0,"hvalue":1},{"pas":1,"hvalue":1},{"pas":2,"hvalue":1},{"pas":3,"hvalue":1},{"pas":4,"hvalue":1},{"pas":5,"hvalue":1},{"pas":6,"hvalue":1},{"pas":7,"hvalue":1},{"pas":8,"hvalue":1},{"pas":9,"hvalue":1},{"pas":10,"hvalue":1},{"pas":11,"hvalue":1},{"pas":12,"hvalue":1},{"pas":13,"hvalue":1},{"pas":14,"hvalue":1},{"pas":15,"hvalue":1},{"pas":16,"hvalue":1},{"pas":17,"hvalue":1},{"pas":18,"hvalue":1},{"pas":19,"hvalue":1},{"pas":20,"hvalue":2},{"pas":21,"hvalue":1},{"pas":22,"hvalue":1},{"pas":23,"hvalue":1}]}, {"GenerationFichier":"2022-09-18T22:00:00+02:00","jour":"2022-09-21T00:00:00+02:00","dvalue":1,"message":"Notre consommation est raisonnable.","values":[{"pas":0,"hvalue":1},{"pas":1,"hvalue":1},{"pas":2,"hvalue":1},{"pas":3,"hvalue":1},{"pas":4,"hvalue":1},{"pas":5,"hvalue":1},{"pas":6,"hvalue":1},{"pas":7,"hvalue":1},{"pas":8,"hvalue":1},{"pas":9,"hvalue":1},{"pas":10,"hvalue":1},{"pas":11,"hvalue":1},{"pas":12,"hvalue":1},{"pas":13,"hvalue":1},{"pas":14,"hvalue":1},{"pas":15,"hvalue":1},{"pas":16,"hvalue":1},{"pas":17,"hvalue":1},{"pas":18,"hvalue":1},{"pas":19,"hvalue":1},{"pas":20,"hvalue":1},{"pas":21,"hvalue":1},{"pas":22,"hvalue":1},{"pas":23,"hvalue":1}]}, -{"GenerationFichier":"2022-09-18T22:00:00+02:00","jour":"2022-09-19T00:00:00+02:00","dvalue":3,"message":"Notre consommation est raisonnable.","values":[{"pas":0,"hvalue":1},{"pas":1,"hvalue":1},{"pas":2,"hvalue":1},{"pas":3,"hvalue":1},{"pas":4,"hvalue":1},{"pas":5,"hvalue":1},{"pas":6,"hvalue":1},{"pas":7,"hvalue":2},{"pas":8,"hvalue":3},{"pas":9,"hvalue":3},{"pas":10,"hvalue":3},{"pas":11,"hvalue":2},{"pas":12,"hvalue":1},{"pas":13,"hvalue":1},{"pas":14,"hvalue":1},{"pas":15,"hvalue":1},{"pas":16,"hvalue":1},{"pas":17,"hvalue":1},{"pas":18,"hvalue":1},{"pas":19,"hvalue":2},{"pas":20,"hvalue":3},{"pas":21,"hvalue":2},{"pas":22,"hvalue":1},{"pas":23,"hvalue":1}]}]} +{"GenerationFichier":"2022-09-18T22:00:00+02:00","jour":"2022-09-19T00:00:00+02:00","dvalue":3,"message":"Notre consommation est raisonnable.","values":[{"pas":0,"hvalue":1},{"pas":1,"hvalue":1},{"pas":2,"hvalue":0},{"pas":3,"hvalue":0},{"pas":4,"hvalue":1},{"pas":5,"hvalue":1},{"pas":6,"hvalue":1},{"pas":7,"hvalue":2},{"pas":8,"hvalue":3},{"pas":9,"hvalue":3},{"pas":10,"hvalue":3},{"pas":11,"hvalue":2},{"pas":12,"hvalue":1},{"pas":13,"hvalue":1},{"pas":14,"hvalue":1},{"pas":15,"hvalue":1},{"pas":16,"hvalue":1},{"pas":17,"hvalue":1},{"pas":18,"hvalue":1},{"pas":19,"hvalue":2},{"pas":20,"hvalue":3},{"pas":21,"hvalue":2},{"pas":22,"hvalue":1},{"pas":23,"hvalue":1}]}]} From 6e58b1ed22bc89751cda47da5f565c3df3843873 Mon Sep 17 00:00:00 2001 From: Cody Cutrer Date: Mon, 27 Nov 2023 11:23:32 -0700 Subject: [PATCH 2/7] Claim ownership of mqtt, mqtt.homie, and mqtt.homeassistant bindings (#15930) Signed-off-by: Cody Cutrer --- CODEOWNERS | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/CODEOWNERS b/CODEOWNERS index 17363df8e07e8..4a68d285a1788 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -217,11 +217,11 @@ /bundles/org.openhab.binding.modbus.sunspec/ @mrbig /bundles/org.openhab.binding.monopriceaudio/ @mlobstein /bundles/org.openhab.binding.mpd/ @stefanroellin -/bundles/org.openhab.binding.mqtt/ @openhab/add-ons-maintainers +/bundles/org.openhab.binding.mqtt/ @ccutrer /bundles/org.openhab.binding.mqtt.espmilighthub/ @Skinah -/bundles/org.openhab.binding.mqtt.generic/ @openhab/add-ons-maintainers -/bundles/org.openhab.binding.mqtt.homeassistant/ @antroids -/bundles/org.openhab.binding.mqtt.homie/ @openhab/add-ons-maintainers +/bundles/org.openhab.binding.mqtt.generic/ @ccutrer +/bundles/org.openhab.binding.mqtt.homeassistant/ @antroids @ccutrer +/bundles/org.openhab.binding.mqtt.homie/ @ccutrer /bundles/org.openhab.binding.mqtt.ruuvigateway/ @ssalonen /bundles/org.openhab.binding.mycroft/ @dalgwen /bundles/org.openhab.binding.mybmw/ @weymann @ntruchsess From 4d13a6d20e67d02ba82da5850670dbffaa2874f5 Mon Sep 17 00:00:00 2001 From: Marcel Date: Tue, 28 Nov 2023 00:10:27 +0100 Subject: [PATCH 3/7] [miio] Fix cleaning record for newer vacuums (#15944) * [miio] Fix cleaning record for newer vacuums Fixes Roborock Q7Max Historical Cleaning record missing closing #15490 --------- Signed-off-by: Marcel Verpaalen --- .../internal/handler/MiIoVacuumHandler.java | 13 ++++++++---- .../miio/internal/robot/HistoryRecordDTO.java | 21 +++++++++++++++++-- 2 files changed, 28 insertions(+), 6 deletions(-) diff --git a/bundles/org.openhab.binding.miio/src/main/java/org/openhab/binding/miio/internal/handler/MiIoVacuumHandler.java b/bundles/org.openhab.binding.miio/src/main/java/org/openhab/binding/miio/internal/handler/MiIoVacuumHandler.java index a36bcf4630441..df76917b550c8 100644 --- a/bundles/org.openhab.binding.miio/src/main/java/org/openhab/binding/miio/internal/handler/MiIoVacuumHandler.java +++ b/bundles/org.openhab.binding.miio/src/main/java/org/openhab/binding/miio/internal/handler/MiIoVacuumHandler.java @@ -539,7 +539,7 @@ private void updateHistoryRecordLegacy(JsonArray historyData) { } private void updateHistoryRecord(HistoryRecordDTO historyRecordDTO) { - JsonObject historyRecord = new JsonObject(); + JsonObject historyRecord = GSON.toJsonTree(historyRecordDTO).getAsJsonObject(); if (historyRecordDTO.getStart() != null) { historyRecord.addProperty("start", historyRecordDTO.getStart().split("\\+")[0]); updateState(CHANNEL_HISTORY_START_TIME, new DateTimeType(historyRecordDTO.getStart().split("\\+")[0])); @@ -703,6 +703,13 @@ public void onMessageReceived(MiIoSendCommand response) { if (response.getResult().isJsonArray() && response.getResult().getAsJsonArray().size() > 0 && response.getResult().getAsJsonArray().get(0).isJsonArray()) { updateHistoryRecordLegacy(response.getResult().getAsJsonArray().get(0).getAsJsonArray()); + } else if (response.getResult().isJsonArray() && response.getResult().getAsJsonArray().size() > 0 + && response.getResult().getAsJsonArray().get(0).isJsonObject()) { + final HistoryRecordDTO historyRecordDTO = GSON.fromJson( + response.getResult().getAsJsonArray().get(0).getAsJsonObject(), HistoryRecordDTO.class); + if (historyRecordDTO != null) { + updateHistoryRecord(historyRecordDTO); + } } else if (response.getResult().isJsonObject()) { final HistoryRecordDTO historyRecordDTO = GSON.fromJson(response.getResult().getAsJsonObject(), HistoryRecordDTO.class); @@ -710,7 +717,7 @@ public void onMessageReceived(MiIoSendCommand response) { updateHistoryRecord(historyRecordDTO); } } else { - logger.debug("Could not extract cleaning history record from: {}", response); + logger.debug("Could not extract cleaning history record from: {}", response.getResult()); } break; case GET_MAP: @@ -734,11 +741,9 @@ public void onMessageReceived(MiIoSendCommand response) { case GET_FW_FEATURES: case GET_CUSTOMIZED_CLEAN_MODE: case GET_MULTI_MAP_LIST: - case SET_COLLECT_DUST: case SET_CLEAN_MOP_START: case SET_CLEAN_MOP_STOP: - for (RobotCababilities cmd : FEATURES_CHANNELS) { if (response.getCommand().getCommand().contentEquals(cmd.getCommand())) { updateState(cmd.getChannel(), new StringType(response.getResult().toString())); diff --git a/bundles/org.openhab.binding.miio/src/main/java/org/openhab/binding/miio/internal/robot/HistoryRecordDTO.java b/bundles/org.openhab.binding.miio/src/main/java/org/openhab/binding/miio/internal/robot/HistoryRecordDTO.java index c30b044536b68..ca21f465de5a0 100644 --- a/bundles/org.openhab.binding.miio/src/main/java/org/openhab/binding/miio/internal/robot/HistoryRecordDTO.java +++ b/bundles/org.openhab.binding.miio/src/main/java/org/openhab/binding/miio/internal/robot/HistoryRecordDTO.java @@ -27,6 +27,9 @@ public class HistoryRecordDTO { @SerializedName("start") @Expose private String start; + @SerializedName("begin") + @Expose + private String begin; @SerializedName("end") @Expose private String end; @@ -45,6 +48,9 @@ public class HistoryRecordDTO { @SerializedName("finished") @Expose private Integer finished; + @SerializedName("complete") + @Expose + private Integer complete; @SerializedName("start_type") @Expose private Integer startType; @@ -57,9 +63,12 @@ public class HistoryRecordDTO { @SerializedName("dust_collection_status") @Expose private Integer dustCollectionStatus; + @SerializedName("map_flag") + @Expose + private Integer mapFlag; public final String getStart() { - return start; + return start != null ? start : begin; } public final void setStart(String start) { @@ -107,7 +116,7 @@ public final void setError(Integer error) { } public final Integer getFinished() { - return finished; + return finished != null ? finished : complete; } public final void setFinished(Integer finished) { @@ -145,4 +154,12 @@ public final Integer getDustCollectionStatus() { public final void setDustCollectionStatus(Integer dustCollectionStatus) { this.dustCollectionStatus = dustCollectionStatus; } + + public final Integer getMapFlag() { + return mapFlag; + } + + public final void setMapFlag(Integer mapFlag) { + this.mapFlag = mapFlag; + } } From f4aa1c6d591230c524de6ac639734ea936196b96 Mon Sep 17 00:00:00 2001 From: Konstantin Polihronov Date: Tue, 28 Nov 2023 01:29:16 +0200 Subject: [PATCH 4/7] [solax] Implementation of refresh command and improved thread safety (#15958) * Implementation of refresh command and better multi-thread handling --------- Signed-off-by: Konstantin Polihronov --- .../solax/internal/SolaxBindingConstants.java | 2 +- .../internal/SolaxLocalAccessHandler.java | 40 +++++++++++++------ 2 files changed, 28 insertions(+), 14 deletions(-) diff --git a/bundles/org.openhab.binding.solax/src/main/java/org/openhab/binding/solax/internal/SolaxBindingConstants.java b/bundles/org.openhab.binding.solax/src/main/java/org/openhab/binding/solax/internal/SolaxBindingConstants.java index 52c871747c288..f527511d98227 100644 --- a/bundles/org.openhab.binding.solax/src/main/java/org/openhab/binding/solax/internal/SolaxBindingConstants.java +++ b/bundles/org.openhab.binding.solax/src/main/java/org/openhab/binding/solax/internal/SolaxBindingConstants.java @@ -26,7 +26,7 @@ @NonNullByDefault public class SolaxBindingConstants { - private static final String BINDING_ID = "solax"; + protected static final String BINDING_ID = "solax"; private static final String THING_LOCAL_CONNECT_INVERTER_ID = "local-connect-inverter"; // List of all Thing Type UIDs diff --git a/bundles/org.openhab.binding.solax/src/main/java/org/openhab/binding/solax/internal/SolaxLocalAccessHandler.java b/bundles/org.openhab.binding.solax/src/main/java/org/openhab/binding/solax/internal/SolaxLocalAccessHandler.java index d53e7aeb40ae2..c08ede75b4405 100644 --- a/bundles/org.openhab.binding.solax/src/main/java/org/openhab/binding/solax/internal/SolaxLocalAccessHandler.java +++ b/bundles/org.openhab.binding.solax/src/main/java/org/openhab/binding/solax/internal/SolaxLocalAccessHandler.java @@ -19,6 +19,7 @@ import java.util.Set; import java.util.concurrent.ScheduledFuture; import java.util.concurrent.TimeUnit; +import java.util.concurrent.locks.ReentrantLock; import javax.measure.Quantity; import javax.measure.Unit; @@ -42,6 +43,7 @@ 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.UnDefType; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -69,6 +71,8 @@ public class SolaxLocalAccessHandler extends BaseThingHandler { private final Set unsupportedExistingChannels = new HashSet(); + private final ReentrantLock retrieveDataCallLock = new ReentrantLock(); + public SolaxLocalAccessHandler(Thing thing) { super(thing); } @@ -88,19 +92,25 @@ public void initialize() { } private void retrieveData() { - try { - String rawJsonData = localHttpConnector.retrieveData(); - logger.debug("Raw data retrieved = {}", rawJsonData); - - if (rawJsonData != null && !rawJsonData.isEmpty()) { - updateFromData(rawJsonData); - } else { - updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, - SolaxBindingConstants.I18N_KEY_OFFLINE_COMMUNICATION_ERROR_JSON_CANNOT_BE_RETRIEVED); + if (retrieveDataCallLock.tryLock()) { + try { + String rawJsonData = localHttpConnector.retrieveData(); + logger.debug("Raw data retrieved = {}", rawJsonData); + + if (rawJsonData != null && !rawJsonData.isEmpty()) { + updateFromData(rawJsonData); + } else { + updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, + SolaxBindingConstants.I18N_KEY_OFFLINE_COMMUNICATION_ERROR_JSON_CANNOT_BE_RETRIEVED); + } + } catch (IOException e) { + logger.debug("Exception received while attempting to retrieve data via HTTP", e); + updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, e.getMessage()); + } finally { + retrieveDataCallLock.unlock(); } - } catch (IOException e) { - logger.debug("Exception received while attempting to retrieve data via HTTP", e); - updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, e.getMessage()); + } else { + logger.debug("Unable to retrieve data because a request is already in progress."); } } @@ -283,7 +293,11 @@ private void logRemovedChannels(List channelsToRemove) { @Override public void handleCommand(ChannelUID channelUID, Command command) { - // Nothing to do here as of now. Maybe implement a REFRESH command in the future. + if (command instanceof RefreshType) { + scheduler.execute(this::retrieveData); + } else { + logger.debug("Binding {} only supports refresh command", SolaxBindingConstants.BINDING_ID); + } } @Override From 6df602599e40f77ee00462deb80ab62dd747940d Mon Sep 17 00:00:00 2001 From: Siegmar Immel <36704892+SiggiFR@users.noreply.github.com> Date: Tue, 28 Nov 2023 22:45:26 +0100 Subject: [PATCH 5/7] [mail] Improve action documentation (#15970) * Use of actions.get instead of getActions Adopt doc to newest default JS environment regarding calling mail actions via actions.get Signed-off-by: Siegmar Immel <36704892+SiggiFR@users.noreply.github.com> * [mail] Adding Javascript examples in documentation Because function name has changed to get actions defined within the mail binding, the examples have been extended by a tab for JavaScript code. * Update bundles/org.openhab.binding.mail/README.md Co-authored-by: J-N-K Signed-off-by: Siegmar Immel <36704892+SiggiFR@users.noreply.github.com> --------- Signed-off-by: Siegmar Immel <36704892+SiggiFR@users.noreply.github.com> Co-authored-by: J-N-K --- bundles/org.openhab.binding.mail/README.md | 46 +++++++++++++++++++++- 1 file changed, 45 insertions(+), 1 deletion(-) diff --git a/bundles/org.openhab.binding.mail/README.md b/bundles/org.openhab.binding.mail/README.md index 21c5ce6d87c8e..04138cede2265 100644 --- a/bundles/org.openhab.binding.mail/README.md +++ b/bundles/org.openhab.binding.mail/README.md @@ -130,7 +130,7 @@ Both functions return a boolean as the result of the operation. `recipient` can be a single address (`mail@example.com`) or a list of addresses, concatenated by a comma (`mail@example.com, mail2@example.com`). -Since there is a separate rule action instance for each `smtp` thing, this needs to be retrieved through `getActions(scope, thingUID)`. +Since there is a separate rule action instance for each `smtp` thing, this needs to be retrieved through `getActions(scope, thingUID)` (DSL) or `actions.get(scope, thingUID)` (Javascript). The first parameter always has to be `mail` and the second is the full Thing UID of the SMTP server that should be used. Once this action instance is retrieved, you can invoke the action method on it. @@ -139,6 +139,10 @@ Using different character sets may produce unwanted results. Examples: +:::: tabs + +::: tab DSL + ```java val mailActions = getActions("mail","mail:smtp:samplesmtp") val success = mailActions.sendMail("mail@example.com", "Test subject", "This is the mail content.") @@ -155,7 +159,30 @@ val List attachmentUrlList = newArrayList( val mailActions = getActions("mail","mail:smtp:sampleserver") mailActions.sendHtmlMailWithAttachments("mail@example.com", "Test subject", "

Header

This is the mail content.", attachmentUrlList) ``` +::: + +::: tab JavaScript + +```javascript +val mailActions = actions.get("mail","mail:smtp:samplesmtp") +val success = mailActions.sendMail("mail@example.com", "Test subject", "This is the mail content.") +success = mailActions.sendMail("mail1@example.com, mail2@example.com", "Test subject", "This is the mail content sent to multiple recipients.") + +``` + +```javascript +import java.util.List + +val List attachmentUrlList = newArrayList( + "http://some.web/site/snap.jpg¶m=value", + "file:///tmp/201601011031.jpg") +val mailActions = actions.get("mail","mail:smtp:sampleserver") +mailActions.sendHtmlMailWithAttachments("mail@example.com", "Test subject", "

Header

This is the mail content.", attachmentUrlList) +``` + +::: +:::: ## Mail Headers The binding allows one to add custom e-mail headers to messages that it sends. @@ -163,6 +190,11 @@ For example if you want e-mails sent by this binding to be grouped into a "threa Headers can be added inside a rule by calling the `mailActions.addHeader()` method before calling the respective `mailActions.sendMail()` method. See the example below. +:::: tabs + +::: tab DSL + + ```java rule "Send Mail with a 'Reference' header; for threaded view in e-mail client" when @@ -173,5 +205,17 @@ then mailActions.sendMail("mail@example.com", "Test subject", "Test message text") end ``` +::: + +::: tab JavaScript + +```javascript +val mailActions = actions.get("mail","mail:smtp:sampleserver") +mailActions.addHeader("Reference", "") +mailActions.sendMail("mail@example.com", "Test subject", "Test message text") +``` + +::: +:::: Note: in the case of the "Reference" header, the `` has to be an ASCII string enclosed in angle brackets. From e33cccc29c7933d25af0504194a8e0a678d101e0 Mon Sep 17 00:00:00 2001 From: mlobstein Date: Wed, 29 Nov 2023 01:12:27 -0600 Subject: [PATCH 6/7] [roku] Improve exception handling for failed http connections (#15932) Signed-off-by: Michael Lobstein --- .../roku/internal/RokuHttpException.java | 4 ++ .../communication/RokuCommunicator.java | 45 ++++++++++--------- 2 files changed, 28 insertions(+), 21 deletions(-) diff --git a/bundles/org.openhab.binding.roku/src/main/java/org/openhab/binding/roku/internal/RokuHttpException.java b/bundles/org.openhab.binding.roku/src/main/java/org/openhab/binding/roku/internal/RokuHttpException.java index 73c638b15ed80..b6dc1786b0ed6 100644 --- a/bundles/org.openhab.binding.roku/src/main/java/org/openhab/binding/roku/internal/RokuHttpException.java +++ b/bundles/org.openhab.binding.roku/src/main/java/org/openhab/binding/roku/internal/RokuHttpException.java @@ -23,6 +23,10 @@ public class RokuHttpException extends Exception { private static final long serialVersionUID = 1L; + public RokuHttpException(String errorMessage, Throwable t) { + super(errorMessage, t); + } + public RokuHttpException(String errorMessage) { super(errorMessage); } diff --git a/bundles/org.openhab.binding.roku/src/main/java/org/openhab/binding/roku/internal/communication/RokuCommunicator.java b/bundles/org.openhab.binding.roku/src/main/java/org/openhab/binding/roku/internal/communication/RokuCommunicator.java index ce3891c6855f9..bf60c38fbebb2 100644 --- a/bundles/org.openhab.binding.roku/src/main/java/org/openhab/binding/roku/internal/communication/RokuCommunicator.java +++ b/bundles/org.openhab.binding.roku/src/main/java/org/openhab/binding/roku/internal/communication/RokuCommunicator.java @@ -35,8 +35,6 @@ import org.openhab.binding.roku.internal.dto.TvChannel; import org.openhab.binding.roku.internal.dto.TvChannels; import org.openhab.binding.roku.internal.dto.TvChannels.Channel; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; /** * Methods for accessing the HTTP interface of the Roku @@ -45,7 +43,6 @@ */ @NonNullByDefault public class RokuCommunicator { - private final Logger logger = LoggerFactory.getLogger(RokuCommunicator.class); private final HttpClient httpClient; private final String urlKeyPress; @@ -113,10 +110,10 @@ public DeviceInfo getDeviceInfo() throws RokuHttpException { try { JAXBContext ctx = JAXBUtils.JAXBCONTEXT_DEVICE_INFO; if (ctx != null) { + final String response = getCommand(urlQryDevice); Unmarshaller unmarshaller = ctx.createUnmarshaller(); if (unmarshaller != null) { - XMLStreamReader xsr = JAXBUtils.XMLINPUTFACTORY - .createXMLStreamReader(new StringReader(getCommand(urlQryDevice))); + XMLStreamReader xsr = JAXBUtils.XMLINPUTFACTORY.createXMLStreamReader(new StringReader(response)); DeviceInfo device = (DeviceInfo) unmarshaller.unmarshal(xsr); if (device != null) { return device; @@ -139,10 +136,10 @@ public ActiveApp getActiveApp() throws RokuHttpException { try { JAXBContext ctx = JAXBUtils.JAXBCONTEXT_ACTIVE_APP; if (ctx != null) { + final String response = getCommand(urlQryActiveApp); Unmarshaller unmarshaller = ctx.createUnmarshaller(); if (unmarshaller != null) { - XMLStreamReader xsr = JAXBUtils.XMLINPUTFACTORY - .createXMLStreamReader(new StringReader(getCommand(urlQryActiveApp))); + XMLStreamReader xsr = JAXBUtils.XMLINPUTFACTORY.createXMLStreamReader(new StringReader(response)); ActiveApp activeApp = (ActiveApp) unmarshaller.unmarshal(xsr); if (activeApp != null) { return activeApp; @@ -165,10 +162,10 @@ public List getAppList() throws RokuHttpException { try { JAXBContext ctx = JAXBUtils.JAXBCONTEXT_APPS; if (ctx != null) { + final String response = getCommand(urlQryApps); Unmarshaller unmarshaller = ctx.createUnmarshaller(); if (unmarshaller != null) { - XMLStreamReader xsr = JAXBUtils.XMLINPUTFACTORY - .createXMLStreamReader(new StringReader(getCommand(urlQryApps))); + XMLStreamReader xsr = JAXBUtils.XMLINPUTFACTORY.createXMLStreamReader(new StringReader(response)); Apps appList = (Apps) unmarshaller.unmarshal(xsr); if (appList != null) { return appList.getApp(); @@ -191,10 +188,10 @@ public Player getPlayerInfo() throws RokuHttpException { try { JAXBContext ctx = JAXBUtils.JAXBCONTEXT_PLAYER; if (ctx != null) { + final String response = getCommand(urlQryPlayer); Unmarshaller unmarshaller = ctx.createUnmarshaller(); if (unmarshaller != null) { - XMLStreamReader xsr = JAXBUtils.XMLINPUTFACTORY - .createXMLStreamReader(new StringReader(getCommand(urlQryPlayer))); + XMLStreamReader xsr = JAXBUtils.XMLINPUTFACTORY.createXMLStreamReader(new StringReader(response)); Player playerInfo = (Player) unmarshaller.unmarshal(xsr); if (playerInfo != null) { return playerInfo; @@ -217,10 +214,10 @@ public TvChannel getActiveTvChannel() throws RokuHttpException { try { JAXBContext ctx = JAXBUtils.JAXBCONTEXT_TVCHANNEL; if (ctx != null) { + final String response = getCommand(urlQryActiveTvChannel); Unmarshaller unmarshaller = ctx.createUnmarshaller(); if (unmarshaller != null) { - XMLStreamReader xsr = JAXBUtils.XMLINPUTFACTORY - .createXMLStreamReader(new StringReader(getCommand(urlQryActiveTvChannel))); + XMLStreamReader xsr = JAXBUtils.XMLINPUTFACTORY.createXMLStreamReader(new StringReader(response)); TvChannel tvChannelInfo = (TvChannel) unmarshaller.unmarshal(xsr); if (tvChannelInfo != null) { return tvChannelInfo; @@ -243,10 +240,10 @@ public List getTvChannelList() throws RokuHttpException { try { JAXBContext ctx = JAXBUtils.JAXBCONTEXT_TVCHANNELS; if (ctx != null) { + final String response = getCommand(urlQryTvChannels); Unmarshaller unmarshaller = ctx.createUnmarshaller(); if (unmarshaller != null) { - XMLStreamReader xsr = JAXBUtils.XMLINPUTFACTORY - .createXMLStreamReader(new StringReader(getCommand(urlQryTvChannels))); + XMLStreamReader xsr = JAXBUtils.XMLINPUTFACTORY.createXMLStreamReader(new StringReader(response)); TvChannels tvChannels = (TvChannels) unmarshaller.unmarshal(xsr); if (tvChannels != null) { return tvChannels.getChannel(); @@ -264,13 +261,16 @@ public List getTvChannelList() throws RokuHttpException { * * @param url The url to send with the command embedded in the URI * @return The response content of the http request + * @throws RokuHttpException */ - private String getCommand(String url) { + private String getCommand(String url) throws RokuHttpException { try { return httpClient.GET(url).getContentAsString(); - } catch (InterruptedException | TimeoutException | ExecutionException e) { - logger.debug("Error executing player GET command, URL: {}, {} ", url, e.getMessage()); - return ""; + } catch (TimeoutException | ExecutionException e) { + throw new RokuHttpException("Error executing GET command for URL: " + url, e); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + throw new RokuHttpException("InterruptedException executing GET command for URL: " + url, e); } } @@ -283,8 +283,11 @@ private String getCommand(String url) { private void postCommand(String url) throws RokuHttpException { try { httpClient.POST(url).method(HttpMethod.POST).send(); - } catch (InterruptedException | TimeoutException | ExecutionException e) { - throw new RokuHttpException("Error executing player POST command, URL: " + url + e.getMessage()); + } catch (TimeoutException | ExecutionException e) { + throw new RokuHttpException("Error executing POST command, URL: " + url, e); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + throw new RokuHttpException("InterruptedException executing POST command for URL: " + url, e); } } } From b80273a88aa59788cb4245551efd48a265be5aad Mon Sep 17 00:00:00 2001 From: Jacob Laursen Date: Wed, 29 Nov 2023 12:20:57 +0100 Subject: [PATCH 7/7] [hue] Refactor method to reduce nesting and code duplication(#15971) Signed-off-by: Jacob Laursen --- .../internal/handler/Clip2ThingHandler.java | 75 ++++++++++--------- 1 file changed, 41 insertions(+), 34 deletions(-) diff --git a/bundles/org.openhab.binding.hue/src/main/java/org/openhab/binding/hue/internal/handler/Clip2ThingHandler.java b/bundles/org.openhab.binding.hue/src/main/java/org/openhab/binding/hue/internal/handler/Clip2ThingHandler.java index aef0ff5bf27ae..292416ec94f90 100644 --- a/bundles/org.openhab.binding.hue/src/main/java/org/openhab/binding/hue/internal/handler/Clip2ThingHandler.java +++ b/bundles/org.openhab.binding.hue/src/main/java/org/openhab/binding/hue/internal/handler/Clip2ThingHandler.java @@ -635,46 +635,53 @@ public void initialize() { * @param resource a Resource object containing the new state. */ public void onResource(Resource resource) { - if (!disposing) { - boolean resourceConsumed = false; - String incomingResourceId = resource.getId(); - if (resourceId.equals(incomingResourceId)) { - if (resource.hasFullState()) { - thisResource = resource; - if (!updatePropertiesDone) { - updateProperties(resource); - resourceConsumed = updatePropertiesDone; - } - } - if (!updateDependenciesDone) { - resourceConsumed = true; - cancelTask(updateDependenciesTask, false); - updateDependenciesTask = scheduler.submit(() -> updateDependencies()); - } - } else if (SUPPORTED_SCENE_TYPES.contains(resource.getType())) { - Resource cachedScene = sceneContributorsCache.get(incomingResourceId); - if (Objects.nonNull(cachedScene)) { - Setters.setResource(resource, cachedScene); - resourceConsumed = updateChannels(resource); - sceneContributorsCache.put(incomingResourceId, resource); - } - } else { - Resource cachedService = serviceContributorsCache.get(incomingResourceId); - if (Objects.nonNull(cachedService)) { - Setters.setResource(resource, cachedService); - resourceConsumed = updateChannels(resource); - serviceContributorsCache.put(incomingResourceId, resource); - if (ResourceType.LIGHT == resource.getType() && !updateLightPropertiesDone) { - updateLightProperties(resource); - } + if (disposing) { + return; + } + boolean resourceConsumed = false; + if (resourceId.equals(resource.getId())) { + if (resource.hasFullState()) { + thisResource = resource; + if (!updatePropertiesDone) { + updateProperties(resource); + resourceConsumed = updatePropertiesDone; } } - if (resourceConsumed) { - logger.debug("{} -> onResource() consumed resource {}", resourceId, resource); + if (!updateDependenciesDone) { + resourceConsumed = true; + cancelTask(updateDependenciesTask, false); + updateDependenciesTask = scheduler.submit(() -> updateDependencies()); } + } else { + Resource cachedResource = getResourceFromCache(resource); + if (cachedResource != null) { + Setters.setResource(resource, cachedResource); + resourceConsumed = updateChannels(resource); + putResourceToCache(resource); + if (ResourceType.LIGHT == resource.getType() && !updateLightPropertiesDone) { + updateLightProperties(resource); + } + } + } + if (resourceConsumed) { + logger.debug("{} -> onResource() consumed resource {}", resourceId, resource); + } + } + + private void putResourceToCache(Resource resource) { + if (SUPPORTED_SCENE_TYPES.contains(resource.getType())) { + sceneContributorsCache.put(resource.getId(), resource); + } else { + serviceContributorsCache.put(resource.getId(), resource); } } + private @Nullable Resource getResourceFromCache(Resource resource) { + return SUPPORTED_SCENE_TYPES.contains(resource.getType()) // + ? sceneContributorsCache.get(resource.getId()) + : serviceContributorsCache.get(resource.getId()); + } + /** * Update the thing internal state depending on a full list of resources sent from the bridge. If the resourceType * is SCENE then call updateScenes(), otherwise if the resource refers to this thing, consume it via onResource() as