surfrock66 / torque

A set of tools used with the Torque app for Android to store OBD2 data in MySQL, view GPS data from Torque on a map in real time using the Google Maps JavaScript API, plot OBD2 data in time series charts, and export the data to CSV or JSON.
Other
43 stars 24 forks source link

Integration with home assistant #54

Closed arduiacob closed 1 year ago

arduiacob commented 1 year ago

Hi, Due to the multiple problems to integrate torque in homeassistant with https and already having the server with the great code of surfrock66, I have added some lines at the end of upload_data.php that make the integration of Torque in Homeassistant work and also we do not lose this fantastic Web. It uploads all the PIDs to HS, but does not upload the geolocation. It may be a problem with the integration of homeassistant that does not recognize it well because the data is sent. Or maybe I did something wrong.

$uri_homeassistant = 'https://SERVER_HOME_ASSITANT:PORT/api/torque'
if (sizeof($_SERVER) > 0) {
    // start the URI from '?'
    $uri = strstr($_SERVER['REQUEST_URI'], '?');

    // get the token
    // start from Bearer
    $token = substr(strstr($_SERVER['HTTP_AUTHORIZATION'],'Bearer'), 7);

    // If we have a new URI and the token, we upload the data:
    if ($token && $uri) {
        // Make a curl request including the Bearer token in https
        // This initializes a new cURL session
        $ch = curl_init();
        // set the request URL
        curl_setopt($ch, CURLOPT_URL, $uri_homeassistant.$uri);
        // return the response instead of printing it directly
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
        // set the Authorization header with the "Bearer" token and verify
        curl_setopt($ch, CURLOPT_HTTPHEADER, array("Authorization: Bearer $token"));
        // the server identity using a valid certificate
        curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, true);

        $response = curl_exec($ch);

        curl_close($ch);
    }       
}
surfrock66 commented 1 year ago

I had never even thought of this, and I'm interested. Is there a torque home assistant add on? I hadn't seen one, I can add it to mine, do you have a link? I can add that to the documentation.

I'd like to keep all configs in the config file, so then it'd get referenced here, but that's not difficult either. Let me set this up in my instance and test.

arduiacob commented 1 year ago

The Torque integration [https://www.home-assistant.io/integrations/torque/]()

surfrock66 commented 1 year ago

If I'm understanding it right, looking here: https://github.com/home-assistant/core/blob/dev/homeassistant/components/torque/sensor.py this basically becomes a go-between for HA and this app. The data gets uploaded to this webapp, then at the end of upload, it publishes data to HA (which doesn't log longitudinal data, but instead just shows last status?). Is that right?

Lemme play with it for a bit; I wonder if there are some additional config options that could work...for example, a "send to HA" option in the PID grid, though that requires some DB rework which may be a headache.

arduiacob commented 1 year ago

Screenshot_20230107_203604_Home Assistant

The data shown is instantaneous.
But there is no geolocation. But if you have the data on the liters you have left in the tank, you can easily check it in the homeassistant application.
The ideal would be to have the location to remember where you parked the car. Homeassistant also offers you statistics with all the data it collects.

surfrock66 commented 1 year ago

Can you choose a sensor? I know sometimes the fancy name doesn't come through, is there a sensor for "kff1005" or "kff1006"? Those are the actual ODB codes for GPS latitude and longitude; they're not handled any differently than any other sensor.

I need to set up my instance...and actually drive somewhere lol, I kind of stopped driving since the pandemic, but I'll get something together today or tomorrow.

surfrock66 commented 1 year ago

On the 13th line, should that be "uri_homeassistant" instead of "url_homeassistant"? I changed it in my local instance; I'm gonna try to drive around this afternoon and generate a session.

arduiacob commented 1 year ago

Yes, edited!

arduiacob commented 1 year ago

It does send the geolocation sensors, but homeassistant does not use them.

This is part of a REQUEST_URI: /torque/debug.php?eml=**@mail.com&v=9&session=1672942579775&id=***2bb05fb588f9d*&time=1672942607908&kff1005=-4.30708333333332&kff1006=43.66885666666667&kff1001=0.0&kff1007=0.0&....

The rest of the sensors do come out. Only the geolocation ones are missing. I am thinking of generating some manual PIDs in Torque with the coordinate data to see if it sends them that way.

surfrock66 commented 1 year ago

I've been through the integration's source and there's nothing in there filtering out the GPS or anything that would indicate why it's not available; can you show the list of entities HA generated?

