[tesla] Adapt binding to changed API from Tesla backend (#14924)

* Adapt binding to changed API from Tesla backend
* Add new translations and vehicle thing type

Signed-off-by: Kai Kreuzer <kai@openhab.org>
This commit is contained in:
Kai Kreuzer 2023-05-03 20:39:05 +02:00 committed by GitHub
parent 91450d0708
commit bf2ce7b418
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
17 changed files with 881 additions and 762 deletions

View File

@ -22,10 +22,11 @@ Access is established through a Tesla account as a bridge.
The account cannot be automatically discovered, but has to be created manually.
Once an account is configured, it is automatically queried for associated vehicles and an Inbox entry is created for each of them.
Note: Vehicles that are asleep might not be discovered, so you might want to wake it up through the Tesla app first.
Once an account is configured, it is queried for associated vehicles and an Inbox entry is created for each of them.
Note: Vehicles that are asleep are discovered and put into the Inbox, but their model cannot be determined.
As an effect, their channels are missing until the vehicle wakes up and can be fully queried.
A vehicle can be manually woken up by opening the Tesla app and checking the vehicle status in there.
## Bridge Configuration
@ -40,7 +41,6 @@ Please note that we in general consider it dangerous to enter your credentials i
When using one of such apps, simply copy and paste the received refresh token into the account configuration.
## Thing Configuration Parameters
The vehicle Thing requires the vehicle's VIN as a configuration parameter `vin`.
@ -52,7 +52,6 @@ Additionally, the follow optional parameters may be defined.
| valetpin | Valet PIN | false | PIN to use when enabling Valet Mode |
| allowWakeupForCommands | Allow Wake-Up For Commands | false | Wake up the vehicle to send commands. May cause vehicle to stay awake |
For further flexibility and experimentation, the following advanced parameters may also be set.
| Parameter Name | Label | Default Value | Description |
@ -70,7 +69,6 @@ This setting is not recommended as it will result in a significant vampire drain
`inactivity` setting is ignored and will always be five minutes if homelink is available (car is at home)
## Channels
All vehicles support a huge number of channels - the following list shows the standard ones:
@ -88,7 +86,6 @@ All vehicles support a huge number of channels - the following list shows the st
| odometer | Number:Length | Odometer | Odometer of the vehicle |
| speed | Number:Speed | Speed | Vehicle speed |
Additionally, these advanced channels are available (not all are available on all vehicle types, e.g., the sunroof):
| Channel ID | Item Type | Label | Description |
@ -195,13 +192,11 @@ Additionally, these advanced channels are available (not all are available on al
| wakeup | Switch | Wake Up | Wake up the vehicle from a (deep) sleep |
| wiperbladeheater | Switch | Wiperblade Heater | Indicates if the wiperblade heater is switched on |
## Example
demo.Things:
```
```java
Bridge tesla:account:myaccount "My Tesla Account" [ refreshToken="xxxx" ] {
model3 mycar "My favorite car" [ vin="5YJSA7H25FFP53736"]
}
@ -209,70 +204,70 @@ Bridge tesla:account:myaccount "My Tesla Account" [ refreshToken="xxxx" ] {
demo.items:
```
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"}
```java
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:
```
```perl
sitemap main label="Main"
{
Text item=TeslaUsableBatteryLevel label="Car" icon="tesla" valuecolor=[<=20="red",>60="green"]
@ -342,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
}
}
}
@ -366,7 +345,7 @@ sitemap main label="Main"
demo.rule (for graphing online status in sitemap above)
```
```java
rule "Tesla State Changed"
when
Item TeslaState changed

View File

@ -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,19 +71,11 @@ 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
public static final ThingTypeUID THING_TYPE_ACCOUNT = new ThingTypeUID(BINDING_ID, "account");
public static final ThingTypeUID THING_TYPE_VEHICLE = new ThingTypeUID(BINDING_ID, "vehicle");
public static final ThingTypeUID THING_TYPE_MODELS = new ThingTypeUID(BINDING_ID, "models");
public static final ThingTypeUID THING_TYPE_MODEL3 = new ThingTypeUID(BINDING_ID, "model3");
public static final ThingTypeUID THING_TYPE_MODELX = new ThingTypeUID(BINDING_ID, "modelx");

View File

@ -48,10 +48,10 @@ public class TeslaChannelSelectorProxy {
AUTO_COND("is_auto_conditioning_on", "autoconditioning", OnOffType.class, false) {
@Override
public State getState(String s, TeslaChannelSelectorProxy proxy, Map<String, String> properties) {
if (s.equals("true") || s.equals("1")) {
if ("true".equals(s) || "1".equals(s)) {
return super.getState("ON");
}
if (s.equals("false") || s.equals("0")) {
if ("false".equals(s) || "0".equals(s)) {
return super.getState("OFF");
}
return super.getState(s);
@ -64,10 +64,10 @@ public class TeslaChannelSelectorProxy {
BATTERY_HEATER("battery_heater_on", "batteryheater", OnOffType.class, false) {
@Override
public State getState(String s, TeslaChannelSelectorProxy proxy, Map<String, String> properties) {
if (s.equals("true") || s.equals("1")) {
if ("true".equals(s) || "1".equals(s)) {
return super.getState("ON");
}
if (s.equals("false") || s.equals("0")) {
if ("false".equals(s) || "0".equals(s)) {
return super.getState("OFF");
}
return super.getState(s);
@ -76,10 +76,10 @@ public class TeslaChannelSelectorProxy {
BATTERY_HEATER_NO_POWER("battery_heater_no_power", "batteryheaternopower", OnOffType.class, false) {
@Override
public State getState(String s, TeslaChannelSelectorProxy proxy, Map<String, String> properties) {
if (s.equals("true") || s.equals("1")) {
if ("true".equals(s) || "1".equals(s)) {
return super.getState("ON");
}
if (s.equals("false") || s.equals("0")) {
if ("false".equals(s) || "0".equals(s)) {
return super.getState("OFF");
}
return super.getState(s);
@ -97,10 +97,10 @@ public class TeslaChannelSelectorProxy {
CALENDAR_SUPPORTED("calendar_supported", "calendarsupported", OnOffType.class, true) {
@Override
public State getState(String s, TeslaChannelSelectorProxy proxy, Map<String, String> properties) {
if (s.equals("true") || s.equals("1")) {
if ("true".equals(s) || "1".equals(s)) {
return super.getState("ON");
}
if (s.equals("false") || s.equals("0")) {
if ("false".equals(s) || "0".equals(s)) {
return super.getState("OFF");
}
return super.getState(s);
@ -109,10 +109,10 @@ public class TeslaChannelSelectorProxy {
CALENDAR_ENABLED("calendar_enabled", "calendarenabled", OnOffType.class, false) {
@Override
public State getState(String s, TeslaChannelSelectorProxy proxy, Map<String, String> properties) {
if (s.equals("true") || s.equals("1")) {
if ("true".equals(s) || "1".equals(s)) {
return super.getState("ON");
}
if (s.equals("false") || s.equals("0")) {
if ("false".equals(s) || "0".equals(s)) {
return super.getState("OFF");
}
return super.getState(s);
@ -123,10 +123,10 @@ public class TeslaChannelSelectorProxy {
CHARGE(null, "charge", OnOffType.class, false) {
@Override
public State getState(String s, TeslaChannelSelectorProxy proxy, Map<String, String> properties) {
if (s.equals("true") || s.equals("1")) {
if ("true".equals(s) || "1".equals(s)) {
return super.getState("ON");
}
if (s.equals("false") || s.equals("0")) {
if ("false".equals(s) || "0".equals(s)) {
return super.getState("OFF");
}
return super.getState(s);
@ -138,10 +138,10 @@ public class TeslaChannelSelectorProxy {
CHARGE_ENABLE_REQUEST("charge_enable_request", "chargeenablerequest", OnOffType.class, false) {
@Override
public State getState(String s, TeslaChannelSelectorProxy proxy, Map<String, String> properties) {
if (s.equals("true") || s.equals("1")) {
if ("true".equals(s) || "1".equals(s)) {
return super.getState("ON");
}
if (s.equals("false") || s.equals("0")) {
if ("false".equals(s) || "0".equals(s)) {
return super.getState("OFF");
}
return super.getState(s);
@ -184,10 +184,10 @@ public class TeslaChannelSelectorProxy {
CHARGE_TO_MAX("charge_to_max_range", "chargetomax", OnOffType.class, false) {
@Override
public State getState(String s, TeslaChannelSelectorProxy proxy, Map<String, String> properties) {
if (s.equals("true") || s.equals("1")) {
if ("true".equals(s) || "1".equals(s)) {
return super.getState("ON");
}
if (s.equals("false") || s.equals("0")) {
if ("false".equals(s) || "0".equals(s)) {
return super.getState("OFF");
}
return super.getState(s);
@ -197,10 +197,10 @@ public class TeslaChannelSelectorProxy {
CHARGEPORT("charge_port_door_open", "chargeport", OnOffType.class, false) {
@Override
public State getState(String s, TeslaChannelSelectorProxy proxy, Map<String, String> properties) {
if (s.equals("true") || s.equals("1")) {
if ("true".equals(s) || "1".equals(s)) {
return super.getState("ON");
}
if (s.equals("false") || s.equals("0")) {
if ("false".equals(s) || "0".equals(s)) {
return super.getState("OFF");
}
return super.getState(s);
@ -214,10 +214,10 @@ public class TeslaChannelSelectorProxy {
CLIMATE_ON("is_climate_on", "climate", OnOffType.class, false) {
@Override
public State getState(String s, TeslaChannelSelectorProxy proxy, Map<String, String> properties) {
if (s.equals("true") || s.equals("1")) {
if ("true".equals(s) || "1".equals(s)) {
return super.getState("ON");
}
if (s.equals("false") || s.equals("0")) {
if ("false".equals(s) || "0".equals(s)) {
return super.getState("OFF");
}
return super.getState(s);
@ -229,10 +229,10 @@ public class TeslaChannelSelectorProxy {
DARK_RIMS("dark_rims", "darkrims", OnOffType.class, true) {
@Override
public State getState(String s, TeslaChannelSelectorProxy proxy, Map<String, String> properties) {
if (s.equals("true") || s.equals("1")) {
if ("true".equals(s) || "1".equals(s)) {
return super.getState("ON");
}
if (s.equals("false") || s.equals("0")) {
if ("false".equals(s) || "0".equals(s)) {
return super.getState("OFF");
}
return super.getState(s);
@ -242,10 +242,10 @@ public class TeslaChannelSelectorProxy {
DF("df", "driverfrontdoor", OpenClosedType.class, false) {
@Override
public State getState(String s, TeslaChannelSelectorProxy proxy, Map<String, String> properties) {
if (s.equals("true") || s.equals("1")) {
if ("true".equals(s) || "1".equals(s)) {
return super.getState("OPEN");
}
if (s.equals("false") || s.equals("0")) {
if ("false".equals(s) || "0".equals(s)) {
return super.getState("CLOSED");
}
return super.getState(s);
@ -254,10 +254,10 @@ public class TeslaChannelSelectorProxy {
DOOR_LOCK("locked", "doorlock", OnOffType.class, false) {
@Override
public State getState(String s, TeslaChannelSelectorProxy proxy, Map<String, String> properties) {
if (s.equals("true") || s.equals("1")) {
if ("true".equals(s) || "1".equals(s)) {
return super.getState("ON");
}
if (s.equals("false") || s.equals("0")) {
if ("false".equals(s) || "0".equals(s)) {
return super.getState("OFF");
}
return super.getState(s);
@ -266,10 +266,10 @@ public class TeslaChannelSelectorProxy {
DR("dr", "driverreardoor", OpenClosedType.class, false) {
@Override
public State getState(String s, TeslaChannelSelectorProxy proxy, Map<String, String> properties) {
if (s.equals("true") || s.equals("1")) {
if ("true".equals(s) || "1".equals(s)) {
return super.getState("OPEN");
}
if (s.equals("false") || s.equals("0")) {
if ("false".equals(s) || "0".equals(s)) {
return super.getState("CLOSED");
}
return super.getState(s);
@ -319,10 +319,10 @@ public class TeslaChannelSelectorProxy {
EU_VEHICLE("eu_vehicle", "european", OnOffType.class, true) {
@Override
public State getState(String s, TeslaChannelSelectorProxy proxy, Map<String, String> properties) {
if (s.equals("true") || s.equals("1")) {
if ("true".equals(s) || "1".equals(s)) {
return super.getState("ON");
}
if (s.equals("false") || s.equals("0")) {
if ("false".equals(s) || "0".equals(s)) {
return super.getState("OFF");
}
return super.getState(s);
@ -332,10 +332,10 @@ public class TeslaChannelSelectorProxy {
FAST_CHARGER("fast_charger_present", "fastcharger", OnOffType.class, true) {
@Override
public State getState(String s, TeslaChannelSelectorProxy proxy, Map<String, String> properties) {
if (s.equals("true") || s.equals("1")) {
if ("true".equals(s) || "1".equals(s)) {
return super.getState("ON");
}
if (s.equals("false") || s.equals("0")) {
if ("false".equals(s) || "0".equals(s)) {
return super.getState("OFF");
}
return super.getState(s);
@ -346,10 +346,10 @@ public class TeslaChannelSelectorProxy {
FLASH(null, "flashlights", OnOffType.class, false) {
@Override
public State getState(String s, TeslaChannelSelectorProxy proxy, Map<String, String> properties) {
if (s.equals("true") || s.equals("1")) {
if ("true".equals(s) || "1".equals(s)) {
return super.getState("ON");
}
if (s.equals("false") || s.equals("0")) {
if ("false".equals(s) || "0".equals(s)) {
return super.getState("OFF");
}
return super.getState(s);
@ -358,10 +358,10 @@ public class TeslaChannelSelectorProxy {
FRONT_DEFROSTER("is_front_defroster_on", "frontdefroster", OnOffType.class, false) {
@Override
public State getState(String s, TeslaChannelSelectorProxy proxy, Map<String, String> properties) {
if (s.equals("true") || s.equals("1")) {
if ("true".equals(s) || "1".equals(s)) {
return super.getState("ON");
}
if (s.equals("false") || s.equals("0")) {
if ("false".equals(s) || "0".equals(s)) {
return super.getState("OFF");
}
return super.getState(s);
@ -370,10 +370,10 @@ public class TeslaChannelSelectorProxy {
FT("ft", "fronttrunk", OnOffType.class, false) {
@Override
public State getState(String s, TeslaChannelSelectorProxy proxy, Map<String, String> properties) {
if (s.equals("true") || s.equals("1")) {
if ("true".equals(s) || "1".equals(s)) {
return super.getState("ON");
}
if (s.equals("false") || s.equals("0")) {
if ("false".equals(s) || "0".equals(s)) {
return super.getState("OFF");
}
return super.getState(s);
@ -397,10 +397,10 @@ public class TeslaChannelSelectorProxy {
HAS_SPOILER("has_spoiler", "spoiler", OnOffType.class, true) {
@Override
public State getState(String s, TeslaChannelSelectorProxy proxy, Map<String, String> properties) {
if (s.equals("true") || s.equals("1")) {
if ("true".equals(s) || "1".equals(s)) {
return super.getState("ON");
}
if (s.equals("false") || s.equals("0")) {
if ("false".equals(s) || "0".equals(s)) {
return super.getState("OFF");
}
return super.getState(s);
@ -417,10 +417,10 @@ public class TeslaChannelSelectorProxy {
HONK_HORN(null, "honkhorn", OnOffType.class, false) {
@Override
public State getState(String s, TeslaChannelSelectorProxy proxy, Map<String, String> properties) {
if (s.equals("true") || s.equals("1")) {
if ("true".equals(s) || "1".equals(s)) {
return super.getState("ON");
}
if (s.equals("false") || s.equals("0")) {
if ("false".equals(s) || "0".equals(s)) {
return super.getState("OFF");
}
return super.getState(s);
@ -429,10 +429,10 @@ public class TeslaChannelSelectorProxy {
HOMELINK_NEARBY("homelink_nearby", "homelink", OnOffType.class, false) {
@Override
public State getState(String s, TeslaChannelSelectorProxy proxy, Map<String, String> properties) {
if (s.equals("true") || s.equals("1")) {
if ("true".equals(s) || "1".equals(s)) {
return super.getState("ON");
}
if (s.equals("false") || s.equals("0")) {
if ("false".equals(s) || "0".equals(s)) {
return super.getState("OFF");
}
return super.getState(s);
@ -491,10 +491,10 @@ public class TeslaChannelSelectorProxy {
MANAGED_CHARGING("managed_charging_active", "managedcharging", OnOffType.class, false) {
@Override
public State getState(String s, TeslaChannelSelectorProxy proxy, Map<String, String> properties) {
if (s.equals("true")) {
if ("true".equals(s)) {
return super.getState("ON");
}
if (s.equals("false")) {
if ("false".equals(s)) {
return super.getState("OFF");
}
return super.getState(s);
@ -504,23 +504,23 @@ public class TeslaChannelSelectorProxy {
false) {
@Override
public State getState(String s, TeslaChannelSelectorProxy proxy, Map<String, String> properties) {
if (s.equals("true")) {
if ("true".equals(s)) {
return super.getState("ON");
}
if (s.equals("false")) {
if ("false".equals(s)) {
return super.getState("OFF");
}
return super.getState(s);
}
},
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 (s.equals("true")) {
if ("true".equals(s)) {
return super.getState("ON");
}
if (s.equals("false")) {
if ("false".equals(s)) {
return super.getState("OFF");
}
return super.getState(s);
@ -529,10 +529,10 @@ public class TeslaChannelSelectorProxy {
MOTORIZED_CHARGE_PORT("motorized_charge_port", "motorizedchargeport", OnOffType.class, true) {
@Override
public State getState(String s, TeslaChannelSelectorProxy proxy, Map<String, String> properties) {
if (s.equals("true") || s.equals("1")) {
if ("true".equals(s) || "1".equals(s)) {
return super.getState("ON");
}
if (s.equals("false") || s.equals("0")) {
if ("false".equals(s) || "0".equals(s)) {
return super.getState("OFF");
}
return super.getState(s);
@ -555,10 +555,10 @@ public class TeslaChannelSelectorProxy {
NATIVE_LOCATION_SUPPORTED("native_location_supported", "nativelocationsupported", OnOffType.class, false) {
@Override
public State getState(String s, TeslaChannelSelectorProxy proxy, Map<String, String> properties) {
if (s.equals("true") || s.equals("1")) {
if ("true".equals(s) || "1".equals(s)) {
return super.getState("ON");
}
if (s.equals("false") || s.equals("0")) {
if ("false".equals(s) || "0".equals(s)) {
return super.getState("OFF");
}
return super.getState(s);
@ -568,10 +568,10 @@ public class TeslaChannelSelectorProxy {
NOT_ENOUGH_POWER_TO_HEAT("not_enough_power_to_heat", "notenoughpower", OnOffType.class, false) {
@Override
public State getState(String s, TeslaChannelSelectorProxy proxy, Map<String, String> properties) {
if (s.equals("true") || s.equals("1")) {
if ("true".equals(s) || "1".equals(s)) {
return super.getState("ON");
}
if (s.equals("false") || s.equals("0")) {
if ("false".equals(s) || "0".equals(s)) {
return super.getState("OFF");
}
return super.getState(s);
@ -580,10 +580,10 @@ public class TeslaChannelSelectorProxy {
NOTIFICATIONS_ENABLED("notifications_enabled", "notificationsenabled", OnOffType.class, false) {
@Override
public State getState(String s, TeslaChannelSelectorProxy proxy, Map<String, String> properties) {
if (s.equals("true") || s.equals("1")) {
if ("true".equals(s) || "1".equals(s)) {
return super.getState("ON");
}
if (s.equals("false") || s.equals("0")) {
if ("false".equals(s) || "0".equals(s)) {
return super.getState("OFF");
}
return super.getState(s);
@ -592,10 +592,10 @@ public class TeslaChannelSelectorProxy {
NOTIFICATIONS_SUPPORTED("notifications_supported", "notificationssupported", OnOffType.class, false) {
@Override
public State getState(String s, TeslaChannelSelectorProxy proxy, Map<String, String> properties) {
if (s.equals("true") || s.equals("1")) {
if ("true".equals(s) || "1".equals(s)) {
return super.getState("ON");
}
if (s.equals("false") || s.equals("0")) {
if ("false".equals(s) || "0".equals(s)) {
return super.getState("OFF");
}
return super.getState(s);
@ -612,10 +612,10 @@ public class TeslaChannelSelectorProxy {
OPEN_FRUNK(null, "openfrunk", OnOffType.class, false) {
@Override
public State getState(String s, TeslaChannelSelectorProxy proxy, Map<String, String> properties) {
if (s.equals("true") || s.equals("1")) {
if ("true".equals(s) || "1".equals(s)) {
return super.getState("ON");
}
if (s.equals("false") || s.equals("0")) {
if ("false".equals(s) || "0".equals(s)) {
return super.getState("OFF");
}
return super.getState(s);
@ -624,10 +624,10 @@ public class TeslaChannelSelectorProxy {
OPEN_TRUNK(null, "opentrunk", OnOffType.class, false) {
@Override
public State getState(String s, TeslaChannelSelectorProxy proxy, Map<String, String> properties) {
if (s.equals("true") || s.equals("1")) {
if ("true".equals(s) || "1".equals(s)) {
return super.getState("ON");
}
if (s.equals("false") || s.equals("0")) {
if ("false".equals(s) || "0".equals(s)) {
return super.getState("OFF");
}
return super.getState(s);
@ -645,10 +645,10 @@ public class TeslaChannelSelectorProxy {
PARSED_CALENDAR("parsed_calendar_supported", "parsedcalendar", OnOffType.class, false) {
@Override
public State getState(String s, TeslaChannelSelectorProxy proxy, Map<String, String> properties) {
if (s.equals("true") || s.equals("1")) {
if ("true".equals(s) || "1".equals(s)) {
return super.getState("ON");
}
if (s.equals("false") || s.equals("0")) {
if ("false".equals(s) || "0".equals(s)) {
return super.getState("OFF");
}
return super.getState(s);
@ -666,10 +666,10 @@ public class TeslaChannelSelectorProxy {
PF("pf", "passengerfrontdoor", OpenClosedType.class, false) {
@Override
public State getState(String s, TeslaChannelSelectorProxy proxy, Map<String, String> properties) {
if (s.equals("true") || s.equals("1")) {
if ("true".equals(s) || "1".equals(s)) {
return super.getState("OPEN");
}
if (s.equals("false") || s.equals("0")) {
if ("false".equals(s) || "0".equals(s)) {
return super.getState("CLOSED");
}
return super.getState(s);
@ -679,10 +679,10 @@ public class TeslaChannelSelectorProxy {
PR("pr", "passengerreardoor", OpenClosedType.class, false) {
@Override
public State getState(String s, TeslaChannelSelectorProxy proxy, Map<String, String> properties) {
if (s.equals("true") || s.equals("1")) {
if ("true".equals(s) || "1".equals(s)) {
return super.getState("OPEN");
}
if (s.equals("false") || s.equals("0")) {
if ("false".equals(s) || "0".equals(s)) {
return super.getState("CLOSED");
}
return super.getState(s);
@ -691,10 +691,10 @@ public class TeslaChannelSelectorProxy {
PRECONDITIONING("is_preconditioning", "preconditioning", OnOffType.class, false) {
@Override
public State getState(String s, TeslaChannelSelectorProxy proxy, Map<String, String> properties) {
if (s.equals("true") || s.equals("1")) {
if ("true".equals(s) || "1".equals(s)) {
return super.getState("ON");
}
if (s.equals("false") || s.equals("0")) {
if ("false".equals(s) || "0".equals(s)) {
return super.getState("OFF");
}
return super.getState(s);
@ -705,7 +705,7 @@ public class TeslaChannelSelectorProxy {
public State getState(String s, TeslaChannelSelectorProxy proxy, Map<String, String> properties) {
State someState = super.getState(s);
BigDecimal value = ((DecimalType) someState).toBigDecimal();
if (properties.containsKey("distanceunits") && properties.get("distanceunits").equals("km/hr")) {
if (properties.containsKey("distanceunits") && "km/hr".equals(properties.get("distanceunits"))) {
return new QuantityType<>(value, MetricPrefix.KILO(SIUnits.METRE));
} else {
return new QuantityType<>(value, ImperialUnits.MILE);
@ -715,10 +715,10 @@ public class TeslaChannelSelectorProxy {
REAR_DEFROSTER("is_rear_defroster_on", "reardefroster", OnOffType.class, false) {
@Override
public State getState(String s, TeslaChannelSelectorProxy proxy, Map<String, String> properties) {
if (s.equals("true") || s.equals("1")) {
if ("true".equals(s) || "1".equals(s)) {
return super.getState("ON");
}
if (s.equals("false") || s.equals("0")) {
if ("false".equals(s) || "0".equals(s)) {
return super.getState("OFF");
}
return super.getState(s);
@ -727,10 +727,10 @@ public class TeslaChannelSelectorProxy {
REAR_SEAT_HEATERS("rear_seat_heaters", "rearseatheaters", OnOffType.class, true) {
@Override
public State getState(String s, TeslaChannelSelectorProxy proxy, Map<String, String> properties) {
if (s.equals("true") || s.equals("1")) {
if ("true".equals(s) || "1".equals(s)) {
return super.getState("ON");
}
if (s.equals("false") || s.equals("0")) {
if ("false".equals(s) || "0".equals(s)) {
return super.getState("OFF");
}
return super.getState(s);
@ -739,10 +739,10 @@ public class TeslaChannelSelectorProxy {
REMOTE_START("remote_start", "remotestart", OnOffType.class, false) {
@Override
public State getState(String s, TeslaChannelSelectorProxy proxy, Map<String, String> properties) {
if (s.equals("true") || s.equals("1")) {
if ("true".equals(s) || "1".equals(s)) {
return super.getState("ON");
}
if (s.equals("false") || s.equals("0")) {
if ("false".equals(s) || "0".equals(s)) {
return super.getState("OFF");
}
return super.getState(s);
@ -751,10 +751,10 @@ public class TeslaChannelSelectorProxy {
REMOTE_START_ENABLED("remote_start_enabled", "remotestartenabled", OnOffType.class, false) {
@Override
public State getState(String s, TeslaChannelSelectorProxy proxy, Map<String, String> properties) {
if (s.equals("true") || s.equals("1")) {
if ("true".equals(s) || "1".equals(s)) {
return super.getState("ON");
}
if (s.equals("false") || s.equals("0")) {
if ("false".equals(s) || "0".equals(s)) {
return super.getState("OFF");
}
return super.getState(s);
@ -763,10 +763,10 @@ public class TeslaChannelSelectorProxy {
REMOTE_START_SUPPORTED("remote_start_supported", "remotestartsupported", OnOffType.class, false) {
@Override
public State getState(String s, TeslaChannelSelectorProxy proxy, Map<String, String> properties) {
if (s.equals("true") || s.equals("1")) {
if ("true".equals(s) || "1".equals(s)) {
return super.getState("ON");
}
if (s.equals("false") || s.equals("0")) {
if ("false".equals(s) || "0".equals(s)) {
return super.getState("OFF");
}
return super.getState(s);
@ -775,10 +775,10 @@ public class TeslaChannelSelectorProxy {
RESET_VALET_PIN(null, "resetvaletpin", OnOffType.class, false) {
@Override
public State getState(String s, TeslaChannelSelectorProxy proxy, Map<String, String> properties) {
if (s.equals("true") || s.equals("1")) {
if ("true".equals(s) || "1".equals(s)) {
return super.getState("ON");
}
if (s.equals("false") || s.equals("0")) {
if ("false".equals(s) || "0".equals(s)) {
return super.getState("OFF");
}
return super.getState(s);
@ -787,10 +787,10 @@ public class TeslaChannelSelectorProxy {
RHD("rhd", "rhd", OnOffType.class, true) {
@Override
public State getState(String s, TeslaChannelSelectorProxy proxy, Map<String, String> properties) {
if (s.equals("true") || s.equals("1")) {
if ("true".equals(s) || "1".equals(s)) {
return super.getState("ON");
}
if (s.equals("false") || s.equals("0")) {
if ("false".equals(s) || "0".equals(s)) {
return super.getState("OFF");
}
return super.getState(s);
@ -801,10 +801,10 @@ public class TeslaChannelSelectorProxy {
RT("rt", "reartrunk", OnOffType.class, false) {
@Override
public State getState(String s, TeslaChannelSelectorProxy proxy, Map<String, String> properties) {
if (s.equals("true") || s.equals("1")) {
if ("true".equals(s) || "1".equals(s)) {
return super.getState("ON");
}
if (s.equals("false") || s.equals("0")) {
if ("false".equals(s) || "0".equals(s)) {
return super.getState("OFF");
}
return super.getState(s);
@ -821,10 +821,10 @@ public class TeslaChannelSelectorProxy {
SCHEDULED_CHARGING_PENDING("scheduled_charging_pending", "scheduledchargingpending", OnOffType.class, false) {
@Override
public State getState(String s, TeslaChannelSelectorProxy proxy, Map<String, String> properties) {
if (s.equals("true") || s.equals("1")) {
if ("true".equals(s) || "1".equals(s)) {
return super.getState("ON");
}
if (s.equals("false") || s.equals("0")) {
if ("false".equals(s) || "0".equals(s)) {
return super.getState("OFF");
}
return super.getState(s);
@ -844,10 +844,10 @@ public class TeslaChannelSelectorProxy {
SENTRY_MODE("sentry_mode", "sentrymode", OnOffType.class, false) {
@Override
public State getState(String s, TeslaChannelSelectorProxy proxy, Map<String, String> properties) {
if (s.equals("true") || s.equals("1")) {
if ("true".equals(s) || "1".equals(s)) {
return super.getState("ON");
}
if (s.equals("false") || s.equals("0")) {
if ("false".equals(s) || "0".equals(s)) {
return super.getState("OFF");
}
return super.getState(s);
@ -856,10 +856,10 @@ public class TeslaChannelSelectorProxy {
SENTRY_MODE_AVAILABLE("sentry_mode_available", "sentrymodeavailable", OnOffType.class, false) {
@Override
public State getState(String s, TeslaChannelSelectorProxy proxy, Map<String, String> properties) {
if (s.equals("true") || s.equals("1")) {
if ("true".equals(s) || "1".equals(s)) {
return super.getState("ON");
}
if (s.equals("false") || s.equals("0")) {
if ("false".equals(s) || "0".equals(s)) {
return super.getState("OFF");
}
return super.getState(s);
@ -869,10 +869,10 @@ public class TeslaChannelSelectorProxy {
SIDEMIRROR_HEATING("side_mirror_heaters", "sidemirrorheaters", OnOffType.class, false) {
@Override
public State getState(String s, TeslaChannelSelectorProxy proxy, Map<String, String> properties) {
if (s.equals("true") || s.equals("1")) {
if ("true".equals(s) || "1".equals(s)) {
return super.getState("ON");
}
if (s.equals("false") || s.equals("0")) {
if ("false".equals(s) || "0".equals(s)) {
return super.getState("OFF");
}
return super.getState(s);
@ -881,10 +881,10 @@ public class TeslaChannelSelectorProxy {
SMART_PRECONDITIONING("smart_preconditioning", "smartpreconditioning", OnOffType.class, false) {
@Override
public State getState(String s, TeslaChannelSelectorProxy proxy, Map<String, String> properties) {
if (s.equals("true") || s.equals("1")) {
if ("true".equals(s) || "1".equals(s)) {
return super.getState("ON");
}
if (s.equals("false") || s.equals("0")) {
if ("false".equals(s) || "0".equals(s)) {
return super.getState("OFF");
}
return super.getState(s);
@ -907,10 +907,10 @@ public class TeslaChannelSelectorProxy {
STEERINGWHEEL_HEATER("steering_wheel_heater", "steeringwheelheater", OnOffType.class, true) {
@Override
public State getState(String s, TeslaChannelSelectorProxy proxy, Map<String, String> properties) {
if (s.equals("true") || s.equals("1")) {
if ("true".equals(s) || "1".equals(s)) {
return super.getState("ON");
}
if (s.equals("false") || s.equals("0")) {
if ("false".equals(s) || "0".equals(s)) {
return super.getState("OFF");
}
return super.getState(s);
@ -919,10 +919,10 @@ public class TeslaChannelSelectorProxy {
SUN_ROOF_PRESENT("sun_roof_installed", "sunroofinstalled", OnOffType.class, true) {
@Override
public State getState(String s, TeslaChannelSelectorProxy proxy, Map<String, String> properties) {
if (s.equals("true") || s.equals("1")) {
if ("true".equals(s) || "1".equals(s)) {
return super.getState("ON");
}
if (s.equals("false") || s.equals("0")) {
if ("false".equals(s) || "0".equals(s)) {
return super.getState("OFF");
}
return super.getState(s);
@ -942,7 +942,7 @@ public class TeslaChannelSelectorProxy {
TIMESTAMP("timestamp", "eventstamp", DateTimeType.class, false) {
@Override
public State getState(String s, TeslaChannelSelectorProxy proxy, Map<String, String> properties) {
Date date = new Date(Long.valueOf(s));
Date date = new Date(Long.parseLong(s));
SimpleDateFormat dateFormatter = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSZ");
return super.getState(dateFormatter.format(date));
}
@ -950,10 +950,10 @@ public class TeslaChannelSelectorProxy {
TRIP_CARGING("trip_charging", "tripcharging", OnOffType.class, false) {
@Override
public State getState(String s, TeslaChannelSelectorProxy proxy, Map<String, String> properties) {
if (s.equals("true") || s.equals("1")) {
if ("true".equals(s) || "1".equals(s)) {
return super.getState("ON");
}
if (s.equals("false") || s.equals("0")) {
if ("false".equals(s) || "0".equals(s)) {
return super.getState("OFF");
}
return super.getState(s);
@ -970,10 +970,10 @@ public class TeslaChannelSelectorProxy {
VALET_MODE("valet_mode", "valetmode", OnOffType.class, false) {
@Override
public State getState(String s, TeslaChannelSelectorProxy proxy, Map<String, String> properties) {
if (s.equals("true") || s.equals("1")) {
if ("true".equals(s) || "1".equals(s)) {
return super.getState("ON");
}
if (s.equals("false") || s.equals("0")) {
if ("false".equals(s) || "0".equals(s)) {
return super.getState("OFF");
}
return super.getState(s);
@ -982,10 +982,10 @@ public class TeslaChannelSelectorProxy {
VALET_PIN("valet_pin_needed", "valetpin", OnOffType.class, false) {
@Override
public State getState(String s, TeslaChannelSelectorProxy proxy, Map<String, String> properties) {
if (s.equals("true") || s.equals("1")) {
if ("true".equals(s) || "1".equals(s)) {
return super.getState("ON");
}
if (s.equals("false") || s.equals("0")) {
if ("false".equals(s) || "0".equals(s)) {
return super.getState("OFF");
}
return super.getState(s);
@ -995,10 +995,10 @@ public class TeslaChannelSelectorProxy {
WAKEUP(null, "wakeup", OnOffType.class, false) {
@Override
public State getState(String s, TeslaChannelSelectorProxy proxy, Map<String, String> properties) {
if (s.equals("true") || s.equals("1")) {
if ("true".equals(s) || "1".equals(s)) {
return super.getState("ON");
}
if (s.equals("false") || s.equals("0")) {
if ("false".equals(s) || "0".equals(s)) {
return super.getState("OFF");
}
return super.getState(s);
@ -1007,10 +1007,10 @@ public class TeslaChannelSelectorProxy {
WIPERBLADE_HEATER("wiper_blade_heater", "wiperbladeheater", OnOffType.class, false) {
@Override
public State getState(String s, TeslaChannelSelectorProxy proxy, Map<String, String> properties) {
if (s.equals("true") || s.equals("1")) {
if ("true".equals(s) || "1".equals(s)) {
return super.getState("ON");
}
if (s.equals("false") || s.equals("0")) {
if ("false".equals(s) || "0".equals(s)) {
return super.getState("OFF");
}
return super.getState(s);
@ -1059,10 +1059,8 @@ public class TeslaChannelSelectorProxy {
if (state != null) {
return state;
}
} catch (NoSuchMethodException e) {
} catch (IllegalArgumentException e) {
} catch (IllegalAccessException e) {
} catch (InvocationTargetException e) {
} catch (NoSuchMethodException | IllegalArgumentException | IllegalAccessException
| InvocationTargetException e) {
}
return null;

View File

@ -27,6 +27,7 @@ import org.openhab.core.io.net.http.HttpClientFactory;
import org.openhab.core.io.net.http.WebSocketFactory;
import org.openhab.core.thing.Bridge;
import org.openhab.core.thing.Thing;
import org.openhab.core.thing.ThingTypeMigrationService;
import org.openhab.core.thing.ThingTypeUID;
import org.openhab.core.thing.binding.BaseThingHandlerFactory;
import org.openhab.core.thing.binding.ThingHandler;
@ -50,21 +51,24 @@ public class TeslaHandlerFactory extends BaseThingHandlerFactory {
private static final int EVENT_STREAM_CONNECT_TIMEOUT = 3;
private static final int EVENT_STREAM_READ_TIMEOUT = 200;
public static final Set<ThingTypeUID> SUPPORTED_THING_TYPES_UIDS = Set.of(THING_TYPE_ACCOUNT, THING_TYPE_MODELS,
THING_TYPE_MODEL3, THING_TYPE_MODELX, THING_TYPE_MODELY);
public static final Set<ThingTypeUID> SUPPORTED_THING_TYPES_UIDS = Set.of(THING_TYPE_ACCOUNT, THING_TYPE_VEHICLE,
THING_TYPE_MODELS, THING_TYPE_MODEL3, THING_TYPE_MODELX, THING_TYPE_MODELY);
private final ClientBuilder clientBuilder;
private final HttpClientFactory httpClientFactory;
private final WebSocketFactory webSocketFactory;
private final ThingTypeMigrationService thingTypeMigrationService;
@Activate
public TeslaHandlerFactory(@Reference ClientBuilder clientBuilder, @Reference HttpClientFactory httpClientFactory,
final @Reference WebSocketFactory webSocketFactory) {
final @Reference WebSocketFactory webSocketFactory,
final @Reference ThingTypeMigrationService thingTypeMigrationService) {
this.clientBuilder = clientBuilder //
.connectTimeout(EVENT_STREAM_CONNECT_TIMEOUT, TimeUnit.SECONDS)
.readTimeout(EVENT_STREAM_READ_TIMEOUT, TimeUnit.SECONDS);
this.httpClientFactory = httpClientFactory;
this.webSocketFactory = webSocketFactory;
this.thingTypeMigrationService = thingTypeMigrationService;
}
@Override
@ -77,7 +81,8 @@ public class TeslaHandlerFactory extends BaseThingHandlerFactory {
ThingTypeUID thingTypeUID = thing.getThingTypeUID();
if (thingTypeUID.equals(THING_TYPE_ACCOUNT)) {
return new TeslaAccountHandler((Bridge) thing, clientBuilder.build(), httpClientFactory);
return new TeslaAccountHandler((Bridge) thing, clientBuilder.build(), httpClientFactory,
thingTypeMigrationService);
} else {
return new TeslaVehicleHandler(thing, webSocketFactory);
}

View File

@ -81,8 +81,10 @@ public class TeslaVehicleDiscoveryService extends AbstractDiscoveryService
@Override
public void vehicleFound(Vehicle vehicle, VehicleConfig vehicleConfig) {
ThingTypeUID type = identifyModel(vehicleConfig);
ThingTypeUID type = vehicleConfig == null ? TeslaBindingConstants.THING_TYPE_VEHICLE
: vehicleConfig.identifyModel();
if (type != null) {
logger.debug("Found a {} vehicle", type.getId());
ThingUID thingUID = new ThingUID(type, handler.getThing().getUID(), vehicle.vin);
DiscoveryResult discoveryResult = DiscoveryResultBuilder.create(thingUID).withLabel(vehicle.display_name)
.withBridge(handler.getThing().getUID()).withProperty(TeslaBindingConstants.VIN, vehicle.vin)
@ -90,22 +92,4 @@ public class TeslaVehicleDiscoveryService extends AbstractDiscoveryService
thingDiscovered(discoveryResult);
}
}
private ThingTypeUID identifyModel(VehicleConfig vehicleConfig) {
logger.debug("Found a {} vehicle", vehicleConfig.car_type);
switch (vehicleConfig.car_type) {
case "models":
case "models2":
return TeslaBindingConstants.THING_TYPE_MODELS;
case "modelx":
return TeslaBindingConstants.THING_TYPE_MODELX;
case "model3":
return TeslaBindingConstants.THING_TYPE_MODEL3;
case "modely":
return TeslaBindingConstants.THING_TYPE_MODELY;
default:
logger.debug("Found unknown vehicle type '{}' - ignoring it.", vehicleConfig.car_type);
return null;
}
}
}

View File

@ -32,9 +32,11 @@ import javax.ws.rs.client.WebTarget;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
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;
@ -43,6 +45,7 @@ import org.openhab.core.thing.Thing;
import org.openhab.core.thing.ThingStatus;
import org.openhab.core.thing.ThingStatusDetail;
import org.openhab.core.thing.ThingStatusInfo;
import org.openhab.core.thing.ThingTypeMigrationService;
import org.openhab.core.thing.binding.BaseBridgeHandler;
import org.openhab.core.thing.binding.ThingHandlerService;
import org.openhab.core.types.Command;
@ -66,7 +69,7 @@ public class TeslaAccountHandler extends BaseBridgeHandler {
public static final int API_MAXIMUM_ERRORS_IN_INTERVAL = 3;
public static final int API_ERROR_INTERVAL_SECONDS = 15;
private static final int CONNECT_RETRY_INTERVAL = 15000;
private static final DateTimeFormatter dateFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")
private static final DateTimeFormatter DATE_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")
.withZone(ZoneId.systemDefault());
private final Logger logger = LoggerFactory.getLogger(TeslaAccountHandler.class);
@ -80,6 +83,7 @@ public class TeslaAccountHandler extends BaseBridgeHandler {
final WebTarget wakeUpTarget;
private final TeslaSSOHandler ssoHandler;
private final ThingTypeMigrationService thingTypeMigrationService;
// Threading and Job related variables
protected ScheduledFuture<?> connectJob;
@ -96,10 +100,12 @@ public class TeslaAccountHandler extends BaseBridgeHandler {
private TokenResponse logonToken;
private final Set<VehicleListener> vehicleListeners = new HashSet<>();
public TeslaAccountHandler(Bridge bridge, Client teslaClient, HttpClientFactory httpClientFactory) {
public TeslaAccountHandler(Bridge bridge, Client teslaClient, HttpClientFactory httpClientFactory,
ThingTypeMigrationService thingTypeMigrationService) {
super(bridge);
this.teslaTarget = teslaClient.target(URI_OWNERS);
this.ssoHandler = new TeslaSSOHandler(httpClientFactory.getCommonHttpClient());
this.thingTypeMigrationService = thingTypeMigrationService;
this.vehiclesTarget = teslaTarget.path(API_VERSION).path(VEHICLES);
this.vehicleTarget = vehiclesTarget.path(PATH_VEHICLE_ID);
@ -143,7 +149,7 @@ public class TeslaAccountHandler extends BaseBridgeHandler {
}
public void scanForVehicles() {
scheduler.execute(() -> queryVehicles());
scheduler.execute(this::queryVehicles);
}
public void addVehicleListener(VehicleListener listener) {
@ -179,7 +185,6 @@ public class TeslaAccountHandler extends BaseBridgeHandler {
ThingStatusInfo authenticationResult = authenticate();
updateStatus(authenticationResult.getStatus(), authenticationResult.getStatusDetail(),
authenticationResult.getDescription());
return false;
} else {
apiIntervalErrors++;
if (immediatelyFail || apiIntervalErrors >= API_MAXIMUM_ERRORS_IN_INTERVAL) {
@ -221,11 +226,11 @@ 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);
if (responseString == null || responseString.isBlank()) {
continue;
String responseString = invokeAndParse(vehicle.id, null, null, dataRequestTarget, 0);
VehicleConfig vehicleConfig = null;
if (responseString != null && !responseString.isBlank()) {
vehicleConfig = gson.fromJson(responseString, VehicleData.class).vehicle_config;
}
VehicleConfig vehicleConfig = gson.fromJson(responseString, VehicleConfig.class);
for (VehicleListener listener : vehicleListeners) {
listener.vehicleFound(vehicle, vehicleConfig);
}
@ -233,6 +238,14 @@ public class TeslaAccountHandler extends BaseBridgeHandler {
if (vehicle.vin.equals(vehicleThing.getConfiguration().get(VIN))) {
TeslaVehicleHandler vehicleHandler = (TeslaVehicleHandler) vehicleThing.getHandler();
if (vehicleHandler != null) {
if (TeslaBindingConstants.THING_TYPE_VEHICLE.equals(vehicleThing.getThingTypeUID())
&& vehicleConfig != null) {
// Seems the type of this vehicle has not been identified before, so let's switch the
// thing type of it
thingTypeMigrationService.migrateThingType(vehicleThing, vehicleConfig.identifyModel(),
vehicleThing.getConfiguration());
break;
}
logger.debug("Querying the vehicle: VIN {}", vehicle.vin);
String vehicleJSON = gson.toJson(vehicle);
vehicleHandler.parseAndUpdate("queryVehicle", null, vehicleJSON);
@ -256,13 +269,13 @@ public class TeslaAccountHandler extends BaseBridgeHandler {
TokenResponse token = logonToken;
boolean hasExpired = true;
logger.debug("Current authentication time {}", dateFormatter.format(Instant.now()));
logger.debug("Current authentication time {}", DATE_FORMATTER.format(Instant.now()));
if (token != null) {
Instant tokenCreationInstant = Instant.ofEpochMilli(token.created_at * 1000);
Instant tokenExpiresInstant = Instant.ofEpochMilli((token.created_at + token.expires_in) * 1000);
logger.debug("Found a request token from {}", dateFormatter.format(tokenCreationInstant));
logger.debug("Access token expiration time {}", dateFormatter.format(tokenExpiresInstant));
logger.debug("Found a request token from {}", DATE_FORMATTER.format(tokenCreationInstant));
logger.debug("Access token expiration time {}", DATE_FORMATTER.format(tokenExpiresInstant));
if (tokenExpiresInstant.isBefore(Instant.now())) {
logger.debug("The access token has expired");
@ -308,8 +321,7 @@ public class TeslaAccountHandler extends BaseBridgeHandler {
.header("Authorization", "Bearer " + logonToken.access_token)
.post(Entity.entity(payLoad, MediaType.APPLICATION_JSON_TYPE));
}
} else {
if (command != null) {
} else if (command != null) {
response = target.resolveTemplate("cmd", command).resolveTemplate("vid", vehicleId)
.request(MediaType.APPLICATION_JSON_TYPE)
.header("Authorization", "Bearer " + logonToken.access_token).get();
@ -317,7 +329,6 @@ public class TeslaAccountHandler extends BaseBridgeHandler {
response = target.resolveTemplate("vid", vehicleId).request(MediaType.APPLICATION_JSON_TYPE)
.header("Authorization", "Bearer " + logonToken.access_token).get();
}
}
if (!checkResponse(response, false)) {
logger.debug("An error occurred while communicating with the vehicle during request {}: {}: {}",
@ -330,7 +341,6 @@ public class TeslaAccountHandler extends BaseBridgeHandler {
logger.debug("Retrying to send the command {}.", command);
return invokeAndParse(vehicleId, command, payLoad, target, noOfretries - 1);
} catch (InterruptedException e) {
return null;
}
}
return null;
@ -353,8 +363,9 @@ public class TeslaAccountHandler extends BaseBridgeHandler {
lock.lock();
ThingStatusInfo status = getThing().getStatusInfo();
if (status.getStatus() != ThingStatus.ONLINE
&& status.getStatusDetail() != ThingStatusDetail.CONFIGURATION_ERROR) {
if ((status.getStatus() != ThingStatus.ONLINE
&& status.getStatusDetail() != ThingStatusDetail.CONFIGURATION_ERROR)
|| hasUnidentifiedVehicles()) {
logger.debug("Setting up an authenticated connection to the Tesla back-end");
ThingStatusInfo authenticationResult = authenticate();
@ -394,19 +405,16 @@ public class TeslaAccountHandler extends BaseBridgeHandler {
}
}
}
} else {
if (response != null) {
} else if (response != null) {
logger.error("Error fetching the list of vehicles : {}:{}", response.getStatus(),
response.getStatusInfo());
updateStatus(ThingStatus.OFFLINE);
}
}
} else if (authenticationResult.getStatusDetail() == ThingStatusDetail.CONFIGURATION_ERROR) {
// make sure to set thing to CONFIGURATION_ERROR in case of failed authentication in order not to
// hit request limit on retries on the Tesla SSO endpoints.
updateStatus(ThingStatus.OFFLINE, authenticationResult.getStatusDetail());
}
}
} catch (Exception e) {
logger.error("An exception occurred while connecting to the Tesla back-end: '{}'", e.getMessage(), e);
@ -415,6 +423,11 @@ public class TeslaAccountHandler extends BaseBridgeHandler {
}
};
private boolean hasUnidentifiedVehicles() {
return getThing().getThings().stream()
.anyMatch(vehicle -> TeslaBindingConstants.THING_TYPE_VEHICLE.equals(vehicle.getThingTypeUID()));
}
protected class Request implements Runnable {
private static final int NO_OF_RETRIES = 3;

View File

@ -22,7 +22,6 @@ import java.nio.charset.CodingErrorAction;
import java.nio.charset.StandardCharsets;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import org.eclipse.jdt.annotation.Nullable;
import org.eclipse.jetty.websocket.api.Session;
@ -32,6 +31,7 @@ import org.eclipse.jetty.websocket.api.WebSocketPingPongListener;
import org.eclipse.jetty.websocket.client.WebSocketClient;
import org.openhab.binding.tesla.internal.protocol.Event;
import org.openhab.core.io.net.http.WebSocketFactory;
import org.openhab.core.thing.ThingUID;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@ -54,7 +54,6 @@ public class TeslaEventEndpoint implements WebSocketListener, WebSocketPingPongL
private String endpointId;
protected WebSocketFactory webSocketFactory;
private static final AtomicInteger INSTANCE_COUNTER = new AtomicInteger();
private WebSocketClient client;
private ConnectionState connectionState = ConnectionState.CLOSED;
@ -62,11 +61,12 @@ public class TeslaEventEndpoint implements WebSocketListener, WebSocketPingPongL
private EventHandler eventHandler;
private final Gson gson = new Gson();
public TeslaEventEndpoint(WebSocketFactory webSocketFactory) {
public TeslaEventEndpoint(ThingUID uid, WebSocketFactory webSocketFactory) {
try {
this.endpointId = "TeslaEventEndpoint-" + INSTANCE_COUNTER.incrementAndGet();
this.endpointId = "TeslaEventEndpoint-" + uid.getAsString();
client = webSocketFactory.createWebSocketClient(endpointId);
String name = ThingWebClientUtil.buildWebClientConsumerName(uid, null);
client = webSocketFactory.createWebSocketClient(name);
this.client.setConnectTimeout(TIMEOUT_MILLISECONDS);
this.client.setMaxIdleTimeout(IDLE_TIMEOUT_MILLISECONDS);
} catch (Exception e) {
@ -74,6 +74,16 @@ public class TeslaEventEndpoint implements WebSocketListener, WebSocketPingPongL
}
}
public void close() {
try {
if (client.isRunning()) {
client.stop();
}
} catch (Exception e) {
logger.warn("An exception occurred while stopping the WebSocket client : {}", e.getMessage());
}
}
public void connect(URI endpointURI) {
if (connectionState == ConnectionState.CONNECTED) {
return;
@ -113,7 +123,7 @@ public class TeslaEventEndpoint implements WebSocketListener, WebSocketPingPongL
this.session = session;
}
public void close() {
public void closeConnection() {
try {
connectionState = ConnectionState.CLOSING;
if (session != null && session.isOpen()) {

View File

@ -46,7 +46,7 @@ public class TeslaSSOHandler {
private final HttpClient httpClient;
private final Gson gson = new Gson();
private final Logger logger = LoggerFactory.getLogger(TeslaSSOHandler.class);
private static final DateTimeFormatter dateFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")
private static final DateTimeFormatter DATE_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")
.withZone(ZoneId.systemDefault());
public TeslaSSOHandler(HttpClient httpClient) {
@ -74,7 +74,7 @@ public class TeslaSSOHandler {
if (tokenResponse != null && tokenResponse.access_token != null && !tokenResponse.access_token.isEmpty()) {
tokenResponse.created_at = Instant.now().getEpochSecond();
logger.debug("Access token expires in {} seconds at {}", tokenResponse.expires_in, dateFormatter
logger.debug("Access token expires in {} seconds at {}", tokenResponse.expires_in, DATE_FORMATTER
.format(Instant.ofEpochMilli((tokenResponse.created_at + tokenResponse.expires_in) * 1000)));
return tokenResponse;
} else {

View File

@ -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();
@ -168,7 +169,7 @@ public class TeslaVehicleHandler extends BaseThingHandler {
account = (TeslaAccountHandler) getBridge().getHandler();
lock = new ReentrantLock();
scheduler.execute(() -> queryVehicleAndUpdate());
scheduler.execute(this::queryVehicleAndUpdate);
lock.lock();
try {
@ -181,23 +182,17 @@ 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);
}
if (enableEvents) {
if (eventThread == null) {
eventThread = new Thread(eventRunnable, "openHAB-Tesla-Events-" + getThing().getUID());
eventThread = new Thread(eventRunnable, "OH-binding-" + getThing().getUID() + "-events");
eventThread.start();
}
}
} finally {
lock.unlock();
}
@ -208,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()) {
@ -257,8 +247,7 @@ public class TeslaVehicleHandler extends BaseThingHandler {
// Request the state of all known variables. This is sub-optimal, but the requests get scheduled and
// throttled so we are safe not to break the Tesla SLA
requestAllData();
} else {
if (selector != null) {
} else if (selector != null) {
if (!isAwake() && allowWakeUpForCommands) {
logger.debug("Waking vehicle to send command.");
wakeUp();
@ -295,8 +284,7 @@ public class TeslaVehicleHandler extends BaseThingHandler {
}
if (amps != null) {
if (amps < 5 || amps > 32) {
logger.warn("Charging amps can only be set in a range of 5-32A, but not to {}A.",
amps);
logger.warn("Charging amps can only be set in a range of 5-32A, but not to {}A.", amps);
return;
}
setChargingAmps(amps);
@ -421,12 +409,10 @@ public class TeslaVehicleHandler extends BaseThingHandler {
if (vehicleState.rt == 0) {
openTrunk();
}
} else {
if (vehicleState.rt == 1) {
} else if (vehicleState.rt == 1) {
closeTrunk();
}
}
}
break;
}
case VALET_MODE: {
@ -466,10 +452,9 @@ public class TeslaVehicleHandler extends BaseThingHandler {
}
}
}
}
public void sendCommand(String command, String payLoad, WebTarget target) {
if (command.equals(COMMAND_WAKE_UP) || isAwake() || allowWakeUpForCommands) {
if (COMMAND_WAKE_UP.equals(command) || isAwake() || allowWakeUpForCommands) {
Request request = account.newRequest(this, command, payLoad, target, allowWakeUpForCommands);
if (stateThrottler != null) {
stateThrottler.submit(COMMAND_THROTTLE, request);
@ -482,7 +467,7 @@ public class TeslaVehicleHandler extends BaseThingHandler {
}
public void sendCommand(String command, String payLoad) {
if (command.equals(COMMAND_WAKE_UP) || isAwake() || allowWakeUpForCommands) {
if (COMMAND_WAKE_UP.equals(command) || isAwake() || allowWakeUpForCommands) {
Request request = account.newRequest(this, command, payLoad, account.commandTarget, allowWakeUpForCommands);
if (stateThrottler != null) {
stateThrottler.submit(COMMAND_THROTTLE, request);
@ -491,7 +476,7 @@ public class TeslaVehicleHandler extends BaseThingHandler {
}
public void sendCommand(String command, WebTarget target) {
if (command.equals(COMMAND_WAKE_UP) || isAwake() || allowWakeUpForCommands) {
if (COMMAND_WAKE_UP.equals(command) || isAwake() || allowWakeUpForCommands) {
Request request = account.newRequest(this, command, "{}", target, allowWakeUpForCommands);
if (stateThrottler != null) {
stateThrottler.submit(COMMAND_THROTTLE, request);
@ -500,7 +485,8 @@ public class TeslaVehicleHandler extends BaseThingHandler {
}
public void requestData(String command, String payLoad) {
if (command.equals(COMMAND_WAKE_UP) || 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);
@ -533,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() {
@ -597,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);
@ -650,7 +632,6 @@ public class TeslaVehicleHandler extends BaseThingHandler {
} else if (response != null && response.getStatus() == 401) {
logger.debug("The access token has expired, trying to get a new one.");
account.authenticate();
return false;
} else {
apiIntervalErrors++;
if (immediatelyFail || apiIntervalErrors >= TeslaAccountHandler.API_MAXIMUM_ERRORS_IN_INTERVAL) {
@ -678,29 +659,25 @@ 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) {
if (state.equals("vent") || state.equals("close")) {
if ("vent".equals(state) || "close".equals(state)) {
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);
}
@ -721,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) {
@ -740,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() {
@ -761,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() {
@ -789,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() {
@ -818,6 +785,7 @@ public class TeslaVehicleHandler extends BaseThingHandler {
if (authHeader != null) {
try {
// get a list of vehicles
synchronized (account.vehiclesTarget) {
Response response = account.vehiclesTarget.request(MediaType.APPLICATION_JSON_TYPE)
.header("Authorization", authHeader).get();
@ -844,6 +812,8 @@ public class TeslaVehicleHandler extends BaseThingHandler {
return vehicle;
}
}
}
} catch (ProcessingException e) {
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, e.getMessage());
}
@ -856,75 +826,24 @@ public class TeslaVehicleHandler extends BaseThingHandler {
}
public void parseAndUpdate(String request, String payLoad, String result) {
final Double LOCATION_THRESHOLD = .0000001;
JsonObject jsonObject = null;
final double locationThreshold = .0000001;
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) > LOCATION_THRESHOLD
|| Math.abs(lastLongitude - driveState.longitude) > LOCATION_THRESHOLD) {
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)) {
@ -955,78 +874,76 @@ 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 '{}'", new Object[] { 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.valueOf(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 {
updateState(selector.getChannelID(), UnDefType.UNDEF);
}
} else {
if (!entry.getValue().isJsonNull()) {
} else if (!entry.getValue().isJsonNull()) {
Map<String, String> properties = editProperties();
properties.put(selector.getChannelID(), entry.getValue().getAsString());
updateProperties(properties);
@ -1036,19 +953,14 @@ public class TeslaVehicleHandler extends BaseThingHandler {
entry.getKey(), entry.getValue(), selector.getChannelID());
}
}
}
} 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();
}
@ -1075,48 +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);
} else {
if (allowWakeUp) {
requestAllData();
} else if (allowWakeUp) {
wakeUp();
} else {
if (isAwake()) {
logger.debug("slowpoll: Throttled to allow sleep, occupied/idle, or in a console mode");
} else if (isAwake()) {
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);
}
};
@ -1127,7 +1013,7 @@ public class TeslaVehicleHandler extends BaseThingHandler {
@Override
public void run() {
eventEndpoint = new TeslaEventEndpoint(webSocketFactory);
eventEndpoint = new TeslaEventEndpoint(getThing().getUID(), webSocketFactory);
eventEndpoint.addEventHandler(new TeslaEventEndpoint.EventHandler() {
@Override
public void handleEvent(Event event) {
@ -1140,7 +1026,7 @@ public class TeslaVehicleHandler extends BaseThingHandler {
logger.debug("Event : Received an update: '{}'", event.value);
String vals[] = event.value.split(",");
long currentTimeStamp = Long.valueOf(vals[0]);
long currentTimeStamp = Long.parseLong(vals[0]);
long systemTimeStamp = System.currentTimeMillis();
if (logger.isDebugEnabled()) {
SimpleDateFormat dateFormatter = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS");
@ -1151,7 +1037,7 @@ public class TeslaVehicleHandler extends BaseThingHandler {
}
if (systemTimeStamp - currentTimeStamp < EVENT_TIMESTAMP_AGE_LIMIT) {
if (currentTimeStamp > lastTimeStamp) {
lastTimeStamp = Long.valueOf(vals[0]);
lastTimeStamp = Long.parseLong(vals[0]);
if (logger.isDebugEnabled()) {
SimpleDateFormat dateFormatter = new SimpleDateFormat(
"yyyy-MM-dd'T'HH:mm:ss.SSS");
@ -1187,8 +1073,7 @@ public class TeslaVehicleHandler extends BaseThingHandler {
}
}
}
} else {
if (logger.isDebugEnabled()) {
} else if (logger.isDebugEnabled()) {
SimpleDateFormat dateFormatter = new SimpleDateFormat(
"yyyy-MM-dd'T'HH:mm:ss.SSS");
logger.debug(
@ -1196,7 +1081,6 @@ public class TeslaVehicleHandler extends BaseThingHandler {
dateFormatter.format(new Date(currentTimeStamp)),
dateFormatter.format(new Date(lastTimeStamp)));
}
}
} else {
if (logger.isDebugEnabled()) {
SimpleDateFormat dateFormatter = new SimpleDateFormat(
@ -1209,13 +1093,13 @@ public class TeslaVehicleHandler extends BaseThingHandler {
}
if (systemTimeStamp - currentTimeStamp > EVENT_TIMESTAMP_MAX_DELTA) {
logger.trace("Event : The event endpoint will be reset");
eventEndpoint.close();
eventEndpoint.closeConnection();
}
}
break;
case "data:error":
logger.debug("Event : Received an error: '{}'/'{}'", event.value, event.error_type);
eventEndpoint.close();
eventEndpoint.closeConnection();
break;
}
}
@ -1260,7 +1144,7 @@ public class TeslaVehicleHandler extends BaseThingHandler {
"Event : Reached the maximum number of errors ({}) for the current interval ({} seconds)",
EVENT_MAXIMUM_ERRORS_IN_INTERVAL, EVENT_ERROR_INTERVAL_SECONDS);
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR);
eventEndpoint.close();
eventEndpoint.closeConnection();
}
if ((System.currentTimeMillis() - eventIntervalTimestamp) > 1000
@ -1298,6 +1182,7 @@ public class TeslaVehicleHandler extends BaseThingHandler {
if (Thread.interrupted()) {
logger.debug("Event : The event thread was interrupted");
eventEndpoint.close();
return;
}
}

View File

@ -0,0 +1,67 @@
/**
* Copyright (c) 2010-2022 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.handler;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.openhab.core.thing.ThingUID;
/**
* {@link ThingWebClientUtil} provides an utility method to create a valid consumer name for web clients.
*
* @author Laurent Garnier - Initial contribution
*/
@NonNullByDefault
public class ThingWebClientUtil {
private static final int MAX_CONSUMER_NAME_LENGTH = 20;
/**
* Build a valid consumer name for HTTP or WebSocket client.
*
* @param uid thing UID for which to associate HTTP or WebSocket client
* @param prefix a prefix to consider for the name; can be null
* @return a valid consumer name
*/
public static String buildWebClientConsumerName(ThingUID uid, @Nullable String prefix) {
String pref = prefix == null ? "" : prefix;
String name = pref + uid.getAsString().replace(':', '-');
if (name.length() > MAX_CONSUMER_NAME_LENGTH) {
// Try to use only prefix + binding ID + thing ID
name = pref + uid.getBindingId();
if (name.length() > (MAX_CONSUMER_NAME_LENGTH / 2)) {
// Truncate the binding ID to keep enough place for thing ID
name = name.substring(0, MAX_CONSUMER_NAME_LENGTH / 2);
}
// Add the thing ID
String id = uid.getId();
int maxIdLength = MAX_CONSUMER_NAME_LENGTH - 1 - name.length();
if (id.length() > maxIdLength) {
// If thing ID is too big, use a hash code of the thing UID instead of thing id
// and truncate it if necessary
id = buildHashCode(uid, maxIdLength);
}
name += "-" + id;
}
return name;
}
// Make the method public just to be able to call it inside the tests
static String buildHashCode(ThingUID uid, int maxLength) {
String result = Integer.toHexString(uid.hashCode());
if (result.length() > maxLength) {
result = result.substring(result.length() - maxLength);
}
return result;
}
}

View File

@ -12,6 +12,8 @@
*/
package org.openhab.binding.tesla.internal.handler;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.openhab.binding.tesla.internal.protocol.Vehicle;
import org.openhab.binding.tesla.internal.protocol.VehicleConfig;
@ -21,12 +23,14 @@ import org.openhab.binding.tesla.internal.protocol.VehicleConfig;
*
* @author Kai Kreuzer - Initial contribution
*/
@NonNullByDefault
public interface VehicleListener {
/**
* This method is called by the {@link TeslaAccountHandler}, if a vehicle is identified.
*
* @param vehicle a vehicle that was found within an account.
* @param vehicleConfig vehicle configuration that was read from the vehicle or null, if not available.
*/
void vehicleFound(Vehicle vehicle, VehicleConfig vehicleConfig);
void vehicleFound(Vehicle vehicle, @Nullable VehicleConfig vehicleConfig);
}

View File

@ -12,6 +12,9 @@
*/
package org.openhab.binding.tesla.internal.protocol;
import org.openhab.binding.tesla.internal.TeslaBindingConstants;
import org.openhab.core.thing.ThingTypeUID;
/**
* The {@link VehicleConfig} is a data structure to capture
* vehicle configuration variables sent by the Tesla Vehicle
@ -41,4 +44,20 @@ public class VehicleConfig {
public String third_row_seats;
public String trim_badging;
public String wheel_type;
public ThingTypeUID identifyModel() {
switch (car_type) {
case "models":
case "models2":
return TeslaBindingConstants.THING_TYPE_MODELS;
case "modelx":
return TeslaBindingConstants.THING_TYPE_MODELX;
case "model3":
return TeslaBindingConstants.THING_TYPE_MODEL3;
case "modely":
return TeslaBindingConstants.THING_TYPE_MODELY;
default:
return TeslaBindingConstants.THING_TYPE_VEHICLE;
}
}
}

View File

@ -0,0 +1,46 @@
/**
* Copyright (c) 2010-2022 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() {
}
}

View File

@ -18,12 +18,12 @@ package org.openhab.binding.tesla.internal.throttler;
* @author Karel Goderis - Initial contribution
*/
public interface TimeProvider {
public static final TimeProvider SYSTEM_PROVIDER = new TimeProvider() {
static final TimeProvider SYSTEM_PROVIDER = new TimeProvider() {
@Override
public long getCurrentTimeInMillis() {
return System.currentTimeMillis();
}
};
public long getCurrentTimeInMillis();
long getCurrentTimeInMillis();
}

View File

@ -15,6 +15,8 @@ thing-type.tesla.modelx.label = Tesla Model X
thing-type.tesla.modelx.description = A Tesla Model X Vehicle
thing-type.tesla.modely.label = Tesla Model Y
thing-type.tesla.modely.description = A Tesla Model Y Vehicle
thing-type.tesla.vehicle.label = Tesla
thing-type.tesla.vehicle.description = A Tesla Vehicle
# thing types config
@ -24,6 +26,14 @@ thing-type.config.tesla.model3.allowWakeup.label = Allow Wake-Up
thing-type.config.tesla.model3.allowWakeup.description = Allows waking up the vehicle. Caution: This can result in huge vampire drain!
thing-type.config.tesla.model3.allowWakeupForCommands.label = Allow Wake-Up For Commands
thing-type.config.tesla.model3.allowWakeupForCommands.description = Allows waking up the vehicle, when commands are sent to it. Execution of commands will be delayed in this case and you could cause the vehicle to stay awake very long.
thing-type.config.tesla.model3.enableEvents.label = Enable Events
thing-type.config.tesla.model3.enableEvents.description = Enable the event stream for the vehicle
thing-type.config.tesla.model3.inactivity.label = Inactivity Interval
thing-type.config.tesla.model3.inactivity.description = The inactivity period in minutes, after which the binding stops for 20 minutes to let the car sleep.
thing-type.config.tesla.model3.useAdvancedStatesForPolling.label = Use Console Modes and Occupancy for Inactivity
thing-type.config.tesla.model3.useAdvancedStatesForPolling.description = Use these states to help continue the fast polling of the API. Do not back off polling if in these states.
thing-type.config.tesla.model3.useDriveState.label = Use Drive State for Inactivity
thing-type.config.tesla.model3.useDriveState.description = Use the drive state instead of location to determine vehicle inactivity.
thing-type.config.tesla.model3.valetpin.label = Valet PIN
thing-type.config.tesla.model3.valetpin.description = PIN to use when enabling Valet Mode
thing-type.config.tesla.model3.vin.label = Vehicle Identification Number
@ -32,6 +42,14 @@ thing-type.config.tesla.models.allowWakeup.label = Allow Wake-Up
thing-type.config.tesla.models.allowWakeup.description = Allows waking up the vehicle. Caution: This can result in huge vampire drain!
thing-type.config.tesla.models.allowWakeupForCommands.label = Allow Wake-Up For Commands
thing-type.config.tesla.models.allowWakeupForCommands.description = Allows waking up the vehicle, when commands are sent to it. Execution of commands will be delayed in this case and you could cause the vehicle to stay awake very long.
thing-type.config.tesla.models.enableEvents.label = Enable Events
thing-type.config.tesla.models.enableEvents.description = Enable the event stream for the vehicle
thing-type.config.tesla.models.inactivity.label = Inactivity Interval
thing-type.config.tesla.models.inactivity.description = The inactivity period in minutes, after which the binding stops for 20 minutes to let the car sleep.
thing-type.config.tesla.models.useAdvancedStatesForPolling.label = Use Console Modes and Occupancy for Inactivity
thing-type.config.tesla.models.useAdvancedStatesForPolling.description = Use these states to help continue the fast polling of the API. Do not back off polling if in these states.
thing-type.config.tesla.models.useDriveState.label = Use Drive State for Inactivity
thing-type.config.tesla.models.useDriveState.description = Use the drive state instead of location to determine vehicle inactivity.
thing-type.config.tesla.models.valetpin.label = Valet PIN
thing-type.config.tesla.models.valetpin.description = PIN to use when enabling Valet Mode
thing-type.config.tesla.models.vin.label = Vehicle Identification Number
@ -40,6 +58,14 @@ thing-type.config.tesla.modelx.allowWakeup.label = Allow Wake-Up
thing-type.config.tesla.modelx.allowWakeup.description = Allows waking up the vehicle. Caution: This can result in huge vampire drain!
thing-type.config.tesla.modelx.allowWakeupForCommands.label = Allow Wake-Up For Commands
thing-type.config.tesla.modelx.allowWakeupForCommands.description = Allows waking up the vehicle, when commands are sent to it. Execution of commands will be delayed in this case and you could cause the vehicle to stay awake very long.
thing-type.config.tesla.modelx.enableEvents.label = Enable Events
thing-type.config.tesla.modelx.enableEvents.description = Enable the event stream for the vehicle
thing-type.config.tesla.modelx.inactivity.label = Inactivity Interval
thing-type.config.tesla.modelx.inactivity.description = The inactivity period in minutes, after which the binding stops for 20 minutes to let the car sleep.
thing-type.config.tesla.modelx.useAdvancedStatesForPolling.label = Use Console Modes and Occupancy for Inactivity
thing-type.config.tesla.modelx.useAdvancedStatesForPolling.description = Use these states to help continue the fast polling of the API. Do not back off polling if in these states.
thing-type.config.tesla.modelx.useDriveState.label = Use Drive State for Inactivity
thing-type.config.tesla.modelx.useDriveState.description = Use the drive state instead of location to determine vehicle inactivity.
thing-type.config.tesla.modelx.valetpin.label = Valet PIN
thing-type.config.tesla.modelx.valetpin.description = PIN to use when enabling Valet Mode
thing-type.config.tesla.modelx.vin.label = Vehicle Identification Number
@ -48,10 +74,34 @@ thing-type.config.tesla.modely.allowWakeup.label = Allow Wake-Up
thing-type.config.tesla.modely.allowWakeup.description = Allows waking up the vehicle. Caution: This can result in huge vampire drain!
thing-type.config.tesla.modely.allowWakeupForCommands.label = Allow Wake-Up For Commands
thing-type.config.tesla.modely.allowWakeupForCommands.description = Allows waking up the vehicle, when commands are sent to it. Execution of commands will be delayed in this case and you could cause the vehicle to stay awake very long.
thing-type.config.tesla.modely.enableEvents.label = Enable Events
thing-type.config.tesla.modely.enableEvents.description = Enable the event stream for the vehicle
thing-type.config.tesla.modely.inactivity.label = Inactivity Interval
thing-type.config.tesla.modely.inactivity.description = The inactivity period in minutes, after which the binding stops for 20 minutes to let the car sleep.
thing-type.config.tesla.modely.useAdvancedStatesForPolling.label = Use Console Modes and Occupancy for Inactivity
thing-type.config.tesla.modely.useAdvancedStatesForPolling.description = Use these states to help continue the fast polling of the API. Do not back off polling if in these states.
thing-type.config.tesla.modely.useDriveState.label = Use Drive State for Inactivity
thing-type.config.tesla.modely.useDriveState.description = Use the drive state instead of location to determine vehicle inactivity.
thing-type.config.tesla.modely.valetpin.label = Valet PIN
thing-type.config.tesla.modely.valetpin.description = PIN to use when enabling Valet Mode
thing-type.config.tesla.modely.vin.label = Vehicle Identification Number
thing-type.config.tesla.modely.vin.description = VIN of the vehicle
thing-type.config.tesla.vehicle.allowWakeup.label = Allow Wake-Up
thing-type.config.tesla.vehicle.allowWakeup.description = Allows waking up the vehicle. Caution: This can result in huge vampire drain!
thing-type.config.tesla.vehicle.allowWakeupForCommands.label = Allow Wake-Up For Commands
thing-type.config.tesla.vehicle.allowWakeupForCommands.description = Allows waking up the vehicle, when commands are sent to it. Execution of commands will be delayed in this case and you could cause the vehicle to stay awake very long.
thing-type.config.tesla.vehicle.enableEvents.label = Enable Events
thing-type.config.tesla.vehicle.enableEvents.description = Enable the event stream for the vehicle
thing-type.config.tesla.vehicle.inactivity.label = Inactivity Interval
thing-type.config.tesla.vehicle.inactivity.description = The inactivity period in minutes, after which the binding stops for 20 minutes to let the car sleep.
thing-type.config.tesla.vehicle.useAdvancedStatesForPolling.label = Use Console Modes and Occupancy for Inactivity
thing-type.config.tesla.vehicle.useAdvancedStatesForPolling.description = Use these states to help continue the fast polling of the API. Do not back off polling if in these states.
thing-type.config.tesla.vehicle.useDriveState.label = Use Drive State for Inactivity
thing-type.config.tesla.vehicle.useDriveState.description = Use the drive state instead of location to determine vehicle inactivity.
thing-type.config.tesla.vehicle.valetpin.label = Valet PIN
thing-type.config.tesla.vehicle.valetpin.description = PIN to use when enabling Valet Mode
thing-type.config.tesla.vehicle.vin.label = Vehicle Identification Number
thing-type.config.tesla.vehicle.vin.description = VIN of the vehicle
# channel types
@ -206,7 +256,7 @@ channel-type.tesla.minavailabletemp.label = Minimum Temperature
channel-type.tesla.minavailabletemp.description = Indicates the minimal inside temperature of the vehicle
channel-type.tesla.mobileenabled.label = Mobile Enabled
channel-type.tesla.mobileenabled.description = Indicates whether the vehicle can be remotely controlled
channel-type.tesla.notenoughpower.label = Not Enought Power
channel-type.tesla.notenoughpower.label = Not Enough Power
channel-type.tesla.notenoughpower.description = Indicates if not enough power (ON) is available to heat the vehicle
channel-type.tesla.notificationsenabled.label = Notifications Enabled
channel-type.tesla.notificationsenabled.description = Not documented / To be defined

View File

@ -421,7 +421,7 @@
</channel-type>
<channel-type id="notenoughpower" advanced="true">
<item-type>Switch</item-type>
<label>Not Enought Power</label>
<label>Not Enough Power</label>
<description>Indicates if not enough power (ON) is available to heat the vehicle</description>
<state readOnly="true"></state>
</channel-type>

View File

@ -0,0 +1,67 @@
<?xml version="1.0" encoding="UTF-8"?>
<thing:thing-descriptions bindingId="tesla"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:thing="https://openhab.org/schemas/thing-description/v1.0.0"
xsi:schemaLocation="https://openhab.org/schemas/thing-description/v1.0.0 https://openhab.org/schemas/thing-description-1.0.0.xsd">
<thing-type id="vehicle">
<supported-bridge-type-refs>
<bridge-type-ref id="account"/>
</supported-bridge-type-refs>
<label>Tesla</label>
<description>A Tesla Vehicle</description>
<config-description>
<parameter name="vin" type="text" required="true">
<label>Vehicle Identification Number</label>
<description>VIN of the vehicle</description>
</parameter>
<parameter name="valetpin" type="integer" min="0" max="9999" required="false">
<context>password</context>
<label>Valet PIN</label>
<description>PIN to use when enabling Valet Mode</description>
</parameter>
<parameter name="allowWakeup" type="boolean" required="false">
<default>false</default>
<label>Allow Wake-Up</label>
<advanced>true</advanced>
<description>Allows waking up the vehicle. Caution: This can result in huge vampire drain!</description>
</parameter>
<parameter name="allowWakeupForCommands" type="boolean" required="false">
<default>false</default>
<label>Allow Wake-Up For Commands</label>
<description>Allows waking up the vehicle, when commands are sent to it. Execution of commands will be delayed in
this case and you could cause the vehicle to stay awake very long.</description>
</parameter>
<parameter name="enableEvents" type="boolean" required="false">
<default>false</default>
<label>Enable Events</label>
<advanced>true</advanced>
<description>Enable the event stream for the vehicle</description>
</parameter>
<parameter name="inactivity" type="integer" min="5" required="false">
<label>Inactivity Interval</label>
<advanced>true</advanced>
<description>The inactivity period in minutes, after which the binding stops for 20 minutes to let the car sleep.</description>
<default>5</default>
</parameter>
<parameter name="useDriveState" type="boolean" required="false">
<default>false</default>
<label>Use Drive State for Inactivity</label>
<advanced>true</advanced>
<description>Use the drive state instead of location to determine vehicle inactivity.</description>
</parameter>
<parameter name="useAdvancedStatesForPolling" type="boolean" required="false">
<default>false</default>
<label>Use Console Modes and Occupancy for Inactivity</label>
<advanced>true</advanced>
<description>Use these states to help continue the fast polling of the API. Do not back off polling if in these
states.</description>
</parameter>
</config-description>
</thing-type>
</thing:thing-descriptions>