mirror of
https://github.com/openhab/openhab-addons.git
synced 2025-01-10 15:11:59 +01:00
[tesla] Adapt binding to changed API from Tesla backend (#14922)
* Adapt binding to changed API from Tesla backend Signed-off-by: Kai Kreuzer <kai@openhab.org>
This commit is contained in:
parent
fb16e90168
commit
f607dde3a5
@ -205,64 +205,64 @@ Bridge tesla:account:myaccount "My Tesla Account" [ refreshToken="xxxx" ] {
|
||||
demo.items:
|
||||
|
||||
```java
|
||||
DateTime TeslaEventstamp {channel="model3:myaccount:mycar:eventstamp"}
|
||||
String TeslaState {channel="model3:myaccount:mycar:state"}
|
||||
Number TeslaSpeed {channel="model3:myaccount:mycar:speed"}
|
||||
String TeslaShiftState {channel="model3:myaccount:mycar:shiftstate"}
|
||||
Number TeslaOdometer {channel="model3:myaccount:mycar:odometer"}
|
||||
Number TeslaRange {channel="model3:myaccount:mycar:range"}
|
||||
DateTime TeslaEventstamp {channel="account:model3:myaccount:mycar:eventstamp"}
|
||||
String TeslaState {channel="account:model3:myaccount:mycar:state"}
|
||||
Number TeslaSpeed {channel="account:model3:myaccount:mycar:speed"}
|
||||
String TeslaShiftState {channel="account:model3:myaccount:mycar:shiftstate"}
|
||||
Number TeslaOdometer {channel="account:model3:myaccount:mycar:odometer"}
|
||||
Number TeslaRange {channel="account:model3:myaccount:mycar:range"}
|
||||
|
||||
Number TeslaBatteryLevel {channel="model3:myaccount:mycar:batterylevel"}
|
||||
Number TeslaPower {channel="model3:myaccount:mycar:power"}
|
||||
Number TeslaBatteryCurrent {channel="model3:myaccount:mycar:batterycurrent"}
|
||||
Number TeslaBatteryRange {channel="model3:myaccount:mycar:batteryrange"}
|
||||
Number TeslaEstBatteryRange {channel="model3:myaccount:mycar:estimatedbatteryrange"}
|
||||
Number TeslaIdealBatteryRange {channel="model3:myaccount:mycar:idealbatteryrange"}
|
||||
Number TeslaUsableBatteryLevel {channel="model3:myaccount:mycar:usablebatterylevel"}
|
||||
Switch TeslaPreconditioning {channel="model3:myaccount:mycar:preconditioning"}
|
||||
Number TeslaBatteryLevel {channel="account:model3:myaccount:mycar:batterylevel"}
|
||||
Number TeslaPower {channel="account:model3:myaccount:mycar:power"}
|
||||
Number TeslaBatteryCurrent {channel="account:model3:myaccount:mycar:batterycurrent"}
|
||||
Number TeslaBatteryRange {channel="account:model3:myaccount:mycar:batteryrange"}
|
||||
Number TeslaEstBatteryRange {channel="account:model3:myaccount:mycar:estimatedbatteryrange"}
|
||||
Number TeslaIdealBatteryRange {channel="account:model3:myaccount:mycar:idealbatteryrange"}
|
||||
Number TeslaUsableBatteryLevel {channel="account:model3:myaccount:mycar:usablebatterylevel"}
|
||||
Switch TeslaPreconditioning {channel="account:model3:myaccount:mycar:preconditioning"}
|
||||
|
||||
Switch TeslaCharge {channel="model3:myaccount:mycar:charge"}
|
||||
Switch TeslaChargeToMax {channel="model3:myaccount:mycar:chargetomax"}
|
||||
Switch TeslaCharge {channel="account:model3:myaccount:mycar:charge"}
|
||||
Switch TeslaChargeToMax {channel="account:model3:myaccount:mycar:chargetomax"}
|
||||
|
||||
Dimmer TeslaChargeLimit {channel="model3:myaccount:mycar:chargelimit"}
|
||||
Number TeslaChargeRate {channel="model3:myaccount:mycar:chargerate"}
|
||||
String TeslaChargingState {channel="model3:myaccount:mycar:chargingstate"}
|
||||
Number TeslaChargerPower {channel="model3:myaccount:mycar:chargerpower"}
|
||||
Number TeslaTimeToFullCharge {channel="model3:myaccount:mycar:timetofullcharge"}
|
||||
Number TeslaMaxCharges {channel="model3:myaccount:mycar:maxcharges"}
|
||||
Dimmer TeslaChargeLimit {channel="account:model3:myaccount:mycar:chargelimit"}
|
||||
Number TeslaChargeRate {channel="account:model3:myaccount:mycar:chargerate"}
|
||||
String TeslaChargingState {channel="account:model3:myaccount:mycar:chargingstate"}
|
||||
Number TeslaChargerPower {channel="account:model3:myaccount:mycar:chargerpower"}
|
||||
Number TeslaTimeToFullCharge {channel="account:model3:myaccount:mycar:timetofullcharge"}
|
||||
Number TeslaMaxCharges {channel="account:model3:myaccount:mycar:maxcharges"}
|
||||
|
||||
Number TeslaChargerVoltage {channel="model3:myaccount:mycar:chargervoltage"}
|
||||
Number TeslaChargerPower {channel="model3:myaccount:mycar:chargerpower"}
|
||||
Number TeslaChargerCurrent {channel="model3:myaccount:mycar:chargercurrent"}
|
||||
Number TeslaChargerVoltage {channel="account:model3:myaccount:mycar:chargervoltage"}
|
||||
Number TeslaChargerPower {channel="account:model3:myaccount:mycar:chargerpower"}
|
||||
Number TeslaChargerCurrent {channel="account:model3:myaccount:mycar:chargercurrent"}
|
||||
|
||||
DateTime TeslaScheduledChargingStart {channel="model3:myaccount:mycar:scheduledchargingstart"}
|
||||
Dimmer TeslaSoC {channel="model3:myaccount:mycar:soc"}
|
||||
DateTime TeslaScheduledChargingStart {channel="account:model3:myaccount:mycar:scheduledchargingstart"}
|
||||
Dimmer TeslaSoC {channel="account:model3:myaccount:mycar:soc"}
|
||||
|
||||
Switch TeslaDoorLock {channel="model3:myaccount:mycar:doorlock"}
|
||||
Switch TeslaHorn {channel="model3:myaccount:mycar:honkhorn"}
|
||||
Switch TeslaStart {channel="model3:myaccount:mycar:remotestart"}
|
||||
Switch TeslaSentry {channel="model3:myaccount:mycar:sentrymode"}
|
||||
Switch TeslaLights {channel="model3:myaccount:mycar:flashlights"}
|
||||
Switch TeslaValet {channel="model3:myaccount:mycar:valetmode"}
|
||||
Switch TeslaDoorLock {channel="account:model3:myaccount:mycar:doorlock"}
|
||||
Switch TeslaHorn {channel="account:model3:myaccount:mycar:honkhorn"}
|
||||
Switch TeslaStart {channel="account:model3:myaccount:mycar:remotestart"}
|
||||
Switch TeslaSentry {channel="account:model3:myaccount:mycar:sentrymode"}
|
||||
Switch TeslaLights {channel="account:model3:myaccount:mycar:flashlights"}
|
||||
Switch TeslaValet {channel="account:model3:myaccount:mycar:valetmode"}
|
||||
|
||||
Switch TeslaWakeup {channel="model3:myaccount:mycar:wakeup"}
|
||||
Switch TeslaWakeup {channel="account:model3:myaccount:mycar:wakeup"}
|
||||
|
||||
Switch TeslaBatteryHeater {channel="model3:myaccount:mycar:batteryheater"}
|
||||
Switch TeslaFrontDefrost {channel="model3:myaccount:mycar:frontdefroster"}
|
||||
Switch TeslaRearDefrost {channel="model3:myaccount:mycar:reardefroster"}
|
||||
Switch TeslaLeftSeatHeater {channel="model3:myaccount:mycar:leftseatheater"}
|
||||
Switch TeslaRightSeatHeater {channel="model3:myaccount:mycar:rightseatheater"}
|
||||
Switch TeslaBatteryHeater {channel="account:model3:myaccount:mycar:batteryheater"}
|
||||
Switch TeslaFrontDefrost {channel="account:model3:myaccount:mycar:frontdefroster"}
|
||||
Switch TeslaRearDefrost {channel="account:model3:myaccount:mycar:reardefroster"}
|
||||
Switch TeslaLeftSeatHeater {channel="account:model3:myaccount:mycar:leftseatheater"}
|
||||
Switch TeslaRightSeatHeater {channel="account:model3:myaccount:mycar:rightseatheater"}
|
||||
|
||||
Switch TeslaHomelink {channel="model3:myaccount:mycar:homelink"}
|
||||
Location TeslaLocation {channel="model3:myaccount:mycar:location"}
|
||||
Number TeslaHeading {channel="model3:myaccount:mycar:heading"}
|
||||
DateTime TeslaLocationTime {channel="model3:myaccount:mycar:gpstimestamp"}
|
||||
Switch TeslaHomelink {channel="account:model3:myaccount:mycar:homelink"}
|
||||
Location TeslaLocation {channel="account:model3:myaccount:mycar:location"}
|
||||
Number TeslaHeading {channel="account:model3:myaccount:mycar:heading"}
|
||||
DateTime TeslaLocationTime {channel="account:model3:myaccount:mycar:gpstimestamp"}
|
||||
|
||||
Switch TeslaAutoconditioning {channel="model3:myaccount:mycar:autoconditioning"}
|
||||
Number:Temperature TeslaTemperature {channel="model3:myaccount:mycar:temperature"}
|
||||
Number:Temperature TeslaTemperatureCombined {channel="model3:myaccount:mycar:combinedtemp"}
|
||||
Number:Temperature TeslaInsideTemperature {channel="model3:myaccount:mycar:insidetemp"}
|
||||
Number:Temperature TeslaOutsideTemperature {channel="model3:myaccount:mycar:outsidetemp"}
|
||||
Switch TeslaAutoconditioning {channel="account:model3:myaccount:mycar:autoconditioning"}
|
||||
Number:Temperature TeslaTemperature {channel="account:model3:myaccount:mycar:temperature"}
|
||||
Number:Temperature TeslaTemperatureCombined {channel="account:model3:myaccount:mycar:combinedtemp"}
|
||||
Number:Temperature TeslaInsideTemperature {channel="account:model3:myaccount:mycar:insidetemp"}
|
||||
Number:Temperature TeslaOutsideTemperature {channel="account:model3:myaccount:mycar:outsidetemp"}
|
||||
```
|
||||
|
||||
demo.sitemap:
|
||||
@ -337,23 +337,7 @@ sitemap main label="Main"
|
||||
}
|
||||
Frame
|
||||
{
|
||||
Switch label="State" item=nTeslaState_chart icon=line mappings=[0="Hide", 1="Hour", 2="Day", 3="Week", 4="Month"]
|
||||
Chart item=nTeslaState period=h refresh=30000 visibility=[nTeslaState_chart==1]
|
||||
Chart item=nTeslaState period=D refresh=30000 visibility=[nTeslaState_chart==2]
|
||||
Chart item=nTeslaState period=W refresh=30000 visibility=[nTeslaState_chart==3]
|
||||
Chart item=nTeslaState period=M refresh=30000 visibility=[nTeslaState_chart==4]
|
||||
}
|
||||
Frame
|
||||
{
|
||||
Switch label="Battery" item=TeslaBatteryLevel_chart icon=line mappings=[0="Hide", 1="Hour", 2="Day", 3="Week", 4="Month"]
|
||||
Chart item=TeslaUsableBatteryLevel period=h refresh=30000 visibility=[TeslaBatteryLevel_chart==1]
|
||||
Chart item=TeslaUsableBatteryLevel period=D refresh=30000 visibility=[TeslaBatteryLevel_chart==2]
|
||||
Chart item=TeslaUsableBatteryLevel period=W refresh=30000 visibility=[TeslaBatteryLevel_chart==3]
|
||||
Chart item=TeslaUsableBatteryLevel period=M refresh=30000 visibility=[TeslaBatteryLevel_chart==4]
|
||||
}
|
||||
Frame
|
||||
{
|
||||
Mapview item=TeslaLocation height=10 icon=location
|
||||
Mapview item=TeslaLocation height=10
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -28,7 +28,7 @@ public class TeslaBindingConstants {
|
||||
public static final String API_NAME = "Tesla Client API";
|
||||
public static final String API_VERSION = "api/1/";
|
||||
public static final String PATH_COMMAND = "command/{cmd}";
|
||||
public static final String PATH_DATA_REQUEST = "data_request/{cmd}";
|
||||
public static final String PATH_DATA_REQUEST = "vehicle_data";
|
||||
public static final String PATH_VEHICLE_ID = "/{vid}/";
|
||||
public static final String PATH_WAKE_UP = "wake_up";
|
||||
public static final String PATH_ACCESS_TOKEN = "oauth/token";
|
||||
@ -71,15 +71,6 @@ public class TeslaBindingConstants {
|
||||
public static final String COMMAND_WAKE_UP = "wake_up";
|
||||
public static final String DATA_THROTTLE = "datathrottle";
|
||||
|
||||
// Tesla REST API vehicle states
|
||||
public static final String CHARGE_STATE = "charge_state";
|
||||
public static final String CLIMATE_STATE = "climate_state";
|
||||
public static final String DRIVE_STATE = "drive_state";
|
||||
public static final String GUI_STATE = "gui_settings";
|
||||
public static final String MOBILE_ENABLED_STATE = "mobile_enabled";
|
||||
public static final String VEHICLE_STATE = "vehicle_state";
|
||||
public static final String VEHICLE_CONFIG = "vehicle_config";
|
||||
|
||||
public static final String BINDING_ID = "tesla";
|
||||
|
||||
// List of all Thing Type UIDs
|
||||
|
@ -514,7 +514,7 @@ public class TeslaChannelSelectorProxy {
|
||||
}
|
||||
},
|
||||
MANAGED_CHARGING_START("managed_charging_start_time", "managedchargingstart", StringType.class, false),
|
||||
MOBILE_ENABLED(TeslaBindingConstants.MOBILE_ENABLED_STATE, "mobileenabled", OnOffType.class, false) {
|
||||
MOBILE_ENABLED("mobile_enabled", "mobileenabled", OnOffType.class, false) {
|
||||
@Override
|
||||
public State getState(String s, TeslaChannelSelectorProxy proxy, Map<String, String> properties) {
|
||||
if ("true".equals(s)) {
|
||||
|
@ -36,6 +36,7 @@ import org.openhab.binding.tesla.internal.TeslaBindingConstants;
|
||||
import org.openhab.binding.tesla.internal.discovery.TeslaVehicleDiscoveryService;
|
||||
import org.openhab.binding.tesla.internal.protocol.Vehicle;
|
||||
import org.openhab.binding.tesla.internal.protocol.VehicleConfig;
|
||||
import org.openhab.binding.tesla.internal.protocol.VehicleData;
|
||||
import org.openhab.binding.tesla.internal.protocol.sso.TokenResponse;
|
||||
import org.openhab.core.io.net.http.HttpClientFactory;
|
||||
import org.openhab.core.thing.Bridge;
|
||||
@ -225,10 +226,10 @@ public class TeslaAccountHandler extends BaseBridgeHandler {
|
||||
Vehicle[] vehicleArray = gson.fromJson(jsonObject.getAsJsonArray("response"), Vehicle[].class);
|
||||
|
||||
for (Vehicle vehicle : vehicleArray) {
|
||||
String responseString = invokeAndParse(vehicle.id, VEHICLE_CONFIG, null, dataRequestTarget, 0);
|
||||
String responseString = invokeAndParse(vehicle.id, null, null, dataRequestTarget, 0);
|
||||
VehicleConfig vehicleConfig = null;
|
||||
if (responseString != null && !responseString.isBlank()) {
|
||||
vehicleConfig = gson.fromJson(responseString, VehicleConfig.class);
|
||||
vehicleConfig = gson.fromJson(responseString, VehicleData.class).vehicle_config;
|
||||
}
|
||||
for (VehicleListener listener : vehicleListeners) {
|
||||
listener.vehicleFound(vehicle, vehicleConfig);
|
||||
|
@ -23,6 +23,7 @@ import java.text.SimpleDateFormat;
|
||||
import java.util.Arrays;
|
||||
import java.util.Date;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ScheduledFuture;
|
||||
@ -48,6 +49,7 @@ import org.openhab.binding.tesla.internal.protocol.DriveState;
|
||||
import org.openhab.binding.tesla.internal.protocol.Event;
|
||||
import org.openhab.binding.tesla.internal.protocol.GUIState;
|
||||
import org.openhab.binding.tesla.internal.protocol.Vehicle;
|
||||
import org.openhab.binding.tesla.internal.protocol.VehicleData;
|
||||
import org.openhab.binding.tesla.internal.protocol.VehicleState;
|
||||
import org.openhab.binding.tesla.internal.throttler.QueueChannelThrottler;
|
||||
import org.openhab.binding.tesla.internal.throttler.Rate;
|
||||
@ -140,8 +142,7 @@ public class TeslaVehicleHandler extends BaseThingHandler {
|
||||
protected QueueChannelThrottler stateThrottler;
|
||||
protected TeslaChannelSelectorProxy teslaChannelSelectorProxy = new TeslaChannelSelectorProxy();
|
||||
protected Thread eventThread;
|
||||
protected ScheduledFuture<?> fastStateJob;
|
||||
protected ScheduledFuture<?> slowStateJob;
|
||||
protected ScheduledFuture<?> stateJob;
|
||||
protected WebSocketFactory webSocketFactory;
|
||||
|
||||
private final Gson gson = new Gson();
|
||||
@ -181,13 +182,8 @@ public class TeslaVehicleHandler extends BaseThingHandler {
|
||||
stateThrottler = new QueueChannelThrottler(firstRate, scheduler, channels);
|
||||
stateThrottler.addRate(secondRate);
|
||||
|
||||
if (fastStateJob == null || fastStateJob.isCancelled()) {
|
||||
fastStateJob = scheduler.scheduleWithFixedDelay(fastStateRunnable, 0, FAST_STATUS_REFRESH_INTERVAL,
|
||||
TimeUnit.MILLISECONDS);
|
||||
}
|
||||
|
||||
if (slowStateJob == null || slowStateJob.isCancelled()) {
|
||||
slowStateJob = scheduler.scheduleWithFixedDelay(slowStateRunnable, 0, SLOW_STATUS_REFRESH_INTERVAL,
|
||||
if (stateJob == null || stateJob.isCancelled()) {
|
||||
stateJob = scheduler.scheduleWithFixedDelay(stateRunnable, 0, SLOW_STATUS_REFRESH_INTERVAL,
|
||||
TimeUnit.MILLISECONDS);
|
||||
}
|
||||
|
||||
@ -207,14 +203,9 @@ public class TeslaVehicleHandler extends BaseThingHandler {
|
||||
logger.trace("Disposing the Tesla handler for {}", getThing().getUID());
|
||||
lock.lock();
|
||||
try {
|
||||
if (fastStateJob != null && !fastStateJob.isCancelled()) {
|
||||
fastStateJob.cancel(true);
|
||||
fastStateJob = null;
|
||||
}
|
||||
|
||||
if (slowStateJob != null && !slowStateJob.isCancelled()) {
|
||||
slowStateJob.cancel(true);
|
||||
slowStateJob = null;
|
||||
if (stateJob != null && !stateJob.isCancelled()) {
|
||||
stateJob.cancel(true);
|
||||
stateJob = null;
|
||||
}
|
||||
|
||||
if (eventThread != null && !eventThread.isInterrupted()) {
|
||||
@ -494,7 +485,8 @@ public class TeslaVehicleHandler extends BaseThingHandler {
|
||||
}
|
||||
|
||||
public void requestData(String command, String payLoad) {
|
||||
if (COMMAND_WAKE_UP.equals(command) || isAwake() || allowWakeUpForCommands) {
|
||||
if (COMMAND_WAKE_UP.equals(command) || isAwake()
|
||||
|| (!"vehicleData".equals(command) && allowWakeUpForCommands)) {
|
||||
Request request = account.newRequest(this, command, payLoad, account.dataRequestTarget, false);
|
||||
if (stateThrottler != null) {
|
||||
stateThrottler.submit(DATA_THROTTLE, request);
|
||||
@ -527,11 +519,7 @@ public class TeslaVehicleHandler extends BaseThingHandler {
|
||||
}
|
||||
|
||||
public void requestAllData() {
|
||||
requestData(DRIVE_STATE);
|
||||
requestData(VEHICLE_STATE);
|
||||
requestData(CHARGE_STATE);
|
||||
requestData(CLIMATE_STATE);
|
||||
requestData(GUI_STATE);
|
||||
requestData("vehicleData", null);
|
||||
}
|
||||
|
||||
protected boolean isAwake() {
|
||||
@ -591,7 +579,7 @@ public class TeslaVehicleHandler extends BaseThingHandler {
|
||||
}
|
||||
}
|
||||
|
||||
if (vehicleState.homelink_nearby) {
|
||||
if (vehicleState != null && vehicleState.homelink_nearby) {
|
||||
computedInactivityPeriod = MOVE_THRESHOLD_INTERVAL_MINUTES_DEFAULT;
|
||||
logger.debug("Car is at home. Movement or drive state threshold is {} min.",
|
||||
MOVE_THRESHOLD_INTERVAL_MINUTES_DEFAULT);
|
||||
@ -671,21 +659,18 @@ public class TeslaVehicleHandler extends BaseThingHandler {
|
||||
JsonObject payloadObject = new JsonObject();
|
||||
payloadObject.addProperty("percent", percent);
|
||||
sendCommand(COMMAND_SET_CHARGE_LIMIT, gson.toJson(payloadObject), account.commandTarget);
|
||||
requestData(CHARGE_STATE);
|
||||
}
|
||||
|
||||
public void setChargingAmps(int amps) {
|
||||
JsonObject payloadObject = new JsonObject();
|
||||
payloadObject.addProperty("charging_amps", amps);
|
||||
sendCommand(COMMAND_SET_CHARGING_AMPS, gson.toJson(payloadObject), account.commandTarget);
|
||||
requestData(CHARGE_STATE);
|
||||
}
|
||||
|
||||
public void setSentryMode(boolean b) {
|
||||
JsonObject payloadObject = new JsonObject();
|
||||
payloadObject.addProperty("on", b);
|
||||
sendCommand(COMMAND_SET_SENTRY_MODE, gson.toJson(payloadObject), account.commandTarget);
|
||||
requestData(VEHICLE_STATE);
|
||||
}
|
||||
|
||||
public void setSunroof(String state) {
|
||||
@ -693,7 +678,6 @@ public class TeslaVehicleHandler extends BaseThingHandler {
|
||||
JsonObject payloadObject = new JsonObject();
|
||||
payloadObject.addProperty("state", state);
|
||||
sendCommand(COMMAND_SUN_ROOF, gson.toJson(payloadObject), account.commandTarget);
|
||||
requestData(VEHICLE_STATE);
|
||||
} else {
|
||||
logger.warn("Ignoring invalid command '{}' for sunroof.", state);
|
||||
}
|
||||
@ -714,7 +698,6 @@ public class TeslaVehicleHandler extends BaseThingHandler {
|
||||
payloadObject.addProperty("driver_temp", driverTemperature);
|
||||
payloadObject.addProperty("passenger_temp", passenegerTemperature);
|
||||
sendCommand(COMMAND_SET_TEMP, gson.toJson(payloadObject), account.commandTarget);
|
||||
requestData(CLIMATE_STATE);
|
||||
}
|
||||
|
||||
public void setCombinedTemperature(float temperature) {
|
||||
@ -733,14 +716,12 @@ public class TeslaVehicleHandler extends BaseThingHandler {
|
||||
JsonObject payloadObject = new JsonObject();
|
||||
payloadObject.addProperty("which_trunk", "front");
|
||||
sendCommand(COMMAND_ACTUATE_TRUNK, gson.toJson(payloadObject), account.commandTarget);
|
||||
requestData(VEHICLE_STATE);
|
||||
}
|
||||
|
||||
public void openTrunk() {
|
||||
JsonObject payloadObject = new JsonObject();
|
||||
payloadObject.addProperty("which_trunk", "rear");
|
||||
sendCommand(COMMAND_ACTUATE_TRUNK, gson.toJson(payloadObject), account.commandTarget);
|
||||
requestData(VEHICLE_STATE);
|
||||
}
|
||||
|
||||
public void closeTrunk() {
|
||||
@ -754,22 +735,18 @@ public class TeslaVehicleHandler extends BaseThingHandler {
|
||||
payloadObject.addProperty("password", String.format("%04d", pin));
|
||||
}
|
||||
sendCommand(COMMAND_SET_VALET_MODE, gson.toJson(payloadObject), account.commandTarget);
|
||||
requestData(VEHICLE_STATE);
|
||||
}
|
||||
|
||||
public void resetValetPin() {
|
||||
sendCommand(COMMAND_RESET_VALET_PIN, account.commandTarget);
|
||||
requestData(VEHICLE_STATE);
|
||||
}
|
||||
|
||||
public void setMaxRangeCharging(boolean b) {
|
||||
sendCommand(b ? COMMAND_CHARGE_MAX : COMMAND_CHARGE_STD, account.commandTarget);
|
||||
requestData(CHARGE_STATE);
|
||||
}
|
||||
|
||||
public void charge(boolean b) {
|
||||
sendCommand(b ? COMMAND_CHARGE_START : COMMAND_CHARGE_STOP, account.commandTarget);
|
||||
requestData(CHARGE_STATE);
|
||||
}
|
||||
|
||||
public void flashLights() {
|
||||
@ -782,17 +759,14 @@ public class TeslaVehicleHandler extends BaseThingHandler {
|
||||
|
||||
public void openChargePort() {
|
||||
sendCommand(COMMAND_OPEN_CHARGE_PORT, account.commandTarget);
|
||||
requestData(CHARGE_STATE);
|
||||
}
|
||||
|
||||
public void lockDoors(boolean b) {
|
||||
sendCommand(b ? COMMAND_DOOR_LOCK : COMMAND_DOOR_UNLOCK, account.commandTarget);
|
||||
requestData(VEHICLE_STATE);
|
||||
}
|
||||
|
||||
public void autoConditioning(boolean b) {
|
||||
sendCommand(b ? COMMAND_AUTO_COND_START : COMMAND_AUTO_COND_STOP, account.commandTarget);
|
||||
requestData(CLIMATE_STATE);
|
||||
}
|
||||
|
||||
public void wakeUp() {
|
||||
@ -854,73 +828,22 @@ public class TeslaVehicleHandler extends BaseThingHandler {
|
||||
public void parseAndUpdate(String request, String payLoad, String result) {
|
||||
final double locationThreshold = .0000001;
|
||||
|
||||
JsonObject jsonObject = null;
|
||||
|
||||
try {
|
||||
if (request != null && result != null && !"null".equals(result)) {
|
||||
updateStatus(ThingStatus.ONLINE);
|
||||
// first, update state objects
|
||||
switch (request) {
|
||||
case DRIVE_STATE: {
|
||||
driveState = gson.fromJson(result, DriveState.class);
|
||||
|
||||
if (Math.abs(lastLatitude - driveState.latitude) > locationThreshold
|
||||
|| Math.abs(lastLongitude - driveState.longitude) > locationThreshold) {
|
||||
logger.debug("Vehicle moved, resetting last location timestamp");
|
||||
|
||||
lastLatitude = driveState.latitude;
|
||||
lastLongitude = driveState.longitude;
|
||||
lastLocationChangeTimestamp = System.currentTimeMillis();
|
||||
}
|
||||
logger.trace("Drive state: {}", driveState.shift_state);
|
||||
|
||||
if ((driveState.shift_state == null) && (lastValidDriveStateNotNull)) {
|
||||
logger.debug("Set NULL shiftstate time");
|
||||
lastValidDriveStateNotNull = false;
|
||||
lastDriveStateChangeToNullTimestamp = System.currentTimeMillis();
|
||||
} else if (driveState.shift_state != null) {
|
||||
logger.trace("Clear NULL shiftstate time");
|
||||
lastValidDriveStateNotNull = true;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
case GUI_STATE: {
|
||||
guiState = gson.fromJson(result, GUIState.class);
|
||||
break;
|
||||
}
|
||||
case VEHICLE_STATE: {
|
||||
vehicleState = gson.fromJson(result, VehicleState.class);
|
||||
break;
|
||||
}
|
||||
case CHARGE_STATE: {
|
||||
chargeState = gson.fromJson(result, ChargeState.class);
|
||||
if (isCharging()) {
|
||||
updateState(CHANNEL_CHARGE, OnOffType.ON);
|
||||
} else {
|
||||
updateState(CHANNEL_CHARGE, OnOffType.OFF);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
case CLIMATE_STATE: {
|
||||
climateState = gson.fromJson(result, ClimateState.class);
|
||||
BigDecimal avgtemp = roundBigDecimal(new BigDecimal(
|
||||
(climateState.driver_temp_setting + climateState.passenger_temp_setting) / 2.0f));
|
||||
updateState(CHANNEL_COMBINED_TEMP, new QuantityType<>(avgtemp, SIUnits.CELSIUS));
|
||||
break;
|
||||
}
|
||||
case "queryVehicle": {
|
||||
if ("queryVehicle".equals(request)) {
|
||||
if (vehicle != null) {
|
||||
logger.debug("Vehicle state is {}", vehicle.state);
|
||||
updateState(TeslaChannelSelector.STATE.getChannelID(), new StringType(vehicle.state));
|
||||
} else {
|
||||
logger.debug("Vehicle state is initializing or unknown");
|
||||
break;
|
||||
return;
|
||||
}
|
||||
|
||||
if (vehicle != null && "asleep".equals(vehicle.state)) {
|
||||
logger.debug("Vehicle is asleep.");
|
||||
break;
|
||||
return;
|
||||
}
|
||||
|
||||
if (vehicle != null && !lastState.equals(vehicle.state)) {
|
||||
@ -951,71 +874,70 @@ public class TeslaVehicleHandler extends BaseThingHandler {
|
||||
logger.debug("Vehicle is inactive");
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
} else if ("vehicleData".equals(request)) {
|
||||
VehicleData vehicleData = gson.fromJson(result, VehicleData.class);
|
||||
if (vehicleData == null) {
|
||||
logger.error("Not able to parse response '{}'", result);
|
||||
return;
|
||||
}
|
||||
|
||||
// secondly, reformat the response string to a JSON compliant
|
||||
// object for some specific non-JSON compatible requests
|
||||
switch (request) {
|
||||
case MOBILE_ENABLED_STATE: {
|
||||
jsonObject = new JsonObject();
|
||||
jsonObject.addProperty(MOBILE_ENABLED_STATE, result);
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
jsonObject = JsonParser.parseString(result).getAsJsonObject();
|
||||
break;
|
||||
}
|
||||
driveState = vehicleData.drive_state;
|
||||
if (Math.abs(lastLatitude - driveState.latitude) > locationThreshold
|
||||
|| Math.abs(lastLongitude - driveState.longitude) > locationThreshold) {
|
||||
logger.debug("Vehicle moved, resetting last location timestamp");
|
||||
|
||||
lastLatitude = driveState.latitude;
|
||||
lastLongitude = driveState.longitude;
|
||||
lastLocationChangeTimestamp = System.currentTimeMillis();
|
||||
}
|
||||
logger.trace("Drive state: {}", driveState.shift_state);
|
||||
|
||||
if ((driveState.shift_state == null) && (lastValidDriveStateNotNull)) {
|
||||
logger.debug("Set NULL shiftstate time");
|
||||
lastValidDriveStateNotNull = false;
|
||||
lastDriveStateChangeToNullTimestamp = System.currentTimeMillis();
|
||||
} else if (driveState.shift_state != null) {
|
||||
logger.trace("Clear NULL shiftstate time");
|
||||
lastValidDriveStateNotNull = true;
|
||||
}
|
||||
|
||||
// process the result
|
||||
if (jsonObject != null && result != null && !"null".equals(result)) {
|
||||
// deal with responses for "set" commands, which get confirmed
|
||||
// positively, or negatively, in which case a reason for failure
|
||||
// is provided
|
||||
if (jsonObject.get("reason") != null && jsonObject.get("reason").getAsString() != null) {
|
||||
boolean requestResult = jsonObject.get("result").getAsBoolean();
|
||||
logger.debug("The request ({}) execution was {}, and reported '{}'", request,
|
||||
requestResult ? "successful" : "not successful", jsonObject.get("reason").getAsString());
|
||||
guiState = vehicleData.gui_settings;
|
||||
|
||||
vehicleState = vehicleData.vehicle_state;
|
||||
|
||||
chargeState = vehicleData.charge_state;
|
||||
if (isCharging()) {
|
||||
updateState(CHANNEL_CHARGE, OnOffType.ON);
|
||||
} else {
|
||||
Set<Map.Entry<String, JsonElement>> entrySet = jsonObject.entrySet();
|
||||
updateState(CHANNEL_CHARGE, OnOffType.OFF);
|
||||
}
|
||||
|
||||
long resultTimeStamp = 0;
|
||||
for (Map.Entry<String, JsonElement> entry : entrySet) {
|
||||
if ("timestamp".equals(entry.getKey())) {
|
||||
resultTimeStamp = Long.parseLong(entry.getValue().getAsString());
|
||||
if (logger.isTraceEnabled()) {
|
||||
Date date = new Date(resultTimeStamp);
|
||||
SimpleDateFormat dateFormatter = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS");
|
||||
logger.trace("The request result timestamp is {}", dateFormatter.format(date));
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
climateState = vehicleData.climate_state;
|
||||
BigDecimal avgtemp = roundBigDecimal(new BigDecimal(
|
||||
(climateState.driver_temp_setting + climateState.passenger_temp_setting) / 2.0f));
|
||||
updateState(CHANNEL_COMBINED_TEMP, new QuantityType<>(avgtemp, SIUnits.CELSIUS));
|
||||
|
||||
try {
|
||||
lock.lock();
|
||||
|
||||
boolean proceed = true;
|
||||
if (resultTimeStamp < lastTimeStamp && request == DRIVE_STATE) {
|
||||
proceed = false;
|
||||
}
|
||||
Set<Map.Entry<String, JsonElement>> entrySet = new HashSet<>();
|
||||
|
||||
entrySet.addAll(gson.toJsonTree(driveState, DriveState.class).getAsJsonObject().entrySet());
|
||||
entrySet.addAll(gson.toJsonTree(guiState, GUIState.class).getAsJsonObject().entrySet());
|
||||
entrySet.addAll(gson.toJsonTree(vehicleState, VehicleState.class).getAsJsonObject().entrySet());
|
||||
entrySet.addAll(gson.toJsonTree(chargeState, ChargeState.class).getAsJsonObject().entrySet());
|
||||
entrySet.addAll(gson.toJsonTree(climateState, ClimateState.class).getAsJsonObject().entrySet());
|
||||
|
||||
if (proceed) {
|
||||
for (Map.Entry<String, JsonElement> entry : entrySet) {
|
||||
try {
|
||||
TeslaChannelSelector selector = TeslaChannelSelector
|
||||
.getValueSelectorFromRESTID(entry.getKey());
|
||||
if (!selector.isProperty()) {
|
||||
if (!entry.getValue().isJsonNull()) {
|
||||
updateState(selector.getChannelID(), teslaChannelSelectorProxy.getState(
|
||||
entry.getValue().getAsString(), selector, editProperties()));
|
||||
updateState(selector.getChannelID(), teslaChannelSelectorProxy
|
||||
.getState(entry.getValue().getAsString(), selector, editProperties()));
|
||||
if (logger.isTraceEnabled()) {
|
||||
logger.trace(
|
||||
"The variable/value pair '{}':'{}' is successfully processed",
|
||||
logger.trace("The variable/value pair '{}':'{}' is successfully processed",
|
||||
entry.getKey(), entry.getValue());
|
||||
}
|
||||
} else {
|
||||
@ -1032,17 +954,13 @@ public class TeslaVehicleHandler extends BaseThingHandler {
|
||||
}
|
||||
}
|
||||
} catch (IllegalArgumentException e) {
|
||||
logger.trace("The variable/value pair '{}':'{}' is not (yet) supported",
|
||||
entry.getKey(), entry.getValue());
|
||||
logger.trace("The variable/value pair '{}':'{}' is not (yet) supported", entry.getKey(),
|
||||
entry.getValue());
|
||||
} catch (ClassCastException | IllegalStateException e) {
|
||||
logger.trace("An exception occurred while converting the JSON data : '{}'",
|
||||
e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
logger.warn("The result for request '{}' is discarded due to an out of sync timestamp",
|
||||
request);
|
||||
}
|
||||
} finally {
|
||||
lock.unlock();
|
||||
}
|
||||
@ -1069,40 +987,22 @@ public class TeslaVehicleHandler extends BaseThingHandler {
|
||||
return value.setScale(1, RoundingMode.HALF_EVEN);
|
||||
}
|
||||
|
||||
protected Runnable slowStateRunnable = () -> {
|
||||
protected Runnable stateRunnable = () -> {
|
||||
try {
|
||||
queryVehicleAndUpdate();
|
||||
boolean allowQuery = allowQuery();
|
||||
|
||||
if (allowQuery) {
|
||||
requestData(CHARGE_STATE);
|
||||
requestData(CLIMATE_STATE);
|
||||
requestData(GUI_STATE);
|
||||
queryVehicle(MOBILE_ENABLED_STATE);
|
||||
requestAllData();
|
||||
} else if (allowWakeUp) {
|
||||
wakeUp();
|
||||
} else if (isAwake()) {
|
||||
logger.debug("slowpoll: Throttled to allow sleep, occupied/idle, or in a console mode");
|
||||
logger.debug("Throttled state polling to allow sleep, occupied/idle, or in a console mode");
|
||||
} else {
|
||||
lastAdvModesTimestamp = System.currentTimeMillis();
|
||||
}
|
||||
} catch (Exception e) {
|
||||
logger.warn("Exception occurred in slowStateRunnable", e);
|
||||
}
|
||||
};
|
||||
|
||||
protected Runnable fastStateRunnable = () -> {
|
||||
if (getThing().getStatus() == ThingStatus.ONLINE) {
|
||||
boolean allowQuery = allowQuery();
|
||||
|
||||
if (allowQuery) {
|
||||
requestData(DRIVE_STATE);
|
||||
requestData(VEHICLE_STATE);
|
||||
} else if (allowWakeUp) {
|
||||
wakeUp();
|
||||
} else if (isAwake()) {
|
||||
logger.debug("fastpoll: Throttled to allow sleep, occupied/idle, or in a console mode");
|
||||
}
|
||||
logger.warn("Exception occurred in stateRunnable", e);
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -0,0 +1,46 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2023 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.tesla.internal.protocol;
|
||||
|
||||
/**
|
||||
* The {@link VehicleData} is a data structure to capture
|
||||
* variables sent by the Tesla API about a vehicle.
|
||||
*
|
||||
* @author Kai Kreuzer - Initial contribution
|
||||
*/
|
||||
public class VehicleData {
|
||||
|
||||
public String color;
|
||||
public String display_name;
|
||||
public String id;
|
||||
public String option_codes;
|
||||
public String vehicle_id;
|
||||
public String vin;
|
||||
public String tokens[];
|
||||
public String state;
|
||||
public boolean remote_start_enabled;
|
||||
public boolean calendar_enabled;
|
||||
public boolean notifications_enabled;
|
||||
public String backseat_token;
|
||||
public String backseat_token_updated_at;
|
||||
|
||||
public ChargeState charge_state;
|
||||
public ClimateState climate_state;
|
||||
public DriveState drive_state;
|
||||
public GUIState gui_settings;
|
||||
public VehicleConfig vehicle_config;
|
||||
public VehicleState vehicle_state;
|
||||
|
||||
VehicleData() {
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user