Added support for commands published to prefix/VIN/command topic.
This commit is contained in:
parent
28698b7f59
commit
f5dd435feb
122
src/commands.js
Normal file
122
src/commands.js
Normal file
@ -0,0 +1,122 @@
|
||||
|
||||
class Commands {
|
||||
static CONSTANTS = {
|
||||
ALERT_ACTION: {
|
||||
FLASH: 'Flash',
|
||||
HONK: 'Honk',
|
||||
},
|
||||
ALERT_OVERRIDE: {
|
||||
DOOR_OPEN: 'DoorOpen',
|
||||
IGNITION_ON: 'IgnitionOn'
|
||||
},
|
||||
CHARGE_OVERRIDE: {
|
||||
CHARGE_NOW: 'CHARGE_NOW',
|
||||
CANCEL_OVERRIDE: 'CANCEL_OVERRIDE'
|
||||
},
|
||||
CHARGING_PROFILE_MODE: {
|
||||
DEFAULT_IMMEDIATE: 'DEFAULT_IMMEDIATE',
|
||||
IMMEDIATE: 'IMMEDIATE',
|
||||
DEPARTURE_BASED: 'DEPARTURE_BASED',
|
||||
RATE_BASED: 'RATE_BASED',
|
||||
PHEV_AFTER_MIDNIGHT: 'PHEV_AFTER_MIDNIGHT'
|
||||
},
|
||||
CHARGING_PROFILE_RATE: {
|
||||
OFFPEAK: 'OFFPEAK',
|
||||
MIDPEAK: 'MIDPEAK',
|
||||
PEAK: 'PEAK'
|
||||
},
|
||||
DIAGNOSTICS: {
|
||||
ENGINE_COOLANT_TEMP: 'ENGINE COOLANT TEMP',
|
||||
ENGINE_RPM: 'ENGINE RPM',
|
||||
LAST_TRIP_FUEL_ECONOMY: 'LAST TRIP FUEL ECONOMY',
|
||||
EV_ESTIMATED_CHARGE_END: 'EV ESTIMATED CHARGE END',
|
||||
EV_BATTERY_LEVEL: 'EV BATTERY LEVEL',
|
||||
OIL_LIFE: 'OIL LIFE',
|
||||
EV_PLUG_VOLTAGE: 'EV PLUG VOLTAGE',
|
||||
LIFETIME_FUEL_ECON: 'LIFETIME FUEL ECON',
|
||||
HOTSPOT_CONFIG: 'HOTSPOT CONFIG',
|
||||
LIFETIME_FUEL_USED: 'LIFETIME FUEL USED',
|
||||
ODOMETER: 'ODOMETER',
|
||||
HOTSPOT_STATUS: 'HOTSPOT STATUS',
|
||||
LIFETIME_EV_ODOMETER: 'LIFETIME EV ODOMETER',
|
||||
EV_PLUG_STATE: 'EV PLUG STATE',
|
||||
EV_CHARGE_STATE: 'EV CHARGE STATE',
|
||||
TIRE_PRESSURE: 'TIRE PRESSURE',
|
||||
AMBIENT_AIR_TEMPERATURE: 'AMBIENT AIR TEMPERATURE',
|
||||
LAST_TRIP_DISTANCE: 'LAST TRIP DISTANCE',
|
||||
INTERM_VOLT_BATT_VOLT: 'INTERM VOLT BATT VOLT',
|
||||
GET_COMMUTE_SCHEDULE: 'GET COMMUTE SCHEDULE',
|
||||
GET_CHARGE_MODE: 'GET CHARGE MODE',
|
||||
EV_SCHEDULED_CHARGE_START: 'EV SCHEDULED CHARGE START',
|
||||
FUEL_TANK_INFO: 'FUEL TANK INFO',
|
||||
HANDS_FREE_CALLING: 'HANDS FREE CALLING',
|
||||
ENERGY_EFFICIENCY: 'ENERGY EFFICIENCY',
|
||||
VEHICLE_RANGE: 'VEHICLE RANGE',
|
||||
}
|
||||
}
|
||||
|
||||
constructor(onstar) {
|
||||
this.onstar = onstar;
|
||||
}
|
||||
|
||||
async getAccountVehicles() {
|
||||
return this.onstar.getAccountVehicles();
|
||||
}
|
||||
|
||||
async startVehicle() {
|
||||
return this.onstar.start();
|
||||
}
|
||||
|
||||
async cancelStartVehicle() {
|
||||
return this.onstar.cancelStart();
|
||||
}
|
||||
|
||||
async alert({action = [Commands.CONSTANTS.ALERT_ACTION.FLASH],
|
||||
delay = 0, duration = 1, override = []}) {
|
||||
return this.onstar.alert({
|
||||
action,
|
||||
delay,
|
||||
duration,
|
||||
override
|
||||
});
|
||||
}
|
||||
|
||||
async cancelAlert() {
|
||||
return this.onstar.cancelAlert();
|
||||
}
|
||||
|
||||
async lockDoor({delay = 0}) {
|
||||
return this.onstar.lockDoor({delay});
|
||||
}
|
||||
|
||||
async unlockDoor({delay = 0}) {
|
||||
return this.onstar.unlockDoor({delay});
|
||||
}
|
||||
|
||||
async chargeOverride({mode = Commands.CONSTANTS.CHARGE_OVERRIDE.CHARGE_NOW}) {
|
||||
return this.onstar.chargeOverride({mode});
|
||||
}
|
||||
|
||||
async cancelChargeOverride({mode = Commands.CONSTANTS.CHARGE_OVERRIDE.CANCEL_OVERRIDE}) {
|
||||
return this.onstar.chargeOverride({mode});
|
||||
}
|
||||
|
||||
async getChargingProfile() {
|
||||
return this.onstar.getChargingProfile();
|
||||
}
|
||||
|
||||
async setChargingProfile() {
|
||||
return this.onstar.setChargingProfile();
|
||||
}
|
||||
|
||||
async diagnostics({diagnosticItem = [
|
||||
Commands.CONSTANTS.DIAGNOSTICS.ODOMETER,
|
||||
Commands.CONSTANTS.DIAGNOSTICS.TIRE_PRESSURE,
|
||||
Commands.CONSTANTS.DIAGNOSTICS.AMBIENT_AIR_TEMPERATURE,
|
||||
Commands.CONSTANTS.DIAGNOSTICS.LAST_TRIP_DISTANCE
|
||||
]}) {
|
||||
return this.onstar.diagnostics({diagnosticItem});
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = Commands;
|
77
src/index.js
77
src/index.js
@ -1,3 +1,4 @@
|
||||
|
||||
const OnStar = require('onstarjs');
|
||||
const mqtt = require('async-mqtt');
|
||||
const uuidv4 = require('uuid').v4;
|
||||
@ -5,6 +6,7 @@ const _ = require('lodash');
|
||||
const Vehicle = require('./vehicle');
|
||||
const {Diagnostic} = require('./diagnostic');
|
||||
const MQTT = require('./mqtt');
|
||||
const Commands = require('./commands');
|
||||
|
||||
const onstarConfig = {
|
||||
deviceId: process.env.ONSTAR_DEVICEID || uuidv4(),
|
||||
@ -13,7 +15,8 @@ const onstarConfig = {
|
||||
password: process.env.ONSTAR_PASSWORD,
|
||||
onStarPin: process.env.ONSTAR_PIN,
|
||||
checkRequestStatus: process.env.ONSTAR_SYNC === "true" || true,
|
||||
refreshInterval: parseInt(process.env.ONSTAR_REFRESH) || (30 * 60 * 1000) // 30 min
|
||||
refreshInterval: parseInt(process.env.ONSTAR_REFRESH) || (30 * 60 * 1000), // 30 min
|
||||
allowCommands: _.toLower(_.get(process, 'env.ONSTAR_ALLOW_COMMANDS', 'true')) === 'true'
|
||||
};
|
||||
|
||||
const mqttConfig = {
|
||||
@ -25,39 +28,63 @@ const mqttConfig = {
|
||||
prefix: process.env.MQTT_PREFIX || 'homeassistant',
|
||||
};
|
||||
|
||||
let loop;
|
||||
let loop, commands, vehicles;
|
||||
|
||||
const init = async () => {
|
||||
commands = new Commands(OnStar.create(onstarConfig));
|
||||
console.log('Requesting vehicles.');
|
||||
const vehiclesRes = await commands.getAccountVehicles();
|
||||
console.log(_.get(vehiclesRes, 'status'));
|
||||
vehicles = _.map(
|
||||
_.get(vehiclesRes, 'response.data.vehicles.vehicle'),
|
||||
v => new Vehicle(v)
|
||||
);
|
||||
console.log('Vehicles returned:');
|
||||
for (const v of vehicles) {
|
||||
console.log(v.toString());
|
||||
}
|
||||
}
|
||||
|
||||
const connectMQTT = async () => {
|
||||
const mqttHA = new MQTT(vehicles[0], 'homeassistant');
|
||||
const availTopic = mqttHA.getAvailabilityTopic();
|
||||
const client = await mqtt.connectAsync(`${mqttConfig.tls
|
||||
? 'mqtts' : 'mqtt'}://${mqttConfig.host}:${mqttConfig.port}`, {
|
||||
username: mqttConfig.username,
|
||||
password: mqttConfig.password,
|
||||
will: {topic: availTopic, payload: 'false', retain: true}
|
||||
});
|
||||
|
||||
if (onstarConfig.allowCommands) {
|
||||
client.on('message', (topic, message) => {
|
||||
console.log(`Subscription message: ${topic} ${message}`);
|
||||
const {command, options} = JSON.parse(message);
|
||||
const commandFn = commands[command].bind(commands);
|
||||
commandFn(options || {})
|
||||
.then(() => console.log(`Command completed: ${command}`))
|
||||
.catch(err=> console.error(`Command error: ${command} ${err}`));
|
||||
});
|
||||
const topic = mqttHA.getCommandTopic();
|
||||
console.log(`Subscribed to: ${topic}`);
|
||||
await client.subscribe(topic);
|
||||
}
|
||||
|
||||
await client.publish(availTopic, 'true', {retain: true});
|
||||
return {mqttHA, client};
|
||||
};
|
||||
|
||||
(async () => {
|
||||
try {
|
||||
const onStar = OnStar.create(onstarConfig);
|
||||
console.log('Requesting vehicles.');
|
||||
const vehiclesRes = await onStar.getAccountVehicles();
|
||||
console.log(_.get(vehiclesRes, 'status'));
|
||||
const vehicles = _.map(
|
||||
_.get(vehiclesRes, 'response.data.vehicles.vehicle'),
|
||||
v => new Vehicle(v)
|
||||
);
|
||||
console.log('Vehicles returned:');
|
||||
for (const v of vehicles) {
|
||||
console.log(v.toString());
|
||||
}
|
||||
await init();
|
||||
|
||||
const mqttHA = new MQTT(vehicles[0], 'homeassistant');
|
||||
const availTopic = mqttHA.getAvailabilityTopic();
|
||||
const client = await mqtt.connectAsync(`${mqttConfig.tls
|
||||
? 'mqtts' : 'mqtt'}://${mqttConfig.host}:${mqttConfig.port}`, {
|
||||
username: mqttConfig.username,
|
||||
password: mqttConfig.password,
|
||||
will: {topic: availTopic, payload: 'false', retain: true}
|
||||
});
|
||||
|
||||
await client.publish(availTopic, 'true', {retain: true});
|
||||
const {mqttHA, client} = await connectMQTT();
|
||||
|
||||
const configurations = new Map();
|
||||
const run = async () => {
|
||||
const states = new Map();
|
||||
const v = vehicles[0];
|
||||
console.log('Requesting diagnostics:')
|
||||
const statsRes = await onStar.diagnostics({
|
||||
const statsRes = await commands.diagnostics({
|
||||
diagnosticItem: v.getSupported()
|
||||
});
|
||||
console.log(_.get(statsRes, 'status'));
|
||||
|
@ -74,6 +74,10 @@ class MQTT {
|
||||
return `${this.prefix}/${this.instance}/available`;
|
||||
}
|
||||
|
||||
getCommandTopic() {
|
||||
return `${this.prefix}/${this.instance}/command`;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {DiagnosticElement} diag
|
||||
|
@ -30,6 +30,15 @@ describe('MQTT', () => {
|
||||
|
||||
describe('topics', () => {
|
||||
let d;
|
||||
|
||||
it('should generate availability topic', () => {
|
||||
assert.strictEqual(mqtt.getAvailabilityTopic(), 'homeassistant/XXX/available');
|
||||
});
|
||||
|
||||
it('should generate command topic', () => {
|
||||
assert.strictEqual(mqtt.getCommandTopic(), 'homeassistant/XXX/command');
|
||||
});
|
||||
|
||||
describe('sensor', () => {
|
||||
beforeEach(() => d = new Diagnostic(_.get(apiResponse, 'commandResponse.body.diagnosticResponse[0]')));
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user