diff --git a/bundles/org.openhab.binding.netatmo/README.md b/bundles/org.openhab.binding.netatmo/README.md index 21fea4fce..fa85b9bab 100644 --- a/bundles/org.openhab.binding.netatmo/README.md +++ b/bundles/org.openhab.binding.netatmo/README.md @@ -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. -**Supported channels for the Home thing:** +**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 + +(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:** diff --git a/bundles/org.openhab.binding.netatmo/src/main/java/org/openhab/binding/netatmo/internal/action/RoomActions.java b/bundles/org.openhab.binding.netatmo/src/main/java/org/openhab/binding/netatmo/internal/action/RoomActions.java index 9b20e29db..ac773acd5 100644 --- a/bundles/org.openhab.binding.netatmo/src/main/java/org/openhab/binding/netatmo/internal/action/RoomActions.java +++ b/bundles/org.openhab.binding.netatmo/src/main/java/org/openhab/binding/netatmo/internal/action/RoomActions.java @@ -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) { diff --git a/bundles/org.openhab.binding.netatmo/src/main/java/org/openhab/binding/netatmo/internal/api/dto/NAObject.java b/bundles/org.openhab.binding.netatmo/src/main/java/org/openhab/binding/netatmo/internal/api/dto/NAObject.java index 63687b6ee..05ab286eb 100644 --- a/bundles/org.openhab.binding.netatmo/src/main/java/org/openhab/binding/netatmo/internal/api/dto/NAObject.java +++ b/bundles/org.openhab.binding.netatmo/src/main/java/org/openhab/binding/netatmo/internal/api/dto/NAObject.java @@ -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; } } diff --git a/bundles/org.openhab.binding.netatmo/src/main/java/org/openhab/binding/netatmo/internal/api/dto/NAThing.java b/bundles/org.openhab.binding.netatmo/src/main/java/org/openhab/binding/netatmo/internal/api/dto/NAThing.java index 61171a8fb..aa181887b 100644 --- a/bundles/org.openhab.binding.netatmo/src/main/java/org/openhab/binding/netatmo/internal/api/dto/NAThing.java +++ b/bundles/org.openhab.binding.netatmo/src/main/java/org/openhab/binding/netatmo/internal/api/dto/NAThing.java @@ -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 index 000000000..d7f1cbf75 --- /dev/null +++ b/bundles/org.openhab.binding.netatmo/src/main/java/org/openhab/binding/netatmo/internal/config/HomeConfiguration.java @@ -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; + } +} diff --git a/bundles/org.openhab.binding.netatmo/src/main/java/org/openhab/binding/netatmo/internal/config/NAThingConfiguration.java b/bundles/org.openhab.binding.netatmo/src/main/java/org/openhab/binding/netatmo/internal/config/NAThingConfiguration.java index da036cb6c..c2870228d 100644 --- a/bundles/org.openhab.binding.netatmo/src/main/java/org/openhab/binding/netatmo/internal/config/NAThingConfiguration.java +++ b/bundles/org.openhab.binding.netatmo/src/main/java/org/openhab/binding/netatmo/internal/config/NAThingConfiguration.java @@ -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; + } } diff --git a/bundles/org.openhab.binding.netatmo/src/main/java/org/openhab/binding/netatmo/internal/handler/CommonInterface.java b/bundles/org.openhab.binding.netatmo/src/main/java/org/openhab/binding/netatmo/internal/handler/CommonInterface.java index 05e1ece16..14d6823cc 100644 --- a/bundles/org.openhab.binding.netatmo/src/main/java/org/openhab/binding/netatmo/internal/handler/CommonInterface.java +++ b/bundles/org.openhab.binding.netatmo/src/main/java/org/openhab/binding/netatmo/internal/handler/CommonInterface.java @@ -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 getActiveChannels() { @@ -135,6 +141,10 @@ public interface CommonInterface { return List.of(); } + default Stream getActiveChildren(FeatureArea area) { + return getActiveChildren().stream().filter(child -> child.getModuleType().feature == area); + } + default > Optional getHomeCapability(Class 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)); } 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 35b25ad63..006e52553 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 @@ -89,7 +89,7 @@ public class CameraCapability extends HomeSecurityThingCapability { @Override protected void beforeNewData() { super.beforeNewData(); - homeCapability.ifPresent(cap -> { + securityCapability.ifPresent(cap -> { NAObjectMap persons = cap.getPersons(); descriptionProvider.setStateOptions(personChannelUID, persons.values().stream() .map(p -> new StateOption(p.getId(), p.getName())).collect(Collectors.toList())); diff --git a/bundles/org.openhab.binding.netatmo/src/main/java/org/openhab/binding/netatmo/internal/handler/capability/CapabilityMap.java b/bundles/org.openhab.binding.netatmo/src/main/java/org/openhab/binding/netatmo/internal/handler/capability/CapabilityMap.java index 002141f0d..033d84b57 100644 --- a/bundles/org.openhab.binding.netatmo/src/main/java/org/openhab/binding/netatmo/internal/handler/capability/CapabilityMap.java +++ b/bundles/org.openhab.binding.netatmo/src/main/java/org/openhab/binding/netatmo/internal/handler/capability/CapabilityMap.java @@ -28,9 +28,10 @@ public class CapabilityMap extends ConcurrentHashMap, Capability> { private static final long serialVersionUID = -3043492242108419801L; public void put(Capability capability) { - Class clazz = capability.getClass(); + Class clazz = capability.getClass(); if (super.get(clazz) == null) { super.put(clazz, capability); + capability.initialize(); } } diff --git a/bundles/org.openhab.binding.netatmo/src/main/java/org/openhab/binding/netatmo/internal/handler/capability/EnergyCapability.java b/bundles/org.openhab.binding.netatmo/src/main/java/org/openhab/binding/netatmo/internal/handler/capability/EnergyCapability.java index 84630117b..efd6eb225 100644 --- a/bundles/org.openhab.binding.netatmo/src/main/java/org/openhab/binding/netatmo/internal/handler/capability/EnergyCapability.java +++ b/bundles/org.openhab.binding.netatmo/src/main/java/org/openhab/binding/netatmo/internal/handler/capability/EnergyCapability.java @@ -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 { 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 rooms = homeData.getRooms(); NAObjectMap 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,46 +87,22 @@ public class EnergyCapability extends RestCapability { protected void updateHomeStatus(HomeStatus homeStatus) { NAObjectMap rooms = homeStatus.getRooms(); NAObjectMap 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); - handler.expireData(); - } catch (NetatmoException e) { - logger.warn("Error setting room thermostat mode '{}' : {}", targetMode, 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); + api.setThermpoint(energyId, roomId, mode, endtime, temp); handler.expireData(); } catch (NetatmoException e) { logger.warn("Error setting room thermostat mode '{}' : {}", mode, e.getMessage()); @@ -131,8 +110,16 @@ public class EnergyCapability extends RestCapability { }); } - public void setRoomThermTemp(String roomId, double temperature) { - setRoomThermTemp(roomId, temperature, setpointEndTimeFromNow(setPointDefaultDuration), SetpointMode.MANUAL); + 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 temp) { + setThermPoint(roomId, SetpointMode.MANUAL, setpointEndTimeFromNow(), temp); } @Override @@ -141,7 +128,7 @@ public class EnergyCapability extends RestCapability { 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 { 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 { }); } - private static long setpointEndTimeFromNow(int duration_min) { - return ZonedDateTime.now().plusMinutes(duration_min).toEpochSecond(); + private long setpointEndTimeFromNow() { + return ZonedDateTime.now().plusMinutes(setPointDefaultDuration).toEpochSecond(); } } 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 93f3a6b92..79891fc43 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 @@ -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 { + private final Logger logger = LoggerFactory.getLogger(HomeCapability.class); - + private final Set featureAreas = new HashSet<>(); private final NetatmoDescriptionProvider descriptionProvider; - - private NAObjectMap persons = new NAObjectMap<>(); - private NAObjectMap modules = new NAObjectMap<>(); - - private Set featuresArea = Set.of(); + private Set 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 getPersons() { - return persons; - } - - public NAObjectMap getModules() { - return modules; + private boolean hasArea(FeatureArea searched) { + return featureAreas.contains(searched); } @Override protected List updateReadings(HomeApi api) { List result = new ArrayList<>(); - try { - HomeData homeData = api.getHomeData(handler.getId()); - if (homeData != null) { - result.add(homeData); - persons = homeData.getPersons(); - modules = homeData.getModules(); + 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()); } - HomeStatus homeStatus = api.getHomeStatus(handler.getId()); - if (homeStatus != null) { - result.add(homeStatus); - } - } catch (NetatmoException e) { - 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 3d07c89f8..596fad6a5 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 @@ -56,7 +56,7 @@ public class PersonCapability extends HomeSecurityThingCapability { @Override protected void beforeNewData() { super.beforeNewData(); - homeCapability.ifPresent(cap -> { + securityCapability.ifPresent(cap -> { Stream cameras = cap.getModules().values().stream() .filter(module -> module.getType() == ModuleType.WELCOME); descriptionProvider.setStateOptions(cameraChannelUID, 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 1bc19c8e9..c2b8fd41a 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 @@ -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 { private final Map eventBuffer = new HashMap<>(); private @Nullable ZonedDateTime freshestEventTime; + private NAObjectMap persons = new NAObjectMap<>(); + private NAObjectMap modules = new NAObjectMap<>(); + private String securityId = ""; + SecurityCapability(CommonInterface handler) { super(handler, SecurityApi.class); } @@ -58,27 +64,22 @@ class SecurityCapability extends RestCapability { public void initialize() { super.initialize(); freshestEventTime = null; + securityId = handler.getConfiguration().as(HomeConfiguration.class).getIdForArea(FeatureArea.SECURITY); } @Override protected void updateHomeData(HomeData homeData) { - NAObjectMap persons = homeData.getPersons(); - NAObjectMap 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 { protected void updateHomeStatus(HomeStatus homeStatus) { NAObjectMap persons = homeStatus.getPersons(); NAObjectMap 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 { @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 updateReadings(SecurityApi api) { List 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 { } } } 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 getPersons() { + return persons; + } + + public NAObjectMap 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 { private Collection 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 { private Collection 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 { 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 { } 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 { 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()); diff --git a/bundles/org.openhab.binding.netatmo/src/main/resources/OH-INF/config/config.xml b/bundles/org.openhab.binding.netatmo/src/main/resources/OH-INF/config/config.xml index 6aa4018c0..808d58f4b 100644 --- a/bundles/org.openhab.binding.netatmo/src/main/resources/OH-INF/config/config.xml +++ b/bundles/org.openhab.binding.netatmo/src/main/resources/OH-INF/config/config.xml @@ -111,11 +111,23 @@ - + @text/config.thingId.description + + + @text/config.securityId.description + true + + + + + @text/config.energyId.description + true + + @text/config.refreshInterval.description diff --git a/bundles/org.openhab.binding.netatmo/src/main/resources/OH-INF/i18n/netatmo.properties b/bundles/org.openhab.binding.netatmo/src/main/resources/OH-INF/i18n/netatmo.properties index afe1576da..7ebb55ae8 100644 --- a/bundles/org.openhab.binding.netatmo/src/main/resources/OH-INF/i18n/netatmo.properties +++ b/bundles/org.openhab.binding.netatmo/src/main/resources/OH-INF/i18n/netatmo.properties @@ -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).