Skip to content

Commit

Permalink
🪿 Update Canadian info 🇨🇦 (#255)
Browse files Browse the repository at this point in the history
  • Loading branch information
djensenius committed Jul 19, 2023
1 parent d3591b9 commit 4e637f7
Show file tree
Hide file tree
Showing 4 changed files with 124 additions and 60 deletions.
13 changes: 9 additions & 4 deletions debug.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ const apiCalls = [

let client: BlueLinky;
let vehicle;
const { username, password, pin } = config;
const { username, password, pin, useInfo = false } = config;

const onReadyHandler = <T extends Vehicle>(vehicles: T[]) => {
vehicle = vehicles[0];
Expand Down Expand Up @@ -122,34 +122,39 @@ async function performCommand(command) {
const status = await vehicle.status({
refresh: false,
parsed: true,
useInfo,
});
console.log('status : ' + JSON.stringify(status, null, 2));
break;
case 'statusU':
const statusU = await vehicle.status({
refresh: false,
parsed: false,
useInfo,
});
console.log('status : ' + JSON.stringify(statusU, null, 2));
break;
case 'statusR':
const statusR = await vehicle.status({
refresh: true,
parsed: true
parsed: true,
useInfo,
});
console.log('status remote : ' + JSON.stringify(statusR, null, 2));
break;
case 'fullStatus':
const fullStatus = await vehicle.fullStatus({
refresh: false,
parsed: false
parsed: false,
useInfo,
});
console.log('full status cached : ' + JSON.stringify(fullStatus, null, 2));
break;
case 'fullStatusR':
const fullStatusR = await vehicle.fullStatus({
refresh: true,
parsed: false
parsed: false,
useInfo,
});
console.log('full status remote : ' + JSON.stringify(fullStatusR, null, 2));
break;
Expand Down
4 changes: 2 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion src/interfaces/common.interfaces.ts
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ export interface VehicleStatus {
steeringwheelHeat: boolean;
sideMirrorHeat: boolean;
rearWindowHeat: boolean;
temperatureSetpoint: number;
temperatureSetpoint: number | string;
temperatureUnit: number;
defrost: boolean;
};
Expand Down Expand Up @@ -399,6 +399,7 @@ export interface VehicleOdometer {
export interface VehicleStatusOptions {
refresh: boolean;
parsed: boolean;
useInfo?: boolean;
}

// VEHICLE COMMANDS /////////////////////////////////////////////
Expand Down
164 changes: 111 additions & 53 deletions src/vehicles/canadian.vehicle.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,16 +18,38 @@ import {
RawVehicleStatus,
FullVehicleStatus,
EVChargeModeTypes,
VehicleInfo,
VehicleFeatureEntry,
} from '../interfaces/common.interfaces';

import { Vehicle } from './vehicle';
import { celciusToTempCode, parseDate } from '../util';
import { CanadianController } from '../controllers/canadian.controller';
import { ManagedBluelinkyError } from '../tools/common.tools';


export interface CanadianInfo {
vehicle: VehicleInfo;
features: {
seatHeatVent: {
drvSeatHeatOption: number;
astSeatHeatOption: number;
rlSeatHeatOption: number;
rrSeatHeatOption: number;
};
hvacTempType: number;
targetMinSoc: number;
strgWhlHeatingOption: number;
};
featuresModel: {
features: [VehicleFeatureEntry];
};
status: RawVehicleStatus;
}

export default class CanadianVehicle extends Vehicle {
public region = REGIONS.CA;
private timeOffset = -(new Date().getTimezoneOffset() / 60);
private _info: CanadianInfo | null = null;

constructor(public vehicleConfig: VehicleRegisterOptions, public controller: CanadianController) {
super(vehicleConfig, controller);
Expand All @@ -48,60 +70,70 @@ export default class CanadianVehicle extends Vehicle {

logger.debug('Begin status request, polling car', statusConfig.refresh);
try {
const endpoint = statusConfig.refresh
? this.controller.environment.endpoints.remoteStatus
: this.controller.environment.endpoints.status;
const response = await this.request(endpoint, {});
const vehicleStatus = response.result?.status;

if (response?.error) {
throw response?.error?.errorDesc;
let vehicleStatus: RawVehicleStatus | null = null;
if (statusConfig.useInfo) {
await this.setInfo(statusConfig.refresh);
if (this._info) {
vehicleStatus = this._info.status;
}
} else {
const endpoint = statusConfig.refresh
? this.controller.environment.endpoints.remoteStatus
: this.controller.environment.endpoints.status;
const response = await this.request(endpoint, {});
vehicleStatus = response.result?.status;

if (response?.error) {
throw response?.error?.errorDesc;
}
}

logger.debug(vehicleStatus);
const parsedStatus: VehicleStatus = {
chassis: {
hoodOpen: vehicleStatus?.hoodOpen,
trunkOpen: vehicleStatus?.trunkOpen,
locked: vehicleStatus?.doorLock,
openDoors: {
frontRight: !!vehicleStatus?.doorOpen?.frontRight,
frontLeft: !!vehicleStatus?.doorOpen?.frontLeft,
backLeft: !!vehicleStatus?.doorOpen?.backLeft,
backRight: !!vehicleStatus?.doorOpen?.backRight,
let parsedStatus: VehicleStatus | null = null;
if (vehicleStatus) {
parsedStatus = {
chassis: {
hoodOpen: vehicleStatus?.hoodOpen,
trunkOpen: vehicleStatus?.trunkOpen,
locked: vehicleStatus?.doorLock,
openDoors: {
frontRight: !!vehicleStatus?.doorOpen?.frontRight,
frontLeft: !!vehicleStatus?.doorOpen?.frontLeft,
backLeft: !!vehicleStatus?.doorOpen?.backLeft,
backRight: !!vehicleStatus?.doorOpen?.backRight,
},
tirePressureWarningLamp: {
rearLeft: !!vehicleStatus?.tirePressureLamp?.tirePressureWarningLampRearLeft,
frontLeft: !!vehicleStatus?.tirePressureLamp?.tirePressureWarningLampFrontLeft,
frontRight: !!vehicleStatus?.tirePressureLamp?.tirePressureWarningLampFrontRight,
rearRight: !!vehicleStatus?.tirePressureLamp?.tirePressureWarningLampRearRight,
all: !!vehicleStatus?.tirePressureLamp?.tirePressureWarningLampAll,
},
},
tirePressureWarningLamp: {
rearLeft: !!vehicleStatus?.tirePressureLamp?.tirePressureWarningLampRearLeft,
frontLeft: !!vehicleStatus?.tirePressureLamp?.tirePressureWarningLampFrontLeft,
frontRight: !!vehicleStatus?.tirePressureLamp?.tirePressureWarningLampFrontRight,
rearRight: !!vehicleStatus?.tirePressureLamp?.tirePressureWarningLampRearRight,
all: !!vehicleStatus?.tirePressureLamp?.tirePressureWarningLampAll,
climate: {
active: vehicleStatus?.airCtrlOn,
steeringwheelHeat: !!vehicleStatus?.steerWheelHeat,
sideMirrorHeat: false,
rearWindowHeat: !!vehicleStatus?.sideBackWindowHeat,
defrost: vehicleStatus?.defrost,
temperatureSetpoint: vehicleStatus?.airTemp?.value,
temperatureUnit: vehicleStatus?.airTemp?.unit,
},
},
climate: {
active: vehicleStatus?.airCtrlOn,
steeringwheelHeat: !!vehicleStatus?.steerWheelHeat,
sideMirrorHeat: false,
rearWindowHeat: !!vehicleStatus?.sideBackWindowHeat,
defrost: vehicleStatus?.defrost,
temperatureSetpoint: vehicleStatus?.airTemp?.value,
temperatureUnit: vehicleStatus?.airTemp?.unit,
},

// TODO: fix props for parsed???
// Seems some of the translation would have to account for EV and ICE
// as they are often in different locations on the response
// example EV status is in lib/__mock__/canadianStatus.json
engine: {
ignition: vehicleStatus?.engine,
accessory: vehicleStatus?.acc,
range: vehicleStatus?.dte?.value,
charging: vehicleStatus?.evStatus?.batteryCharge,
batteryCharge12v: vehicleStatus?.battery?.batSoc,
batteryChargeHV: vehicleStatus?.evStatus?.batteryStatus,
},
lastupdate: vehicleStatus?.time ? parseDate(vehicleStatus?.lastStatusDate) : null,
};
// TODO: fix props for parsed???
// Seems some of the translation would have to account for EV and ICE
// as they are often in different locations on the response
// example EV status is in lib/__mock__/canadianStatus.json
engine: {
ignition: vehicleStatus?.engine,
accessory: vehicleStatus?.acc,
range: vehicleStatus?.dte?.value,
charging: vehicleStatus?.evStatus?.batteryCharge,
batteryCharge12v: vehicleStatus?.battery?.batSoc,
batteryChargeHV: vehicleStatus?.evStatus?.batteryStatus,
},
lastupdate: parseDate(vehicleStatus?.lastStatusDate),
};
}

this._status = statusConfig.parsed ? parsedStatus : vehicleStatus;
return this._status;
Expand Down Expand Up @@ -289,8 +321,17 @@ export default class CanadianVehicle extends Vehicle {
}

// TODO: @Seb to take a look at doing this
public odometer(): Promise<VehicleOdometer | null> {
throw new Error('Method not implemented.');
public async odometer(): Promise<VehicleOdometer | null> {
try {
await this.setInfo();
if (this._info) {
return { unit: this._info.vehicle.odometer, value: this._info.vehicle.odometerUnit };
} else {
throw 'error: no info';
}
} catch (err) {
throw 'error: ' + err;
}
}

public async location(): Promise<VehicleLocation> {
Expand Down Expand Up @@ -325,7 +366,7 @@ export default class CanadianVehicle extends Vehicle {

// TODO: not sure how to type a dynamic response
/* eslint-disable @typescript-eslint/no-explicit-any */
private async request(endpoint, body: any, headers: any = {}): Promise<any | null> {
private async request(endpoint: string, body: any, headers: any = {}): Promise<any | null> {
logger.debug(`[${endpoint}] ${JSON.stringify(headers)} ${JSON.stringify(body)}`);

// add logic for token refresh to ensure we don't use a stale token
Expand Down Expand Up @@ -361,4 +402,21 @@ export default class CanadianVehicle extends Vehicle {
throw 'error: ' + err;
}
}

private async setInfo(refresh = false): Promise<void> {
if (this._info !== null && !refresh) {
return;
}
try {
const preAuth = await this.getPreAuth();
const response = await this.request(
this.controller.environment.endpoints.vehicleInfo,
{},
{ pAuth: preAuth }
);
this._info = response.result as CanadianInfo;
} catch (err) {
throw 'error: ' + err;
}
}
}

0 comments on commit 4e637f7

Please sign in to comment.