2 * Copyright (c) 2010-2022 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.concurrent.TimeUnit;
21 import java.util.stream.Collectors;
22 import java.util.stream.Stream;
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.dto.NAObject;
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.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;
49 * {@link CommonInterface} defines common methods of AccountHandler and NAThingHandlers used by Capabilities
51 * @author Gaƫl L'hopital - Initial contribution
55 public interface CommonInterface {
58 ThingBuilder editThing();
60 CapabilityMap getCapabilities();
64 ScheduledExecutorService getScheduler();
66 boolean isLinked(ChannelUID channelUID);
68 void updateState(ChannelUID channelUID, State state);
70 void setThingStatus(ThingStatus thingStatus, ThingStatusDetail thingStatusDetail,
71 @Nullable String thingStatusReason);
73 void triggerChannel(String channelID, String event);
75 void updateThing(Thing thing);
80 default @Nullable CommonInterface getBridgeHandler() {
81 Bridge bridge = getBridge();
82 return bridge != null && bridge.getHandler() instanceof DeviceHandler ? (DeviceHandler) bridge.getHandler()
86 default @Nullable ApiBridgeHandler getAccountHandler() {
87 Bridge bridge = getBridge();
88 BridgeHandler bridgeHandler = null;
90 bridgeHandler = bridge.getHandler();
91 while (bridgeHandler != null && !(bridgeHandler instanceof ApiBridgeHandler)) {
92 bridge = ((CommonInterface) bridgeHandler).getBridge();
93 bridgeHandler = bridge != null ? bridge.getHandler() : null;
96 return (ApiBridgeHandler) bridgeHandler;
99 default @Nullable String getBridgeId() {
100 CommonInterface bridge = getBridgeHandler();
101 return bridge != null ? bridge.getId() : null;
104 default void expireData() {
105 getCapabilities().values().forEach(cap -> cap.expireData());
108 default String getId() {
109 return (String) getThing().getConfiguration().get(NAThingConfiguration.ID);
112 default Stream<Channel> getActiveChannels() {
113 return getThing().getChannels().stream()
114 .filter(channel -> ChannelKind.STATE.equals(channel.getKind()) && isLinked(channel.getUID()));
117 default Optional<CommonInterface> getHomeHandler() {
118 CommonInterface bridgeHandler = getBridgeHandler();
119 if (bridgeHandler != null) {
120 return bridgeHandler.getCapabilities().get(HomeCapability.class).isPresent() ? Optional.of(bridgeHandler)
123 return Optional.empty();
126 default List<CommonInterface> getActiveChildren() {
127 Thing thing = getThing();
128 if (thing instanceof Bridge) {
129 return ((Bridge) thing).getThings().stream().filter(Thing::isEnabled).map(Thing::getHandler)
130 .filter(Objects::nonNull).map(CommonInterface.class::cast).collect(Collectors.toList());
135 default <T extends RestCapability<?>> Optional<T> getHomeCapability(Class<T> clazz) {
136 return getHomeHandler().map(handler -> handler.getCapabilities().get(clazz)).orElse(Optional.empty());
139 default void setNewData(NAObject newData) {
140 String finalReason = null;
141 for (Capability cap : getCapabilities().values()) {
142 String thingStatusReason = cap.setNewData(newData);
143 if (thingStatusReason != null) {
144 finalReason = thingStatusReason;
147 if (!newData.isIgnoredForThingUpdate()) {
148 setThingStatus(finalReason == null ? ThingStatus.ONLINE : ThingStatus.OFFLINE, ThingStatusDetail.NONE,
153 default void commonHandleCommand(ChannelUID channelUID, Command command) {
154 if (ThingStatus.ONLINE.equals(getThing().getStatus())) {
155 if (command == RefreshType.REFRESH) {
159 String channelName = channelUID.getIdWithoutGroup();
160 getCapabilities().values().forEach(cap -> cap.handleCommand(channelName, command));
162 getLogger().debug("Command {}, on channel {} dropped - thing is not ONLINE", command, channelUID);
166 default void proceedWithUpdate() {
167 updateReadings().forEach(dataSet -> setNewData(dataSet));
170 default List<NAObject> updateReadings() {
171 List<NAObject> result = new ArrayList<>();
172 getCapabilities().values().forEach(cap -> result.addAll(cap.updateReadings()));
173 getActiveChildren().forEach(child -> result.addAll(child.updateReadings()));
177 default void commonInitialize() {
178 Bridge bridge = getBridge();
179 if (bridge == null || bridge.getHandler() == null) {
180 setThingStatus(ThingStatus.OFFLINE, ThingStatusDetail.BRIDGE_UNINITIALIZED, null);
181 } else if (!ThingStatus.ONLINE.equals(bridge.getStatus())) {
182 setThingStatus(ThingStatus.OFFLINE, ThingStatusDetail.BRIDGE_OFFLINE, null);
183 removeRefreshCapability();
185 setThingStatus(ThingStatus.UNKNOWN, ThingStatusDetail.NONE, null);
186 setRefreshCapability();
187 getCapabilities().values().forEach(cap -> cap.initialize());
188 getScheduler().schedule(() -> {
189 CommonInterface bridgeHandler = getBridgeHandler();
190 if (bridgeHandler != null) {
191 bridgeHandler.expireData();
193 }, 1, TimeUnit.SECONDS);
197 default void setRefreshCapability() {
198 ModuleType moduleType = ModuleType.from(getThing().getThingTypeUID());
199 if (ModuleType.ACCOUNT.equals(moduleType.getBridge())) {
200 NAThingConfiguration config = getThing().getConfiguration().as(NAThingConfiguration.class);
201 getCapabilities().put(new RefreshCapability(this, getScheduler(), config.refreshInterval));
205 default void removeRefreshCapability() {
206 Capability refreshCap = getCapabilities().remove(RefreshCapability.class);
207 if (refreshCap != null) {
208 refreshCap.dispose();
212 default void commonDispose() {
213 getCapabilities().values().forEach(Capability::dispose);
216 default void removeChannels(List<Channel> channels) {
217 ThingBuilder builder = editThing().withoutChannels(channels);
218 updateThing(builder.build());