From 3e98f4d2274560e891a57dcbace4ad01438d7482 Mon Sep 17 00:00:00 2001 From: Brett Date: Fri, 14 Jun 2024 16:01:30 +1000 Subject: [PATCH] Updates --- src/services/base.ts | 23 +++++++++ src/services/battery.ts | 12 ++--- src/services/door.ts | 27 ++++++----- src/services/firmware.ts | 53 +++++++++++++++++++++ src/services/information.ts | 28 +++-------- src/services/thermostat.ts | 95 +++++++++++++++++++++++++++++++++++++ src/services/windows.ts | 48 +++++++++++++++++++ src/vehicle.ts | 8 +++- 8 files changed, 250 insertions(+), 44 deletions(-) create mode 100644 src/services/base.ts create mode 100644 src/services/firmware.ts create mode 100644 src/services/thermostat.ts create mode 100644 src/services/windows.ts diff --git a/src/services/base.ts b/src/services/base.ts new file mode 100644 index 0000000..6b440e1 --- /dev/null +++ b/src/services/base.ts @@ -0,0 +1,23 @@ +import { Service } from "homebridge"; +import { VehicleAccessory } from "../vehicle.js"; + +export abstract class BaseService { + protected service: Service; + + constructor( + protected parent: VehicleAccessory, + definition: any, + name: string | undefined = undefined + ) { + this.service = + this.parent.accessory.getService(definition) || + this.parent.accessory.addService(definition); + + if (name) { + this.service.setCharacteristic( + this.parent.platform.Characteristic.Name, + `${this.parent.accessory.displayName} ${name}` + ); + } + } +} diff --git a/src/services/battery.ts b/src/services/battery.ts index b346eab..8cd16f9 100644 --- a/src/services/battery.ts +++ b/src/services/battery.ts @@ -1,13 +1,9 @@ -import { Service } from "homebridge"; import { VehicleAccessory } from "../vehicle.js"; +import { BaseService } from "./base.js"; -export class BatteryService { - service: Service; - - constructor(private parent: VehicleAccessory) { - this.service = - this.parent.accessory.getService(this.parent.platform.Service.Battery) || - this.parent.accessory.addService(this.parent.platform.Service.Battery); +export class BatteryService extends BaseService { + constructor(parent: VehicleAccessory) { + super(parent, parent.platform.Service.Battery, "SOC"); const batteryLevel = this.service .getCharacteristic(this.parent.platform.Characteristic.BatteryLevel) diff --git a/src/services/door.ts b/src/services/door.ts index 69a3300..96e437c 100644 --- a/src/services/door.ts +++ b/src/services/door.ts @@ -1,23 +1,24 @@ -import { CharacteristicValue, Service } from "homebridge"; +//https://developers.homebridge.io/#/service/Door + +import { CharacteristicValue } from "homebridge"; import { VehicleAccessory } from "../vehicle.js"; +import { BaseService } from "./base.js"; -export class DoorService { - service: Service; +export class DoorService extends BaseService { key: "ft" | "rt"; - constructor( - private parent: VehicleAccessory, - private trunk: "front" | "rear" - ) { - this.service = - this.parent.accessory.getService(this.parent.platform.Service.Door) || - this.parent.accessory.addService(this.parent.platform.Service.Door); + constructor(parent: VehicleAccessory, private trunk: "front" | "rear") { + super( + parent, + parent.platform.Service.Door, + trunk === "front" ? "Frunk" : "Trunk" + ); this.key = this.trunk === "front" ? "ft" : "rt"; const currentPosition = this.service .getCharacteristic(this.parent.platform.Characteristic.CurrentPosition) - .onGet(this.getPosition); + .onGet(this.getPosition.bind(this)); /*const positionState = this.service .getCharacteristic(this.parent.platform.Characteristic.PositionState) @@ -25,8 +26,8 @@ export class DoorService { const targetPosition = this.service .getCharacteristic(this.parent.platform.Characteristic.TargetPosition) - .onGet(this.getPosition) - .onSet(this.setPosition); + .onGet(this.getPosition.bind(this)) + .onSet(this.setPosition.bind(this)); this.parent.emitter.on("vehicle_data", () => { currentPosition.updateValue(this.getPosition()); diff --git a/src/services/firmware.ts b/src/services/firmware.ts new file mode 100644 index 0000000..14c7b81 --- /dev/null +++ b/src/services/firmware.ts @@ -0,0 +1,53 @@ +//https://developers.homebridge.io/#/service/FirmwareUpdate + +import { CharacteristicValue } from "homebridge"; +import { VehicleAccessory } from "../vehicle.js"; +import { BaseService } from "./base.js"; + +export class WindowService extends BaseService { + constructor(parent: VehicleAccessory) { + super(parent, parent.platform.Service.FirmwareUpdate, "Update"); + + const readiness = this.service + .getCharacteristic( + this.parent.platform.Characteristic.FirmwareUpdateReadiness + ) + .onGet(this.getReadiness.bind(this)); + + const status = this.service + .getCharacteristic( + this.parent.platform.Characteristic.FirmwareUpdateStatus + ) + .onGet(this.getStatus.bind(this)); + + const staged = this.service + .getCharacteristic( + this.parent.platform.Characteristic.StagedFirmwareVersion + ) + .onGet(this.getStaged.bind(this)); + + this.parent.emitter.on("vehicle_data", () => { + //currentPosition.updateValue(this.getPosition()); + //targetPosition.updateValue(this.getPosition()); + }); + } + + getReadiness(): boolean { + return ( + this.parent.accessory?.context?.vehicle_state?.software_update?.status === + "downloaded" + ); + } + + getStatus(): string { + return ( + this.parent.accessory?.context?.vehicle_state?.software_update?.status || + "No update" + ); + } + + getStaged(): string { + return this.parent.accessory?.context?.vehicle_state?.software_update + ?.version; + } +} diff --git a/src/services/information.ts b/src/services/information.ts index 3cfef7a..f7a3bd9 100644 --- a/src/services/information.ts +++ b/src/services/information.ts @@ -1,20 +1,11 @@ // https://developers.homebridge.io/#/service/AccessoryInformation -import { CharacteristicValue, Service } from "homebridge"; import { VehicleAccessory } from "../vehicle.js"; +import { BaseService } from "./base.js"; -export class AccessoryInformationService { - service: Service; - - constructor(private parent: VehicleAccessory) { - this.parent = parent; - this.service = - this.parent.accessory.getService( - this.parent.platform.Service.AccessoryInformation - ) || - this.parent.accessory.addService( - this.parent.platform.Service.AccessoryInformation - ); +export class AccessoryInformationService extends BaseService { + constructor(parent: VehicleAccessory) { + super(parent, parent.platform.Service.AccessoryInformation); this.service .setCharacteristic( @@ -45,16 +36,11 @@ export class AccessoryInformationService { getVersion(): string { return ( - this.parent.accessory.context?.vehicle_state?.software_update?.version ?? - "unknown" + this.parent.accessory.context?.vehicle_state?.car_version ?? "unknown" ); } - async setIdentify(value: CharacteristicValue) { - console.log(value); - return this.parent - .wake_up() - .then(() => this.parent.vehicle.flash_lights()) - .then(() => false); + async setIdentify(): Promise { + await this.parent.wake_up().then(() => this.parent.vehicle.flash_lights()); } } diff --git a/src/services/thermostat.ts b/src/services/thermostat.ts new file mode 100644 index 0000000..de37d72 --- /dev/null +++ b/src/services/thermostat.ts @@ -0,0 +1,95 @@ +//https://developers.homebridge.io/#/service/Window + +import { CharacteristicValue } from "homebridge"; +import { VehicleAccessory } from "../vehicle.js"; +import { BaseService } from "./base.js"; + +export class ClimateService extends BaseService { + constructor(parent: VehicleAccessory) { + super(parent, parent.platform.Service.Thermostat, "Climate"); + + const currentState = this.service + .getCharacteristic( + this.parent.platform.Characteristic.CurrentHeatingCoolingState + ) + .onGet(this.getCurrentState.bind(this)); + + const targetState = this.service + .getCharacteristic( + this.parent.platform.Characteristic.TargetHeatingCoolingState + ) + .onGet(this.getTargetState.bind(this)) + .onSet(this.setTargetState.bind(this)); + + const currentTemp = this.service + .getCharacteristic(this.parent.platform.Characteristic.CurrentTemperature) + .onGet(this.getCurrentTemp.bind(this)); + + const targetTemp = this.service + .getCharacteristic(this.parent.platform.Characteristic.TargetTemperature) + .onGet(this.getTargetTemp.bind(this)) + .onSet(this.setTargetTemp.bind(this)); + + this.service.setCharacteristic( + this.parent.platform.Characteristic.TemperatureDisplayUnits, + this.parent.platform.Characteristic.TemperatureDisplayUnits.CELSIUS + ); + + this.parent.emitter.on("vehicle_data", () => { + currentState.updateValue(this.getCurrentState()); + targetState.updateValue(this.getTargetState()); + currentTemp.updateValue(this.getCurrentTemp()); + targetTemp.updateValue(this.getTargetTemp()); + }); + } + + getCurrentState(): number { + if (!this.parent.accessory.context?.climate_state.is_climate_on) { + return this.parent.platform.Characteristic.CurrentHeatingCoolingState.OFF; + } + if (this.getCurrentTemp() < this.getTargetTemp()) { + return this.parent.platform.Characteristic.CurrentHeatingCoolingState + .HEAT; + } + return this.parent.platform.Characteristic.CurrentHeatingCoolingState.COOL; + } + + getTargetState(): number { + if (!this.parent.accessory.context?.climate_state.is_climate_on) { + return this.parent.platform.Characteristic.TargetHeatingCoolingState.OFF; + } + return this.parent.platform.Characteristic.TargetHeatingCoolingState.AUTO; + } + + async setTargetState(value: CharacteristicValue) { + return value + ? this.parent.vehicle + .auto_conditioning_start() + .then( + () => + this.parent.platform.Characteristic.TargetHeatingCoolingState.AUTO + ) + : this.parent.vehicle + .auto_conditioning_stop() + .then( + () => + this.parent.platform.Characteristic.TargetHeatingCoolingState.OFF + ); + } + + getCurrentTemp(): number { + return this.parent.accessory.context?.climate_state?.inside_temp ?? 0; + } + + getTargetTemp(): number { + return ( + this.parent.accessory.context?.climate_state?.driver_temp_setting ?? 0 + ); + } + + async setTargetTemp(value: CharacteristicValue) { + return this.parent.vehicle + .set_temps(value as number, value as number) + .then(() => value); + } +} diff --git a/src/services/windows.ts b/src/services/windows.ts new file mode 100644 index 0000000..ea3194b --- /dev/null +++ b/src/services/windows.ts @@ -0,0 +1,48 @@ +//https://developers.homebridge.io/#/service/Window + +import { CharacteristicValue } from "homebridge"; +import { VehicleAccessory } from "../vehicle.js"; +import { BaseService } from "./base.js"; + +export class WindowService extends BaseService { + constructor(parent: VehicleAccessory) { + super(parent, parent.platform.Service.Window, "Windows"); + + const currentPosition = this.service + .getCharacteristic(this.parent.platform.Characteristic.CurrentPosition) + .onGet(this.getPosition.bind(this)); + + /*const positionState = this.service + .getCharacteristic(this.parent.platform.Characteristic.PositionState) + .onGet(() => this.getChargingState());*/ + + const targetPosition = this.service + .getCharacteristic(this.parent.platform.Characteristic.TargetPosition) + .onGet(this.getPosition.bind(this)) + .onSet(this.setPosition.bind(this)); + + this.parent.emitter.on("vehicle_data", () => { + currentPosition.updateValue(this.getPosition()); + targetPosition.updateValue(this.getPosition()); + }); + } + + getPosition(): number { + return this.parent.accessory.context?.vehicle_state?.fd_window | + this.parent.accessory.context?.vehicle_state?.fp_window | + this.parent.accessory.context?.vehicle_state?.rd_window | + this.parent.accessory.context?.vehicle_state?.rp_window + ? 100 + : 0; + } + + async setPosition(value: CharacteristicValue) { + console.log(value); + const { latitude, longitude } = + this.parent.accessory.context?.drive_state ?? {}; + + return this.parent.vehicle + .window_control(value === 100 ? "close" : "vent", latitude, longitude) + .then(() => value); + } +} diff --git a/src/vehicle.ts b/src/vehicle.ts index 85b7167..308ba6e 100644 --- a/src/vehicle.ts +++ b/src/vehicle.ts @@ -1,4 +1,4 @@ -import { CharacteristicValue, PlatformAccessory, Service } from "homebridge"; +import { PlatformAccessory } from "homebridge"; import { VehicleSpecific } from "tesla-fleet-api"; import { @@ -9,10 +9,11 @@ import { VehicleConfig, VehicleState, } from "tesla-fleet-api/dist/types/vehicle_data"; -import { VehicleDataResponse } from "tesla-fleet-api/dist/types/vehicle_data.js"; import { TeslaFleetApiPlatform } from "./platform.js"; import { BatteryService } from "./services/battery.js"; +import { DoorService } from "./services/door.js"; import { AccessoryInformationService } from "./services/information.js"; +import { ClimateService } from "./services/thermostat.js"; import { REFRESH_INTERVAL } from "./settings.js"; import { EventEmitter } from "./utils/event.js"; @@ -57,6 +58,9 @@ export class VehicleAccessory { new AccessoryInformationService(this); new BatteryService(this); + new DoorService(this, "front"); + new DoorService(this, "rear"); + new ClimateService(this); } async refresh(): Promise {