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.easee.internal.handler;
15 import static org.openhab.binding.easee.internal.EaseeBindingConstants.*;
18 import java.util.concurrent.Future;
19 import java.util.concurrent.TimeUnit;
20 import java.util.concurrent.atomic.AtomicReference;
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;
49 import com.google.gson.JsonObject;
52 * The {@link EaseeChargerHandler} is responsible for handling commands, which are
53 * sent to one of the channels.
55 * @author Alexander Friese - initial contribution
58 public class EaseeChargerHandler extends BaseThingHandler implements EaseeThingHandler, AtomicReferenceTrait {
59 private final Logger logger = LoggerFactory.getLogger(EaseeChargerHandler.class);
62 * Schedule for polling live data
64 private final AtomicReference<@Nullable Future<?>> dataPollingJobReference;
66 public EaseeChargerHandler(Thing thing) {
68 this.dataPollingJobReference = new AtomicReference<>(null);
72 public void initialize() {
73 logger.debug("About to initialize Charger");
74 logger.debug("Easee Charger initialized with id: {}", getId());
76 updateStatus(ThingStatus.UNKNOWN, ThingStatusDetail.NONE, STATUS_WAITING_FOR_BRIDGE);
79 enqueueCommand(new Charger(this, getId(), this::updatePropertiesAndOnlineStatus));
82 public String getId() {
83 return getConfig().get(EaseeBindingConstants.THING_CONFIG_ID).toString();
86 private void updatePropertiesAndOnlineStatus(CommunicationStatus status, JsonObject charger) {
87 updateOnlineStatus(status, charger);
88 Map<String, String> properties = editProperties();
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);
97 properties.put(THING_CONFIG_IS_MASTER, GENERIC_NO);
99 properties.put(THING_CONFIG_BACK_PLATE_ID, backPlateId);
100 properties.put(THING_CONFIG_MASTER_BACK_PLATE_ID, masterBackPlateId);
102 String chargerName = Utils.getAsString(charger, JSON_KEY_GENERIC_NAME);
103 if (chargerName != null) {
104 properties.put(JSON_KEY_GENERIC_NAME, chargerName);
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);
111 updateProperties(properties);
117 private void startPolling() {
118 updateJobReference(dataPollingJobReference, scheduler.scheduleWithFixedDelay(this::pollingRun,
119 POLLING_INITIAL_DELAY, getBridgeConfiguration().getDataPollingInterval(), TimeUnit.SECONDS));
123 * Poll the Easee Cloud API one time.
126 String chargerId = getConfig().get(EaseeBindingConstants.THING_CONFIG_ID).toString();
127 logger.debug("polling charger data for {}", chargerId);
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));
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.
141 public void setOnline(boolean isOnline) {
143 super.updateStatus(ThingStatus.ONLINE, ThingStatusDetail.NONE);
145 super.updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, STATUS_NO_CONNECTION);
150 * result processor to handle online status updates
152 * @param status of command execution
153 * @param jsonObject json respone result
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();
161 switch (status.getHttpCode()) {
164 super.updateStatus(ThingStatus.ONLINE, ThingStatusDetail.NONE);
167 super.updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, msg);
172 * Disposes the thing.
175 public void dispose() {
176 logger.debug("Handler disposed.");
177 cancelJobReference(dataPollingJobReference);
181 * will update all channels provided in the map
184 public void updateChannelStatus(Map<Channel, State> values) {
185 logger.debug("Handling charger channel update.");
187 for (Channel channel : values.keySet()) {
188 if (getThing().getChannels().contains(channel)) {
189 State value = values.get(channel);
191 logger.debug("Channel is to be updated: {}: {}", channel.getUID().getAsString(), value);
192 updateState(channel.getUID(), value);
194 logger.debug("Value is null or not provided by Easee Cloud (channel: {})",
195 channel.getUID().getAsString());
196 updateState(channel.getUID(), UnDefType.UNDEF);
199 logger.debug("Could not identify channel: {} for model {}", channel.getUID().getAsString(),
200 getThing().getThingTypeUID().getAsString());
206 public void enqueueCommand(EaseeCommand command) {
207 EaseeBridgeHandler bridgeHandler = getBridgeHandler();
208 if (bridgeHandler != null) {
209 bridgeHandler.enqueueCommand(command);
211 // this should not happen
212 logger.warn("no bridge handler found");
216 private @Nullable EaseeBridgeHandler getBridgeHandler() {
217 Bridge bridge = getBridge();
218 return bridge == null ? null : ((EaseeBridgeHandler) bridge.getHandler());
222 public EaseeConfiguration getBridgeConfiguration() {
223 EaseeBridgeHandler bridgeHandler = getBridgeHandler();
224 return bridgeHandler == null ? new EaseeConfiguration() : bridgeHandler.getBridgeConfiguration();
228 public EaseeCommand buildEaseeCommand(Command command, Channel channel) {
229 String chargerId = getConfig().get(EaseeBindingConstants.THING_CONFIG_ID).toString();
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);
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());
250 public Logger getLogger() {