bmw-connected-drive
Version:
This package can be used to access the BMW ConnectedDrive services.
190 lines • 11 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.ConnectedDrive = void 0;
const Account_1 = require("./Account");
const cross_fetch_1 = require("cross-fetch");
const Constants_1 = require("./Constants");
const RemoteServices_1 = require("./RemoteServices");
const RemoteServiceExecutionState_1 = require("./RemoteServiceExecutionState");
const CarBrand_1 = require("./CarBrand");
const uuid_1 = require("uuid");
const Utils_1 = require("./Utils");
class ConnectedDrive {
constructor(username, password, region, tokenStore, logger, captchaToken) {
this.serviceExecutionStatusCheckInterval = 5000;
this.account = new Account_1.Account(username, password, region, tokenStore, logger, captchaToken);
this.logger = logger;
}
async getVehicles() {
var _a;
(_a = this.logger) === null || _a === void 0 ? void 0 : _a.LogInformation("Getting all vehicles");
let result = [];
for (let key in CarBrand_1.CarBrand) {
const brand = CarBrand_1.CarBrand[key];
let vehicles = await this.getVehiclesByBrand(brand);
result.push(...vehicles);
}
return (result);
}
async getVehiclesByBrand(brand) {
var _a;
(_a = this.logger) === null || _a === void 0 ? void 0 : _a.LogInformation(`Getting ${brand} vehicles`);
const params = `apptimezone=${120}&appDateTime=${Date.now()}`;
const url = `https://${Constants_1.Constants.ServerEndpoints[this.account.region]}${Constants_1.Constants.getVehicles}?${params}`;
const vehicles = await this.getFromJson(url, brand);
// Patching the brand to the vehicles as the brand returned might be "BMW_I"
// https://github.com/lsiddiquee/com.rexwel.bmwconnected/issues/56
vehicles.forEach((vehicle) => vehicle.attributes.brand = brand);
return vehicles;
}
async getVehicleStatus(vin, brand = CarBrand_1.CarBrand.Bmw) {
var _a;
(_a = this.logger) === null || _a === void 0 ? void 0 : _a.LogInformation("Getting vehicle status.");
const params = `apptimezone=${120}&appDateTime=${Date.now()}`;
const url = `https://${Constants_1.Constants.ServerEndpoints[this.account.region]}${Constants_1.Constants.getVehicles}/state?${params}`;
return (await this.getFromJson(url, brand, { "bmw-vin": vin })).state;
}
async getVehicleCapabilities(vin, brand = CarBrand_1.CarBrand.Bmw) {
var _a;
(_a = this.logger) === null || _a === void 0 ? void 0 : _a.LogInformation("Getting vehicle capabilities.");
const params = `apptimezone=${120}&appDateTime=${Date.now()}`;
const url = `https://${Constants_1.Constants.ServerEndpoints[this.account.region]}${Constants_1.Constants.getVehicles}/state?${params}`;
return (await this.getFromJson(url, brand, { "bmw-vin": vin })).capabilities;
}
async lockDoors(vin, brand = CarBrand_1.CarBrand.Bmw, waitExecution = false) {
var _a;
(_a = this.logger) === null || _a === void 0 ? void 0 : _a.LogInformation("Locking doors");
return await this.executeService(vin, brand, RemoteServices_1.RemoteServices.LockDoors, {}, waitExecution);
}
async unlockDoors(vin, brand = CarBrand_1.CarBrand.Bmw, waitExecution = false) {
var _a;
(_a = this.logger) === null || _a === void 0 ? void 0 : _a.LogInformation("Unlocking doors");
return await this.executeService(vin, brand, RemoteServices_1.RemoteServices.UnlockDoors, {}, waitExecution);
}
async startClimateControl(vin, brand = CarBrand_1.CarBrand.Bmw, waitExecution = false) {
var _a;
(_a = this.logger) === null || _a === void 0 ? void 0 : _a.LogInformation("Start Climate Control");
return await this.executeService(vin, brand, RemoteServices_1.RemoteServices.ClimateNow, { "action": "START" }, waitExecution);
}
async stopClimateControl(vin, brand = CarBrand_1.CarBrand.Bmw, waitExecution = false) {
var _a;
(_a = this.logger) === null || _a === void 0 ? void 0 : _a.LogInformation("Stop Climate Control");
return await this.executeService(vin, brand, RemoteServices_1.RemoteServices.ClimateNow, { "action": "STOP" }, waitExecution);
}
async flashLights(vin, brand = CarBrand_1.CarBrand.Bmw, waitExecution = false) {
return await this.executeService(vin, brand, RemoteServices_1.RemoteServices.FlashLight, {}, waitExecution);
}
async blowHorn(vin, brand = CarBrand_1.CarBrand.Bmw, waitExecution = false) {
var _a;
(_a = this.logger) === null || _a === void 0 ? void 0 : _a.LogInformation("Blow Horn");
return await this.executeService(vin, brand, RemoteServices_1.RemoteServices.BlowHorn, {}, waitExecution);
}
async startCharging(vin, brand = CarBrand_1.CarBrand.Bmw, waitExecution = false) {
var _a;
(_a = this.logger) === null || _a === void 0 ? void 0 : _a.LogInformation("Start Charging");
return await this.executeService(vin, brand, RemoteServices_1.RemoteServices.ChargeStart, {}, waitExecution, Constants_1.Constants.vehicleChargingStartStopUrl);
}
async stopCharging(vin, brand = CarBrand_1.CarBrand.Bmw, waitExecution = false) {
var _a;
(_a = this.logger) === null || _a === void 0 ? void 0 : _a.LogInformation("Stop Charging");
return await this.executeService(vin, brand, RemoteServices_1.RemoteServices.ChargeStop, {}, waitExecution, Constants_1.Constants.vehicleChargingStartStopUrl);
}
async executeService(vin, brand, serviceType, params, waitExecution, remoteServiceUrl = Constants_1.Constants.executeRemoteServices) {
let url = `https://${Constants_1.Constants.ServerEndpoints[this.account.region]}${remoteServiceUrl}`;
url = url.replace("{vehicleVin}", vin);
url = url.replace("{serviceType}", serviceType);
const headers = {
"bmw-vin": vin
};
if (Object.keys(params).length > 0) {
const queryString = Object.keys(params)
.map(key => `${encodeURIComponent(key)}=${encodeURIComponent(params[key])}`)
.join('&');
url += `?${queryString}`;
}
const response = await this.postAsJson(url, brand, {}, headers);
if (waitExecution) {
let status = RemoteServiceExecutionState_1.RemoteServiceExecutionState.UNKNOWN;
while (status !== RemoteServiceExecutionState_1.RemoteServiceExecutionState.EXECUTED
&& status !== RemoteServiceExecutionState_1.RemoteServiceExecutionState.CANCELLED_WITH_ERROR
&& status !== RemoteServiceExecutionState_1.RemoteServiceExecutionState.ERROR) {
status = await this.getServiceStatus(response.eventId, brand);
await Utils_1.Utils.Delay(this.serviceExecutionStatusCheckInterval, this.logger);
}
}
return response;
}
async getServiceStatus(eventId, brand = CarBrand_1.CarBrand.Bmw) {
let url = `https://${Constants_1.Constants.ServerEndpoints[this.account.region]}${Constants_1.Constants.statusRemoteServices}`;
url = url.replace("{eventId}", eventId);
return (await this.postAsJson(url, brand)).eventStatus;
}
async getImage(vin, brand = CarBrand_1.CarBrand.Bmw, view) {
let url = `https://${Constants_1.Constants.ServerEndpoints[this.account.region]}${Constants_1.Constants.getImages}`;
url = url.replace("{carView}", view);
const headers = {
"accept": "image/png",
"bmw-app-vehicle-type": "connected",
"bmw-vin": vin
};
const response = await this.get(url, brand, headers);
return await response.arrayBuffer();
}
async sendMessage(vin, brand = CarBrand_1.CarBrand.Bmw, subject, message) {
var _a;
// TODO: Cleanup
let url = `https://${Constants_1.Constants.ServerEndpoints[this.account.region]}`;
const requestBody = { "vins": [vin], "message": message, "subject": subject };
return ((_a = (await this.postAsJson(url, brand, requestBody))) === null || _a === void 0 ? void 0 : _a.status) === "OK";
}
async get(url, brand = CarBrand_1.CarBrand.Bmw, headers = {}) {
return await this.request(url, brand, false, null, headers);
}
async getFromJson(url, brand = CarBrand_1.CarBrand.Bmw, headers = {}) {
return (await this.get(url, brand, headers)).json();
}
async postAsJson(url, brand = CarBrand_1.CarBrand.Bmw, requestBody = {}, headers = {}) {
return (await this.request(url, brand, true, requestBody, headers)).json();
}
async request(url, brand = CarBrand_1.CarBrand.Bmw, isPost = false, requestBody, headers = {}) {
var _a, _b;
const correlationId = (0, uuid_1.v4)();
const httpMethod = isPost ? "POST" : "GET";
const requestBodyContent = requestBody ? JSON.stringify(requestBody) : null;
let retryCount = 0;
const completeHeaders = {
"accept": "application/json",
"accept-language": "en",
"content-type": "application/json;charset=UTF-8",
"authorization": `Bearer ${(await this.account.getToken()).accessToken}`,
"user-agent": Constants_1.Constants.User_Agent,
"x-user-agent": Constants_1.Constants.X_User_Agent(this.account.region, brand),
"x-identity-provider": "gcdm",
"bmw-session-id": correlationId,
"x-correlation-id": correlationId,
"bmw-correlation-id": correlationId,
...headers
};
let response;
do {
if (retryCount !== 0) {
await Utils_1.Utils.Delay(2000, this.logger);
}
response = await (0, cross_fetch_1.fetch)(url, {
method: httpMethod,
body: requestBodyContent,
headers: completeHeaders,
credentials: "same-origin"
});
completeHeaders["authorization"] = "Bearer xxx"; // Mask the token in the logs
(_a = this.logger) === null || _a === void 0 ? void 0 : _a.LogTrace(`Request: ${url}, Method: ${httpMethod}, Headers: ${JSON.stringify(completeHeaders)}, Body: ${requestBodyContent}`);
(_b = this.logger) === null || _b === void 0 ? void 0 : _b.LogTrace(`Response: ${response.status}, Headers: ${JSON.stringify(response.headers)}`);
} while (retryCount++ < 5 && (response.status === 429 || (response.status === 403 && response.statusText.includes("quota"))));
if (!response.ok) {
throw new Error(`Error occurred while attempting '${httpMethod}' at url '${url}' with ${response.status} body (${requestBodyContent})\n${await response.text()}`);
}
return response;
}
}
exports.ConnectedDrive = ConnectedDrive;
//# sourceMappingURL=ConnectedDrive.js.map