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.
+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 might not be discovered, so you might want to wake it up through the Tesla app first.
+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
// 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");
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;
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
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);
}
@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)
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;
- }
- }
}
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.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;
final WebTarget wakeUpTarget;
private final TeslaSSOHandler ssoHandler;
+ private final ThingTypeMigrationService thingTypeMigrationService;
// Threading and Job related variables
protected ScheduledFuture<?> connectJob;
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);
for (Vehicle vehicle : vehicleArray) {
String responseString = invokeAndParse(vehicle.id, VEHICLE_CONFIG, null, dataRequestTarget, 0);
- if (responseString == null || responseString.isBlank()) {
- continue;
+ VehicleConfig vehicleConfig = null;
+ if (responseString != null && !responseString.isBlank()) {
+ vehicleConfig = gson.fromJson(responseString, VehicleConfig.class);
}
- VehicleConfig vehicleConfig = gson.fromJson(responseString, VehicleConfig.class);
for (VehicleListener listener : vehicleListeners) {
listener.vehicleFound(vehicle, vehicleConfig);
}
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);
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();
}
};
+ 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;
*/
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;
*
* @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);
}
*/
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
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;
+ }
+ }
}
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
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
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
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
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
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
</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>
--- /dev/null
+<?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>