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