2 * Copyright (c) 2010-2024 Contributors to the openHAB project
4 * See the NOTICE file(s) distributed with this work for additional
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
11 * SPDX-License-Identifier: EPL-2.0
13 package org.openhab.binding.netatmo.internal.handler;
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;
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.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;
50 * {@link CommonInterface} defines common methods of AccountHandler and NAThingHandlers used by Capabilities
52 * @author Gaƫl L'hopital - Initial contribution
56 public interface CommonInterface {
59 ThingBuilder editThing();
61 CapabilityMap getCapabilities();
65 ScheduledExecutorService getScheduler();
67 boolean isLinked(ChannelUID channelUID);
69 void updateState(ChannelUID channelUID, State state);
71 default void updateState(String groupId, String id, State state) {
72 updateState(new ChannelUID(getThing().getUID(), groupId, id), state);
75 void setThingStatus(ThingStatus thingStatus, ThingStatusDetail thingStatusDetail,
76 @Nullable String thingStatusReason);
78 void triggerChannel(String channelID, String event);
80 void updateThing(Thing thing);
85 default @Nullable CommonInterface getBridgeHandler() {
86 Bridge bridge = getBridge();
87 return bridge != null && bridge.getHandler() instanceof DeviceHandler ? (DeviceHandler) bridge.getHandler()
91 default @Nullable ApiBridgeHandler getAccountHandler() {
92 Bridge bridge = getBridge();
93 BridgeHandler bridgeHandler = null;
95 bridgeHandler = bridge.getHandler();
96 while (bridgeHandler != null && !(bridgeHandler instanceof ApiBridgeHandler)) {
97 bridge = ((CommonInterface) bridgeHandler).getBridge();
98 bridgeHandler = bridge != null ? bridge.getHandler() : null;
101 return (ApiBridgeHandler) bridgeHandler;
104 default @Nullable String getBridgeId() {
105 CommonInterface bridge = getBridgeHandler();
106 return bridge != null ? bridge.getId() : null;
109 default void expireData() {
110 getCapabilities().values().forEach(cap -> cap.expireData());
113 default String getId() {
114 return getThingConfigAs(NAThingConfiguration.class).getId();
117 default <T> T getThingConfigAs(Class<T> configurationClass) {
118 return getThing().getConfiguration().as(configurationClass);
121 default Stream<Channel> getActiveChannels() {
122 return getThing().getChannels().stream()
123 .filter(channel -> ChannelKind.STATE.equals(channel.getKind()) && isLinked(channel.getUID()));
126 default Optional<CommonInterface> recurseUpToHomeHandler(@Nullable CommonInterface handler) {
127 if (handler == null) {
128 return Optional.empty();
130 return handler.getCapabilities().get(HomeCapability.class).isPresent() ? Optional.of(handler)
131 : recurseUpToHomeHandler(handler.getBridgeHandler());
135 * Recurses down in the home/module/device tree
138 * @return the list of childs of the bridge
140 default List<CommonInterface> getAllActiveChildren(Bridge bridge) {
141 List<CommonInterface> result = new ArrayList<>();
142 bridge.getThings().stream().filter(Thing::isEnabled).map(Thing::getHandler).forEach(childHandler -> {
143 if (childHandler != null) {
144 Thing childThing = childHandler.getThing();
145 if (childThing instanceof Bridge bridgeChild) {
146 result.addAll(getAllActiveChildren(bridgeChild));
148 result.add((CommonInterface) childHandler);
154 default List<CommonInterface> getActiveChildren() {
155 Thing thing = getThing();
156 if (thing instanceof Bridge bridge) {
157 return bridge.getThings().stream().filter(Thing::isEnabled)
158 .filter(th -> th.getStatusInfo().getStatusDetail() != ThingStatusDetail.BRIDGE_OFFLINE)
159 .map(Thing::getHandler).filter(Objects::nonNull).map(CommonInterface.class::cast).toList();
164 default Stream<CommonInterface> getActiveChildren(FeatureArea area) {
165 return getActiveChildren().stream().filter(child -> child.getModuleType().feature == area);
168 default <T extends RestCapability<?>> Optional<T> getHomeCapability(Class<T> clazz) {
169 return recurseUpToHomeHandler(this).map(handler -> handler.getCapabilities().get(clazz))
170 .orElse(Optional.empty());
173 default void setNewData(NAObject newData) {
174 if (newData instanceof NAThing thingData) {
175 if (getId().equals(thingData.getBridge())) {
176 getActiveChildren().stream().filter(child -> child.getId().equals(thingData.getId())).findFirst()
177 .ifPresent(child -> child.setNewData(thingData));
181 String finalReason = null;
182 for (Capability cap : getCapabilities().values()) {
183 String thingStatusReason = cap.setNewData(newData);
184 if (thingStatusReason != null) {
185 finalReason = thingStatusReason;
188 // Prevent turning ONLINE myself if in the meantime something turned account OFFLINE
189 ApiBridgeHandler accountHandler = getAccountHandler();
190 if (accountHandler != null && accountHandler.isConnected() && !newData.isIgnoredForThingUpdate()) {
191 setThingStatus(finalReason == null ? ThingStatus.ONLINE : ThingStatus.OFFLINE, ThingStatusDetail.NONE,
196 default void commonHandleCommand(ChannelUID channelUID, Command command) {
197 if (ThingStatus.ONLINE.equals(getThing().getStatus())) {
198 if (command == RefreshType.REFRESH) {
202 String channelName = channelUID.getIdWithoutGroup();
203 getCapabilities().values().forEach(cap -> cap.handleCommand(channelName, command));
205 getLogger().debug("Command {} on channel {} dropped - thing is not ONLINE", command, channelUID);
209 default void proceedWithUpdate() {
210 updateReadings().forEach(dataSet -> setNewData(dataSet));
213 default List<NAObject> updateReadings() {
214 List<NAObject> result = new ArrayList<>();
215 getCapabilities().values().forEach(cap -> result.addAll(cap.updateReadings()));
216 getActiveChildren().forEach(child -> result.addAll(child.updateReadings()));
220 default void commonInitialize() {
221 Bridge bridge = getBridge();
222 if (bridge == null || bridge.getHandler() == null) {
223 setThingStatus(ThingStatus.OFFLINE, ThingStatusDetail.BRIDGE_UNINITIALIZED, null);
224 } else if (!ThingStatus.ONLINE.equals(bridge.getStatus())) {
225 setThingStatus(ThingStatus.OFFLINE, ThingStatusDetail.BRIDGE_OFFLINE, null);
226 getCapabilities().remove(RefreshCapability.class);
227 getCapabilities().remove(ParentUpdateCapability.class);
229 setThingStatus(ThingStatus.UNKNOWN, ThingStatusDetail.NONE, null);
230 if (ModuleType.ACCOUNT.equals(getModuleType().getBridge())) {
231 NAThingConfiguration config = getThing().getConfiguration().as(NAThingConfiguration.class);
232 getCapabilities().put(new RefreshCapability(this, config.refreshInterval));
234 getCapabilities().put(new ParentUpdateCapability(this));
238 default ModuleType getModuleType() {
239 return ModuleType.from(getThing().getThingTypeUID());
242 default void commonDispose() {
243 getCapabilities().values().forEach(Capability::dispose);
246 default void removeChannels(List<Channel> channels) {
247 ThingBuilder builder = editThing().withoutChannels(channels);
248 updateThing(builder.build());