]> git.basschouten.com Git - openhab-addons.git/blob
508fc42093ae83b8b4e9f2cf31c5f70eee2693b5
[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.easee.internal.handler;
14
15 import static org.openhab.binding.easee.internal.EaseeBindingConstants.*;
16
17 import java.util.Map;
18 import java.util.concurrent.Future;
19 import java.util.concurrent.TimeUnit;
20 import java.util.concurrent.atomic.AtomicReference;
21
22 import org.eclipse.jdt.annotation.NonNullByDefault;
23 import org.eclipse.jdt.annotation.Nullable;
24 import org.openhab.binding.easee.internal.AtomicReferenceTrait;
25 import org.openhab.binding.easee.internal.EaseeBindingConstants;
26 import org.openhab.binding.easee.internal.Utils;
27 import org.openhab.binding.easee.internal.command.EaseeCommand;
28 import org.openhab.binding.easee.internal.command.charger.ChangeConfiguration;
29 import org.openhab.binding.easee.internal.command.charger.Charger;
30 import org.openhab.binding.easee.internal.command.charger.GetConfiguration;
31 import org.openhab.binding.easee.internal.command.charger.LatestChargingSession;
32 import org.openhab.binding.easee.internal.command.charger.SendCommand;
33 import org.openhab.binding.easee.internal.command.charger.SendCommandPauseResume;
34 import org.openhab.binding.easee.internal.command.charger.SendCommandStartStop;
35 import org.openhab.binding.easee.internal.config.EaseeConfiguration;
36 import org.openhab.binding.easee.internal.connector.CommunicationStatus;
37 import org.openhab.core.thing.Bridge;
38 import org.openhab.core.thing.Channel;
39 import org.openhab.core.thing.Thing;
40 import org.openhab.core.thing.ThingStatus;
41 import org.openhab.core.thing.ThingStatusDetail;
42 import org.openhab.core.thing.binding.BaseThingHandler;
43 import org.openhab.core.types.Command;
44 import org.openhab.core.types.State;
45 import org.openhab.core.types.UnDefType;
46 import org.slf4j.Logger;
47 import org.slf4j.LoggerFactory;
48
49 import com.google.gson.JsonObject;
50
51 /**
52  * The {@link EaseeChargerHandler} is responsible for handling commands, which are
53  * sent to one of the channels.
54  *
55  * @author Alexander Friese - initial contribution
56  */
57 @NonNullByDefault
58 public class EaseeChargerHandler extends BaseThingHandler implements EaseeThingHandler, AtomicReferenceTrait {
59     private final Logger logger = LoggerFactory.getLogger(EaseeChargerHandler.class);
60
61     /**
62      * Schedule for polling live data
63      */
64     private final AtomicReference<@Nullable Future<?>> dataPollingJobReference;
65
66     public EaseeChargerHandler(Thing thing) {
67         super(thing);
68         this.dataPollingJobReference = new AtomicReference<>(null);
69     }
70
71     @Override
72     public void initialize() {
73         logger.debug("About to initialize Charger");
74         logger.debug("Easee Charger initialized with id: {}", getId());
75
76         updateStatus(ThingStatus.UNKNOWN, ThingStatusDetail.NONE, STATUS_WAITING_FOR_BRIDGE);
77         startPolling();
78
79         enqueueCommand(new Charger(this, getId(), this::updatePropertiesAndOnlineStatus));
80     }
81
82     public String getId() {
83         return getConfig().get(EaseeBindingConstants.THING_CONFIG_ID).toString();
84     }
85
86     private void updatePropertiesAndOnlineStatus(CommunicationStatus status, JsonObject charger) {
87         updateOnlineStatus(status, charger);
88         Map<String, String> properties = editProperties();
89
90         String backPlateId = Utils.getAsString(charger.getAsJsonObject(JSON_KEY_BACK_PLATE), JSON_KEY_GENERIC_ID);
91         String masterBackPlateId = Utils.getAsString(charger.getAsJsonObject(JSON_KEY_BACK_PLATE),
92                 JSON_KEY_MASTER_BACK_PLATE_ID);
93         if (backPlateId != null && masterBackPlateId != null) {
94             if (backPlateId.equals(masterBackPlateId)) {
95                 properties.put(THING_CONFIG_IS_MASTER, GENERIC_YES);
96             } else {
97                 properties.put(THING_CONFIG_IS_MASTER, GENERIC_NO);
98             }
99             properties.put(THING_CONFIG_BACK_PLATE_ID, backPlateId);
100             properties.put(THING_CONFIG_MASTER_BACK_PLATE_ID, masterBackPlateId);
101         }
102         String chargerName = Utils.getAsString(charger, JSON_KEY_GENERIC_NAME);
103         if (chargerName != null) {
104             properties.put(JSON_KEY_GENERIC_NAME, chargerName);
105         }
106         String circuitId = Utils.getAsString(charger.getAsJsonObject(JSON_KEY_BACK_PLATE), JSON_KEY_CIRCUIT_ID);
107         if (circuitId != null) {
108             properties.put(JSON_KEY_CIRCUIT_ID, circuitId);
109         }
110
111         updateProperties(properties);
112     }
113
114     /**
115      * Start the polling.
116      */
117     private void startPolling() {
118         updateJobReference(dataPollingJobReference, scheduler.scheduleWithFixedDelay(this::pollingRun,
119                 POLLING_INITIAL_DELAY, getBridgeConfiguration().getDataPollingInterval(), TimeUnit.SECONDS));
120     }
121
122     /**
123      * Poll the Easee Cloud API one time.
124      */
125     void pollingRun() {
126         String chargerId = getConfig().get(EaseeBindingConstants.THING_CONFIG_ID).toString();
127         logger.debug("polling charger data for {}", chargerId);
128
129         // proceed if charger is online
130         if (getThing().getStatus() == ThingStatus.ONLINE) {
131             enqueueCommand(new GetConfiguration(this, chargerId, this::updateOnlineStatus));
132             enqueueCommand(new LatestChargingSession(this, chargerId, this::updateOnlineStatus));
133         }
134     }
135
136     /**
137      * updates online status depending on online information received from the API. this is called by the SiteState
138      * Command which retrieves whole site data inclusing charger status.
139      *
140      */
141     public void setOnline(boolean isOnline) {
142         if (isOnline) {
143             super.updateStatus(ThingStatus.ONLINE, ThingStatusDetail.NONE);
144         } else {
145             super.updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, STATUS_NO_CONNECTION);
146         }
147     }
148
149     /**
150      * result processor to handle online status updates
151      *
152      * @param status of command execution
153      * @param jsonObject json respone result
154      */
155     protected final void updateOnlineStatus(CommunicationStatus status, JsonObject jsonObject) {
156         String msg = Utils.getAsString(jsonObject, JSON_KEY_ERROR_TITLE);
157         if (msg == null || msg.isBlank()) {
158             msg = status.getMessage();
159         }
160
161         switch (status.getHttpCode()) {
162             case OK:
163             case ACCEPTED:
164                 super.updateStatus(ThingStatus.ONLINE, ThingStatusDetail.NONE);
165                 break;
166             default:
167                 super.updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, msg);
168         }
169     }
170
171     /**
172      * Disposes the thing.
173      */
174     @Override
175     public void dispose() {
176         logger.debug("Handler disposed.");
177         cancelJobReference(dataPollingJobReference);
178     }
179
180     /**
181      * will update all channels provided in the map
182      */
183     @Override
184     public void updateChannelStatus(Map<Channel, State> values) {
185         logger.debug("Handling charger channel update.");
186
187         for (Channel channel : values.keySet()) {
188             if (getThing().getChannels().contains(channel)) {
189                 State value = values.get(channel);
190                 if (value != null) {
191                     logger.debug("Channel is to be updated: {}: {}", channel.getUID().getAsString(), value);
192                     updateState(channel.getUID(), value);
193                 } else {
194                     logger.debug("Value is null or not provided by Easee Cloud (channel: {})",
195                             channel.getUID().getAsString());
196                     updateState(channel.getUID(), UnDefType.UNDEF);
197                 }
198             } else {
199                 logger.debug("Could not identify channel: {} for model {}", channel.getUID().getAsString(),
200                         getThing().getThingTypeUID().getAsString());
201             }
202         }
203     }
204
205     @Override
206     public void enqueueCommand(EaseeCommand command) {
207         EaseeBridgeHandler bridgeHandler = getBridgeHandler();
208         if (bridgeHandler != null) {
209             bridgeHandler.enqueueCommand(command);
210         } else {
211             // this should not happen
212             logger.warn("no bridge handler found");
213         }
214     }
215
216     private @Nullable EaseeBridgeHandler getBridgeHandler() {
217         Bridge bridge = getBridge();
218         return bridge == null ? null : ((EaseeBridgeHandler) bridge.getHandler());
219     }
220
221     @Override
222     public EaseeConfiguration getBridgeConfiguration() {
223         EaseeBridgeHandler bridgeHandler = getBridgeHandler();
224         return bridgeHandler == null ? new EaseeConfiguration() : bridgeHandler.getBridgeConfiguration();
225     }
226
227     @Override
228     public EaseeCommand buildEaseeCommand(Command command, Channel channel) {
229         String chargerId = getConfig().get(EaseeBindingConstants.THING_CONFIG_ID).toString();
230
231         switch (Utils.getWriteCommand(channel)) {
232             case COMMAND_CHANGE_CONFIGURATION:
233                 return new ChangeConfiguration(this, chargerId, channel, command, this::updateOnlineStatus);
234             case COMMAND_SEND_COMMAND:
235                 return new SendCommand(this, chargerId, channel, command, this::updateOnlineStatus);
236             case COMMAND_SEND_COMMAND_START_STOP:
237                 return new SendCommandStartStop(this, chargerId, channel, command, this::updateOnlineStatus);
238             case COMMAND_SEND_COMMAND_PAUSE_RESUME:
239                 return new SendCommandPauseResume(this, chargerId, channel, command, this::updateOnlineStatus);
240             default:
241                 // this should not happen
242                 logger.error("write command '{}' not found for channel '{}'", command.toString(),
243                         channel.getUID().getIdWithoutGroup());
244                 throw new UnsupportedOperationException(
245                         "write command not found for channel: " + channel.getUID().getIdWithoutGroup());
246         }
247     }
248
249     @Override
250     public Logger getLogger() {
251         return logger;
252     }
253 }