2 * Copyright (c) 2010-2023 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.groheondus.internal.handler;
15 import java.io.IOException;
16 import java.util.concurrent.ScheduledFuture;
17 import java.util.concurrent.TimeUnit;
19 import org.eclipse.jdt.annotation.NonNullByDefault;
20 import org.eclipse.jdt.annotation.Nullable;
21 import org.openhab.binding.groheondus.internal.GroheOndusApplianceConfiguration;
22 import org.openhab.core.thing.Bridge;
23 import org.openhab.core.thing.ChannelUID;
24 import org.openhab.core.thing.Thing;
25 import org.openhab.core.thing.ThingStatus;
26 import org.openhab.core.thing.ThingStatusDetail;
27 import org.openhab.core.thing.binding.BaseThingHandler;
28 import org.openhab.core.thing.binding.BridgeHandler;
29 import org.openhab.core.types.UnDefType;
30 import org.slf4j.Logger;
31 import org.slf4j.LoggerFactory;
33 import io.github.floriansw.ondus.api.OndusService;
34 import io.github.floriansw.ondus.api.model.BaseAppliance;
35 import io.github.floriansw.ondus.api.model.Location;
36 import io.github.floriansw.ondus.api.model.Room;
39 * @author Florian Schmidt - Initial contribution
42 public abstract class GroheOndusBaseHandler<T extends BaseAppliance, M> extends BaseThingHandler {
43 private final Logger logger = LoggerFactory.getLogger(GroheOndusBaseHandler.class);
45 protected @Nullable GroheOndusApplianceConfiguration config;
47 private @Nullable ScheduledFuture<?> poller;
49 private final int applianceType;
51 // Used to space scheduled updates apart by 1 second to avoid rate limiting from service
52 private int thingCounter = 0;
54 public GroheOndusBaseHandler(Thing thing, int applianceType, int thingCounter) {
56 this.applianceType = applianceType;
57 this.thingCounter = thingCounter;
60 protected void schedulePolling() {
61 OndusService ondusService = getOndusService();
62 if (ondusService == null) {
63 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.BRIDGE_OFFLINE, "@text/error.noservice");
68 T appliance = getAppliance(ondusService);
69 if (appliance == null) {
70 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, "@text/error.empty.response");
73 int pollingInterval = getPollingInterval(appliance);
74 ScheduledFuture<?> poller = this.poller;
76 // Cancel any previous polling
79 this.poller = scheduler.scheduleWithFixedDelay(this::updateChannels, thingCounter, pollingInterval,
81 logger.debug("Scheduled polling every {}s for appliance {}", pollingInterval, thing.getUID());
85 public void dispose() {
86 logger.debug("Disposing scheduled updater for thing {}", thing.getUID());
87 ScheduledFuture<?> poller = this.poller;
95 public void initialize() {
96 config = getConfigAs(GroheOndusApplianceConfiguration.class);
100 public void updateChannels() {
101 logger.debug("Updating channels for appliance {}", thing.getUID());
102 OndusService ondusService = getOndusService();
103 if (ondusService == null) {
104 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.BRIDGE_OFFLINE, "@text/error.noservice");
105 // Update channels to UNDEF
111 T appliance = getAppliance(ondusService);
112 if (appliance == null) {
113 logger.debug("Updating channels failed since appliance is null, thing {}", thing.getUID());
117 M measurement = getLastDataPoint(appliance);
118 if (measurement != null) {
119 getThing().getChannels().forEach(channel -> updateChannel(channel.getUID(), appliance, measurement));
120 updateStatus(ThingStatus.ONLINE);
122 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, "@text/error.failedtoloaddata");
126 protected abstract M getLastDataPoint(T appliance);
128 protected abstract void updateChannel(ChannelUID channelUID, T appliance, M measurement);
130 public @Nullable OndusService getOndusService() {
131 Bridge bridge = getBridge();
132 if (bridge == null) {
135 BridgeHandler handler = bridge.getHandler();
136 if (!(handler instanceof GroheOndusAccountHandler)) {
140 return ((GroheOndusAccountHandler) handler).getService();
141 } catch (IllegalStateException e) {
142 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.BRIDGE_OFFLINE);
147 protected Room getRoom() {
148 return new Room(config.roomId, getLocation());
151 protected Location getLocation() {
152 return new Location(config.locationId);
155 protected @Nullable T getAppliance(OndusService ondusService) {
157 BaseAppliance appliance = ondusService.getAppliance(getRoom(), config.applianceId).orElse(null);
158 if (appliance != null) {
159 if (appliance.getType() != getType()) {
160 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, "@text/error.wrongtype");
163 return (T) appliance;
165 logger.debug("getAppliance for thing {} returned null", thing.getUID());
166 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR,
167 "@text/error.failedtoloaddata");
168 getThing().getChannels().forEach(channel -> updateState(channel.getUID(), UnDefType.UNDEF));
171 } catch (IOException e) {
172 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, e.getMessage());
173 getThing().getChannels().forEach(channel -> updateState(channel.getUID(), UnDefType.UNDEF));
174 logger.debug("Could not load appliance", e);
179 protected abstract int getPollingInterval(T appliance);
181 private int getType() {
182 return this.applianceType;