]> git.basschouten.com Git - openhab-addons.git/commitdiff
[Netatmo] Add the capability to have a single home (#14595)
authorGaël L'hopital <gael@lhopital.org>
Sat, 25 Mar 2023 15:59:51 +0000 (16:59 +0100)
committerGitHub <noreply@github.com>
Sat, 25 Mar 2023 15:59:51 +0000 (16:59 +0100)
* Adding the capability to have a single home, common to energy and security

---------

Signed-off-by: clinique <gael@lhopital.org>
15 files changed:
bundles/org.openhab.binding.netatmo/README.md
bundles/org.openhab.binding.netatmo/src/main/java/org/openhab/binding/netatmo/internal/action/RoomActions.java
bundles/org.openhab.binding.netatmo/src/main/java/org/openhab/binding/netatmo/internal/api/dto/NAObject.java
bundles/org.openhab.binding.netatmo/src/main/java/org/openhab/binding/netatmo/internal/api/dto/NAThing.java
bundles/org.openhab.binding.netatmo/src/main/java/org/openhab/binding/netatmo/internal/config/HomeConfiguration.java [new file with mode: 0644]
bundles/org.openhab.binding.netatmo/src/main/java/org/openhab/binding/netatmo/internal/config/NAThingConfiguration.java
bundles/org.openhab.binding.netatmo/src/main/java/org/openhab/binding/netatmo/internal/handler/CommonInterface.java
bundles/org.openhab.binding.netatmo/src/main/java/org/openhab/binding/netatmo/internal/handler/capability/CameraCapability.java
bundles/org.openhab.binding.netatmo/src/main/java/org/openhab/binding/netatmo/internal/handler/capability/CapabilityMap.java
bundles/org.openhab.binding.netatmo/src/main/java/org/openhab/binding/netatmo/internal/handler/capability/EnergyCapability.java
bundles/org.openhab.binding.netatmo/src/main/java/org/openhab/binding/netatmo/internal/handler/capability/HomeCapability.java
bundles/org.openhab.binding.netatmo/src/main/java/org/openhab/binding/netatmo/internal/handler/capability/PersonCapability.java
bundles/org.openhab.binding.netatmo/src/main/java/org/openhab/binding/netatmo/internal/handler/capability/SecurityCapability.java
bundles/org.openhab.binding.netatmo/src/main/resources/OH-INF/config/config.xml
bundles/org.openhab.binding.netatmo/src/main/resources/OH-INF/i18n/netatmo.properties

index 21fea4fce1668415713408a12b71cb2d2be1e2d1..fa85b9babd63d12e24e493e1111e325148451412 100644 (file)
@@ -444,11 +444,31 @@ All these channels except setpoint and setpoint-mode are read only.
 | battery       | low-battery | Switch       | Low battery                                      |
 | battery       | status      | String       | Description of the battery status (*)            |
 
-### Welcome Home
+### Home
 
-All these channels are read only.
+A Home is the Thing holding various modules and devices. They can hold two areas of equipments : Security and Energy.
+Depending on the way it is configured the behaviour will be adapted and available channels can vary.
+
+**Home Configuration**
+
+The Home thing has the following configuration elements:
+
+| Parameter  | Type   | Required | Description                                                                         |
+| ---------- | ------ | -------- | ----------------------------------------------------------------------------------- |
+| id (1)     | String | No       | If you have a single type of equipment, this id is to be used for the home          |
+| energyId   | String | No       | Id of a home holding energy control devices                                         |
+| securityId | String | No       | Id of a home holding security monitoring devices                                    |
+
+At least one of these parameter must be filled - at most two : 
+* id or securityId
+* id or energyId
+* securityId and energyId
 
-**Supported channels for the Home thing:**
+(1) this parameter is only kept for backward compatibility.
+
+All channels are read only.
+
+**Supported channels for the Security Home thing:**
 
 | Channel Group | Channel Id             | Item Type | Description                                      |
 | ------------- | ---------------------- | --------- | ------------------------------------------------ |
@@ -456,9 +476,7 @@ All these channels are read only.
 | security      | unknown-person-count   | Number    | Total number of unknown persons that are at home |
 | security      | unknown-person-picture | Image     | Snapshot of unknown person that is at home       |
 
-All these channels are read only.
-
-**Supported trigger channels for the Home thing:**
+**Supported trigger channels for the Security Home thing:**
 
 | Channel Type ID  | Options            | Description                                                                                                                                                                      |
 | ---------------- | ------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
@@ -493,7 +511,7 @@ All these channels are read only.
 Warnings:
 
 - The URL of the live snapshot is a fixed URL so the value of the channel cameraLivePictureUrl / welcomeCameraLivePictureUrl will never be updated once first set by the binding. So to get a refreshed picture, you need to use the refresh parameter in your sitemap image element.
-- Some features like the video surveillance are accessed via the local network, so it may be helpful to set a static IP address for the camera within your local network.
+- Some features like the video monitoring are accessed via the local network, so it may be helpful to set a static IP address for the camera within your local network.
 
 **Supported channels for the Welcome Camera thing:**
 
index 9b20e29db53252800171c04a2f0de419454c3e5b..ac773acd5b10e11488e279de1f55aab8cac3b9b0 100644 (file)
@@ -84,7 +84,7 @@ public class RoomActions implements ThingActions {
             return;
         }
         getEnergyCapability()
-                .ifPresent(cap -> cap.setRoomThermTemp(roomHandler.getId(), temp, endTime, SetpointMode.MANUAL));
+                .ifPresent(cap -> cap.setRoomThermTemp(roomHandler.getId(), SetpointMode.MANUAL, endTime, temp));
     }
 
     @RuleAction(label = "@text/actionSetThermRoomModeSetpointLabel", description = "@text/actionSetThermRoomModeSetpointDesc")
