]> git.basschouten.com Git - openhab-addons.git/blob
0af1180dc409aebd258da5dad2964ea758d653b8
[openhab-addons.git] /
1 /**
2  * Copyright (c) 2010-2023 Contributors to the openHAB project
3  *
4  * See the NOTICE file(s) distributed with this work for additional
5  * information.
6  *
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
10  *
11  * SPDX-License-Identifier: EPL-2.0
12  */
13 package org.openhab.binding.solaredge.internal.handler;
14
15 import static org.openhab.binding.solaredge.internal.SolarEdgeBindingConstants.STATUS_WAITING_FOR_LOGIN;
16
17 import java.util.ArrayList;
18 import java.util.List;
19 import java.util.Map;
20 import java.util.concurrent.Future;
21 import java.util.concurrent.TimeUnit;
22 import java.util.concurrent.atomic.AtomicReference;
23
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;
51
52 /**
53  * The {@link SolarEdgeGenericHandler} is responsible for handling commands, which are
54  * sent to one of the channels.
55  *
56  * @author Alexander Friese - initial contribution
57  */
58 @NonNullByDefault
59 public class SolarEdgeGenericHandler extends BaseThingHandler implements SolarEdgeHandler, AtomicReferenceTrait {
60     private final Logger logger = LoggerFactory.getLogger(SolarEdgeGenericHandler.class);
61
62     private static final long LIVE_POLLING_INITIAL_DELAY = 1;
63     private static final long AGGREGATE_POLLING_INITIAL_DELAY = 2;
64
65     /**
66      * Interface object for querying the Solaredge web interface
67      */
68     private WebInterface webInterface;
69
70     /**
71      * Schedule for polling live data
72      */
73     private final AtomicReference<@Nullable Future<?>> liveDataPollingJobReference;
74
75     /**
76      * Schedule for polling aggregate data
77      */
78     private final AtomicReference<@Nullable Future<?>> aggregateDataPollingJobReference;
79
80     public SolarEdgeGenericHandler(Thing thing, HttpClient httpClient) {
81         super(thing);
82         this.webInterface = new WebInterface(scheduler, this, httpClient);
83         this.liveDataPollingJobReference = new AtomicReference<>(null);
84         this.aggregateDataPollingJobReference = new AtomicReference<>(null);
85     }
86
87     @Override
88     public void handleCommand(ChannelUID channelUID, Command command) {
89         logger.debug("command for {}: {}", channelUID, command);
90         // write access is not supported.
91     }
92
93     @Override
94     public void initialize() {
95         logger.debug("About to initialize SolarEdge");
96         SolarEdgeConfiguration config = getConfiguration();
97         logger.debug("SolarEdge initialized with configuration: {}", config);
98
99         updateStatus(ThingStatus.UNKNOWN, ThingStatusDetail.NONE, STATUS_WAITING_FOR_LOGIN);
100         webInterface.start();
101         startPolling();
102     }
103
104     /**
105      * Start the polling.
106      */
107     private void startPolling() {
108         updateJobReference(liveDataPollingJobReference, scheduler.scheduleWithFixedDelay(this::liveDataPollingRun,
109                 LIVE_POLLING_INITIAL_DELAY, getConfiguration().getLiveDataPollingInterval(), TimeUnit.MINUTES));
110
111         updateJobReference(aggregateDataPollingJobReference,
112                 scheduler.scheduleWithFixedDelay(this::aggregateDataPollingRun, AGGREGATE_POLLING_INITIAL_DELAY,
113                         getConfiguration().getAggregateDataPollingInterval(), TimeUnit.MINUTES));
114     }
115
116     /**
117      * Poll the SolarEdge Webservice one time per call to retrieve live data.
118      */
119     void liveDataPollingRun() {
120         logger.debug("polling SolarEdge live data {}", getConfiguration());
121         SolarEdgeCommand ldu;
122
123         if (getConfiguration().isUsePrivateApi()) {
124             ldu = new LiveDataUpdatePrivateApi(this, this::updateOnlineStatus);
125         } else {
126             if (getConfiguration().isMeterInstalled()) {
127                 ldu = new LiveDataUpdatePublicApi(this, this::updateOnlineStatus);
128             } else {
129                 ldu = new LiveDataUpdateMeterless(this, this::updateOnlineStatus);
130             }
131         }
132         getWebInterface().enqueueCommand(ldu);
133     }
134
135     /**
136      * Poll the SolarEdge Webservice one time per call to retrieve aggregate data.
137      */
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<>();
143
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));
149             } else {
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));
154             }
155
156             for (SolarEdgeCommand command : commands) {
157                 getWebInterface().enqueueCommand(command);
158             }
159         }
160     }
161
162     private void updateOnlineStatus(CommunicationStatus status) {
163         switch (status.getHttpCode()) {
164             case SERVICE_UNAVAILABLE:
165                 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.BRIDGE_OFFLINE, status.getMessage());
166                 break;
167             case OK:
168                 updateStatus(ThingStatus.ONLINE, ThingStatusDetail.NONE);
169                 break;
170             default:
171                 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, status.getMessage());
172         }
173     }
174
175     /**
176      * Disposes the bridge.
177      */
178     @Override
179     public void dispose() {
180         logger.debug("Handler disposed.");
181
182         cancelJobReference(liveDataPollingJobReference);
183         cancelJobReference(aggregateDataPollingJobReference);
184
185         webInterface.dispose();
186     }
187
188     @Override
189     public WebInterface getWebInterface() {
190         return webInterface;
191     }
192
193     /**
194      * will update all channels provided in the map
195      */
196     @Override
197     public void updateChannelStatus(Map<Channel, State> values) {
198         logger.debug("Handling channel update.");
199
200         for (Channel channel : values.keySet()) {
201             if (getChannels().contains(channel)) {
202                 State value = values.get(channel);
203                 if (value != null) {
204                     logger.debug("Channel is to be updated: {}: {}", channel.getUID().getAsString(), value);
205                     updateState(channel.getUID(), value);
206                 } else {
207                     logger.debug("Value is null or not provided by solaredge (channel: {})",
208                             channel.getUID().getAsString());
209                     updateState(channel.getUID(), UnDefType.UNDEF);
210                 }
211             } else {
212                 logger.debug("Could not identify channel: {} for model {}", channel.getUID().getAsString(),
213                         getThing().getThingTypeUID().getAsString());
214             }
215         }
216     }
217
218     @Override
219     public void setStatusInfo(ThingStatus status, ThingStatusDetail statusDetail, @Nullable String description) {
220         super.updateStatus(status, statusDetail, description);
221     }
222
223     @Override
224     public SolarEdgeConfiguration getConfiguration() {
225         return this.getConfigAs(SolarEdgeConfiguration.class);
226     }
227
228     @Override
229     public List<Channel> getChannels() {
230         return getThing().getChannels();
231     }
232
233     @Override
234     public @Nullable Channel getChannel(String groupId, String channelId) {
235         ThingUID thingUID = this.getThing().getUID();
236         ChannelGroupUID channelGroupUID = new ChannelGroupUID(thingUID, groupId);
237         Channel channel = getThing().getChannel(new ChannelUID(channelGroupUID, channelId));
238         return channel;
239     }
240 }