]> git.basschouten.com Git - openhab-addons.git/commitdiff
[tesla] Options to control polling frequency and sleep (#13337)
authorbillfor <billfor@users.noreply.github.com>
Sat, 15 Oct 2022 12:25:29 +0000 (08:25 -0400)
committerGitHub <noreply@github.com>
Sat, 15 Oct 2022 12:25:29 +0000 (14:25 +0200)
* Options to control polling frequency and sleep mode
* Update documentation
* Fix request token expiration calculation and additional logging for tokens

Signed-off-by: Bill Forsyth <git@billforsyth.net>
bundles/org.openhab.binding.tesla/README.md
bundles/org.openhab.binding.tesla/src/main/java/org/openhab/binding/tesla/internal/TeslaBindingConstants.java
bundles/org.openhab.binding.tesla/src/main/java/org/openhab/binding/tesla/internal/handler/TeslaAccountHandler.java
bundles/org.openhab.binding.tesla/src/main/java/org/openhab/binding/tesla/internal/handler/TeslaSSOHandler.java
bundles/org.openhab.binding.tesla/src/main/java/org/openhab/binding/tesla/internal/handler/TeslaVehicleHandler.java
bundles/org.openhab.binding.tesla/src/main/java/org/openhab/binding/tesla/internal/protocol/VehicleState.java
bundles/org.openhab.binding.tesla/src/main/resources/OH-INF/thing/model3.xml
bundles/org.openhab.binding.tesla/src/main/resources/OH-INF/thing/models.xml
bundles/org.openhab.binding.tesla/src/main/resources/OH-INF/thing/modelx.xml
bundles/org.openhab.binding.tesla/src/main/resources/OH-INF/thing/modely.xml

index 6c98808961a9ccd1afb595125d4f58d1d5c1ea32..d09b47ae8528e06fa82f8e4c5d0a393731e8930c 100644 (file)
@@ -41,16 +41,35 @@ 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
+## Thing Configuration Parameters
 
 The vehicle Thing requires the vehicle's VIN as a configuration parameter `vin`.
 
-Additionally, the optional boolean parameter `allowWakeup` can be set.
-This determines whether openHAB is allowed to wake up the vehicle in order to retrieve data from it.
+Additionally, the follow optional parameters may be defined.
+
+| Parameter Name         | Label                      | Default Value | Description                                                                  |
+|------------------------|----------------------------|---------------|------------------------------------------------------------------------------|
+| 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                                                                                        |
+|-----------------------------|------------------------------------------------|---------------|----------------------------------------------------------------------------------------------------|
+| allowWakeup                 | Allow Wake-Up                                  | false         | Allows waking up the vehicle to retrieve data. See caution below                                   |
+| enableEvents                | Enable Events                                  | false         | Enable the event stream for the vehicle. See note below                                            |
+| inactivity                  | Inactivity Interval                            | 5             | The inactivity period in minutes after which the binding stops for 20 minutes to let the car sleep |
+| useDriveState               | Use Drive State for Inactivity                 | false         | Use the drive state instead of location to determine vehicle inactivity                            |
+| useAdvancedStatesForPolling | Use Console Modes and Occupancy for Inactivity | false         | Use these states to help continue the fast polling of the API                                      |
+
+`allowWakeup` should be used with caution as this determines whether openHAB is allowed to wake up the vehicle in order to retrieve data from it.
 This setting is not recommended as it will result in a significant vampire drain (i.e. energy consumption although the vehicle is parking). 
 
-In addition, the optional boolean parameter `enableEvents` can be set.
-By doing so, events streamed by the Tesla back-end system will be captured and processed, providing near real-time updates of some key variables generated by the vehicle.
+`enableEvents` captures and processes data in near real-time for key variables by enabling events streamed by the Tesla back-end system.
+
+`inactivity` setting is ignored and will always be five minutes if homelink is available (car is at home)
+
 
 ## Channels
 
@@ -191,60 +210,186 @@ Bridge tesla:account:myaccount "My Tesla Account" [ refreshToken="xxxx" ] {
 demo.items:
 
 ```
-Switch               TeslaCharge                 {channel="tesla:model3:myaccount:mycar:charge"}
-Location             TeslaLocation               {channel="tesla:model3:myaccount:mycar:location"}
-Dimmer               TeslaChargeLimit            {channel="tesla:model3:myaccount:mycar:chargelimit"}
-String               TeslaChargeRate             {channel="tesla:model3:myaccount:mycar:chargerate"}
-String               TeslaChargingState          {channel="tesla:model3:myaccount:mycar:chargingstate"}
-Number               TeslaTimeToFullCharge       {channel="tesla:model3:myaccount:mycar:timetofullcharge"}
-Number               TeslaChargerPower           {channel="tesla:model3:myaccount:mycar:chargerpower"}
-DateTime             TeslaScheduledChargingStart {channel="tesla:model3:myaccount:mycar:scheduledchargingstart"}
-Dimmer               TeslaSoC                    {channel="tesla:model3:myaccount:mycar:soc"}
-Number:Speed         TeslaSpeed                  {channel="tesla:model3:myaccount:mycar:speed"}
-String               TeslaState                  {channel="tesla:model3:myaccount:mycar:state"}
-Number               TeslaPower                  {channel="tesla:model3:myaccount:mycar:power"}
-Number:Temperature   TeslaInsideTemperature      {channel="tesla:model3:myaccount:mycar:insidetemp"}
-Number:Temperature   TeslaOutsideTemperature     {channel="tesla:model3:myaccount:mycar:outsidetemp"}
-Switch               TeslaAutoconditioning       {channel="tesla:model3:myaccount:mycar:autoconditioning"}
-Number:Temperature   TeslaTemperature            {channel="tesla:model3:myaccount:mycar:temperature"}
-String               TeslaShiftState             {channel="tesla:model3:myaccount:mycar:shiftstate"}
-Number               TeslaBatteryCurrent         {channel="tesla:model3:myaccount:mycar:batterycurrent"}
-Number               TeslaBatteryLevel           {channel="tesla:model3:myaccount:mycar:batterylevel"}
-DateTime             TeslaEventstamp             {channel="tesla:model3:myaccount:mycar:eventstamp"}
-Number:Length        TeslaOdometer               {channel="tesla:model3:myaccount:mycar:odometer"}
-Number               TeslaHeading                {channel="tesla:model3:myaccount:mycar:heading"}
-DateTime             TeslaGPSStamp               {channel="tesla:model3:myaccount:mycar:gpstimestamp"}
+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"}
+
+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"}
+
+Switch              TeslaCharge                 {channel="model3:myaccount:mycar:charge"}
+Switch              TeslaChargeToMax            {channel="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"}
+
+Number              TeslaChargerVoltage         {channel="model3:myaccount:mycar:chargervoltage"}
+Number              TeslaChargerPower           {channel="model3:myaccount:mycar:chargerpower"}
+Number              TeslaChargerCurrent         {channel="model3:myaccount:mycar:chargercurrent"}
+
+DateTime            TeslaScheduledChargingStart {channel="model3:myaccount:mycar:scheduledchargingstart"}
+Dimmer              TeslaSoC                    {channel="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              TeslaWakeup                 {channel="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              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              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"}
 ```
 
 demo.sitemap:
 
 ```
-sitemap demo label="Main Menu"
+sitemap main label="Main"
 {
-                       Text label="Car" {
-                               Text label="Drive" {
-                                       Text item=TeslaEventstamp label="Last Event Timestamp [%1$td.%1$tm.%1$tY %1$tT]"
-                                       Text item=TeslaState label="State [%s]"
-                                       Text item=TeslaSpeed label="Speed [%.1f]"
-                                       Text item=TeslaShiftState label="Shift State [%s]"
-                                       Text item=TeslaOdometer label="Odometer [%.1f km]"
-                               }
-                               Text label="Climate" {
-                                       Switch item=TeslaAutoconditioning label="Auto Conditioning"  mappings=[ON=ON, OFF=OFF ]
-                                       Setpoint item=TeslaTemperature step=0.5 minValue=18 maxValue=34 label="Auto Conditioning Temperature [%.1f °C]" icon="temperature"
-                                       Text item=TeslaInsideTemperature label="Inside Temperature [%.1f]"
-                               }
-                               Text label="Power" {
-                                       Text item=TeslaBatteryCurrent label="Current [%.1f]"
-                               }
-                               Text item=TeslaSoC {
-                                       Switch item=TeslaCharge label="Charge" mappings=[ON=ON, OFF=OFF ]
-                                       Slider item=TeslaChargeLimit label="Charge Limit [%.1f]"
-                                       Text item=TeslaChargingState label="Charging State [%s]"
-                                       Text item=TeslaChargeRate label="Charge Rate [%s]"
-                                       Text item=TeslaScheduledChargingStart label="Charging Start [%1$td.%1$tm.%1$tY %1$tT]"
-                                       Text item=TeslaTimeToFullCharge label="Time To Full Charge [%.1f hours]"
-                               }
-                       }
+    Text item=TeslaUsableBatteryLevel label="Car" icon="tesla" valuecolor=[<=20="red",>60="green"]
+    {
+        Frame 
+        {
+            Text item=TeslaEventstamp icon="time"
+            Text item=TeslaState label="State [%s]" icon=""
+            Text item=TeslaHomelink label="Homelink Available[%s]" icon=""
+            Text item=TeslaDistance
+            Text item=TeslaSpeed label="Speed [%.1f]"
+            Text item=TeslaShiftState label="Shift State [%s]" icon=""
+            Text item=nTeslaShiftState 
+            Text item=TeslaOdometer label="Odometer [%.1f miles]"
+            Text item=TeslaRange 
+        }
+        Frame
+        {
+            Switch item=TeslaAutoconditioning label="Enable Heat or AC"
+            Setpoint item=TeslaTemperature step=0.5 minValue=65 maxValue=78 label="Auto Conditioning Temperature [%.1f °F]"
+            Text item=TeslaInsideTemperature label="Inside Temperature [%.1f °F]" valuecolor=[<=32="blue",>95="red"]
+            Text item=TeslaOutsideTemperature label="Outside Temperature [%.1f °F]" valuecolor=[<=32="blue",>95="red"]
+        }
+        Frame
+        {
+            Text item=TeslaBatteryLevel
+            Text item=TeslaUsableBatteryLevel
+            Text item=TeslaPower
+            Text item=TeslaBatteryCurrent label="Current [%.1f]"
+            Text item=TeslaBatteryRange label="Battery Range [%.1f miles]"
+            Text item=TeslaEstBatteryRange label="Battery Est Range [%.1f miles]"
+            Text item=TeslaIdealBatteryRange label="Battery Ideal Range [%.1f miles]"
+        }
+        Frame
+        {
+            Switch item=TeslaCharge label="Charge"
+            Slider item=TeslaChargeLimit label="Charge Limit [%.1f]"
+            Text item=TeslaChargingState label="Charging State [%s]" icon=""
+            Text item=TeslaTimeToFullCharge label="Time To Full Charge [%.1f hours]"
+            Text item=TeslaPreconditioning label="Preconditioning [%s]" icon=""
+            Text item=TeslaChargeRate label="Charge Rate [%d miles/hr]"
+            Text item=TeslaScheduledChargingStart icon="time"
+            Text item=TeslaChargerVoltage label="Charge Voltage [%.1f V]"
+            Text item=TeslaChargerPower label="Charge Power [%.1f kW]"
+            Text item=TeslaChargerCurrent label="Charge Current [%.1f A]"
+            Text item=TeslaChargeToMax label="Charge To Max [%s]" icon=""
+            Text item=TeslaMaxCharges label="Consec Max Charge[%d]"
+        }
+        Frame
+        {
+            Switch item=TeslaWakeup label="Wakeup the Car"
+        }
+        Frame
+        {
+            Switch item=TeslaDoorLock label="Doorlock"
+            Switch item=TeslaHorn label="Horn" 
+            Switch item=TeslaLights label="Lights"
+            Switch item=TeslaStart label="Remote Start"
+            Switch item=TeslaValet label="Valet Mode"
+            Switch item=TeslaSentry label="Sentry Mode"
+
+            Switch item=TeslaBatteryHeater label="Battery Heater"   
+            Switch item=TeslaFrontDefrost label="Defrost Front"     
+            Switch item=TeslaRearDefrost label="Defrost Rear"                                               
+            Switch item=TeslaLeftSeatHeater label="Seat Heat Left" 
+            Switch item=TeslaRightSeatHeater label="Seat Heat Right" 
+        }
+        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
+        }
+    }
 }
 ```
+
+demo.rule (for graphing online status in sitemap above)
+
+```
+rule "Tesla State Changed"
+    when
+    Item TeslaState changed
+
+    then
+        if (previousState == NULL) return;
+        switch (TeslaState.state) {
+            case "online" : {
+                nTeslaState.postUpdate(1)
+            }
+            case "asleep" : {
+                nTeslaState.postUpdate(0)
+            }
+            case "offline" : {
+                nTeslaState.postUpdate(-0.5)
+            }
+            case "waking" : {
+                nTeslaState.postUpdate(0.5)
+            }
+            case "unknown" : {
+                nTeslaState.postUpdate(-1)
+            }
+
+        }
+end
+```
index ac95a27c81852908120fbb0aac020d5f1d8c1224..372c0c2969c4725871a91417ad0f5b8d26e2b5d4 100644 (file)
@@ -112,4 +112,7 @@ public class TeslaBindingConstants {
     public static final String CONFIG_ALLOWWAKEUPFORCOMMANDS = "allowWakeupForCommands";
     public static final String CONFIG_ENABLEEVENTS = "enableEvents";
     public static final String CONFIG_REFRESHTOKEN = "refreshToken";
+    public static final String CONFIG_INACTIVITY = "inactivity";
+    public static final String CONFIG_USEDRIVESTATE = "useDriveState";
+    public static final String CONFIG_USEDADVANCEDSTATES = "useAdvancedStatesForPolling";
 }
index e809c3c64d2dc8e11921674c05b3efa79c3eac6f..d803d03a08615fd6688f5d49b9155ad8de7e9579 100644 (file)
@@ -110,7 +110,7 @@ public class TeslaAccountHandler extends BaseBridgeHandler {
 
     @Override
     public void initialize() {
-        logger.trace("Initializing the Tesla account handler for {}", this.getStorageKey());
+        logger.debug("Initializing the Tesla account handler for {}", this.getStorageKey());
 
         updateStatus(ThingStatus.UNKNOWN);
 
@@ -129,7 +129,7 @@ public class TeslaAccountHandler extends BaseBridgeHandler {
 
     @Override
     public void dispose() {
-        logger.trace("Disposing the Tesla account handler for {}", getThing().getUID());
+        logger.debug("Disposing the Tesla account handler for {}", getThing().getUID());
 
         lock.lock();
         try {
@@ -256,16 +256,19 @@ public class TeslaAccountHandler extends BaseBridgeHandler {
         TokenResponse token = logonToken;
 
         boolean hasExpired = true;
+        logger.debug("Current authentication time {}", dateFormatter.format(Instant.now()));
 
         if (token != null) {
             Instant tokenCreationInstant = Instant.ofEpochMilli(token.created_at * 1000);
-            logger.debug("Found a request token created at {}", dateFormatter.format(tokenCreationInstant));
-            Instant tokenExpiresInstant = Instant.ofEpochMilli(token.created_at * 1000 + 60 * token.expires_in);
+            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));
 
             if (tokenExpiresInstant.isBefore(Instant.now())) {
-                logger.debug("The token has expired at {}", dateFormatter.format(tokenExpiresInstant));
+                logger.debug("The access token has expired");
                 hasExpired = true;
             } else {
+                logger.debug("The access token has not expired yet");
                 hasExpired = false;
             }
         }
index 6d7602a01c80a6f765dde0640991d1485dc272ef..d2bf5073e598e1da2c712ac4d54e39784fce3275 100644 (file)
@@ -15,6 +15,8 @@ package org.openhab.binding.tesla.internal.handler;
 import static org.openhab.binding.tesla.internal.TeslaBindingConstants.*;
 
 import java.time.Instant;
+import java.time.ZoneId;
+import java.time.format.DateTimeFormatter;
 import java.util.concurrent.ExecutionException;
 import java.util.concurrent.TimeUnit;
 import java.util.concurrent.TimeoutException;
@@ -44,6 +46,8 @@ 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")
+            .withZone(ZoneId.systemDefault());
 
     public TeslaSSOHandler(HttpClient httpClient) {
         this.httpClient = httpClient;
@@ -70,6 +74,8 @@ 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
+                        .format(Instant.ofEpochMilli((tokenResponse.created_at + tokenResponse.expires_in) * 1000)));
                 return tokenResponse;
             } else {
                 logger.debug("An error occurred while exchanging SSO auth token for API access token.");
index 04fe260758438146d3fb5cf56340cc0189ded408..8053d47ae1d48272926089b3944fa6fa79b16f4f 100644 (file)
@@ -89,7 +89,8 @@ public class TeslaVehicleHandler extends BaseThingHandler {
     private static final int FAST_STATUS_REFRESH_INTERVAL = 15000;
     private static final int SLOW_STATUS_REFRESH_INTERVAL = 60000;
     private static final int API_SLEEP_INTERVAL_MINUTES = 20;
-    private static final int MOVE_THRESHOLD_INTERVAL_MINUTES = 5;
+    private static final int MOVE_THRESHOLD_INTERVAL_MINUTES_DEFAULT = 5;
+    private static final int THRESHOLD_INTERVAL_FOR_ADVANCED_MINUTES = 60;
     private static final int EVENT_MAXIMUM_ERRORS_IN_INTERVAL = 10;
     private static final int EVENT_ERROR_INTERVAL_SECONDS = 15;
     private static final int EVENT_STREAM_PAUSE = 3000;
@@ -111,18 +112,26 @@ public class TeslaVehicleHandler extends BaseThingHandler {
     protected boolean allowWakeUp;
     protected boolean allowWakeUpForCommands;
     protected boolean enableEvents = false;
+    protected boolean useDriveState = false;
+    protected boolean useAdvancedStates = false;
+    protected boolean lastValidDriveStateNotNull = true;
+
     protected long lastTimeStamp;
     protected long apiIntervalTimestamp;
     protected int apiIntervalErrors;
     protected long eventIntervalTimestamp;
     protected int eventIntervalErrors;
+    protected int inactivity = MOVE_THRESHOLD_INTERVAL_MINUTES_DEFAULT;
     protected ReentrantLock lock;
 
     protected double lastLongitude;
     protected double lastLatitude;
     protected long lastLocationChangeTimestamp;
-
+    protected long lastDriveStateChangeToNullTimestamp;
+    protected long lastAdvModesTimestamp = System.currentTimeMillis();
     protected long lastStateTimestamp = System.currentTimeMillis();
+    protected int backOffCounter = 0;
+
     protected String lastState = "";
     protected boolean isInactive = false;
 
@@ -150,6 +159,12 @@ public class TeslaVehicleHandler extends BaseThingHandler {
         allowWakeUp = (boolean) getConfig().get(TeslaBindingConstants.CONFIG_ALLOWWAKEUP);
         allowWakeUpForCommands = (boolean) getConfig().get(TeslaBindingConstants.CONFIG_ALLOWWAKEUPFORCOMMANDS);
         enableEvents = (boolean) getConfig().get(TeslaBindingConstants.CONFIG_ENABLEEVENTS);
+        Number inactivityParam = (Number) getConfig().get(TeslaBindingConstants.CONFIG_INACTIVITY);
+        inactivity = inactivityParam == null ? MOVE_THRESHOLD_INTERVAL_MINUTES_DEFAULT : inactivityParam.intValue();
+        Boolean useDriveStateParam = (boolean) getConfig().get(TeslaBindingConstants.CONFIG_USEDRIVESTATE);
+        useDriveState = useDriveStateParam == null ? false : useDriveStateParam;
+        Boolean useAdvancedStatesParam = (boolean) getConfig().get(TeslaBindingConstants.CONFIG_USEDADVANCEDSTATES);
+        useAdvancedStates = useAdvancedStatesParam == null ? false : useAdvancedStatesParam;
 
         account = (TeslaAccountHandler) getBridge().getHandler();
         lock = new ReentrantLock();
@@ -535,17 +550,79 @@ public class TeslaVehicleHandler extends BaseThingHandler {
     protected boolean isInactive() {
         // vehicle is inactive in case
         // - it does not charge
-        // - it has not moved in the observation period
-        return isInactive && !isCharging() && !hasMovedInSleepInterval();
+        // - it has not moved or optionally stopped reporting drive state, in the observation period
+        // - it is not in dog, camp, keep, sentry or any other mode that keeps it online
+        return isInactive && !isCharging() && !notReadyForSleep();
     }
 
     protected boolean isCharging() {
         return chargeState != null && "Charging".equals(chargeState.charging_state);
     }
 
-    protected boolean hasMovedInSleepInterval() {
-        return lastLocationChangeTimestamp > (System.currentTimeMillis()
-                - (MOVE_THRESHOLD_INTERVAL_MINUTES * 60 * 1000));
+    protected boolean notReadyForSleep() {
+        boolean status;
+        int computedInactivityPeriod = inactivity;
+
+        if (useAdvancedStates) {
+            if (vehicleState.is_user_present && !isInMotion()) {
+                logger.debug("Car is occupied but stationary.");
+                if (lastAdvModesTimestamp < (System.currentTimeMillis()
+                        - (THRESHOLD_INTERVAL_FOR_ADVANCED_MINUTES * 60 * 1000))) {
+                    logger.debug("Ignoring after {} minutes.", THRESHOLD_INTERVAL_FOR_ADVANCED_MINUTES);
+                } else {
+                    return (backOffCounter++ % 6 == 0); // using 6 should make sure 1 out of 5 pollers get serviced,
+                                                        // about every min.
+                }
+            } else if (vehicleState.sentry_mode) {
+                logger.debug("Car is in sentry mode.");
+                if (lastAdvModesTimestamp < (System.currentTimeMillis()
+                        - (THRESHOLD_INTERVAL_FOR_ADVANCED_MINUTES * 60 * 1000))) {
+                    logger.debug("Ignoring after {} minutes.", THRESHOLD_INTERVAL_FOR_ADVANCED_MINUTES);
+                } else {
+                    return (backOffCounter++ % 6 == 0);
+                }
+            } else if ((vehicleState.center_display_state != 0) && (!isInMotion())) {
+                logger.debug("Car is in camp, climate keep, dog, or other mode preventing sleep. Mode {}",
+                        vehicleState.center_display_state);
+                return (backOffCounter++ % 6 == 0);
+            } else {
+                lastAdvModesTimestamp = System.currentTimeMillis();
+            }
+        }
+
+        if (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);
+        }
+
+        if (useDriveState) {
+            if (driveState.shift_state != null) {
+                logger.debug("Car drive state not null and not ready to sleep.");
+                return true;
+            } else {
+                status = lastDriveStateChangeToNullTimestamp > (System.currentTimeMillis()
+                        - (computedInactivityPeriod * 60 * 1000));
+                if (status) {
+                    logger.debug("Drivestate is null but has changed recently, therefore continuing to poll.");
+                    return status;
+                } else {
+                    logger.debug("Drivestate has changed to null after interval {} min and can now be put to sleep.",
+                            computedInactivityPeriod);
+                    return status;
+                }
+            }
+        } else {
+            status = lastLocationChangeTimestamp > (System.currentTimeMillis()
+                    - (computedInactivityPeriod * 60 * 1000));
+            if (status) {
+                logger.debug("Car has moved recently and can not sleep");
+                return status;
+            } else {
+                logger.debug("Car has not moved in {} min, and can sleep", computedInactivityPeriod);
+                return status;
+            }
+        }
     }
 
     protected boolean allowQuery() {
@@ -555,6 +632,7 @@ public class TeslaVehicleHandler extends BaseThingHandler {
     protected void setActive() {
         isInactive = false;
         lastLocationChangeTimestamp = System.currentTimeMillis();
+        lastDriveStateChangeToNullTimestamp = System.currentTimeMillis();
         lastLatitude = 0;
         lastLongitude = 0;
     }
@@ -762,9 +840,6 @@ public class TeslaVehicleHandler extends BaseThingHandler {
 
     protected void queryVehicleAndUpdate() {
         vehicle = queryVehicle();
-        if (vehicle != null) {
-            parseAndUpdate("queryVehicle", null, vehicleJSON);
-        }
     }
 
     public void parseAndUpdate(String request, String payLoad, String result) {
@@ -788,6 +863,16 @@ public class TeslaVehicleHandler extends BaseThingHandler {
                             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;
                     }
@@ -817,6 +902,18 @@ public class TeslaVehicleHandler extends BaseThingHandler {
                         break;
                     }
                     case "queryVehicle": {
+                        if (vehicle != null) {
+                            logger.debug("Vehicle state is {}", vehicle.state);
+                        } else {
+                            logger.debug("Vehicle state is initializing or unknown");
+                            break;
+                        }
+
+                        if (vehicle != null && "asleep".equals(vehicle.state)) {
+                            logger.debug("Vehicle is asleep.");
+                            break;
+                        }
+
                         if (vehicle != null && !lastState.equals(vehicle.state)) {
                             lastState = vehicle.state;
 
@@ -824,6 +921,7 @@ public class TeslaVehicleHandler extends BaseThingHandler {
                             if (isAwake()) {
                                 logger.debug("Vehicle is now awake, updating all data");
                                 lastLocationChangeTimestamp = System.currentTimeMillis();
+                                lastDriveStateChangeToNullTimestamp = System.currentTimeMillis();
                                 requestAllData();
                             }
 
@@ -837,7 +935,7 @@ public class TeslaVehicleHandler extends BaseThingHandler {
                             setActive();
                         } else {
                             boolean wasInactive = isInactive;
-                            isInactive = !isCharging() && !hasMovedInSleepInterval();
+                            isInactive = !isCharging() && !notReadyForSleep();
 
                             if (!wasInactive && isInactive) {
                                 lastStateTimestamp = System.currentTimeMillis();
@@ -967,7 +1065,6 @@ public class TeslaVehicleHandler extends BaseThingHandler {
     protected Runnable slowStateRunnable = () -> {
         try {
             queryVehicleAndUpdate();
-
             boolean allowQuery = allowQuery();
 
             if (allowQuery) {
@@ -980,7 +1077,9 @@ public class TeslaVehicleHandler extends BaseThingHandler {
                     wakeUp();
                 } else {
                     if (isAwake()) {
-                        logger.debug("Vehicle is neither charging nor moving, skipping updates to allow it to sleep");
+                        logger.debug("slowpoll: Throttled to allow sleep, occupied/idle, or in a console mode");
+                    } else {
+                        lastAdvModesTimestamp = System.currentTimeMillis();
                     }
                 }
             }
@@ -1001,7 +1100,7 @@ public class TeslaVehicleHandler extends BaseThingHandler {
                     wakeUp();
                 } else {
                     if (isAwake()) {
-                        logger.debug("Vehicle is neither charging nor moving, skipping updates to allow it to sleep");
+                        logger.debug("fastpoll: Throttled to allow sleep, occupied/idle, or in a console mode");
                     }
                 }
             }
index 8ab891d467f4c13c3373d3e63871938be0d20bf9..9b6f408c14aea61f5ddaca3c49c9f1d12b02e506 100644 (file)
@@ -23,12 +23,14 @@ public class VehicleState {
     public boolean dark_rims;
     public boolean has_spoiler;
     public boolean homelink_nearby;
+    public boolean is_user_present;
     public boolean locked;
     public boolean notifications_supported;
     public boolean parsed_calendar_supported;
     public boolean remote_start;
     public boolean remote_start_supported;
     public boolean rhd;
+    public boolean sentry_mode;
     public boolean valet_mode;
     public boolean valet_pin_needed;
     public float odometer;
index c1662d7c955afbf9f759a42596cacc18c60f8f79..50d9a626aa8fdfe96e092b20c2fcc92cab9b8064 100644 (file)
                        <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">
                        <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>
index a7ba2e2c9dfe00195b0f4aa0012bd685ba7d60dc..1a7b7c13ecd8bae4c206d9de68d4f6d63731f775 100644 (file)
                        <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">
                        <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>
index 96b7d1e8017577a9ad0823a4334c08393094e926..369fffc35339b078c8af90a5d3510a968012a02e 100644 (file)
                        <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">
                        <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>
index 76b3fdea0ed089d51fb7b1e439be32ecb52bf0f3..4aa802eff67308504c9056a81d8c2e14190c068a 100644 (file)
                        <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">
                        <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>