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.solarmax.internal;
15 import java.util.concurrent.ScheduledFuture;
16 import java.util.concurrent.TimeUnit;
18 import javax.measure.Unit;
20 import org.eclipse.jdt.annotation.NonNullByDefault;
21 import org.eclipse.jdt.annotation.Nullable;
22 import org.openhab.binding.solarmax.internal.connector.SolarMaxCommandKey;
23 import org.openhab.binding.solarmax.internal.connector.SolarMaxConnector;
24 import org.openhab.binding.solarmax.internal.connector.SolarMaxData;
25 import org.openhab.binding.solarmax.internal.connector.SolarMaxException;
26 import org.openhab.core.library.types.DecimalType;
27 import org.openhab.core.library.types.QuantityType;
28 import org.openhab.core.thing.Channel;
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.State;
36 import org.slf4j.Logger;
37 import org.slf4j.LoggerFactory;
40 * The {@link SolarMaxHandler} is responsible for handling commands, which are
41 * sent to one of the channels.
43 * @author Jamie Townsend - Initial contribution
46 public class SolarMaxHandler extends BaseThingHandler {
48 private final Logger logger = LoggerFactory.getLogger(SolarMaxHandler.class);
50 private SolarMaxConfiguration config = getConfigAs(SolarMaxConfiguration.class);
53 private ScheduledFuture<?> pollingJob;
55 public SolarMaxHandler(final Thing thing) {
60 public void handleCommand(final ChannelUID channelUID, final Command command) {
65 public void initialize() {
66 config = getConfigAs(SolarMaxConfiguration.class);
68 configurePolling(); // Setup the scheduler
72 * This is called to start the refresh job and also to reset that refresh job when a config change is done.
74 private void configurePolling() {
75 logger.debug("Polling data from {} at {}:{} every {} seconds ", getThing().getUID(), this.config.host,
76 this.config.portNumber, this.config.refreshInterval);
77 if (this.config.refreshInterval > 0) {
78 if (pollingJob == null || pollingJob.isCancelled()) {
79 pollingJob = scheduler.scheduleWithFixedDelay(pollingRunnable, 0, this.config.refreshInterval,
86 public void dispose() {
87 if (pollingJob != null && !pollingJob.isCancelled()) {
88 pollingJob.cancel(true);
94 * Polling event used to get data from the SolarMax device
96 private Runnable pollingRunnable = () -> {
97 updateValuesFromDevice();
100 private synchronized void updateValuesFromDevice() {
101 logger.debug("Updating data from {} at {}:{} ", getThing().getUID(), this.config.host, this.config.portNumber);
102 // get the data from the SolarMax device
104 SolarMaxData solarMaxData = SolarMaxConnector.getAllValuesFromSolarMax(config.host, config.portNumber);
106 if (solarMaxData.wasCommunicationSuccessful()) {
107 updateStatus(ThingStatus.ONLINE);
108 updateProperties(solarMaxData);
109 updateChannels(solarMaxData);
112 } catch (SolarMaxException e) {
113 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR,
114 "Communication error with the device: " + e.getMessage());
119 * Update the channels
121 private void updateChannels(SolarMaxData solarMaxData) {
122 logger.debug("Updating all channels");
123 for (SolarMaxChannel solarMaxChannel : SolarMaxChannel.values()) {
124 String channelId = solarMaxChannel.getChannelId();
125 Channel channel = getThing().getChannel(channelId);
127 if (channelId.equals(SolarMaxChannel.CHANNEL_LAST_UPDATED.getChannelId())) {
128 // CHANNEL_LAST_UPDATED shows when the device was last read and does not come from the device, so handle
130 State state = solarMaxData.getDataDateTime();
131 logger.debug("Update channel state: {} - {}", channelId, state);
132 updateState(channel.getUID(), state);
135 // must be somthing to collect from the device, so...
136 if (solarMaxData.has(SolarMaxCommandKey.valueOf(channelId))) {
137 if (channel == null) {
138 logger.error("No channel found with id: {}", channelId);
140 State state = convertValueToState(solarMaxData.get(SolarMaxCommandKey.valueOf(channelId)),
141 solarMaxChannel.getUnit());
143 if (channel != null && state != null) {
144 logger.debug("Update channel state: {} - {}", channelId, state);
145 updateState(channel.getUID(), state);
147 logger.debug("Error refreshing channel {}: {}", getThing().getUID(), channelId);
154 private @Nullable State convertValueToState(Number value, @Nullable Unit<?> unit) {
156 return new DecimalType(value.floatValue());
158 return new QuantityType<>(value, unit);
162 * Update the properties
164 private void updateProperties(SolarMaxData solarMaxData) {
165 logger.debug("Updating properties");
166 for (SolarMaxProperty solarMaxProperty : SolarMaxProperty.values()) {
167 String propertyId = solarMaxProperty.getPropertyId();
168 Number valNumber = solarMaxData.get(SolarMaxCommandKey.valueOf(propertyId));
169 if (valNumber == null) {
170 logger.debug("Null value returned for value of {}: {}", getThing().getUID(), propertyId);
174 // deal with properties
175 if (propertyId.equals(SolarMaxProperty.PROPERTY_BUILD_NUMBER.getPropertyId())
176 || propertyId.equals(SolarMaxProperty.PROPERTY_SOFTWARE_VERSION.getPropertyId())) {
177 updateProperty(solarMaxProperty.getPropertyId(), valNumber.toString());