2 * Copyright (c) 2010-2022 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.renault.internal.handler;
15 import static org.openhab.binding.renault.internal.RenaultBindingConstants.*;
16 import static org.openhab.core.library.unit.MetricPrefix.KILO;
17 import static org.openhab.core.library.unit.SIUnits.METRE;
19 import java.util.concurrent.ScheduledFuture;
20 import java.util.concurrent.TimeUnit;
22 import javax.measure.quantity.Length;
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.renault.internal.RenaultConfiguration;
28 import org.openhab.binding.renault.internal.api.Car;
29 import org.openhab.binding.renault.internal.api.MyRenaultHttpSession;
30 import org.openhab.binding.renault.internal.api.exceptions.RenaultForbiddenException;
31 import org.openhab.binding.renault.internal.api.exceptions.RenaultNotImplementedException;
32 import org.openhab.binding.renault.internal.api.exceptions.RenaultUpdateException;
33 import org.openhab.core.library.types.DecimalType;
34 import org.openhab.core.library.types.OnOffType;
35 import org.openhab.core.library.types.PointType;
36 import org.openhab.core.library.types.QuantityType;
37 import org.openhab.core.library.types.StringType;
38 import org.openhab.core.thing.ChannelUID;
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.slf4j.Logger;
45 import org.slf4j.LoggerFactory;
48 * The {@link RenaultHandler} is responsible for handling commands, which are
49 * sent to one of the channels.
51 * @author Doug Culnane - Initial contribution
54 public class RenaultHandler extends BaseThingHandler {
56 private final Logger logger = LoggerFactory.getLogger(RenaultHandler.class);
58 private RenaultConfiguration config = new RenaultConfiguration();
60 private @Nullable ScheduledFuture<?> pollingJob;
62 private HttpClient httpClient;
66 public RenaultHandler(Thing thing, HttpClient httpClient) {
69 this.httpClient = httpClient;
73 public void handleCommand(ChannelUID channelUID, Command command) {
74 // This binding only polls status data automatically.
78 public void initialize() {
79 // reset the car on initialize
81 this.config = getConfigAs(RenaultConfiguration.class);
83 // Validate configuration
84 if (this.config.myRenaultUsername.isBlank()) {
85 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, "MyRenault Username is empty!");
88 if (this.config.myRenaultPassword.isBlank()) {
89 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, "MyRenault Password is empty!");
92 if (this.config.locale.isBlank()) {
93 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, "Location is empty!");
96 if (this.config.vin.isBlank()) {
97 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, "VIN is empty!");
100 if (this.config.refreshInterval < 1) {
101 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR,
102 "The refresh interval mush to be larger than 1");
105 updateStatus(ThingStatus.UNKNOWN);
107 // Background initialization:
108 ScheduledFuture<?> job = pollingJob;
109 if (job == null || job.isCancelled()) {
110 pollingJob = scheduler.scheduleWithFixedDelay(this::getStatus, 0, config.refreshInterval, TimeUnit.MINUTES);
115 public void dispose() {
116 ScheduledFuture<?> job = pollingJob;
124 private void getStatus() {
125 MyRenaultHttpSession httpSession = new MyRenaultHttpSession(this.config, httpClient);
127 httpSession.initSesssion(car);
128 updateStatus(ThingStatus.ONLINE);
129 } catch (Exception e) {
131 logger.warn("Error My Renault Http Session.", e);
132 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, e.getMessage());
134 if (httpSession != null) {
135 String imageURL = car.getImageURL();
136 if (imageURL != null && !imageURL.isEmpty()) {
137 updateState(CHANNEL_IMAGE, new StringType(imageURL));
139 updateHvacStatus(httpSession);
140 updateCockpit(httpSession);
141 updateLocation(httpSession);
142 updateBattery(httpSession);
146 private void updateHvacStatus(MyRenaultHttpSession httpSession) {
147 if (!car.isDisableHvac()) {
149 httpSession.getHvacStatus(car);
150 Boolean hvacstatus = car.getHvacstatus();
151 if (hvacstatus != null) {
152 updateState(CHANNEL_HVAC_STATUS, OnOffType.from(hvacstatus.booleanValue()));
154 } catch (RenaultNotImplementedException e) {
155 car.setDisableHvac(true);
156 } catch (RenaultForbiddenException | RenaultUpdateException e) {
161 private void updateLocation(MyRenaultHttpSession httpSession) {
162 if (!car.isDisableLocation()) {
164 httpSession.getLocation(car);
165 Double latitude = car.getGpsLatitude();
166 Double longitude = car.getGpsLongitude();
167 if (latitude != null && longitude != null) {
168 updateState(CHANNEL_LOCATION, new PointType(new DecimalType(latitude.doubleValue()),
169 new DecimalType(longitude.doubleValue())));
171 } catch (RenaultNotImplementedException e) {
172 car.setDisableLocation(true);
173 } catch (RenaultForbiddenException | RenaultUpdateException e) {
178 private void updateCockpit(MyRenaultHttpSession httpSession) {
179 if (!car.isDisableCockpit()) {
181 httpSession.getCockpit(car);
182 Double odometer = car.getOdometer();
183 if (odometer != null) {
184 updateState(CHANNEL_ODOMETER, new QuantityType<Length>(odometer.doubleValue(), KILO(METRE)));
186 } catch (RenaultNotImplementedException e) {
187 car.setDisableCockpit(true);
188 } catch (RenaultForbiddenException | RenaultUpdateException e) {
193 private void updateBattery(MyRenaultHttpSession httpSession) {
194 if (!car.isDisableBattery()) {
196 httpSession.getBatteryStatus(car);
197 Double batteryLevel = car.getBatteryLevel();
198 if (batteryLevel != null) {
199 updateState(CHANNEL_BATTERY_LEVEL, new DecimalType(batteryLevel.doubleValue()));
201 } catch (RenaultNotImplementedException e) {
202 car.setDisableBattery(true);
203 } catch (RenaultForbiddenException | RenaultUpdateException e) {