From 5e2872c0a5c3c523a18e43d160a605b9b8a78e64 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ga=C3=ABl=20L=27hopital?= Date: Fri, 7 Oct 2022 11:56:15 +0200 Subject: [PATCH] [netatmo] Buffer last event requests per child modules (#13489) * Buffer last event requests per child modules This is targetted to decrease the number of requests transmitted to Netatmo API. Solves #13358 Signed-off-by: clinique --- .../netatmo/internal/api/EnergyApi.java | 6 +- .../netatmo/internal/api/RestManager.java | 9 ++- .../netatmo/internal/api/SecurityApi.java | 49 +++++++--------- .../internal/api/data/NetatmoConstants.java | 9 +++ .../handler/capability/CameraCapability.java | 6 +- .../handler/capability/HomeCapability.java | 2 +- .../handler/capability/PersonCapability.java | 7 +-- .../capability/SecurityCapability.java | 58 ++++++++++++++++++- .../handler/capability/SmokeCapability.java | 7 +-- 9 files changed, 101 insertions(+), 52 deletions(-) diff --git a/bundles/org.openhab.binding.netatmo/src/main/java/org/openhab/binding/netatmo/internal/api/EnergyApi.java b/bundles/org.openhab.binding.netatmo/src/main/java/org/openhab/binding/netatmo/internal/api/EnergyApi.java index b213fb592cb1c..00c2296305cc8 100644 --- a/bundles/org.openhab.binding.netatmo/src/main/java/org/openhab/binding/netatmo/internal/api/EnergyApi.java +++ b/bundles/org.openhab.binding.netatmo/src/main/java/org/openhab/binding/netatmo/internal/api/EnergyApi.java @@ -47,7 +47,7 @@ public EnergyApi(ApiBridgeHandler apiClient) { public void switchSchedule(String homeId, String scheduleId) throws NetatmoException { UriBuilder uriBuilder = getAppUriBuilder(SUB_PATH_SWITCH_SCHEDULE, PARAM_HOME_ID, homeId, PARAM_SCHEDULE_ID, scheduleId); - post(uriBuilder, ApiResponse.Ok.class, null, null); + post(uriBuilder, ApiResponse.Ok.class, null); } /** @@ -64,7 +64,7 @@ public void switchSchedule(String homeId, String scheduleId) throws NetatmoExcep */ public void setThermMode(String homeId, String mode) throws NetatmoException { UriBuilder uriBuilder = getApiUriBuilder(SUB_PATH_SET_THERM_MODE, PARAM_HOME_ID, homeId, PARAM_MODE, mode); - post(uriBuilder, ApiResponse.Ok.class, null, null); + post(uriBuilder, ApiResponse.Ok.class, null); } /** @@ -88,6 +88,6 @@ public void setThermpoint(String homeId, String roomId, SetpointMode mode, long uriBuilder.queryParam("temp", temp > THERM_MAX_SETPOINT ? THERM_MAX_SETPOINT : temp); } } - post(uriBuilder, ApiResponse.Ok.class, null, null); + post(uriBuilder, ApiResponse.Ok.class, null); } } diff --git a/bundles/org.openhab.binding.netatmo/src/main/java/org/openhab/binding/netatmo/internal/api/RestManager.java b/bundles/org.openhab.binding.netatmo/src/main/java/org/openhab/binding/netatmo/internal/api/RestManager.java index c556a8a0a900f..b35d23376e189 100644 --- a/bundles/org.openhab.binding.netatmo/src/main/java/org/openhab/binding/netatmo/internal/api/RestManager.java +++ b/bundles/org.openhab.binding.netatmo/src/main/java/org/openhab/binding/netatmo/internal/api/RestManager.java @@ -52,14 +52,13 @@ protected > T get(UriBuilder uriBuilder, Class clazz return executeUri(uriBuilder, HttpMethod.GET, clazz, null, null); } - protected > T post(UriBuilder uriBuilder, Class clazz, @Nullable String payload, - @Nullable String contentType) throws NetatmoException { - return executeUri(uriBuilder, HttpMethod.POST, clazz, payload, contentType); + protected > T post(UriBuilder uriBuilder, Class clazz, @Nullable String payload) + throws NetatmoException { + return executeUri(uriBuilder, HttpMethod.POST, clazz, payload, payload == null ? null : CONTENT_APP_JSON); } protected T post(URI uri, Class clazz, Map entries) throws NetatmoException { - return apiBridge.executeUri(uri, POST, clazz, toRequest(entries), - "application/x-www-form-urlencoded;charset=UTF-8", 3); + return apiBridge.executeUri(uri, POST, clazz, toRequest(entries), CONTENT_APP_FORM, 3); } private > T executeUri(UriBuilder uriBuilder, HttpMethod method, Class clazz, diff --git a/bundles/org.openhab.binding.netatmo/src/main/java/org/openhab/binding/netatmo/internal/api/SecurityApi.java b/bundles/org.openhab.binding.netatmo/src/main/java/org/openhab/binding/netatmo/internal/api/SecurityApi.java index aa2157b6e7984..7b0e49b9b8bdd 100644 --- a/bundles/org.openhab.binding.netatmo/src/main/java/org/openhab/binding/netatmo/internal/api/SecurityApi.java +++ b/bundles/org.openhab.binding.netatmo/src/main/java/org/openhab/binding/netatmo/internal/api/SecurityApi.java @@ -16,7 +16,6 @@ import java.net.URI; import java.util.Collection; -import java.util.stream.Collectors; import javax.ws.rs.core.UriBuilder; @@ -52,7 +51,7 @@ public SecurityApi(ApiBridgeHandler apiClient) { */ public void dropWebhook() throws NetatmoException { UriBuilder uriBuilder = getApiUriBuilder(SUB_PATH_DROP_WEBHOOK); - post(uriBuilder, ApiResponse.Ok.class, null, null); + post(uriBuilder, ApiResponse.Ok.class, null); } /** @@ -63,37 +62,33 @@ public void dropWebhook() throws NetatmoException { */ public boolean addwebhook(URI uri) throws NetatmoException { UriBuilder uriBuilder = getApiUriBuilder(SUB_PATH_ADD_WEBHOOK, PARAM_URL, uri.toString()); - post(uriBuilder, ApiResponse.Ok.class, null, null); + post(uriBuilder, ApiResponse.Ok.class, null); return true; } - public Collection getPersonEvents(String homeId, String personId) throws NetatmoException { - UriBuilder uriBuilder = getApiUriBuilder(SUB_PATH_GET_EVENTS, PARAM_HOME_ID, homeId, PARAM_PERSON_ID, personId, - PARAM_OFFSET, 1); - NAEventsDataResponse response = get(uriBuilder, NAEventsDataResponse.class); - BodyResponse body = response.getBody(); + private Collection getEvents(@Nullable Object... params) throws NetatmoException { + UriBuilder uriBuilder = getApiUriBuilder(SUB_PATH_GET_EVENTS, params); + BodyResponse body = get(uriBuilder, NAEventsDataResponse.class).getBody(); if (body != null) { Home home = body.getElement(); if (home != null) { - return home.getEvents().stream().filter(event -> personId.equals(event.getPersonId())) - .collect(Collectors.toList()); + return home.getEvents(); } } throw new NetatmoException("home should not be null"); } + public Collection getHomeEvents(String homeId) throws NetatmoException { + return getEvents(PARAM_HOME_ID, homeId); + } + + public Collection getPersonEvents(String homeId, String personId) throws NetatmoException { + return getEvents(PARAM_HOME_ID, homeId, PARAM_PERSON_ID, personId, PARAM_OFFSET, 1); + } + public Collection getDeviceEvents(String homeId, String deviceId, String deviceType) throws NetatmoException { - UriBuilder uriBuilder = getApiUriBuilder(SUB_PATH_GET_EVENTS, PARAM_HOME_ID, homeId, PARAM_DEVICE_ID, deviceId, - PARAM_DEVICES_TYPE, deviceType); - BodyResponse body = get(uriBuilder, NAEventsDataResponse.class).getBody(); - if (body != null) { - Home home = body.getElement(); - if (home != null) { - return home.getEvents(); - } - } - throw new NetatmoException("home should not be null"); + return getEvents(PARAM_HOME_ID, homeId, PARAM_DEVICE_ID, deviceId, PARAM_DEVICES_TYPE, deviceType); } public @Nullable String ping(String vpnUrl) { @@ -109,22 +104,18 @@ public Collection getDeviceEvents(String homeId, String deviceId, Str public void changeStatus(String localCameraURL, boolean setOn) throws NetatmoException { UriBuilder uriBuilder = UriBuilder.fromUri(localCameraURL).path(PATH_COMMAND).path(SUB_PATH_CHANGESTATUS); uriBuilder.queryParam(PARAM_STATUS, setOn ? "on" : "off"); - post(uriBuilder, ApiResponse.Ok.class, null, null); + post(uriBuilder, ApiResponse.Ok.class, null); } public void changeFloodLightMode(String homeId, String cameraId, FloodLightMode mode) throws NetatmoException { UriBuilder uriBuilder = getAppUriBuilder(PATH_STATE); - String payload = String.format( - "{\"home\": {\"id\":\"%s\",\"modules\": [ {\"id\":\"%s\",\"floodlight\":\"%s\"} ]}}", homeId, cameraId, - mode.name().toLowerCase()); - post(uriBuilder, ApiResponse.Ok.class, payload, "application/json;charset=utf-8"); + String payload = String.format(PAYLOAD_FLOODLIGHT, homeId, cameraId, mode.name().toLowerCase()); + post(uriBuilder, ApiResponse.Ok.class, payload); } public void setPersonAwayStatus(String homeId, String personId, boolean away) throws NetatmoException { UriBuilder uriBuilder = getAppUriBuilder(away ? SUB_PATH_PERSON_AWAY : SUB_PATH_PERSON_HOME); - String payload = String.format( - away ? "{\"home_id\":\"%s\",\"person_id\":\"%s\"}" : "{\"home_id\":\"%s\",\"person_ids\":[\"%s\"]}", - homeId, personId); - post(uriBuilder, ApiResponse.Ok.class, payload, "application/json;charset=utf-8"); + String payload = String.format(away ? PAYLOAD_PERSON_AWAY : PAYLOAD_PERSON_HOME, homeId, personId); + post(uriBuilder, ApiResponse.Ok.class, payload); } } diff --git a/bundles/org.openhab.binding.netatmo/src/main/java/org/openhab/binding/netatmo/internal/api/data/NetatmoConstants.java b/bundles/org.openhab.binding.netatmo/src/main/java/org/openhab/binding/netatmo/internal/api/data/NetatmoConstants.java index a8f3f83568f9d..f864790c4e17c 100644 --- a/bundles/org.openhab.binding.netatmo/src/main/java/org/openhab/binding/netatmo/internal/api/data/NetatmoConstants.java +++ b/bundles/org.openhab.binding.netatmo/src/main/java/org/openhab/binding/netatmo/internal/api/data/NetatmoConstants.java @@ -114,6 +114,10 @@ public enum MeasureClass { } } + // Content types + public static final String CONTENT_APP_JSON = "application/json;charset=utf-8"; + public static final String CONTENT_APP_FORM = "application/x-www-form-urlencoded;charset=UTF-8"; + // Netatmo API urls public static final String URL_API = "https://api.netatmo.com/"; public static final String URL_APP = "https://app.netatmo.net/"; @@ -152,6 +156,11 @@ public enum MeasureClass { public static final String PARAM_STATUS = "status"; public static final String PARAM_DEVICES_TYPE = "device_types"; + // Payloads + public static final String PAYLOAD_FLOODLIGHT = "{\"home\": {\"id\":\"%s\",\"modules\": [ {\"id\":\"%s\",\"floodlight\":\"%s\"} ]}}"; + public static final String PAYLOAD_PERSON_AWAY = "{\"home_id\":\"%s\",\"person_id\":\"%s\"}"; + public static final String PAYLOAD_PERSON_HOME = "{\"home_id\":\"%s\",\"person_ids\":[\"%s\"]}"; + // Autentication process params public static final String PARAM_ERROR = "error"; diff --git a/bundles/org.openhab.binding.netatmo/src/main/java/org/openhab/binding/netatmo/internal/handler/capability/CameraCapability.java b/bundles/org.openhab.binding.netatmo/src/main/java/org/openhab/binding/netatmo/internal/handler/capability/CameraCapability.java index 910c018c56cb3..d96c8b35ae09a 100644 --- a/bundles/org.openhab.binding.netatmo/src/main/java/org/openhab/binding/netatmo/internal/handler/capability/CameraCapability.java +++ b/bundles/org.openhab.binding.netatmo/src/main/java/org/openhab/binding/netatmo/internal/handler/capability/CameraCapability.java @@ -15,7 +15,6 @@ import static org.openhab.binding.netatmo.internal.NetatmoBindingConstants.*; import java.util.ArrayList; -import java.util.Collection; import java.util.List; import java.util.stream.Collectors; @@ -101,9 +100,8 @@ protected void beforeNewData() { public List updateReadings() { List result = new ArrayList<>(); securityCapability.ifPresent(cap -> { - Collection events = cap.getDeviceEvents(handler.getId(), moduleType.apiName); - if (!events.isEmpty()) { - HomeEvent event = events.iterator().next(); + HomeEvent event = cap.getLastDeviceEvent(handler.getId(), moduleType.apiName); + if (event != null) { result.add(event); result.addAll(event.getSubevents()); } diff --git a/bundles/org.openhab.binding.netatmo/src/main/java/org/openhab/binding/netatmo/internal/handler/capability/HomeCapability.java b/bundles/org.openhab.binding.netatmo/src/main/java/org/openhab/binding/netatmo/internal/handler/capability/HomeCapability.java index 0e001bafd435a..045a368be2572 100644 --- a/bundles/org.openhab.binding.netatmo/src/main/java/org/openhab/binding/netatmo/internal/handler/capability/HomeCapability.java +++ b/bundles/org.openhab.binding.netatmo/src/main/java/org/openhab/binding/netatmo/internal/handler/capability/HomeCapability.java @@ -113,7 +113,7 @@ protected List updateReadings(HomeApi api) { result.add(homeStatus); } } catch (NetatmoException e) { - logger.warn("Error gettting Home informations : {}", e.getMessage()); + logger.warn("Error getting Home informations : {}", e.getMessage()); } return result; } diff --git a/bundles/org.openhab.binding.netatmo/src/main/java/org/openhab/binding/netatmo/internal/handler/capability/PersonCapability.java b/bundles/org.openhab.binding.netatmo/src/main/java/org/openhab/binding/netatmo/internal/handler/capability/PersonCapability.java index f03d1306f83d4..aa88faaca5537 100644 --- a/bundles/org.openhab.binding.netatmo/src/main/java/org/openhab/binding/netatmo/internal/handler/capability/PersonCapability.java +++ b/bundles/org.openhab.binding.netatmo/src/main/java/org/openhab/binding/netatmo/internal/handler/capability/PersonCapability.java @@ -16,7 +16,6 @@ import java.time.ZonedDateTime; import java.util.ArrayList; -import java.util.Collection; import java.util.List; import java.util.stream.Collectors; import java.util.stream.Stream; @@ -90,9 +89,9 @@ public void updateEvent(Event event) { public List updateReadings() { List result = new ArrayList<>(); securityCapability.ifPresent(cap -> { - Collection events = cap.getPersonEvents(handler.getId()); - if (!events.isEmpty()) { - result.add(events.iterator().next()); + HomeEvent event = cap.getLastPersonEvent(handler.getId()); + if (event != null) { + result.add(event); } }); return result; diff --git a/bundles/org.openhab.binding.netatmo/src/main/java/org/openhab/binding/netatmo/internal/handler/capability/SecurityCapability.java b/bundles/org.openhab.binding.netatmo/src/main/java/org/openhab/binding/netatmo/internal/handler/capability/SecurityCapability.java index 2803973576f51..380eaa2d13059 100644 --- a/bundles/org.openhab.binding.netatmo/src/main/java/org/openhab/binding/netatmo/internal/handler/capability/SecurityCapability.java +++ b/bundles/org.openhab.binding.netatmo/src/main/java/org/openhab/binding/netatmo/internal/handler/capability/SecurityCapability.java @@ -12,8 +12,11 @@ */ package org.openhab.binding.netatmo.internal.handler.capability; +import java.util.ArrayList; import java.util.Collection; +import java.util.HashMap; import java.util.List; +import java.util.Map; import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.Nullable; @@ -27,6 +30,7 @@ import org.openhab.binding.netatmo.internal.api.dto.HomeStatusModule; import org.openhab.binding.netatmo.internal.api.dto.HomeStatusPerson; import org.openhab.binding.netatmo.internal.api.dto.NAHomeStatus.HomeStatus; +import org.openhab.binding.netatmo.internal.api.dto.NAObject; import org.openhab.binding.netatmo.internal.deserialization.NAObjectMap; import org.openhab.binding.netatmo.internal.handler.CommonInterface; import org.slf4j.Logger; @@ -42,6 +46,8 @@ class SecurityCapability extends RestCapability { private final Logger logger = LoggerFactory.getLogger(SecurityCapability.class); + private static final Map eventBuffer = new HashMap<>(); + SecurityCapability(CommonInterface handler) { super(handler, SecurityApi.class); } @@ -110,7 +116,55 @@ protected void updateHomeEvent(HomeEvent homeEvent) { }); } - public Collection getDeviceEvents(String cameraId, String deviceType) { + @Override + protected List updateReadings(SecurityApi api) { + List result = new ArrayList<>(); + try { + Collection lastEvents = api.getHomeEvents(handler.getId()); + lastEvents.stream().forEach(event -> { + HomeEvent previousEvent = eventBuffer.get(event.getCameraId()); + if (previousEvent == null || previousEvent.getTime().isBefore(event.getTime())) { + eventBuffer.put(event.getCameraId(), event); + } + String personId = event.getPersonId(); + if (personId != null) { + previousEvent = eventBuffer.get(personId); + if (previousEvent == null || previousEvent.getTime().isBefore(event.getTime())) { + eventBuffer.put(personId, event); + } + } + }); + } catch (NetatmoException e) { + logger.warn("Error retrieving last events for home '{}' : {}", handler.getId(), e.getMessage()); + } + return result; + } + + public @Nullable HomeEvent getLastPersonEvent(String personId) { + HomeEvent event = eventBuffer.get(personId); + if (event == null) { + Collection events = requestPersonEvents(personId); + if (!events.isEmpty()) { + event = events.iterator().next(); + eventBuffer.put(personId, event); + } + } + return event; + } + + public @Nullable HomeEvent getLastDeviceEvent(String cameraId, String deviceType) { + HomeEvent event = eventBuffer.get(cameraId); + if (event == null) { + Collection events = requestDeviceEvents(cameraId, deviceType); + if (!events.isEmpty()) { + event = events.iterator().next(); + eventBuffer.put(cameraId, event); + } + } + return event; + } + + private Collection requestDeviceEvents(String cameraId, String deviceType) { return getApi().map(api -> { try { return api.getDeviceEvents(handler.getId(), cameraId, deviceType); @@ -121,7 +175,7 @@ public Collection getDeviceEvents(String cameraId, String deviceType) }).orElse(List.of()); } - public Collection getPersonEvents(String personId) { + private Collection requestPersonEvents(String personId) { return getApi().map(api -> { try { return api.getPersonEvents(handler.getId(), personId); diff --git a/bundles/org.openhab.binding.netatmo/src/main/java/org/openhab/binding/netatmo/internal/handler/capability/SmokeCapability.java b/bundles/org.openhab.binding.netatmo/src/main/java/org/openhab/binding/netatmo/internal/handler/capability/SmokeCapability.java index be188f1d07871..560bc3b7e7ad4 100644 --- a/bundles/org.openhab.binding.netatmo/src/main/java/org/openhab/binding/netatmo/internal/handler/capability/SmokeCapability.java +++ b/bundles/org.openhab.binding.netatmo/src/main/java/org/openhab/binding/netatmo/internal/handler/capability/SmokeCapability.java @@ -13,7 +13,6 @@ package org.openhab.binding.netatmo.internal.handler.capability; import java.util.ArrayList; -import java.util.Collection; import java.util.List; import org.eclipse.jdt.annotation.NonNullByDefault; @@ -41,9 +40,9 @@ public SmokeCapability(CommonInterface handler, NetatmoDescriptionProvider descr public List updateReadings() { List result = new ArrayList<>(); securityCapability.ifPresent(cap -> { - Collection events = cap.getDeviceEvents(handler.getId(), moduleType.apiName); - if (!events.isEmpty()) { - result.add(events.iterator().next()); + HomeEvent event = cap.getLastDeviceEvent(handler.getId(), moduleType.apiName); + if (event != null) { + result.add(event); } }); return result;