]> git.basschouten.com Git - openhab-addons.git/blob
c80eec1df168366819abe9bb115767c5ba8ccb5b
[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;
14
15 import java.util.ArrayList;
16 import java.util.List;
17 import java.util.Objects;
18 import java.util.Optional;
19 import java.util.concurrent.ScheduledExecutorService;
20 import java.util.concurrent.TimeUnit;
21 import java.util.stream.Collectors;
22 import java.util.stream.Stream;
23
24 import org.eclipse.jdt.annotation.NonNullByDefault;
25 import org.eclipse.jdt.annotation.Nullable;
26 import org.openhab.binding.netatmo.internal.api.data.ModuleType;
27 import org.openhab.binding.netatmo.internal.api.data.NetatmoConstants.FeatureArea;
28 import org.openhab.binding.netatmo.internal.api.dto.NAObject;
29 import org.openhab.binding.netatmo.internal.api.dto.NAThing;
30 import org.openhab.binding.netatmo.internal.config.NAThingConfiguration;
31 import org.openhab.binding.netatmo.internal.handler.capability.Capability;
32 import org.openhab.binding.netatmo.internal.handler.capability.CapabilityMap;
33 import org.openhab.binding.netatmo.internal.handler.capability.HomeCapability;
34 import org.openhab.binding.netatmo.internal.handler.capability.RefreshCapability;
35 import org.openhab.binding.netatmo.internal.handler.capability.RestCapability;
36 import org.openhab.core.config.core.Configuration;
37 import org.openhab.core.thing.Bridge;
38 import org.openhab.core.thing.Channel;
39 import org.openhab.core.thing.ChannelUID;
40 import org.openhab.core.thing.Thing;
41 import org.openhab.core.thing.ThingStatus;
42 import org.openhab.core.thing.ThingStatusDetail;
43 import org.openhab.core.thing.binding.BridgeHandler;
44 import org.openhab.core.thing.binding.builder.ThingBuilder;
45 import org.openhab.core.thing.type.ChannelKind;
46 import org.openhab.core.types.Command;
47 import org.openhab.core.types.RefreshType;
48 import org.openhab.core.types.State;
49 import org.slf4j.Logger;
50
51 /**
52  * {@link CommonInterface} defines common methods of AccountHandler and NAThingHandlers used by Capabilities
53  *
54  * @author GaĆ«l L'hopital - Initial contribution
55  *
56  */
57 @NonNullByDefault
58 public interface CommonInterface {
59     Thing getThing();
60
61     ThingBuilder editThing();
62
63     CapabilityMap getCapabilities();
64
65     Logger getLogger();
66
67     ScheduledExecutorService getScheduler();
68
69     boolean isLinked(ChannelUID channelUID);
70
71     void updateState(ChannelUID channelUID, State state);
72
73     void setThingStatus(ThingStatus thingStatus, ThingStatusDetail thingStatusDetail,
74             @Nullable String thingStatusReason);
75
76     void triggerChannel(String channelID, String event);
77
78     void updateThing(Thing thing);
79
80     @Nullable
81     Bridge getBridge();
82
83     default @Nullable CommonInterface getBridgeHandler() {
84         Bridge bridge = getBridge();
85         return bridge != null && bridge.getHandler() instanceof DeviceHandler ? (DeviceHandler) bridge.getHandler()
86                 : null;
87     }
88
89     default @Nullable ApiBridgeHandler getAccountHandler() {
90         Bridge bridge = getBridge();
91         BridgeHandler bridgeHandler = null;
92         if (bridge != null) {
93             bridgeHandler = bridge.getHandler();
94             while (bridgeHandler != null && !(bridgeHandler instanceof ApiBridgeHandler)) {
95                 bridge = ((CommonInterface) bridgeHandler).getBridge();
96                 bridgeHandler = bridge != null ? bridge.getHandler() : null;
97             }
98         }
99         return (ApiBridgeHandler) bridgeHandler;
100     }
101
102     default @Nullable String getBridgeId() {
103         CommonInterface bridge = getBridgeHandler();
104         return bridge != null ? bridge.getId() : null;
105     }
106
107     default void expireData() {
108         getCapabilities().values().forEach(cap -> cap.expireData());
109     }
110
111     default String getId() {
112         return getConfiguration().as(NAThingConfiguration.class).getId();
113     }
114
115     default Configuration getConfiguration() {
116         return getThing().getConfiguration();
117     }
118
119     default Stream<Channel> getActiveChannels() {
120         return getThing().getChannels().stream()
121                 .filter(channel -> ChannelKind.STATE.equals(channel.getKind()) && isLinked(channel.getUID()));
122     }
123
124     default Optional<CommonInterface> recurseUpToHomeHandler(@Nullable CommonInterface handler) {
125         if (handler == null) {
126             return Optional.empty();
127         }
128         return handler.getCapabilities().get(HomeCapability.class).isPresent() ? Optional.of(handler)
129                 : recurseUpToHomeHandler(handler.getBridgeHandler());
130     }
131
132     default List<CommonInterface> getActiveChildren() {
133         Thing thing = getThing();
134         if (thing instanceof Bridge) {
135             return ((Bridge) thing).getThings().stream().filter(Thing::isEnabled)
136                     .filter(th -> th.getStatusInfo().getStatusDetail() != ThingStatusDetail.BRIDGE_OFFLINE)
137                     .map(Thing::getHandler).filter(Objects::nonNull).map(CommonInterface.class::cast)
138                     .collect(Collectors.toList());
139         }
140         return List.of();
141     }
142
143     default Stream<CommonInterface> getActiveChildren(FeatureArea area) {
144         return getActiveChildren().stream().filter(child -> child.getModuleType().feature == area);
145     }
146
147     default <T extends RestCapability<?>> Optional<T> getHomeCapability(Class<T> clazz) {
148         return recurseUpToHomeHandler(this).map(handler -> handler.getCapabilities().get(clazz))
149                 .orElse(Optional.empty());
150     }
151
152     default void setNewData(NAObject newData) {
153         if (newData instanceof NAThing) {
154             NAThing thingData = (NAThing) newData;
155             if (getId().equals(thingData.getBridge())) {
156                 getActiveChildren().stream().filter(child -> child.getId().equals(thingData.getId())).findFirst()
157                         .ifPresent(child -> child.setNewData(thingData));
158                 return;
159             }
160         }
161         String finalReason = null;
162         for (Capability cap : getCapabilities().values()) {
163             String thingStatusReason = cap.setNewData(newData);
164             if (thingStatusReason != null) {
165                 finalReason = thingStatusReason;
166             }
167         }
168         // Prevent turning ONLINE myself if in the meantime something turned account OFFLINE
169         ApiBridgeHandler accountHandler = getAccountHandler();
170         if (accountHandler != null && accountHandler.isConnected() && !newData.isIgnoredForThingUpdate()) {
171             setThingStatus(finalReason == null ? ThingStatus.ONLINE : ThingStatus.OFFLINE, ThingStatusDetail.NONE,
172                     finalReason);
173         }
174     }
175
176     default void commonHandleCommand(ChannelUID channelUID, Command command) {
177         if (ThingStatus.ONLINE.equals(getThing().getStatus())) {
178             if (command == RefreshType.REFRESH) {
179                 expireData();
180                 return;
181             }
182             String channelName = channelUID.getIdWithoutGroup();
183             getCapabilities().values().forEach(cap -> cap.handleCommand(channelName, command));
184         } else {
185             getLogger().debug("Command {}, on channel {} dropped - thing is not ONLINE", command, channelUID);
186         }
187     }
188
189     default void proceedWithUpdate() {
190         updateReadings().forEach(dataSet -> setNewData(dataSet));
191     }
192
193     default List<NAObject> updateReadings() {
194         List<NAObject> result = new ArrayList<>();
195         getCapabilities().values().forEach(cap -> result.addAll(cap.updateReadings()));
196         getActiveChildren().forEach(child -> result.addAll(child.updateReadings()));
197         return result;
198     }
199
200     default void commonInitialize() {
201         Bridge bridge = getBridge();
202         if (bridge == null || bridge.getHandler() == null) {
203             setThingStatus(ThingStatus.OFFLINE, ThingStatusDetail.BRIDGE_UNINITIALIZED, null);
204         } else if (!ThingStatus.ONLINE.equals(bridge.getStatus())) {
205             setThingStatus(ThingStatus.OFFLINE, ThingStatusDetail.BRIDGE_OFFLINE, null);
206             removeRefreshCapability();
207         } else {
208             setThingStatus(ThingStatus.UNKNOWN, ThingStatusDetail.NONE, null);
209             setRefreshCapability();
210             getScheduler().schedule(() -> {
211                 CommonInterface bridgeHandler = getBridgeHandler();
212                 if (bridgeHandler != null) {
213                     bridgeHandler.expireData();
214                 }
215             }, 1, TimeUnit.SECONDS);
216         }
217     }
218
219     default ModuleType getModuleType() {
220         return ModuleType.from(getThing().getThingTypeUID());
221     }
222
223     default void setRefreshCapability() {
224         if (ModuleType.ACCOUNT.equals(getModuleType().getBridge())) {
225             NAThingConfiguration config = getThing().getConfiguration().as(NAThingConfiguration.class);
226             getCapabilities().put(new RefreshCapability(this, getScheduler(), config.refreshInterval));
227         }
228     }
229
230     default void removeRefreshCapability() {
231         Capability refreshCap = getCapabilities().remove(RefreshCapability.class);
232         if (refreshCap != null) {
233             refreshCap.dispose();
234         }
235     }
236
237     default void commonDispose() {
238         getCapabilities().values().forEach(Capability::dispose);
239     }
240
241     default void removeChannels(List<Channel> channels) {
242         ThingBuilder builder = editThing().withoutChannels(channels);
243         updateThing(builder.build());
244     }
245 }