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