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