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;
|
43
src/index.js
43
src/index.js
@ -1,3 +1,4 @@
|
|||||||
|
|
||||||
const OnStar = require('onstarjs');
|
const OnStar = require('onstarjs');
|
||||||
const mqtt = require('async-mqtt');
|
const mqtt = require('async-mqtt');
|
||||||
const uuidv4 = require('uuid').v4;
|
const uuidv4 = require('uuid').v4;
|
||||||
@ -5,6 +6,7 @@ const _ = require('lodash');
|
|||||||
const Vehicle = require('./vehicle');
|
const Vehicle = require('./vehicle');
|
||||||
const {Diagnostic} = require('./diagnostic');
|
const {Diagnostic} = require('./diagnostic');
|
||||||
const MQTT = require('./mqtt');
|
const MQTT = require('./mqtt');
|
||||||
|
const Commands = require('./commands');
|
||||||
|
|
||||||
const onstarConfig = {
|
const onstarConfig = {
|
||||||
deviceId: process.env.ONSTAR_DEVICEID || uuidv4(),
|
deviceId: process.env.ONSTAR_DEVICEID || uuidv4(),
|
||||||
@ -13,7 +15,8 @@ const onstarConfig = {
|
|||||||
password: process.env.ONSTAR_PASSWORD,
|
password: process.env.ONSTAR_PASSWORD,
|
||||||
onStarPin: process.env.ONSTAR_PIN,
|
onStarPin: process.env.ONSTAR_PIN,
|
||||||
checkRequestStatus: process.env.ONSTAR_SYNC === "true" || true,
|
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 = {
|
const mqttConfig = {
|
||||||
@ -25,14 +28,14 @@ const mqttConfig = {
|
|||||||
prefix: process.env.MQTT_PREFIX || 'homeassistant',
|
prefix: process.env.MQTT_PREFIX || 'homeassistant',
|
||||||
};
|
};
|
||||||
|
|
||||||
let loop;
|
let loop, commands, vehicles;
|
||||||
(async () => {
|
|
||||||
try {
|
const init = async () => {
|
||||||
const onStar = OnStar.create(onstarConfig);
|
commands = new Commands(OnStar.create(onstarConfig));
|
||||||
console.log('Requesting vehicles.');
|
console.log('Requesting vehicles.');
|
||||||
const vehiclesRes = await onStar.getAccountVehicles();
|
const vehiclesRes = await commands.getAccountVehicles();
|
||||||
console.log(_.get(vehiclesRes, 'status'));
|
console.log(_.get(vehiclesRes, 'status'));
|
||||||
const vehicles = _.map(
|
vehicles = _.map(
|
||||||
_.get(vehiclesRes, 'response.data.vehicles.vehicle'),
|
_.get(vehiclesRes, 'response.data.vehicles.vehicle'),
|
||||||
v => new Vehicle(v)
|
v => new Vehicle(v)
|
||||||
);
|
);
|
||||||
@ -40,7 +43,9 @@ let loop;
|
|||||||
for (const v of vehicles) {
|
for (const v of vehicles) {
|
||||||
console.log(v.toString());
|
console.log(v.toString());
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const connectMQTT = async () => {
|
||||||
const mqttHA = new MQTT(vehicles[0], 'homeassistant');
|
const mqttHA = new MQTT(vehicles[0], 'homeassistant');
|
||||||
const availTopic = mqttHA.getAvailabilityTopic();
|
const availTopic = mqttHA.getAvailabilityTopic();
|
||||||
const client = await mqtt.connectAsync(`${mqttConfig.tls
|
const client = await mqtt.connectAsync(`${mqttConfig.tls
|
||||||
@ -50,14 +55,36 @@ let loop;
|
|||||||
will: {topic: availTopic, payload: 'false', retain: true}
|
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});
|
await client.publish(availTopic, 'true', {retain: true});
|
||||||
|
return {mqttHA, client};
|
||||||
|
};
|
||||||
|
|
||||||
|
(async () => {
|
||||||
|
try {
|
||||||
|
await init();
|
||||||
|
|
||||||
|
const {mqttHA, client} = await connectMQTT();
|
||||||
|
|
||||||
const configurations = new Map();
|
const configurations = new Map();
|
||||||
const run = async () => {
|
const run = async () => {
|
||||||
const states = new Map();
|
const states = new Map();
|
||||||
const v = vehicles[0];
|
const v = vehicles[0];
|
||||||
console.log('Requesting diagnostics:')
|
console.log('Requesting diagnostics:')
|
||||||
const statsRes = await onStar.diagnostics({
|
const statsRes = await commands.diagnostics({
|
||||||
diagnosticItem: v.getSupported()
|
diagnosticItem: v.getSupported()
|
||||||
});
|
});
|
||||||
console.log(_.get(statsRes, 'status'));
|
console.log(_.get(statsRes, 'status'));
|
||||||
|
@ -74,6 +74,10 @@ class MQTT {
|
|||||||
return `${this.prefix}/${this.instance}/available`;
|
return `${this.prefix}/${this.instance}/available`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getCommandTopic() {
|
||||||
|
return `${this.prefix}/${this.instance}/command`;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* @param {DiagnosticElement} diag
|
* @param {DiagnosticElement} diag
|
||||||
|
@ -30,6 +30,15 @@ describe('MQTT', () => {
|
|||||||
|
|
||||||
describe('topics', () => {
|
describe('topics', () => {
|
||||||
let d;
|
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', () => {
|
describe('sensor', () => {
|
||||||
beforeEach(() => d = new Diagnostic(_.get(apiResponse, 'commandResponse.body.diagnosticResponse[0]')));
|
beforeEach(() => d = new Diagnostic(_.get(apiResponse, 'commandResponse.body.diagnosticResponse[0]')));
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user