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