2 * Copyright (c) 2010-2024 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 {}:{} (Device Address {}) every {} seconds ", getThing().getUID(),
76 this.config.host, this.config.portNumber, this.config.deviceAddress, 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 {}:{} (Device Address {}) ", getThing().getUID(), this.config.host,
102 this.config.portNumber, this.config.deviceAddress);
103 // get the data from the SolarMax device
105 SolarMaxData solarMaxData = SolarMaxConnector.getAllValuesFromSolarMax(config.host, config.portNumber,
106 this.config.deviceAddress);
108 if (solarMaxData.wasCommunicationSuccessful()) {
109 updateStatus(ThingStatus.ONLINE);
110 updateProperties(solarMaxData);
111 updateChannels(solarMaxData);
114 } catch (SolarMaxException e) {
115 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR,
116 "Communication error with the device: " + e.getMessage());
121 * Update the channels
123 private void updateChannels(SolarMaxData solarMaxData) {
124 logger.debug("Updating all channels");
125 for (SolarMaxChannel solarMaxChannel : SolarMaxChannel.values()) {
126 String channelId = solarMaxChannel.getChannelId();
127 Channel channel = getThing().getChannel(channelId);
129 if (channelId.equals(SolarMaxChannel.CHANNEL_LAST_UPDATED.getChannelId())) {
130 // CHANNEL_LAST_UPDATED shows when the device was last read and does not come from the device, so handle
132 State state = solarMaxData.getDataDateTime();
133 logger.debug("Update channel state: {} - {}", channelId, state);
134 updateState(channel.getUID(), state);
137 // must be somthing to collect from the device, so...
138 if (solarMaxData.has(SolarMaxCommandKey.valueOf(channelId))) {
139 if (channel == null) {
140 logger.error("No channel found with id: {}", channelId);
142 State state = convertValueToState(solarMaxData.get(SolarMaxCommandKey.valueOf(channelId)),
143 solarMaxChannel.getUnit());
145 if (channel != null && state != null) {
146 logger.debug("Update channel state: {} - {}", channelId, state);
147 updateState(channel.getUID(), state);
149 logger.debug("Error refreshing channel {}: {}", getThing().getUID(), channelId);
156 private @Nullable State convertValueToState(Number value, @Nullable Unit<?> unit) {
158 return new DecimalType(value.floatValue());
160 return new QuantityType<>(value, unit);
164 * Update the properties
166 private void updateProperties(SolarMaxData solarMaxData) {
167 logger.debug("Updating properties");
168 for (SolarMaxProperty solarMaxProperty : SolarMaxProperty.values()) {
169 String propertyId = solarMaxProperty.getPropertyId();
170 Number valNumber = solarMaxData.get(SolarMaxCommandKey.valueOf(propertyId));
171 if (valNumber == null) {
172 logger.debug("Null value returned for value of {}: {}", getThing().getUID(), propertyId);
176 // deal with properties
177 if (propertyId.equals(SolarMaxProperty.PROPERTY_BUILD_NUMBER.getPropertyId())
178 || propertyId.equals(SolarMaxProperty.PROPERTY_SOFTWARE_VERSION.getPropertyId())) {
179 updateProperty(solarMaxProperty.getPropertyId(), valNumber.toString());