[Netatmo] Add the capability to have a single home (#14595)
* Adding the capability to have a single home, common to energy and security --------- Signed-off-by: clinique <gael@lhopital.org>
This commit is contained in:
parent
f2e7597db6
commit
f37c4e555d
@ -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:**
|
||||
|
||||
|
||||
@ -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) {
|
||||
|
||||
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
@ -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;
|
||||
}
|
||||
}
|
||||
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@ -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));
|
||||
}
|
||||
|
||||
@ -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()));
|
||||
|
||||
@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -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,30 +50,31 @@ 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);
|
||||
});
|
||||
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);
|
||||
});
|
||||
.forEach(bridgedModule -> childHandler.setNewData(bridgedModule));
|
||||
});
|
||||
});
|
||||
descriptionProvider.setStateOptions(new ChannelUID(thing.getUID(), GROUP_ENERGY, CHANNEL_PLANNING),
|
||||
@ -84,46 +87,22 @@ 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);
|
||||
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<EnergyApi> {
|
||||
});
|
||||
}
|
||||
|
||||
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<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();
|
||||
}
|
||||
}
|
||||
|
||||
@ -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 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<>();
|
||||
homeIds.stream().filter(id -> !id.isEmpty()).forEach(id -> {
|
||||
try {
|
||||
HomeData homeData = api.getHomeData(handler.getId());
|
||||
HomeData homeData = api.getHomeData(id);
|
||||
if (homeData != null) {
|
||||
result.add(homeData);
|
||||
persons = homeData.getPersons();
|
||||
modules = homeData.getModules();
|
||||
featureAreas.addAll(homeData.getFeatures());
|
||||
}
|
||||
HomeStatus homeStatus = api.getHomeStatus(handler.getId());
|
||||
HomeStatus homeStatus = api.getHomeStatus(id);
|
||||
if (homeStatus != null) {
|
||||
result.add(homeStatus);
|
||||
}
|
||||
} catch (NetatmoException e) {
|
||||
logger.warn("Error getting Home informations : {}", e.getMessage());
|
||||
}
|
||||
});
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
@ -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,
|
||||
|
||||
@ -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,26 +64,21 @@ 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);
|
||||
});
|
||||
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);
|
||||
});
|
||||
.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());
|
||||
}
|
||||
String cameraId = homeEvent.getCameraId();
|
||||
handler.getActiveChildren().stream().filter(handler -> cameraId.equals(handler.getId())).findFirst()
|
||||
.ifPresent(handler -> {
|
||||
homeEvent.setIgnoredForThingUpdate(true);
|
||||
handler.setNewData(homeEvent);
|
||||
});
|
||||
|
||||
private void addEventIfKnownObject(HomeEvent homeEvent, @Nullable String objectId) {
|
||||
if (objectId == null) {
|
||||
return;
|
||||
}
|
||||
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());
|
||||
|
||||
@ -111,11 +111,23 @@
|
||||
</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>
|
||||
|
||||
@ -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).
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user