Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

#iain-dev->#main (v0.4.1b-dev ) #141

Merged
merged 76 commits into from
Jan 15, 2025
Merged

#iain-dev->#main (v0.4.1b-dev ) #141

merged 76 commits into from
Jan 15, 2025

Conversation

iainbullock
Copy link
Contributor

0.4.1

  • RELEASE NEW Feature: Automatic Polling is now possible for state

  • NEW Feature: The following new states / entities are added:

    • Sensors: Awake (updated approx every 30 secs from body-controller-state)
    • Binary_Sensors: Presence_BC (experimental presence detection updated approx every 30 secs from body_controller_state rather than listening for BLE mac)
    • Switches: Polling
    • Numbers: Polling Interval
    • Buttons: Force Update buttons for individual state categories
  • NEW Feature: Environment variable NO_POLL_SECTIONS is provided to disable updating of various state categories during polling. This speeds up state updates, though less state entities are updated by the polling. The entities can still be manually updated by pressing the Force Update button for the relevent state category

  • Changes:

    • 'Force Data Update' Button is renamed to 'Force Update All'
    • Errors which occur whilst reading state will not automatically prevent the next state or state category being read. This will fix an issue reported in Temp units incorrect for new Inside Temp and Outside Temp #135 below where a user doesn't have a Heated Steering Wheel. This previously prevented any states after this one from being read
  • Fixes:

@raphmur
Copy link
Contributor

raphmur commented Jan 12, 2025

Fantastic @iainbullock!! Looks great!
Are you sure that periodically calling body-controller-state does not prevent the car to sleep?

read-state.sh Show resolved Hide resolved
read-state.sh Show resolved Hide resolved
read-state.sh Show resolved Hide resolved
read-state.sh Show resolved Hide resolved
read-state.sh Show resolved Hide resolved
version.sh Outdated Show resolved Hide resolved
@iainbullock
Copy link
Contributor Author

Fantastic @iainbullock!! Looks great! Are you sure that periodically calling body-controller-state does not prevent the car to sleep?

It doesn't seem to. If you run tesla-control --help, it says this:

body-controller-state        Fetch limited vehicle state information. Works over BLE when infotainment is asleep.

You can run the command without authentication, you just need the VIN (which you can see through the windscreen of the car). A bad actor could deliberately wake local cars if they knew the VIN, which would be bad. So again I think it can't wake the car

tesla-control -ble -vin $VIN body-controller-state

Not conclusive I know.

We could have an environment variable to turn it off for all cars so there are no automatic body-controller-state calls at all. The code currently does a body-controller-state call every 30 secs regardless of whether polling is turned on for all vehicles. This is to ensure the Presence BC and Awake sensors work all the time. (I think we can stop using the old method of scanning BLE if the body-controller-state method proves to be reliable)

@iainbullock
Copy link
Contributor Author

@raphmur have a look at my comments and decide which you prefer if I've given an option. I will describe how the polling loop works shortly, I am being called away ....

@iainbullock
Copy link
Contributor Author

iainbullock commented Jan 13, 2025

Here's a high level description of how the polling loop works. I had several attempts before I was happy with it.

Firstly my requirements:

  • Allow independent turn on/off polling and set polling interval for each car. This had to be dynamic from HA, not fixed by an environment variable. Therefore we have HA switch and number entities created via MQTT discovery
  • There was a challenge to ensure these values get translated into variables which are dynamically readable wherever needed in the code. Sounds simple, however shell doesn't have global variables like proper languages do. Whilst functions and sub-processes get a 'copy' of exported variables, their copy doesn't change if it changes at the 'global' level, nor can they change the 'global' value if they need to. This means I had to reorganise the structure of the code slightly. The MQTT 'version' of these variables is considered to be the 'master' as this can be accessed anywhere in the code
  • Calling tesla-control must be done in a single thread. I originally had sending commands (original code), polling, and requesting body-controller-state all happening independently of each other in different threads. Inevitably, there would be times when two threads accessed tesla-control at the same time. This caused unpredictable behaviour sometimes locking up the container and requiring a restart, sometimes even needing a reboot of the host. This required further restructuring of the code. All calls to tesla-control to send commands are done by publishing to the relevent MQTT topic which is only read and acted on by listen_to_mqtt(). This does two things - it queues the commands so they are processed in order, secondly the calling of tesla-control is always in the same thread, so it doesn't cause the problem described above
  • See whether body-controller-state can be used to provide awake and presence status, potentially superceding the exiting method of presence detection in the future