I have to run errands with my kids tomorrow, so I'll get a session so I can test here.

arduiacob commented 1 year ago

That's it!, In the torque app, I had not selected gps longitude and gps latitude in the data record. On your website it is not necessary, because the torque app always sends them. I have selected them and now they do appear in the homeassistant integration. In homeassistat all the PIDs that are selected in the PIDs to register option appear.

surfrock66 commented 1 year ago

Okay very cool, I am going to wrap the config so it can be set up from the actual config file and then I'll push it.

surfrock66 commented 1 year ago

I'm still wrapping this up, I log EVERYTHING to torque, but I wouldn't want everything to go to HA; I need to find a better way to add a 2nd layer of selection here.

I drive so infrequently, I only make 1 session a day max. I am seeing this on PHP 8.1:

PHP Warning: Undefined array key "HTTP_AUTHORIZATION" in /var/www/torque/upload_data.php on line 226

I do have a rewrite rule, so maybe it needs a check for "REWRITE_HTTP_AUTHORIZATION' or something? I haven't messed with that, so if you have an idea I can do it. I hate debugging these lol; it's hard to view the live data from a track since it's automatic. I long ago thought of making a dummy track that I could just wget create in bash or something, but I never did it.

arduiacob commented 1 year ago

I don't know if I understand well. Get the header before the rewrite rule with getallheaders() or apache_request_headers(). or maybe if (isset($_SERVER['HTTP_AUTHORIZATION'])){ $token = substr(strstr($_SERVER['HTTP_AUTHORIZATION'],'Bearer'), 7); }

For debugging, I put the mobile next to the window and select in Data Record, both "only when obd connect" to off. So the torque app is continuously sending the GPS position and I can see some change.

surfrock66 commented 1 year ago

So I set the following in the data upload per some stuff I read

    224     // get the token
    225     $token = null;
    226     if ( isset( $_SERVER['AUTHORIZATION'] ) ) {
    227         $token = $_SERVER['AUTHORIZATION'];
    228     } elseif ( isset( $_SERVER['HTTP_AUTHORIZATION'] ) ) {
    229         $token = $_SERVER["HTTP_AUTHORIZATION"];
    230     } elseif ( isset( $_SERVER['REDIRECT_HTTP_AUTHORIZATION'] ) ) {
    231        $token = $_SERVER["REDIRECT_HTTP_AUTHORIZATION"];
    232     }
    233 error_log($token);

and I get this in my PHP error log:

[10-Jan-2023 18:17:25 America/Los_Angeles]
[10-Jan-2023 18:17:26 America/Los_Angeles]
[10-Jan-2023 18:17:27 America/Los_Angeles]
[10-Jan-2023 18:17:28 America/Los_Angeles]
[10-Jan-2023 18:17:29 America/Los_Angeles]
[10-Jan-2023 18:17:30 America/Los_Angeles]
[10-Jan-2023 18:17:31 America/Los_Angeles]
[10-Jan-2023 18:17:32 America/Los_Angeles]

So, the bearer token isn't getting pulled out, meaning it can never upload to HA. I'm sure this is some config in my apache, I've seen suggestions on how to change .htaccess to route around it, but I need to look more to see if there's a php solution to detect and bypass any server config.

surfrock66 commented 1 year ago

