2 * Copyright (c) 2010-2020 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.luftdateninfo.internal.handler;
15 import java.time.LocalDateTime;
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.luftdateninfo.internal.LuftdatenInfoConfiguration;
22 import org.openhab.binding.luftdateninfo.internal.utils.DateTimeUtils;
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.types.Command;
29 import org.openhab.core.types.RefreshType;
30 import org.slf4j.Logger;
31 import org.slf4j.LoggerFactory;
34 * The {@link PMHandler} is responsible for handling commands, which are
35 * sent to one of the channels.
37 * @author Bernd Weymann - Initial contribution
40 public abstract class BaseSensorHandler extends BaseThingHandler {
41 private static final LuftdatenInfoConfiguration DEFAULT_CONFIG = new LuftdatenInfoConfiguration();
42 private static final String EMPTY = "";
44 protected static final int REFRESH_INTERVAL_MIN = 5;
45 protected final Logger logger = LoggerFactory.getLogger(BaseSensorHandler.class);
46 protected LuftdatenInfoConfiguration config = DEFAULT_CONFIG;
47 protected ConfigStatus configStatus = ConfigStatus.UNKNOWN;
48 protected ThingStatus myThingStatus = ThingStatus.UNKNOWN;
49 protected UpdateStatus lastUpdateStatus = UpdateStatus.UNKNOWN;
50 protected @Nullable ScheduledFuture<?> refreshJob;
52 public enum ConfigStatus {
60 public enum UpdateStatus {
69 protected LifecycleStatus lifecycleStatus = LifecycleStatus.UNKNOWN;
71 public enum LifecycleStatus {
78 public BaseSensorHandler(Thing thing) {
83 public void handleCommand(ChannelUID channelUID, Command command) {
84 if (command instanceof RefreshType) {
90 public void initialize() {
91 lifecycleStatus = LifecycleStatus.INITIALIZING;
92 scheduler.execute(this::startUp);
95 private void startUp() {
96 config = getConfigAs(LuftdatenInfoConfiguration.class);
97 configStatus = checkConfig(config);
98 if (configStatus == ConfigStatus.OK) {
99 // start getting values
102 // config error, no further actions triggered - Thing Status visible in UI
103 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR,
104 "Configuration not valid. Sensor ID as a number is mandatory!");
106 lifecycleStatus = LifecycleStatus.RUNNING;
109 private void startSchedule() {
110 ScheduledFuture<?> localRefreshJob = refreshJob;
111 if (localRefreshJob != null) {
112 if (localRefreshJob.isCancelled()) {
113 refreshJob = scheduler.scheduleWithFixedDelay(this::dataUpdate, 5, REFRESH_INTERVAL_MIN,
115 } // else - scheduler is already running!
117 refreshJob = scheduler.scheduleWithFixedDelay(this::dataUpdate, 5, REFRESH_INTERVAL_MIN, TimeUnit.MINUTES);
122 public void dispose() {
123 ScheduledFuture<?> localRefreshJob = refreshJob;
124 if (localRefreshJob != null) {
125 localRefreshJob.cancel(true);
127 lifecycleStatus = LifecycleStatus.DISPOSED;
131 * Checks if config is valid - a) not null and b) sensorid is a number
136 private ConfigStatus checkConfig(@Nullable LuftdatenInfoConfiguration c) {
138 if (c.sensorid >= 0) {
139 return ConfigStatus.OK;
141 return ConfigStatus.SENSOR_ID_NEGATIVE;
144 return ConfigStatus.IS_NULL;
148 public LifecycleStatus getLifecycleStatus() {
149 return lifecycleStatus;
152 protected void dataUpdate() {
153 HTTPHandler.getHandler().request(config.sensorid, this);
156 public void onResponse(String data) {
157 lastUpdateStatus = updateChannels(data);
158 statusUpdate(lastUpdateStatus, EMPTY);
161 public void onError(String errorReason) {
162 statusUpdate(UpdateStatus.CONNECTION_EXCEPTION,
163 errorReason + " / " + LocalDateTime.now().format(DateTimeUtils.DTF));
166 protected void statusUpdate(UpdateStatus updateStatus, String details) {
167 if (updateStatus == UpdateStatus.OK) {
168 updateStatus(ThingStatus.ONLINE, ThingStatusDetail.NONE, null);
171 switch (updateStatus) {
172 case CONNECTION_ERROR:
173 // start job even first update delivers no data - recovery is possible
175 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR,
176 "Update failed due to Connection error. Trying to recover in next refresh");
178 case CONNECTION_EXCEPTION:
179 // start job even first update delivers a Connection Exception - recovery is possible
181 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, details);
184 // start job even if first update delivers no values - recovery possible
186 updateStatus(ThingStatus.ONLINE, ThingStatusDetail.NONE,
187 "No values delivered by Sensor. Trying to recover in next refresh");
190 // final status - values from sensor are wrong and manual check is needed
191 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR,
192 "Sensor values doesn't match - please check if Sensor ID is delivering the correct Thing channel values");
195 // final status - Configuration is wrong
196 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR,
197 "Error during update - please check your config data");
204 protected void updateStatus(ThingStatus status, ThingStatusDetail statusDetail, @Nullable String description) {
205 myThingStatus = status;
206 super.updateStatus(status, statusDetail, description);
209 protected abstract UpdateStatus updateChannels(@Nullable String json);
211 protected abstract void updateFromCache();