With these things in mind, I'll talk through how it works:

function poll_state_loop()

  • Loops indefinitely, via the outer while loop
  • Inside that is a another while loop which counts seconds (it resets every 24 hours so the number doesn't get too big and break something when it overflows). This is used to allow multiple cars to have different polling intervals (see later). There is also a nominal 30 second delay when the loop repeats so we don't send body-controller-state requests more often than necessary
  • Inside that is a for loop which cycles though multiple cars in VIN_LIST. For each car i,t publishes to MQTT command topic containing the VIN and count from the above while loop. Function listen_to_mqtt() 'hears' this and calls function poll_state(). Function poll_state() is not called directly as this would break the requirements to have tesla-control calls all in one thread

function poll_state()
This gets called approx every 30 secs for each VIN in turn as described above. For the specified VIN we test whether the conditions for polling are met, and if so initiate the poll, publishing the results to MQTT. In more detail:

  • Get the 'global' polling and polling_interval variables for the specified VIN from MQTT. The code is a bit difficult to follow here as the version of shell we are using (ash) needs to use eval to perform dynamic variable substitution based on the VIN
  • Send the body-controller-state command to the car, which will return an exit code, and if the car is present some JSON
  • Use the exit code and JSON to determine presence and asleep status, publishing to MQTT to update the associated HA entities. Exit unless the car is present and awake
  • Check if polling is turned on for this VIN, exit if not
  • Check the loop counter to see if it is time to poll this car. This is done by dividing the loop counter by the car's polling_interval. If it divides exactly (i.e. no remainder / modulus is zero), then it is time to poll. Exit if not
  • Poll the car. This is done by publishing to the relevent MQTT topic. This is 'heard' by listen_to_mqtt(), which results in a call to function read-state(). This reads the state using tesla-control and publishes the result to MQTT. As above we don't call read-state() directly here as this would break the requirements to have tesla-control calls all in one thread

I probably should put this is a seperate document for future reference

@iainbullock
Copy link
Contributor Author

iainbullock commented Jan 15, 2025

@raphmur shall I merge as it is, or make the changes? I'd prefer to merge as is and implement any changes on the next version. If you think differently no problem I can do it.

For the next update I am currently working on:

Whilst I'm away on holiday I should make some progress, especially if I can leave my car and home at take my bike instead - weather dependent!

It would be good to get _core#stage up to date as its hard to track lots of changes otherwise

@raphmur
Copy link
Contributor

raphmur commented Jan 15, 2025

Let's go for merge

@iainbullock iainbullock requested a review from raphmur January 15, 2025 12:48
@iainbullock iainbullock dismissed raphmur’s stale review January 15, 2025 12:48

Send for re-review

@iainbullock iainbullock merged commit 47f13e4 into stage Jan 15, 2025
1 check passed
@BogdanDIA
Copy link

Hi, great job!
I just wanted to add that body-controller-state returns also when the car is asleep, which is good because the output can be used to prevent execution of other commands while the car is asleep.
However, in my view, using body-controller-state for car's presence is not a good idea because it will try connecting the car and will rely to retries, timeouts and fail connection that tesla-control is doing internally. If the timeouts are set high to tesla-control, this becomes too heavy for the controller. Still the best solution is the passive scan.

Bogdan

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants