]> git.basschouten.com Git - openhab-addons.git/blob
51beb8de5ccd8fb8e4464f83ec61848186e68baa
[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.time.Duration;
16 import java.util.ArrayList;
17 import java.util.List;
18 import java.util.Optional;
19 import java.util.concurrent.ScheduledExecutorService;
20 import java.util.concurrent.ScheduledFuture;
21 import java.util.concurrent.TimeUnit;
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.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     default void updateState(String groupId, String id, State state) {
72         updateState(new ChannelUID(getThing().getUID(), groupId, id), state);
73     }
74
75     void setThingStatus(ThingStatus thingStatus, ThingStatusDetail thingStatusDetail,
76             @Nullable String thingStatusReason);
77
78     void triggerChannel(String channelID, String event);
79
80     void updateThing(Thing thing);
81
82     @Nullable
83     Bridge getBridge();
84
85     default @Nullable CommonInterface getBridgeHandler() {
86         Bridge bridge = getBridge();
87         return bridge != null && bridge.getHandler() instanceof DeviceHandler ? (DeviceHandler) bridge.getHandler()
88                 : null;
89     }
90
91     default Optional<ScheduledFuture<?>> schedule(Runnable arg0, Duration delay) {
92         return Optional.of(getScheduler().schedule(arg0, delay.getSeconds(), TimeUnit.SECONDS));
93     }
94
95     default @Nullable ApiBridgeHandler getAccountHandler() {
96         Bridge bridge = getBridge();
97         BridgeHandler bridgeHandler = null;
98         if (bridge != null) {
99             bridgeHandler = bridge.getHandler();
100             while (bridgeHandler != null && !(bridgeHandler instanceof ApiBridgeHandler)) {
101                 bridge = ((CommonInterface) bridgeHandler).getBridge();
102                 bridgeHandler = bridge != null ? bridge.getHandler() : null;
103             }
104         }
105         return (ApiBridgeHandler) bridgeHandler;
106     }
107
108     default @Nullable String getBridgeId() {
109         CommonInterface bridge = getBridgeHandler();
110         return bridge != null ? bridge.getId() : null;
111     }
112
113     default void expireData() {
114         getCapabilities().values().forEach(Capability::expireData);
115     }
116
117     default String getId() {
118         return getThingConfigAs(NAThingConfiguration.class).getId();
119     }
120
121     default <T> T getThingConfigAs(Class<T> configurationClass) {
122         return getThing().getConfiguration().as(configurationClass);
123     }
124
125     default Stream<Channel> getActiveChannels() {
126         return getThing().getChannels().stream()
127                 .filter(channel -> ChannelKind.STATE.equals(channel.getKind()) && isLinked(channel.getUID()));
128     }
129
130     default Optional<CommonInterface> recurseUpToHomeHandler(@Nullable CommonInterface handler) {
131         if (handler == null) {
132             return Optional.empty();
133         }
134         return handler.getCapabilities().get(HomeCapability.class).isPresent() ? Optional.of(handler)
135                 : recurseUpToHomeHandler(handler.getBridgeHandler());
136     }
137
138     /**
139      * Recurses down in the home/module/device tree
140      *
141      * @param bridge
142      * @return the list of childs of the bridge
143      */
144     default List<CommonInterface> getAllActiveChildren(Bridge bridge) {
145         List<CommonInterface> result = new ArrayList<>();
146         bridge.getThings().stream().filter(Thing::isEnabled).map(Thing::getHandler).forEach(childHandler -> {
147             if (childHandler != null) {
148                 Thing childThing = childHandler.getThing();
149                 if (childThing instanceof Bridge bridgeChild) {
150                     result.addAll(getAllActiveChildren(bridgeChild));
151                 }
152                 result.add((CommonInterface) childHandler);
153             }
154         });
155         return result;
156     }
157
158     default List<CommonInterface> getActiveChildren() {
159         return getThing() instanceof Bridge bridge
160                 ? bridge.getThings().stream().filter(Thing::isEnabled)
161                         .filter(th -> th.getStatusInfo().getStatusDetail() != ThingStatusDetail.BRIDGE_OFFLINE)
162                         .map(Thing::getHandler).filter(CommonInterface.class::isInstance)
163                         .map(CommonInterface.class::cast).toList()
164                 : List.of();
165     }
166
167     default Stream<CommonInterface> getActiveChildren(FeatureArea area) {
168         return getActiveChildren().stream().filter(child -> child.getModuleType().feature == area);
169     }
170
171     default <T extends RestCapability<?>> Optional<T> getHomeCapability(Class<T> clazz) {
172         return recurseUpToHomeHandler(this).map(handler -> handler.getCapabilities().get(clazz))
173                 .orElse(Optional.empty());
174     }
175
176     default void setNewData(NAObject newData) {
177         if (newData instanceof NAThing thingData) {
178             if (getId().equals(thingData.getBridge())) {
179                 getActiveChildren().stream().filter(child -> child.getId().equals(thingData.getId())).findFirst()
180                         .ifPresent(child -> child.setNewData(thingData));
181                 return;
182             }
183         }
184         String finalReason = null;
185         for (Capability cap : getCapabilities().values()) {
186             String thingStatusReason = cap.setNewData(newData);
187             if (thingStatusReason != null) {
188                 finalReason = thingStatusReason;
189             }
190         }
191         // Prevent turning ONLINE myself if in the meantime something turned account OFFLINE
192         ApiBridgeHandler accountHandler = getAccountHandler();
193         if (accountHandler != null && accountHandler.isConnected() && !newData.isIgnoredForThingUpdate()) {
194             setThingStatus(finalReason == null ? ThingStatus.ONLINE : ThingStatus.OFFLINE, ThingStatusDetail.NONE,
195                     finalReason);
196         }
197     }
198
199     default void commonHandleCommand(ChannelUID channelUID, Command command) {
200         if (ThingStatus.ONLINE.equals(getThing().getStatus())) {
201             if (command == RefreshType.REFRESH) {
202                 expireData();
203                 return;
204             }
205             String channelName = channelUID.getIdWithoutGroup();
206             getCapabilities().values().forEach(cap -> cap.handleCommand(channelName, command));
207         } else {
208             getLogger().debug("Command {} on channel {} dropped - thing is not ONLINE", command, channelUID);
209         }
210     }
211
212     default void proceedWithUpdate() {
213         updateReadings().forEach(this::setNewData);
214     }
215
216     default List<NAObject> updateReadings() {
217         List<NAObject> result = new ArrayList<>();
218         getCapabilities().values().forEach(cap -> result.addAll(cap.updateReadings()));
219         getActiveChildren().forEach(child -> result.addAll(child.updateReadings()));
220         return result;
221     }
222
223     default void commonInitialize() {
224         Bridge bridge = getBridge();
225         if (bridge == null || bridge.getHandler() == null) {
226             setThingStatus(ThingStatus.OFFLINE, ThingStatusDetail.BRIDGE_UNINITIALIZED, null);
227         } else if (!ThingStatus.ONLINE.equals(bridge.getStatus())) {
228             setThingStatus(ThingStatus.OFFLINE, ThingStatusDetail.BRIDGE_OFFLINE, null);
229             getCapabilities().getParentUpdate().ifPresent(Capability::dispose);
230             getCapabilities().getRefresh().ifPresent(Capability::dispose);
231         } else {
232             setThingStatus(ThingStatus.UNKNOWN, ThingStatusDetail.NONE, null);
233             getCapabilities().getParentUpdate().ifPresentOrElse(Capability::initialize, () -> {
234                 int interval = getThingConfigAs(NAThingConfiguration.class).getRefreshInterval();
235                 getCapabilities().getRefresh().ifPresent(cap -> cap.setInterval(Duration.ofSeconds(interval)));
236             });
237         }
238     }
239
240     default ModuleType getModuleType() {
241         return ModuleType.from(getThing().getThingTypeUID());
242     }
243
244     default void commonDispose() {
245         getCapabilities().values().forEach(Capability::dispose);
246     }
247
248     default void removeChannels(List<Channel> channels) {
249         updateThing(editThing().withoutChannels(channels).build());
250     }
251 }