UNPKG

bmw-connected-drive

Version:

This package can be used to access the BMW ConnectedDrive services.

190 lines 11 kB
"use strict"; 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