Skip to content

Commit

Permalink
1.4.3
Browse files Browse the repository at this point in the history
Added rate limit and app version check for proxy commands.
  • Loading branch information
RonnyWinkler committed May 6, 2024
1 parent 4719abc commit fe7d405
Show file tree
Hide file tree
Showing 11 changed files with 137 additions and 6 deletions.
3 changes: 3 additions & 0 deletions .homeychangelog.json
Original file line number Diff line number Diff line change
Expand Up @@ -206,5 +206,8 @@
},
"1.4.2": {
"en": "Added user presence for car device (flow trigger and condition)."
},
"1.4.3": {
"en": "Added rate limit and app version check for proxy commands."
}
}
2 changes: 1 addition & 1 deletion .homeycompose/app.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"id": "com.tesla.car",
"version": "1.4.2",
"version": "1.4.3",
"compatibility": ">=5.0.0",
"platforms": ["local"],
"sdk": 3,
Expand Down
15 changes: 15 additions & 0 deletions .homeycompose/drivers/settings/api_rate_limit.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
{
"id": "api_rate_limit",
"type": "label",
"label": {
"en": "API rate limit",
"de": "API Rate Limit",
"nl": "API rate limiet"
},
"hint": {
"en": "This field shows the current rate limit usage. If it gets over 100%, the API will be blocked for a while until the average usage is below 100% again.",
"de": "Dieses Feld zeigt die aktuelle API-Rate-Limit-Nutzung an. Wenn sie über 100% steigt, wird die API für eine Weile blockiert, bis die durchschnittliche Nutzung wieder unter 100% liegt.",
"nl": "Dit veld toont het huidige gebruik van de tariefbeperking van de API. Als het boven de 100% komt, wordt de API een tijdje geblokkeerd totdat het gemiddelde gebruik weer onder de 100% ligt."
},
"value": "0 %"
}
17 changes: 16 additions & 1 deletion app.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"_comment": "This file is generated. Please edit .homeycompose/app.json instead.",
"id": "com.tesla.car",
"version": "1.4.2",
"version": "1.4.3",
"compatibility": ">=5.0.0",
"platforms": [
"local"
Expand Down Expand Up @@ -4390,6 +4390,21 @@
"nl": "Dit veld toont de API-status van de laatste Tesla API-aanroep. Details: https://developer.tesla.com/docs/fleet-api?javascript#response-codes"
},
"value": ""
},
{
"id": "api_rate_limit",
"type": "label",
"label": {
"en": "API rate limit",
"de": "API Rate Limit",
"nl": "API rate limiet"
},
"hint": {
"en": "This field shows the current rate limit usage. If it gets over 100%, the API will be blocked for a while until the average usage is below 100% again.",
"de": "Dieses Feld zeigt die aktuelle API-Rate-Limit-Nutzung an. Wenn sie über 100% steigt, wird die API für eine Weile blockiert, bis die durchschnittliche Nutzung wieder unter 100% liegt.",
"nl": "Dit veld toont het huidige gebruik van de tariefbeperking van de API. Als het boven de 100% komt, wordt de API een tijdje geblokkeerd totdat het gemiddelde gebruik weer onder de 100% ligt."
},
"value": "0 %"
}
]
},
Expand Down
28 changes: 28 additions & 0 deletions drivers/car/device.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ const Homey = require('homey');
const { CarServer } = require('../../lib/CarServer.js');
const Eckey = require('eckey-utils');
const crypt = require('../../lib/crypt');
const SlidingWindowLog = require('../../lib/SlidingWindowLog.js');

const CAPABILITY_DEBOUNCE = 500;
const DEFAULT_SYNC_INTERVAL = 1000 * 60 * 10; // 10 min
Expand Down Expand Up @@ -40,6 +41,8 @@ module.exports = class CarDevice extends TeslaOAuth2Device {
// }
}, CAPABILITY_DEBOUNCE);

// Rate limit log
this.rateLimitLog = new SlidingWindowLog();

