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