]> git.basschouten.com Git - openhab-addons.git/blob
c2b8fd41a81f9d01e882ee5916d4999554d08fe8
[openhab-addons.git] /
1 /**
2  * Copyright (c) 2010-2023 Contributors to the openHAB project
3  *
4  * See the NOTICE file(s) distributed with this work for additional
5  * information.
6  *
7  * This program and the accompanying materials are made available under the
8  * terms of the Eclipse Public License 2.0 which is available at
9  * http://www.eclipse.org/legal/epl-2.0
10  *
11  * SPDX-License-Identifier: EPL-2.0
12  */
13 package org.openhab.binding.netatmo.internal.handler.capability;
14
15 import java.time.ZonedDateTime;
16 import java.util.ArrayList;
17 import java.util.Collection;
18 import java.util.HashMap;
19 import java.util.List;
20 import java.util.Map;
21
22 import org.eclipse.jdt.annotation.NonNullByDefault;
23 import org.eclipse.jdt.annotation.Nullable;
24 import org.openhab.binding.netatmo.internal.api.NetatmoException;
25 import org.openhab.binding.netatmo.internal.api.SecurityApi;
26 import org.openhab.binding.netatmo.internal.api.data.NetatmoConstants.FeatureArea;
27 import org.openhab.binding.netatmo.internal.api.data.NetatmoConstants.FloodLightMode;
28 import org.openhab.binding.netatmo.internal.api.dto.HomeData;
29 import org.openhab.binding.netatmo.internal.api.dto.HomeDataModule;
30 import org.openhab.binding.netatmo.internal.api.dto.HomeDataPerson;
31 import org.openhab.binding.netatmo.internal.api.dto.HomeEvent;
32 import org.openhab.binding.netatmo.internal.api.dto.HomeStatusModule;
33 import org.openhab.binding.netatmo.internal.api.dto.HomeStatusPerson;
34 import org.openhab.binding.netatmo.internal.api.dto.NAHomeStatus.HomeStatus;
35 import org.openhab.binding.netatmo.internal.api.dto.NAObject;
36 import org.openhab.binding.netatmo.internal.config.HomeConfiguration;
37 import org.openhab.binding.netatmo.internal.deserialization.NAObjectMap;
38 import org.openhab.binding.netatmo.internal.handler.CommonInterface;
39 import org.slf4j.Logger;
40 import org.slf4j.LoggerFactory;
41
42 /**
43  * The {@link SecurityCapability} is the base class for handler able to handle security features
44  *
45  * @author GaĆ«l L'hopital - Initial contribution
46  *
47  */
48 @NonNullByDefault
49 class SecurityCapability extends RestCapability<SecurityApi> {
50     private final Logger logger = LoggerFactory.getLogger(SecurityCapability.class);
51
52     private final Map<String, HomeEvent> eventBuffer = new HashMap<>();
53     private @Nullable ZonedDateTime freshestEventTime;
54
55     private NAObjectMap<HomeDataPerson> persons = new NAObjectMap<>();
56     private NAObjectMap<HomeDataModule> modules = new NAObjectMap<>();
57     private String securityId = "";
58
59     SecurityCapability(CommonInterface handler) {
60         super(handler, SecurityApi.class);
61     }
62
63     @Override
64     public void initialize() {
65         super.initialize();
66         freshestEventTime = null;
67         securityId = handler.getConfiguration().as(HomeConfiguration.class).getIdForArea(FeatureArea.SECURITY);
68     }
69
70     @Override
71     protected void updateHomeData(HomeData homeData) {
72         persons = homeData.getPersons();
73         modules = homeData.getModules();
74         handler.getActiveChildren(FeatureArea.SECURITY).forEach(childHandler -> {
75             String childId = childHandler.getId();
76             persons.getOpt(childId)
77                     .ifPresentOrElse(personData -> childHandler.setNewData(personData.ignoringForThingUpdate()), () -> {
78                         modules.getOpt(childId)
79                                 .ifPresent(childData -> childHandler.setNewData(childData.ignoringForThingUpdate()));
80                         modules.values().stream().filter(module -> childId.equals(module.getBridge()))
81                                 .forEach(bridgedModule -> childHandler.setNewData(bridgedModule));
82                     });
83         });
84     }
85
86     @Override
87     protected void updateHomeStatus(HomeStatus homeStatus) {
88         NAObjectMap<HomeStatusPerson> persons = homeStatus.getPersons();
89         NAObjectMap<HomeStatusModule> modules = homeStatus.getModules();
90         handler.getActiveChildren(FeatureArea.SECURITY).forEach(childHandler -> {
91             String childId = childHandler.getId();
92             persons.getOpt(childId).ifPresentOrElse(personData -> childHandler.setNewData(personData), () -> {
93                 modules.getOpt(childId).ifPresent(childData -> {
94                     childHandler.setNewData(childData);
95                     modules.values().stream().filter(module -> childId.equals(module.getBridge()))
96                             .forEach(bridgedModule -> childHandler.setNewData(bridgedModule));
97                 });
98             });
99         });
100     }
101
102     @Override
103     protected void updateHomeEvent(HomeEvent homeEvent) {
104         addEventIfKnownObject(homeEvent, homeEvent.getPersonId());
105         addEventIfKnownObject(homeEvent, homeEvent.getCameraId());
106     }
107
108     private void addEventIfKnownObject(HomeEvent homeEvent, @Nullable String objectId) {
109         if (objectId == null) {
110             return;
111         }
112         handler.getActiveChildren(FeatureArea.SECURITY).filter(child -> child.getId().equals(objectId))
113                 .forEach(child -> child.setNewData(homeEvent.ignoringForThingUpdate()));
114     }
115
116     @Override
117     protected List<NAObject> updateReadings(SecurityApi api) {
118         List<NAObject> result = new ArrayList<>();
119         try {
120             for (HomeEvent event : api.getHomeEvents(securityId, freshestEventTime)) {
121                 HomeEvent previousEvent = eventBuffer.get(event.getCameraId());
122                 if (previousEvent == null || previousEvent.getTime().isBefore(event.getTime())) {
123                     eventBuffer.put(event.getCameraId(), event);
124                 }
125                 String personId = event.getPersonId();
126                 if (personId != null) {
127                     previousEvent = eventBuffer.get(personId);
128                     if (previousEvent == null || previousEvent.getTime().isBefore(event.getTime())) {
129                         eventBuffer.put(personId, event);
130                     }
131                 }
132                 if (freshestEventTime == null || event.getTime().isAfter(freshestEventTime)) {
133                     freshestEventTime = event.getTime();
134                 }
135             }
136         } catch (NetatmoException e) {
137             logger.warn("Error retrieving last events for home '{}' : {}", securityId, e.getMessage());
138         }
139         return result;
140     }
141
142     public NAObjectMap<HomeDataPerson> getPersons() {
143         return persons;
144     }
145
146     public NAObjectMap<HomeDataModule> getModules() {
147         return modules;
148     }
149
150     public @Nullable HomeEvent getLastPersonEvent(String personId) {
151         HomeEvent event = eventBuffer.get(personId);
152         if (event == null) {
153             Collection<HomeEvent> events = requestPersonEvents(personId);
154             if (!events.isEmpty()) {
155                 event = events.iterator().next();
156                 eventBuffer.put(personId, event);
157             }
158         }
159         return event;
160     }
161
162     public @Nullable HomeEvent getDeviceLastEvent(String moduleId, String deviceType) {
163         HomeEvent event = eventBuffer.get(moduleId);
164         if (event == null) {
165             Collection<HomeEvent> events = requestDeviceEvents(moduleId, deviceType);
166             if (!events.isEmpty()) {
167                 event = events.iterator().next();
168                 eventBuffer.put(moduleId, event);
169             }
170         }
171         return event;
172     }
173
174     private Collection<HomeEvent> requestDeviceEvents(String moduleId, String deviceType) {
175         return getApi().map(api -> {
176             try {
177                 return api.getDeviceEvents(securityId, moduleId, deviceType);
178             } catch (NetatmoException e) {
179                 logger.warn("Error retrieving last events of camera '{}' : {}", moduleId, e.getMessage());
180                 return null;
181             }
182         }).orElse(List.of());
183     }
184
185     private Collection<HomeEvent> requestPersonEvents(String personId) {
186         return getApi().map(api -> {
187             try {
188                 return api.getPersonEvents(securityId, personId);
189             } catch (NetatmoException e) {
190                 logger.warn("Error retrieving last events of person '{}' : {}", personId, e.getMessage());
191                 return null;
192             }
193         }).orElse(List.of());
194     }
195
196     public void setPersonAway(String personId, boolean away) {
197         getApi().ifPresent(api -> {
198             try {
199                 api.setPersonAwayStatus(securityId, personId, away);
200                 handler.expireData();
201             } catch (NetatmoException e) {
202                 logger.warn("Error setting person away/at home '{}' : {}", personId, e.getMessage());
203             }
204         });
205     }
206
207     public @Nullable String ping(String vpnUrl) {
208         return getApi().map(api -> api.ping(vpnUrl)).orElse(null);
209     }
210
211     public void changeStatus(@Nullable String localURL, boolean status) {
212         if (localURL == null) {
213             logger.info("Monitoring changes can only be done on local camera.");
214             return;
215         }
216         getApi().ifPresent(api -> {
217             try {
218                 api.changeStatus(localURL, status);
219                 handler.expireData();
220             } catch (NetatmoException e) {
221                 logger.warn("Error changing camera monitoring status '{}' : {}", status, e.getMessage());
222             }
223         });
224     }
225
226     public void changeFloodlightMode(String cameraId, FloodLightMode mode) {
227         getApi().ifPresent(api -> {
228             try {
229                 api.changeFloodLightMode(securityId, cameraId, mode);
230                 handler.expireData();
231             } catch (NetatmoException e) {
232                 logger.warn("Error changing Presence floodlight mode '{}' : {}", mode, e.getMessage());
233             }
234         });
235     }
236 }