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.seneye.internal.handler;
15 import static org.openhab.binding.seneye.internal.SeneyeBindingConstants.*;
17 import java.util.concurrent.TimeUnit;
19 import org.openhab.binding.seneye.internal.CommunicationException;
20 import org.openhab.binding.seneye.internal.InvalidConfigurationException;
21 import org.openhab.binding.seneye.internal.ReadingsUpdate;
22 import org.openhab.binding.seneye.internal.SeneyeConfigurationParameters;
23 import org.openhab.binding.seneye.internal.SeneyeDeviceReading;
24 import org.openhab.binding.seneye.internal.SeneyeService;
25 import org.openhab.core.cache.ExpiringCache;
26 import org.openhab.core.library.types.DateTimeType;
27 import org.openhab.core.library.types.DecimalType;
28 import org.openhab.core.library.types.StringType;
29 import org.openhab.core.thing.ChannelUID;
30 import org.openhab.core.thing.Thing;
31 import org.openhab.core.thing.ThingStatus;
32 import org.openhab.core.thing.ThingStatusDetail;
33 import org.openhab.core.thing.binding.BaseThingHandler;
34 import org.openhab.core.types.Command;
35 import org.openhab.core.types.RefreshType;
36 import org.slf4j.Logger;
37 import org.slf4j.LoggerFactory;
40 * The {@link SeneyeHandler} is responsible for handling commands, which are
41 * sent to one of the channels.
43 * @author Niko Tanghe - Initial contribution
45 public final class SeneyeHandler extends BaseThingHandler implements ReadingsUpdate {
47 private final Logger logger = LoggerFactory.getLogger(SeneyeHandler.class);
48 private SeneyeService seneyeService;
49 private ExpiringCache<SeneyeDeviceReading> cachedSeneyeDeviceReading;
51 public SeneyeHandler(Thing thing) {
56 public void handleCommand(ChannelUID channelUID, Command command) {
57 if (seneyeService == null || !seneyeService.isInitialized()) {
61 if (command instanceof RefreshType) {
62 SeneyeDeviceReading readings = cachedSeneyeDeviceReading.getValue();
65 logger.debug("Command {} is not supported for channel: {}", command, channelUID.getId());
70 public void newState(SeneyeDeviceReading readings) {
71 if (readings != null) {
72 logger.debug("Updating readings for sensor type {}", seneyeService.seneyeType);
73 switch (seneyeService.seneyeType) {
75 updateState(CHANNEL_NH4, new DecimalType(readings.nh4.curr));
76 updateState(CHANNEL_PAR, new DecimalType(readings.par.curr));
77 updateState(CHANNEL_LUX, new DecimalType(readings.lux.curr));
78 updateState(CHANNEL_KELVIN, new DecimalType(readings.kelvin.curr));
80 updateState(CHANNEL_O2, new DecimalType(readings.o2.curr));
82 updateState(CHANNEL_TEMPERATURE, new DecimalType(readings.temperature.curr));
83 updateState(CHANNEL_NH3, new DecimalType(readings.nh3.curr));
84 updateState(CHANNEL_PH, new DecimalType(readings.ph.curr));
85 updateState(CHANNEL_LASTREADING, new DateTimeType(readings.status.getLast_experimentDate()));
86 updateState(CHANNEL_SLIDEEXPIRES, new DateTimeType(readings.status.getSlide_expiresDate()));
87 updateState(CHANNEL_WRONGSLIDE, new StringType(readings.status.getWrong_slideString()));
88 updateState(CHANNEL_SLIDESERIAL, new StringType(readings.status.getSlide_serialString()));
89 updateState(CHANNEL_OUTOFWATER, new StringType(readings.status.getOut_of_waterString()));
90 updateState(CHANNEL_DISCONNECTED, new StringType(readings.status.getDisconnectedString()));
96 public void invalidConfig() {
97 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR);
101 public void initialize() {
102 SeneyeConfigurationParameters config = getConfigAs(SeneyeConfigurationParameters.class);
104 if (config.aquarium_name == null || config.aquarium_name.isEmpty()) {
105 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR,
106 "Check configuration, Aquarium name must be provided");
109 if (config.username == null || config.username.isEmpty()) {
110 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR,
111 "Check configuration, Seneye username must be provided");
114 if (config.password == null || config.password.isEmpty()) {
115 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR,
116 "Check configuration, Seneye password must be provided");
120 logger.debug("Initializing Seneye API service.");
122 this.seneyeService = new SeneyeService(config);
123 } catch (CommunicationException ex) {
124 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, ex.getMessage());
125 return; // critical error
128 updateStatus(ThingStatus.ONLINE);
130 // contact Seneye API
131 scheduler.submit(() -> {
132 initializeSeneyeService();
136 private void initializeSeneyeService() {
138 seneyeService.initialize();
139 } catch (CommunicationException ex) {
140 // try again in 30 secs
141 scheduler.schedule(() -> {
142 initializeSeneyeService();
143 }, 30, TimeUnit.SECONDS);
146 } catch (InvalidConfigurationException ex) {
147 // bad configuration, stay offline until user corrects the configuration
148 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, ex.getMessage());
153 // ok, initialization succeeded
154 cachedSeneyeDeviceReading = new ExpiringCache<>(TimeUnit.SECONDS.toMillis(10), () -> {
155 return seneyeService.getDeviceReadings();
158 seneyeService.startAutomaticRefresh(scheduler, this);
160 updateStatus(ThingStatus.ONLINE);
164 public void dispose() {
165 seneyeService.stopAutomaticRefresh();
166 seneyeService = null;