this._settings = this.getSettings();
await this._startSync();
Expand Down Expand Up @@ -162,6 +165,10 @@ module.exports = class CarDevice extends TeslaOAuth2Device {
};
await this.homey.flow.getDeviceTriggerCard('alarm_api_error_on').trigger(this, tokens);
}
// // Add readable message for http-426 (update required)
// if (error.status == 426 || error.status == 412){
// error.message = 'App update required. Please update the app in the Homey app store or enable automatic updates.';
// }
}
catch(error){
this.log(error);
Expand Down Expand Up @@ -288,6 +295,13 @@ module.exports = class CarDevice extends TeslaOAuth2Device {
await this.getCarData();
await this.handleApiOk();
this.setAvailable();

// Update rate limit state
this.rateLimitLog.refresh();
await this.setSettings({
api_rate_limit: this.rateLimitLog.getState() + ' %'
});

}
catch(error){
this.log("Device update error (getState): ID: "+this.getData().id+" Name: "+this.getName()+" Error: "+error.message);
Expand Down Expand Up @@ -650,6 +664,20 @@ module.exports = class CarDevice extends TeslaOAuth2Device {

async sendCommand(apiFunction, params){
try{
try{
this.rateLimitLog.add();
this.log("RateLimit: "+this.rateLimitLog.getState()+"%");
await this.setSettings({
api_rate_limit: this.rateLimitLog.getState() + ' %'
});
}
catch(error){
this.log("RateLimit exeeded: "+this.rateLimitLog.getState()+"%");
await this.setSettings({
api_rate_limit: this.rateLimitLog.getState() + ' %'
});
throw error;
}
// Wake up the car if needed
let state = await this.wakeUpIfNeeded();
// If wakeUp is processed, the car state is returned. If not, "undefined" is returned.
Expand Down
4 changes: 4 additions & 0 deletions drivers/car/driver.settings.compose.json
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,10 @@
{
"$extends": "api_state",
"id": "api_state"
},
{
"$extends": "api_rate_limit",
"id": "api_rate_limit"
}
]
},
Expand Down
28 changes: 28 additions & 0 deletions lib/SlidingWindowLog.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
const CAPACITY = 60; // Allowed amound of API calls for the window
const WINDOW_SIZE = 600; // Window size in seconds

module.exports = class SlidingWindowLog {

constructor(capacity = CAPACITY) {
this.cache = [];
this.capacity = capacity;
}

add() {
this.cache.push(new Date());
this.refresh();
if (this.cache.length > this.capacity) {
throw new Error('Rate limit exceeded');
}
return true;
}

refresh() {
let windowStart = new Date(Date.now() - WINDOW_SIZE * 1000);
this.cache = this.cache.filter((timestamp) => timestamp > windowStart);
}

getState(){
return Math.round( this.cache.length * 100 / this.capacity );
}
}
37 changes: 36 additions & 1 deletion lib/TeslaOAuth2Client.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ const CONSTANTS = require('./constants');


// const COMMAND_API_URL = 'https://teslaproxy.hpwinkler.de';
const COMMAND_API_URL = 'https://teslaproxy.rwdevelopment.de';
const COMMAND_API_URL = 'https://teslaproxy2.rwdevelopment.de';

module.exports = class TesladOAuth2Client extends OAuth2Client {
// Required:
Expand Down Expand Up @@ -213,6 +213,29 @@ module.exports = class TesladOAuth2Client extends OAuth2Client {
throw err;
}

async onHandleNotOK({
body,
status,
statusText,
headers,
}) {
if (status == 426){
const message = this.homey.__('devices.car.command_error_426');
const err = new Error(message);
err.status = status;
err.statusText = statusText;
return err;
}
else{
return super.onHandleNotOK({
body,
status,
statusText,
headers,
});
}
}

// API HANDLING =======================================================================================
async getCommandUrl({host, path, api='command'}){
let result = path;
Expand Down Expand Up @@ -240,6 +263,17 @@ module.exports = class TesladOAuth2Client extends OAuth2Client {
return result;
}

getCommandHeaders(){
let appVersion = this.homey.app.manifest.version.split('.');
let version = appVersion[0]*10000 + appVersion[1]*100 + appVersion[2]*1;
version = version.toString();
if (version.length < 6){
version = '0' + version;
}
return {
'X-Homey-App-Version': version
}
}

// RESULT HANDLING =======================================================================================
async _checkResult(result){
Expand Down Expand Up @@ -376,6 +410,7 @@ module.exports = class TesladOAuth2Client extends OAuth2Client {
host: this.getToken().fleet_api_base_url,
path: '/api/1/vehicles/'+id+'/command/set_sentry_mode',
api: api}),
headers: this.getCommandHeaders(),
json: {"on": state}
});
await this._checkResult(result);
Expand Down
3 changes: 2 additions & 1 deletion locales/de.json
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,8 @@
"command_pair_state_paired": "verbunden",
"command_pair_state_unpaired": "nicht verbunden",
"command_pair_state_undefined": "nicht bekannt",
"app_not_registered": "App ist nicht im Auto registriert. Registrieren die App im 'Reparieren'-Dialog des Auto-Gerät."
"app_not_registered": "App ist nicht im Auto registriert. Registrieren die App im 'Reparieren'-Dialog des Auto-Gerät.",
"command_error_426": "App-Update erforderlich. Bitte aktualisiere die App im Homey App-Store oder aktiviere automatische App-Updates."
}
},
"app":{
Expand Down
3 changes: 2 additions & 1 deletion locales/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,8 @@
"command_pair_state_paired": "paired",
"command_pair_state_unpaired": "unpaired",
"command_pair_state_undefined": "unknown",
"app_not_registered": "App is not registered in the car. Register via car device repair process."
"app_not_registered": "App is not registered in the car. Register via car device repair process.",
"command_error_426": "App update required. Please update the app in the Homey app store or enable automatic updates."
}
},
"app":{
Expand Down
3 changes: 2 additions & 1 deletion locales/nl.json
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,8 @@
"command_pair_state_paired": "gepaard",
"command_pair_state_unpaired": "ongepaard",
"command_pair_state_undefined": "onbekend",
"app_not_registered": "App is niet geregistreerd in de auto. Registreer u via het reparatieproces van het autoapparaat."
"app_not_registered": "App is niet geregistreerd in de auto. Registreer u via het reparatieproces van het autoapparaat.",
"command_error_426": "App-update vereist. Update de app in de Homey app store of schakel automatische updates in."
}
},
"app":{
Expand Down

0 comments on commit fe7d405

Please sign in to comment.