@@ -123,7 +123,7 @@ public class RoomActions implements ThingActions {
 
         long setpointEnd = targetEndTime;
         SetpointMode setpointMode = targetMode;
-        getEnergyCapability().ifPresent(cap -> cap.setRoomThermTemp(roomHandler.getId(), 0, setpointEnd, setpointMode));
+        getEnergyCapability().ifPresent(cap -> cap.setRoomThermTemp(roomHandler.getId(), setpointMode, setpointEnd, 0));
     }
 
     public static void setThermRoomTempSetpoint(ThingActions actions, @Nullable Double temp, @Nullable Long endTime) {
index 63687b6eea11f970070d1bab212f32c779d633bf..05ab286eb53a01aca53b5d57421c4e5c649bea78 100644 (file)
@@ -44,7 +44,8 @@ public class NAObject {
         return ignoredForThingUpdate;
     }
 
-    public void setIgnoredForThingUpdate(boolean ignoredForThingUpdate) {
-        this.ignoredForThingUpdate = ignoredForThingUpdate;
+    public NAObject ignoringForThingUpdate() {
+        this.ignoredForThingUpdate = true;
+        return this;
     }
 }
index 61171a8fb44f1671f4fe15ceefd81d264a563939..aa181887b897fbc59dc326a4c018eb4bb0370353 100644 (file)
@@ -55,10 +55,6 @@ public class NAThing extends NAObject implements NAModule {
         return localReachable != null ? localReachable : true;
     }
 
-    public void setReachable(boolean reachable) {
-        this.reachable = reachable;
-    }
-
     public @Nullable Dashboard getDashboardData() {
         return dashboardData;
     }
diff --git a/bundles/org.openhab.binding.netatmo/src/main/java/org/openhab/binding/netatmo/internal/config/HomeConfiguration.java b/bundles/org.openhab.binding.netatmo/src/main/java/org/openhab/binding/netatmo/internal/config/HomeConfiguration.java
new file mode 100644 (file)
index 0000000..d7f1cbf
--- /dev/null
@@ -0,0 +1,38 @@
+/**
+ * Copyright (c) 2010-2023 Contributors to the openHAB project
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ */
+package org.openhab.binding.netatmo.internal.config;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.openhab.binding.netatmo.internal.api.data.NetatmoConstants.FeatureArea;
+
+/**
+ * The {@link HomeConfiguration} is responsible for holding configuration information for any
+ * Netatmo Home - security or energy, or both
+ *
+ * @author Gaël L'hopital - Initial contribution
+ */
+@NonNullByDefault
+public class HomeConfiguration extends NAThingConfiguration {
+    public String securityId = "";
+    public String energyId = "";
+
+    @Override
+    public String getId() {
+        return getIdForArea(energyId.isBlank() ? FeatureArea.SECURITY : FeatureArea.ENERGY);
+    }
+
+    public String getIdForArea(FeatureArea feature) {
+        return FeatureArea.ENERGY.equals(feature) ? energyId.isBlank() ? id : energyId
+                : FeatureArea.SECURITY.equals(feature) ? securityId.isBlank() ? id : securityId : id;
+    }
+}
index da036cb6cfe323f6f060c13ead66494b983d51cf..c2870228d175a4a169915dfa9e85d2bd430bad7c 100644 (file)
@@ -24,6 +24,10 @@ import org.eclipse.jdt.annotation.NonNullByDefault;
 public class NAThingConfiguration {
     public static final String ID = "id";
 
-    public String id = "";
+    protected String id = "";
     public int refreshInterval = -1;
+
+    public String getId() {
+        return id;
+    }
 }
index 05e1ece1686b6fef77a9075a06a0f0adba869e4c..14d6823cc7a27e6e16042e4353027bf8246ac84d 100644 (file)
@@ -24,6 +24,7 @@ import java.util.stream.Stream;
 import org.eclipse.jdt.annotation.NonNullByDefault;
 import org.eclipse.jdt.annotation.Nullable;
 import org.openhab.binding.netatmo.internal.api.data.ModuleType;
+import org.openhab.binding.netatmo.internal.api.data.NetatmoConstants.FeatureArea;
 import org.openhab.binding.netatmo.internal.api.dto.NAObject;
 import org.openhab.binding.netatmo.internal.api.dto.NAThing;
 import org.openhab.binding.netatmo.internal.config.NAThingConfiguration;
@@ -32,6 +33,7 @@ import org.openhab.binding.netatmo.internal.handler.capability.CapabilityMap;
 import org.openhab.binding.netatmo.internal.handler.capability.HomeCapability;
 import org.openhab.binding.netatmo.internal.handler.capability.RefreshCapability;
 import org.openhab.binding.netatmo.internal.handler.capability.RestCapability;
+import org.openhab.core.config.core.Configuration;
 import org.openhab.core.thing.Bridge;
 import org.openhab.core.thing.Channel;
 import org.openhab.core.thing.ChannelUID;
@@ -107,7 +109,11 @@ public interface CommonInterface {
     }
 
     default String getId() {
-        return (String) getThing().getConfiguration().get(NAThingConfiguration.ID);
+        return getConfiguration().as(NAThingConfiguration.class).getId();
+    }
+
+    default Configuration getConfiguration() {
+        return getThing().getConfiguration();
     }
 
     default Stream<Channel> getActiveChannels() {
@@ -135,6 +141,10 @@ public interface CommonInterface {
         return List.of();
     }
 
+    default Stream<CommonInterface> getActiveChildren(FeatureArea area) {
+        return getActiveChildren().stream().filter(child -> child.getModuleType().feature == area);
+    }
+
     default <T extends RestCapability<?>> Optional<T> getHomeCapability(Class<T> clazz) {
         return getHomeHandler().map(handler -> handler.getCapabilities().get(clazz)).orElse(Optional.empty());
     }
@@ -197,7 +207,6 @@ public interface CommonInterface {
         } else {
             setThingStatus(ThingStatus.UNKNOWN, ThingStatusDetail.NONE, null);
             setRefreshCapability();
-            getCapabilities().values().forEach(cap -> cap.initialize());
             getScheduler().schedule(() -> {
                 CommonInterface bridgeHandler = getBridgeHandler();
                 if (bridgeHandler != null) {
@@ -207,9 +216,12 @@ public interface CommonInterface {
         }
     }
 
+    default ModuleType getModuleType() {
+        return ModuleType.from(getThing().getThingTypeUID());
+    }
+
     default void setRefreshCapability() {
-        ModuleType moduleType = ModuleType.from(getThing().getThingTypeUID());
-        if (ModuleType.ACCOUNT.equals(moduleType.getBridge())) {
+        if (ModuleType.ACCOUNT.equals(getModuleType().getBridge())) {
             NAThingConfiguration config = getThing().getConfiguration().as(NAThingConfiguration.class);
             getCapabilities().put(new RefreshCapability(this, getScheduler(), config.refreshInterval));
         }
index 35b25ad63bce5469d2990b3d9d9f12d66679c1f8..006e52553b451686c8e55676cff46a256363625c 100644 (file)
@@ -89,7 +89,7 @@ public class CameraCapability extends HomeSecurityThingCapability {
     @Override
     protected void beforeNewData() {
         super.beforeNewData();
-        homeCapability.ifPresent(cap -> {
+        securityCapability.ifPresent(cap -> {
             NAObjectMap<HomeDataPerson> persons = cap.getPersons();
             descriptionProvider.setStateOptions(personChannelUID, persons.values().stream()
                     .map(p -> new StateOption(p.getId(), p.getName())).collect(Collectors.toList()));
index 002141f0d17bc4206eeb03b555fd6cf2cc5916a1..033d84b57cc8d075c85551dfa968bb403e3ea918 100644 (file)
@@ -28,9 +28,10 @@ public class CapabilityMap extends ConcurrentHashMap<Class<?>, Capability> {
     private static final long serialVersionUID = -3043492242108419801L;
 
     public void put(Capability capability) {
-        Class<? extends Capability> clazz = capability.getClass();
+        Class<?> clazz = capability.getClass();
         if (super.get(clazz) == null) {
             super.put(clazz, capability);
+            capability.initialize();
         }
     }
 
index 84630117b0c2f2161a4b6204090875f49d78ea27..efd6eb22572211de33fa7aa28ae60aae46281077 100644 (file)
@@ -20,6 +20,7 @@ import java.util.stream.Collectors;
 import org.eclipse.jdt.annotation.NonNullByDefault;
 import org.openhab.binding.netatmo.internal.api.EnergyApi;
 import org.openhab.binding.netatmo.internal.api.NetatmoException;
+import org.openhab.binding.netatmo.internal.api.data.NetatmoConstants.FeatureArea;
 import org.openhab.binding.netatmo.internal.api.data.NetatmoConstants.SetpointMode;
 import org.openhab.binding.netatmo.internal.api.dto.HomeData;
 import org.openhab.binding.netatmo.internal.api.dto.HomeDataModule;
@@ -27,6 +28,7 @@ import org.openhab.binding.netatmo.internal.api.dto.HomeDataRoom;
 import org.openhab.binding.netatmo.internal.api.dto.HomeStatusModule;
 import org.openhab.binding.netatmo.internal.api.dto.NAHomeStatus.HomeStatus;
 import org.openhab.binding.netatmo.internal.api.dto.Room;
+import org.openhab.binding.netatmo.internal.config.HomeConfiguration;
 import org.openhab.binding.netatmo.internal.deserialization.NAObjectMap;
 import org.openhab.binding.netatmo.internal.handler.CommonInterface;
 import org.openhab.binding.netatmo.internal.providers.NetatmoDescriptionProvider;
@@ -48,31 +50,32 @@ public class EnergyCapability extends RestCapability<EnergyApi> {
 
     private int setPointDefaultDuration = -1;
     private final NetatmoDescriptionProvider descriptionProvider;
+    private String energyId = "";
 
     EnergyCapability(CommonInterface handler, NetatmoDescriptionProvider descriptionProvider) {
         super(handler, EnergyApi.class);
         this.descriptionProvider = descriptionProvider;
     }
 
+    @Override
+    public void initialize() {
+        super.initialize();
+        energyId = handler.getConfiguration().as(HomeConfiguration.class).getIdForArea(FeatureArea.ENERGY);
+    }
+
     @Override
     protected void updateHomeData(HomeData homeData) {
         NAObjectMap<HomeDataRoom> rooms = homeData.getRooms();
         NAObjectMap<HomeDataModule> modules = homeData.getModules();
-        handler.getActiveChildren().forEach(childHandler -> {
+        handler.getActiveChildren(FeatureArea.ENERGY).forEach(childHandler -> {
             String childId = childHandler.getId();
-            rooms.getOpt(childId).ifPresentOrElse(roomData -> {
-                roomData.setIgnoredForThingUpdate(true);
-                childHandler.setNewData(roomData);
-            }, () -> {
-                modules.getOpt(childId).ifPresent(childData -> {
-                    childData.setIgnoredForThingUpdate(true);
-                    childHandler.setNewData(childData);
-                });
-                modules.values().stream().filter(module -> childId.equals(module.getBridge()))
-                        .forEach(bridgedModule -> {
-                            childHandler.setNewData(bridgedModule);
-                        });
-            });
+            rooms.getOpt(childId)
+                    .ifPresentOrElse(roomData -> childHandler.setNewData(roomData.ignoringForThingUpdate()), () -> {
+                        modules.getOpt(childId)
+                                .ifPresent(childData -> childHandler.setNewData(childData.ignoringForThingUpdate()));
+                        modules.values().stream().filter(module -> childId.equals(module.getBridge()))
+                                .forEach(bridgedModule -> childHandler.setNewData(bridgedModule));
+                    });
         });
         descriptionProvider.setStateOptions(new ChannelUID(thing.getUID(), GROUP_ENERGY, CHANNEL_PLANNING),
                 homeData.getThermSchedules().stream().map(p -> new StateOption(p.getId(), p.getName()))
@@ -84,55 +87,39 @@ public class EnergyCapability extends RestCapability<EnergyApi> {
     protected void updateHomeStatus(HomeStatus homeStatus) {
         NAObjectMap<Room> rooms = homeStatus.getRooms();
         NAObjectMap<HomeStatusModule> modules = homeStatus.getModules();
-        handler.getActiveChildren().forEach(childHandler -> {
+        handler.getActiveChildren(FeatureArea.ENERGY).forEach(childHandler -> {
             String childId = childHandler.getId();
             rooms.getOpt(childId).ifPresentOrElse(roomData -> childHandler.setNewData(roomData), () -> {
-                modules.getOpt(childId).ifPresentOrElse(childData -> {
-                    childHandler.setNewData(childData);
+                modules.getOpt(childId).ifPresent(moduleData -> {
+                    childHandler.setNewData(moduleData);
                     modules.values().stream().filter(module -> childId.equals(module.getBridge()))
-                            .forEach(bridgedModule -> {
-                                childHandler.setNewData(bridgedModule);
-                            });
-
-                }, () -> {
-                    // This module is not present in the homestatus data, so it is considered as unreachable
-                    HomeStatusModule module = new HomeStatusModule();
-                    module.setReachable(false);
-                    childHandler.setNewData(module);
+                            .forEach(bridgedModule -> childHandler.setNewData(bridgedModule));
                 });
             });
         });
     }
 
-    public int getSetpointDefaultDuration() {
-        return setPointDefaultDuration;
-    }
-
-    public void setRoomThermMode(String roomId, SetpointMode targetMode) {
+    public void setThermPoint(String roomId, SetpointMode mode, long endtime, double temp) {
         getApi().ifPresent(api -> {
             try {
-                api.setThermpoint(handler.getId(), roomId, targetMode,
-                        targetMode == SetpointMode.MAX ? setpointEndTimeFromNow(setPointDefaultDuration) : 0, 0);
+                api.setThermpoint(energyId, roomId, mode, endtime, temp);
                 handler.expireData();
             } catch (NetatmoException e) {
-                logger.warn("Error setting room thermostat mode '{}' : {}", targetMode, e.getMessage());
+                logger.warn("Error setting room thermostat mode '{}' : {}", mode, e.getMessage());
             }
         });
     }
 
-    public void setRoomThermTemp(String roomId, double temperature, long endtime, SetpointMode mode) {
-        getApi().ifPresent(api -> {
-            try {
-                api.setThermpoint(handler.getId(), roomId, mode, endtime, temperature);
-                handler.expireData();
-            } catch (NetatmoException e) {
-                logger.warn("Error setting room thermostat mode '{}' : {}", mode, e.getMessage());
-            }
-        });
+    public void setRoomThermTemp(String roomId, SetpointMode mode, long endtime, double temp) {
+        setThermPoint(roomId, mode, endtime, temp);
+    }
+
+    public void setRoomThermMode(String roomId, SetpointMode targetMode) {
+        setThermPoint(roomId, targetMode, targetMode == SetpointMode.MAX ? setpointEndTimeFromNow() : 0, 0);
     }
 
-    public void setRoomThermTemp(String roomId, double temperature) {
-        setRoomThermTemp(roomId, temperature, setpointEndTimeFromNow(setPointDefaultDuration), SetpointMode.MANUAL);
+    public void setRoomThermTemp(String roomId, double temp) {
+        setThermPoint(roomId, SetpointMode.MANUAL, setpointEndTimeFromNow(), temp);
     }
 
     @Override
@@ -141,7 +128,7 @@ public class EnergyCapability extends RestCapability<EnergyApi> {
             try {
                 switch (channelName) {
                     case CHANNEL_PLANNING:
-                        api.switchSchedule(handler.getId(), command.toString());
+                        api.switchSchedule(energyId, command.toString());
                         break;
                     case CHANNEL_SETPOINT_MODE:
                         SetpointMode targetMode = SetpointMode.valueOf(command.toString());
@@ -149,7 +136,7 @@ public class EnergyCapability extends RestCapability<EnergyApi> {
                             logger.info("Switch to 'Manual' is done by setting a setpoint temp, command ignored");
                             return;
                         }
-                        api.setThermMode(handler.getId(), targetMode.apiDescriptor);
+                        api.setThermMode(energyId, targetMode.apiDescriptor);
                         break;
                 }
                 handler.expireData();
@@ -161,7 +148,7 @@ public class EnergyCapability extends RestCapability<EnergyApi> {
         });
     }
 
-    private static long setpointEndTimeFromNow(int duration_min) {
-        return ZonedDateTime.now().plusMinutes(duration_min).toEpochSecond();
+    private long setpointEndTimeFromNow() {
+        return ZonedDateTime.now().plusMinutes(setPointDefaultDuration).toEpochSecond();
     }
 }
index 93f3a6b923919b35d16a5d186dc9ba2bcca5992a..79891fc43564e7331dc0c8c690548b4c62e67634 100644 (file)
@@ -15,6 +15,7 @@ package org.openhab.binding.netatmo.internal.handler.capability;
 import static org.openhab.binding.netatmo.internal.NetatmoBindingConstants.*;
 
 import java.util.ArrayList;
+import java.util.HashSet;
 import java.util.List;
 import java.util.Set;
 import java.util.stream.Collectors;
@@ -25,12 +26,10 @@ import org.openhab.binding.netatmo.internal.api.HomeApi;
 import org.openhab.binding.netatmo.internal.api.NetatmoException;
 import org.openhab.binding.netatmo.internal.api.data.NetatmoConstants.FeatureArea;
 import org.openhab.binding.netatmo.internal.api.dto.HomeData;
-import org.openhab.binding.netatmo.internal.api.dto.HomeDataModule;
-import org.openhab.binding.netatmo.internal.api.dto.HomeDataPerson;
 import org.openhab.binding.netatmo.internal.api.dto.Location;
 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.config.HomeConfiguration;
 import org.openhab.binding.netatmo.internal.handler.CommonInterface;
 import org.openhab.binding.netatmo.internal.providers.NetatmoDescriptionProvider;
 import org.slf4j.Logger;
@@ -44,77 +43,74 @@ import org.slf4j.LoggerFactory;
  */
 @NonNullByDefault
 public class HomeCapability extends RestCapability<HomeApi> {
-    private final Logger logger = LoggerFactory.getLogger(HomeCapability.class);
 
+    private final Logger logger = LoggerFactory.getLogger(HomeCapability.class);
+    private final Set<FeatureArea> featureAreas = new HashSet<>();
     private final NetatmoDescriptionProvider descriptionProvider;
-
-    private NAObjectMap<HomeDataPerson> persons = new NAObjectMap<>();
-    private NAObjectMap<HomeDataModule> modules = new NAObjectMap<>();
-
-    private Set<FeatureArea> featuresArea = Set.of();
+    private Set<String> homeIds = Set.of();
 
     public HomeCapability(CommonInterface handler, NetatmoDescriptionProvider descriptionProvider) {
         super(handler, HomeApi.class);
         this.descriptionProvider = descriptionProvider;
     }
 
+    @Override
+    public void initialize() {
+        super.initialize();
+        HomeConfiguration config = handler.getConfiguration().as(HomeConfiguration.class);
+        homeIds = Set.of(config.getId(), config.energyId, config.securityId);
+    }
+
     @Override
     protected void updateHomeData(HomeData home) {
-        featuresArea = home.getFeatures();
-        if (hasFeature(FeatureArea.SECURITY) && !handler.getCapabilities().containsKey(SecurityCapability.class)) {
+        if (hasArea(FeatureArea.SECURITY) && !handler.getCapabilities().containsKey(SecurityCapability.class)) {
             handler.getCapabilities().put(new SecurityCapability(handler));
         }
-        if (hasFeature(FeatureArea.ENERGY) && !handler.getCapabilities().containsKey(EnergyCapability.class)) {
+        if (hasArea(FeatureArea.ENERGY) && !handler.getCapabilities().containsKey(EnergyCapability.class)) {
             handler.getCapabilities().put(new EnergyCapability(handler, descriptionProvider));
         }
         if (firstLaunch) {
             home.getCountry().map(country -> properties.put(PROPERTY_COUNTRY, country));
             home.getTimezone().map(tz -> properties.put(PROPERTY_TIMEZONE, tz));
             properties.put(GROUP_LOCATION, ((Location) home).getLocation().toString());
-            properties.put(PROPERTY_FEATURE, featuresArea.stream().map(f -> f.name()).collect(Collectors.joining(",")));
+            properties.put(PROPERTY_FEATURE,
+                    featureAreas.stream().map(FeatureArea::name).collect(Collectors.joining(",")));
         }
     }
 
     @Override
     protected void afterNewData(@Nullable NAObject newData) {
         super.afterNewData(newData);
-        if (firstLaunch && !hasFeature(FeatureArea.SECURITY)) {
+        if (firstLaunch && !hasArea(FeatureArea.SECURITY)) {
             handler.removeChannels(thing.getChannelsOfGroup(GROUP_SECURITY));
         }
-        if (firstLaunch && !hasFeature(FeatureArea.ENERGY)) {
+        if (firstLaunch && !hasArea(FeatureArea.ENERGY)) {
             handler.removeChannels(thing.getChannelsOfGroup(GROUP_ENERGY));
         }
     }
 
-    private boolean hasFeature(FeatureArea seeked) {
-        return featuresArea.contains(seeked);
-    }
-
-    public NAObjectMap<HomeDataPerson> getPersons() {
-        return persons;
-    }
-
-    public NAObjectMap<HomeDataModule> getModules() {
-        return modules;
+    private boolean hasArea(FeatureArea searched) {
+        return featureAreas.contains(searched);
     }
 
     @Override
     protected List<NAObject> updateReadings(HomeApi api) {
         List<NAObject> result = new ArrayList<>();
-        try {
-            HomeData homeData = api.getHomeData(handler.getId());
-            if (homeData != null) {
-                result.add(homeData);
-                persons = homeData.getPersons();
-                modules = homeData.getModules();
-            }
-            HomeStatus homeStatus = api.getHomeStatus(handler.getId());
-            if (homeStatus != null) {
-                result.add(homeStatus);
+        homeIds.stream().filter(id -> !id.isEmpty()).forEach(id -> {
+            try {
+                HomeData homeData = api.getHomeData(id);
+                if (homeData != null) {
+                    result.add(homeData);
+                    featureAreas.addAll(homeData.getFeatures());
+                }
+                HomeStatus homeStatus = api.getHomeStatus(id);
+                if (homeStatus != null) {
+                    result.add(homeStatus);
+                }
+            } catch (NetatmoException e) {
+                logger.warn("Error getting Home informations : {}", e.getMessage());
             }
-        } catch (NetatmoException e) {
-            logger.warn("Error getting Home informations : {}", e.getMessage());
-        }
+        });
         return result;
     }
 }
index 3d07c89f87b8c2e6e14ace679655bef2812730e8..596fad6a565f63086beef78f81f6404223d028d7 100644 (file)
@@ -56,7 +56,7 @@ public class PersonCapability extends HomeSecurityThingCapability {
     @Override
     protected void beforeNewData() {
         super.beforeNewData();
-        homeCapability.ifPresent(cap -> {
+        securityCapability.ifPresent(cap -> {
             Stream<HomeDataModule> cameras = cap.getModules().values().stream()
                     .filter(module -> module.getType() == ModuleType.WELCOME);
             descriptionProvider.setStateOptions(cameraChannelUID,
index 1bc19c8e974758e6edafab907b24646286c15c84..c2b8fd41a81f9d01e882ee5916d4999554d08fe8 100644 (file)
@@ -23,6 +23,7 @@ import org.eclipse.jdt.annotation.NonNullByDefault;
 import org.eclipse.jdt.annotation.Nullable;
 import org.openhab.binding.netatmo.internal.api.NetatmoException;
 import org.openhab.binding.netatmo.internal.api.SecurityApi;
+import org.openhab.binding.netatmo.internal.api.data.NetatmoConstants.FeatureArea;
 import org.openhab.binding.netatmo.internal.api.data.NetatmoConstants.FloodLightMode;
 import org.openhab.binding.netatmo.internal.api.dto.HomeData;
 import org.openhab.binding.netatmo.internal.api.dto.HomeDataModule;
@@ -32,6 +33,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.config.HomeConfiguration;
 import org.openhab.binding.netatmo.internal.deserialization.NAObjectMap;
 import org.openhab.binding.netatmo.internal.handler.CommonInterface;
 import org.slf4j.Logger;
@@ -50,6 +52,10 @@ class SecurityCapability extends RestCapability<SecurityApi> {
     private final Map<String, HomeEvent> eventBuffer = new HashMap<>();
     private @Nullable ZonedDateTime freshestEventTime;
 
+    private NAObjectMap<HomeDataPerson> persons = new NAObjectMap<>();
+    private NAObjectMap<HomeDataModule> modules = new NAObjectMap<>();
+    private String securityId = "";
+
     SecurityCapability(CommonInterface handler) {
         super(handler, SecurityApi.class);
     }
@@ -58,27 +64,22 @@ class SecurityCapability extends RestCapability<SecurityApi> {
     public void initialize() {
         super.initialize();
         freshestEventTime = null;
+        securityId = handler.getConfiguration().as(HomeConfiguration.class).getIdForArea(FeatureArea.SECURITY);
     }
 
     @Override
     protected void updateHomeData(HomeData homeData) {
-        NAObjectMap<HomeDataPerson> persons = homeData.getPersons();
-        NAObjectMap<HomeDataModule> modules = homeData.getModules();
-        handler.getActiveChildren().forEach(childHandler -> {
+        persons = homeData.getPersons();
+        modules = homeData.getModules();
+        handler.getActiveChildren(FeatureArea.SECURITY).forEach(childHandler -> {
             String childId = childHandler.getId();
-            persons.getOpt(childId).ifPresentOrElse(personData -> {
-                personData.setIgnoredForThingUpdate(true);
-                childHandler.setNewData(personData);
-            }, () -> {
-                modules.getOpt(childId).ifPresent(childData -> {
-                    childData.setIgnoredForThingUpdate(true);
-                    childHandler.setNewData(childData);
-                });
-                modules.values().stream().filter(module -> childId.equals(module.getBridge()))
-                        .forEach(bridgedModule -> {
-                            childHandler.setNewData(bridgedModule);
-                        });
-            });
+            persons.getOpt(childId)
+                    .ifPresentOrElse(personData -> childHandler.setNewData(personData.ignoringForThingUpdate()), () -> {
+                        modules.getOpt(childId)
+                                .ifPresent(childData -> childHandler.setNewData(childData.ignoringForThingUpdate()));
+                        modules.values().stream().filter(module -> childId.equals(module.getBridge()))
+                                .forEach(bridgedModule -> childHandler.setNewData(bridgedModule));
+                    });
         });
     }
 
@@ -86,21 +87,13 @@ class SecurityCapability extends RestCapability<SecurityApi> {
     protected void updateHomeStatus(HomeStatus homeStatus) {
         NAObjectMap<HomeStatusPerson> persons = homeStatus.getPersons();
         NAObjectMap<HomeStatusModule> modules = homeStatus.getModules();
-        handler.getActiveChildren().forEach(childHandler -> {
+        handler.getActiveChildren(FeatureArea.SECURITY).forEach(childHandler -> {
             String childId = childHandler.getId();
             persons.getOpt(childId).ifPresentOrElse(personData -> childHandler.setNewData(personData), () -> {
-                modules.getOpt(childId).ifPresentOrElse(childData -> {
+                modules.getOpt(childId).ifPresent(childData -> {
                     childHandler.setNewData(childData);
                     modules.values().stream().filter(module -> childId.equals(module.getBridge()))
-                            .forEach(bridgedModule -> {
-                                childHandler.setNewData(bridgedModule);
-                            });
-
-                }, () -> {
-                    // This module is not present in the homestatus data, so it is considered as unreachable
-                    HomeStatusModule module = new HomeStatusModule();
-                    module.setReachable(false);
-                    childHandler.setNewData(module);
+                            .forEach(bridgedModule -> childHandler.setNewData(bridgedModule));
                 });
             });
         });
@@ -108,27 +101,23 @@ class SecurityCapability extends RestCapability<SecurityApi> {
 
     @Override
     protected void updateHomeEvent(HomeEvent homeEvent) {
-        String personId = homeEvent.getPersonId();
-        if (personId != null) {
-            handler.getActiveChildren().stream().filter(handler -> personId.equals(handler.getId())).findFirst()
-                    .ifPresent(handler -> {
-                        homeEvent.setIgnoredForThingUpdate(true);
-                        handler.setNewData(homeEvent);
-                    });
+        addEventIfKnownObject(homeEvent, homeEvent.getPersonId());
+        addEventIfKnownObject(homeEvent, homeEvent.getCameraId());
+    }
+
+    private void addEventIfKnownObject(HomeEvent homeEvent, @Nullable String objectId) {
+        if (objectId == null) {
+            return;
         }
-        String cameraId = homeEvent.getCameraId();
-        handler.getActiveChildren().stream().filter(handler -> cameraId.equals(handler.getId())).findFirst()
-                .ifPresent(handler -> {
-                    homeEvent.setIgnoredForThingUpdate(true);
-                    handler.setNewData(homeEvent);
-                });
+        handler.getActiveChildren(FeatureArea.SECURITY).filter(child -> child.getId().equals(objectId))
+                .forEach(child -> child.setNewData(homeEvent.ignoringForThingUpdate()));
     }
 
     @Override
     protected List<NAObject> updateReadings(SecurityApi api) {
         List<NAObject> result = new ArrayList<>();
         try {
-            for (HomeEvent event : api.getHomeEvents(handler.getId(), freshestEventTime)) {
+            for (HomeEvent event : api.getHomeEvents(securityId, freshestEventTime)) {
                 HomeEvent previousEvent = eventBuffer.get(event.getCameraId());
                 if (previousEvent == null || previousEvent.getTime().isBefore(event.getTime())) {
                     eventBuffer.put(event.getCameraId(), event);
@@ -145,11 +134,19 @@ class SecurityCapability extends RestCapability<SecurityApi> {
                 }
             }
         } catch (NetatmoException e) {
-            logger.warn("Error retrieving last events for home '{}' : {}", handler.getId(), e.getMessage());
+            logger.warn("Error retrieving last events for home '{}' : {}", securityId, e.getMessage());
         }
         return result;
     }
 
+    public NAObjectMap<HomeDataPerson> getPersons() {
+        return persons;
+    }
+
+    public NAObjectMap<HomeDataModule> getModules() {
+        return modules;
+    }
+
     public @Nullable HomeEvent getLastPersonEvent(String personId) {
         HomeEvent event = eventBuffer.get(personId);
         if (event == null) {
@@ -177,7 +174,7 @@ class SecurityCapability extends RestCapability<SecurityApi> {
     private Collection<HomeEvent> requestDeviceEvents(String moduleId, String deviceType) {
         return getApi().map(api -> {
             try {
-                return api.getDeviceEvents(handler.getId(), moduleId, deviceType);
+                return api.getDeviceEvents(securityId, moduleId, deviceType);
             } catch (NetatmoException e) {
                 logger.warn("Error retrieving last events of camera '{}' : {}", moduleId, e.getMessage());
                 return null;
@@ -188,7 +185,7 @@ class SecurityCapability extends RestCapability<SecurityApi> {
     private Collection<HomeEvent> requestPersonEvents(String personId) {
         return getApi().map(api -> {
             try {
-                return api.getPersonEvents(handler.getId(), personId);
+                return api.getPersonEvents(securityId, personId);
             } catch (NetatmoException e) {
                 logger.warn("Error retrieving last events of person '{}' : {}", personId, e.getMessage());
                 return null;
@@ -199,7 +196,7 @@ class SecurityCapability extends RestCapability<SecurityApi> {
     public void setPersonAway(String personId, boolean away) {
         getApi().ifPresent(api -> {
             try {
-                api.setPersonAwayStatus(handler.getId(), personId, away);
+                api.setPersonAwayStatus(securityId, personId, away);
                 handler.expireData();
             } catch (NetatmoException e) {
                 logger.warn("Error setting person away/at home '{}' : {}", personId, e.getMessage());
@@ -208,9 +205,7 @@ class SecurityCapability extends RestCapability<SecurityApi> {
     }
 
     public @Nullable String ping(String vpnUrl) {
-        return getApi().map(api -> {
-            return api.ping(vpnUrl);
-        }).orElse(null);
+        return getApi().map(api -> api.ping(vpnUrl)).orElse(null);
     }
 
     public void changeStatus(@Nullable String localURL, boolean status) {
@@ -231,7 +226,7 @@ class SecurityCapability extends RestCapability<SecurityApi> {
     public void changeFloodlightMode(String cameraId, FloodLightMode mode) {
         getApi().ifPresent(api -> {
             try {
-                api.changeFloodLightMode(handler.getId(), cameraId, mode);
+                api.changeFloodLightMode(securityId, cameraId, mode);
                 handler.expireData();
             } catch (NetatmoException e) {
                 logger.warn("Error changing Presence floodlight mode '{}' : {}", mode, e.getMessage());
index 6aa4018c0166197151040977c08bbb37100cd445..808d58f4b1aaf95c2102a2ca8cb2886fd592920e 100644 (file)
        </config-description>
 
        <config-description uri="netatmo:home">
-               <parameter name="id" type="text" required="true">
+               <parameter name="id" type="text" required="false">
                        <label>@text/config.thingId.label</label>
                        <description>@text/config.thingId.description</description>
                </parameter>
 
+               <parameter name="securityId" type="text" required="false">
+                       <label>@text/config.securityId.label</label>
+                       <description>@text/config.securityId.description</description>
+                       <advanced>true</advanced>
+               </parameter>
+
+               <parameter name="energyId" type="text" required="false">
+                       <label>@text/config.energyId.label</label>
+                       <description>@text/config.energyId.description</description>
+                       <advanced>true</advanced>
+               </parameter>
+
                <parameter name="refreshInterval" type="integer" min="20" unit="s">
                        <label>@text/config.refreshInterval.label</label>
                        <description>@text/config.refreshInterval.description</description>
index afe1576dac7ec0e59289dbdb2037a3a5be81f498..7ebb55ae82a85abf258625bf7f3f85f0cbcd7311 100644 (file)
@@ -437,6 +437,10 @@ config.equipmentId.label = Equipment ID
 config.equipmentId.description = ID of the device (MAC address).
 config.thingId.label = Thing ID
 config.thingId.description = Unique identifier of the thing defined by Netatmo.
+config.securityId.label = Security ID
+config.securityId.description = Unique identifier of the home handling security devices.
+config.energyId.label = Energy ID
+config.energyId.description = Unique identifier of the home handling energy devices.
 config.refreshInterval.label = Refresh Interval
 config.refreshInterval.description = The refresh interval to poll Netatmo API (in seconds).