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.solaredge.internal.handler;
15 import static org.openhab.binding.solaredge.internal.SolarEdgeBindingConstants.STATUS_WAITING_FOR_LOGIN;
17 import java.util.ArrayList;
18 import java.util.List;
20 import java.util.concurrent.Future;
21 import java.util.concurrent.TimeUnit;
22 import java.util.concurrent.atomic.AtomicReference;
24 import org.eclipse.jdt.annotation.NonNullByDefault;
25 import org.eclipse.jdt.annotation.Nullable;
26 import org.eclipse.jetty.client.HttpClient;
27 import org.openhab.binding.solaredge.internal.AtomicReferenceTrait;
28 import org.openhab.binding.solaredge.internal.command.AggregateDataUpdatePrivateApi;
29 import org.openhab.binding.solaredge.internal.command.AggregateDataUpdatePublicApi;
30 import org.openhab.binding.solaredge.internal.command.LiveDataUpdateMeterless;
31 import org.openhab.binding.solaredge.internal.command.LiveDataUpdatePrivateApi;
32 import org.openhab.binding.solaredge.internal.command.LiveDataUpdatePublicApi;
33 import org.openhab.binding.solaredge.internal.command.SolarEdgeCommand;
34 import org.openhab.binding.solaredge.internal.config.SolarEdgeConfiguration;
35 import org.openhab.binding.solaredge.internal.connector.CommunicationStatus;
36 import org.openhab.binding.solaredge.internal.connector.WebInterface;
37 import org.openhab.binding.solaredge.internal.model.AggregatePeriod;
38 import org.openhab.core.thing.Channel;
39 import org.openhab.core.thing.ChannelGroupUID;
40 import org.openhab.core.thing.ChannelUID;
41 import org.openhab.core.thing.Thing;
42 import org.openhab.core.thing.ThingStatus;
43 import org.openhab.core.thing.ThingStatusDetail;
44 import org.openhab.core.thing.ThingUID;
45 import org.openhab.core.thing.binding.BaseThingHandler;
46 import org.openhab.core.types.Command;
47 import org.openhab.core.types.State;
48 import org.openhab.core.types.UnDefType;
49 import org.slf4j.Logger;
50 import org.slf4j.LoggerFactory;
53 * The {@link SolarEdgeGenericHandler} is responsible for handling commands, which are
54 * sent to one of the channels.
56 * @author Alexander Friese - initial contribution
59 public class SolarEdgeGenericHandler extends BaseThingHandler implements SolarEdgeHandler, AtomicReferenceTrait {
60 private final Logger logger = LoggerFactory.getLogger(SolarEdgeGenericHandler.class);
62 private static final long LIVE_POLLING_INITIAL_DELAY = 1;
63 private static final long AGGREGATE_POLLING_INITIAL_DELAY = 2;
66 * Interface object for querying the Solaredge web interface
68 private WebInterface webInterface;
71 * Schedule for polling live data
73 private final AtomicReference<@Nullable Future<?>> liveDataPollingJobReference;
76 * Schedule for polling aggregate data
78 private final AtomicReference<@Nullable Future<?>> aggregateDataPollingJobReference;
80 public SolarEdgeGenericHandler(Thing thing, HttpClient httpClient) {
82 this.webInterface = new WebInterface(scheduler, this, httpClient);
83 this.liveDataPollingJobReference = new AtomicReference<>(null);
84 this.aggregateDataPollingJobReference = new AtomicReference<>(null);
88 public void handleCommand(ChannelUID channelUID, Command command) {
89 logger.debug("command for {}: {}", channelUID, command);
90 // write access is not supported.
94 public void initialize() {
95 logger.debug("About to initialize SolarEdge");
96 SolarEdgeConfiguration config = getConfiguration();
97 logger.debug("SolarEdge initialized with configuration: {}", config);
99 updateStatus(ThingStatus.UNKNOWN, ThingStatusDetail.NONE, STATUS_WAITING_FOR_LOGIN);
100 webInterface.start();
107 private void startPolling() {
108 updateJobReference(liveDataPollingJobReference, scheduler.scheduleWithFixedDelay(this::liveDataPollingRun,
109 LIVE_POLLING_INITIAL_DELAY, getConfiguration().getLiveDataPollingInterval(), TimeUnit.MINUTES));
111 updateJobReference(aggregateDataPollingJobReference,
112 scheduler.scheduleWithFixedDelay(this::aggregateDataPollingRun, AGGREGATE_POLLING_INITIAL_DELAY,
113 getConfiguration().getAggregateDataPollingInterval(), TimeUnit.MINUTES));
117 * Poll the SolarEdge Webservice one time per call to retrieve live data.
119 void liveDataPollingRun() {
120 logger.debug("polling SolarEdge live data {}", getConfiguration());
121 SolarEdgeCommand ldu;
123 if (getConfiguration().isUsePrivateApi()) {
124 ldu = new LiveDataUpdatePrivateApi(this, this::updateOnlineStatus);
126 if (getConfiguration().isMeterInstalled()) {
127 ldu = new LiveDataUpdatePublicApi(this, this::updateOnlineStatus);
129 ldu = new LiveDataUpdateMeterless(this, this::updateOnlineStatus);
132 getWebInterface().enqueueCommand(ldu);
136 * Poll the SolarEdge Webservice one time per call to retrieve aggregate data.
138 void aggregateDataPollingRun() {
139 // if no meter is present all data will be fetched by the 'LiveDataUpdateMeterless'
140 if (getConfiguration().isMeterInstalled()) {
141 logger.debug("polling SolarEdge aggregate data {}", getConfiguration());
142 List<SolarEdgeCommand> commands = new ArrayList<>();
144 if (getConfiguration().isUsePrivateApi()) {
145 commands.add(new AggregateDataUpdatePrivateApi(this, AggregatePeriod.DAY, this::updateOnlineStatus));
146 commands.add(new AggregateDataUpdatePrivateApi(this, AggregatePeriod.WEEK, this::updateOnlineStatus));
147 commands.add(new AggregateDataUpdatePrivateApi(this, AggregatePeriod.MONTH, this::updateOnlineStatus));
148 commands.add(new AggregateDataUpdatePrivateApi(this, AggregatePeriod.YEAR, this::updateOnlineStatus));
150 commands.add(new AggregateDataUpdatePublicApi(this, AggregatePeriod.DAY, this::updateOnlineStatus));
151 commands.add(new AggregateDataUpdatePublicApi(this, AggregatePeriod.WEEK, this::updateOnlineStatus));
152 commands.add(new AggregateDataUpdatePublicApi(this, AggregatePeriod.MONTH, this::updateOnlineStatus));
153 commands.add(new AggregateDataUpdatePublicApi(this, AggregatePeriod.YEAR, this::updateOnlineStatus));
156 for (SolarEdgeCommand command : commands) {
157 getWebInterface().enqueueCommand(command);
162 private void updateOnlineStatus(CommunicationStatus status) {
163 switch (status.getHttpCode()) {
164 case SERVICE_UNAVAILABLE:
165 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.BRIDGE_OFFLINE, status.getMessage());
168 updateStatus(ThingStatus.ONLINE, ThingStatusDetail.NONE);
171 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, status.getMessage());
176 * Disposes the bridge.
179 public void dispose() {
180 logger.debug("Handler disposed.");
182 cancelJobReference(liveDataPollingJobReference);
183 cancelJobReference(aggregateDataPollingJobReference);
185 webInterface.dispose();
189 public WebInterface getWebInterface() {
194 * will update all channels provided in the map
197 public void updateChannelStatus(Map<Channel, State> values) {
198 logger.debug("Handling channel update.");
200 for (Channel channel : values.keySet()) {
201 if (getChannels().contains(channel)) {
202 State value = values.get(channel);
204 logger.debug("Channel is to be updated: {}: {}", channel.getUID().getAsString(), value);
205 updateState(channel.getUID(), value);
207 logger.debug("Value is null or not provided by solaredge (channel: {})",
208 channel.getUID().getAsString());
209 updateState(channel.getUID(), UnDefType.UNDEF);
212 logger.debug("Could not identify channel: {} for model {}", channel.getUID().getAsString(),
213 getThing().getThingTypeUID().getAsString());
219 public void setStatusInfo(ThingStatus status, ThingStatusDetail statusDetail, @Nullable String description) {
220 super.updateStatus(status, statusDetail, description);
224 public SolarEdgeConfiguration getConfiguration() {
225 return this.getConfigAs(SolarEdgeConfiguration.class);
229 public List<Channel> getChannels() {
230 return getThing().getChannels();
234 public @Nullable Channel getChannel(String groupId, String channelId) {
235 ThingUID thingUID = this.getThing().getUID();
236 ChannelGroupUID channelGroupUID = new ChannelGroupUID(thingUID, groupId);
237 return getThing().getChannel(new ChannelUID(channelGroupUID, channelId));