Got it; I wanna test, but I was able to get sensor info in Home Assistant:

    217 // Based on if the Home Assistant config variable is set, forward data to Home Assistant
    218 if ( ( sizeof($_SERVER) > 0 ) and ( $uri_homeassistant != '' ) ) {
    219     // start the URI from 
    220     $uri = strstr($_SERVER['REQUEST_URI'], '?');
    221     
    222 
    223     $headers = null;
    224     $token = null;
    225     if ( isset( $_SERVER['Authorization'] ) ) {
    226         $headers = trim( $_SERVER["Authorization"] );
    227     } elseif ( isset( $_SERVER['HTTP_AUTHORIZATION'] ) ) { //Nginx or fast CGI
    228         $headers = trim( $_SERVER["HTTP_AUTHORIZATION"] ); 
    229     } elseif ( isset( $_SERVER['REDIRECT_HTTP_AUTHORIZATION'] ) ) {
    230         $headers = trim( $_SERVER["REDIRECT_HTTP_AUTHORIZATION"] );
    231     } elseif ( function_exists( 'apache_request_headers' ) ) {
    232         $requestHeaders = apache_request_headers();
    233         // Server-side fix for bug in old Android versions (a nice side-effect of this fix means we don't care about capitalization for Authorization)
    234         $requestHeaders = array_combine(array_map('ucwords', array_keys($requestHeaders)), array_values($requestHeaders));
    235         if ( isset( $requestHeaders['Authorization'] ) ) {
    236             $headers = trim( $requestHeaders['Authorization'] );
    237         }   
    238     }   
    239 
    240     if ( !empty( $headers ) ) {
    241         if ( preg_match( '/Bearer\s(\S+)/', $headers, $matches ) ) {
    242             $token = $matches[1];
    243         }   
    244     }   
    245 
    246     // If we have a new URI and the token, we upload the data:
    247     if ($token && $uri) {
    248         // Make a curl request including the Bearer token in https
    249         // This initializes a new cURL session
    250         $ch = curl_init();
    251         // set the request URL
    252         curl_setopt($ch, CURLOPT_URL, $uri_homeassistant.$uri);
    253         // return the response instead of printing it directly
    254         curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
    255         // set the Authorization header with the "Bearer" token and verify
    256         curl_setopt($ch, CURLOPT_HTTPHEADER, array("Authorization: Bearer $token"));
    257         // the server identity using a valid certificate
    258         curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, true);
    259 
    260         $response = curl_exec($ch);
    261 
    262         curl_close($ch);
    263     }
    264 }
surfrock66 commented 1 year ago

Ok, I realized "why are pulling the HA token from the request, why can't we just paste it?" DUH. But why not both! So here's what I'm testing now:

upload_data.php:

// Based on if the Home Assistant config variable is set, forward data to Home Assistant
if ( ( sizeof($_SERVER) > 0 ) and ( $uri_homeassistant != '' ) ) {
  // start the URI from
  $uri = strstr($_SERVER['REQUEST_URI'], '?');
  // If the bearer token is set in config use it, if not extract it from the app request
  if ( isset( $token_homeassistant ) and ( $token_homeassistant != '' ) ) {
    $token = $token_homeassistant;
  } else {
    $headers = null;
    $token = null;
    if ( isset( $_SERVER['Authorization'] ) ) {
      $headers = trim( $_SERVER["Authorization"] );
    } elseif ( isset( $_SERVER['HTTP_AUTHORIZATION'] ) ) { //Nginx or fast CGI
      $headers = trim( $_SERVER["HTTP_AUTHORIZATION"] );
    } elseif ( isset( $_SERVER['REDIRECT_HTTP_AUTHORIZATION'] ) ) {
      $headers = trim( $_SERVER["REDIRECT_HTTP_AUTHORIZATION"] );
    } elseif ( function_exists( 'apache_request_headers' ) ) {
      $requestHeaders = apache_request_headers();
      // Server-side fix for bug in old Android versions (a nice side-effect of this fix means we don't care about capitalization for Authorization)
      $requestHeaders = array_combine(array_map('ucwords', array_keys($requestHeaders)), array_values($requestHeaders));
      if ( isset( $requestHeaders['Authorization'] ) ) {
        $headers = trim( $requestHeaders['Authorization'] );
      }
    }
    if ( !empty( $headers ) ) {
      if ( preg_match( '/Bearer\s(\S+)/', $headers, $matches ) ) {
        $token = $matches[1];
      }
    }
  }
  // If we have a new URI and the token, we upload the data:
  if ($token && $uri) {
    // Make a curl request including the Bearer token in https
    // This initializes a new cURL session
    $ch = curl_init();
    // set the request URL
    curl_setopt($ch, CURLOPT_URL, $uri_homeassistant.$uri);
    // return the response instead of printing it directly
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
    // set the Authorization header with the "Bearer" token and verify
    curl_setopt($ch, CURLOPT_HTTPHEADER, array("Authorization: Bearer $token"));
    // the server identity using a valid certificate
    curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, true);
    $response = curl_exec($ch);
    curl_close($ch);
  }
}

Then creds_sample.php now has this:

// OPTIONAL set the URI here for your HomeAssistant instance; format: https://SERVER_HOME_ASSITANT:PORT/api/torque Leave blank if unused
$uri_homeassistant = '';
// OPTIONAL Uncomment and paste the bearer token from Home Assistant here to use it directly, rather than pulling it from the Torque App's web request
//$token_homeassistant = '';

Testing now

surfrock66 commented 1 year ago

Fully tested, committed here: https://github.com/surfrock66/torque/commit/0e4fa7430a76a6ffb08ccc51a6d9ef66070da398 Thanks for the code and suggestion!