Support conversions to imperial.

This commit is contained in:
Michael Woods 2021-06-11 10:15:14 -04:00
parent 8c9747d711
commit bd331a477c
6 changed files with 125 additions and 18 deletions

View File

@ -10,6 +10,9 @@ class Diagnostic {
d => _.has(d, 'value') && _.has(d, 'unit') d => _.has(d, 'value') && _.has(d, 'unit')
); );
this.diagnosticElements = _.map(validEle, e => new DiagnosticElement(e)); this.diagnosticElements = _.map(validEle, e => new DiagnosticElement(e));
const converted = _.map(_.filter(this.diagnosticElements, e => e.isConvertible),
e => DiagnosticElement.convert(e));
this.diagnosticElements.push(... converted);
} }
hasElements() { hasElements() {
@ -24,6 +27,29 @@ class Diagnostic {
} }
class DiagnosticElement { class DiagnosticElement {
/**
*
* @param {DiagnosticElement} element
*/
static convert(element) {
const {name, unit, value} = element;
const convertedUnit = Measurement.convertUnit(unit);
return new DiagnosticElement({
name: DiagnosticElement.convertName(name, convertedUnit),
unit: convertedUnit,
value: Measurement.convertValue(value, unit)
})
}
static convertName(name, unit) {
return `${name} ${_.replace(_.toUpper(unit), /\W/g, '')}`;
}
/**
* @param {string} ele.name
* @param {string|number} ele.value
* @param {string} ele.unit
*/
constructor(ele) { constructor(ele) {
this._name = ele.name; this._name = ele.name;
this.measurement = new Measurement(ele.value, ele.unit); this.measurement = new Measurement(ele.value, ele.unit);
@ -41,6 +67,10 @@ class DiagnosticElement {
return this.measurement.unit; return this.measurement.unit;
} }
get isConvertible() {
return this.measurement.isConvertible;
}
toString() { toString() {
return `${this.name}: ${this.measurement.toString()}`; return `${this.name}: ${this.measurement.toString()}`;
} }

View File

@ -5,7 +5,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 Commands = require('./commands');
const logger = require('./logger'); const logger = require('./logger');

View File

@ -1,9 +1,18 @@
// const convert = require('convert-units'); const _ = require('lodash');
const convert = require('convert-units');
class Measurement { class Measurement {
static CONVERTABLE_UNITS = [
'°C',
'km',
'kPa',
'km/l(e)'
];
constructor(value, unit) { constructor(value, unit) {
this.value = value; this.value = value;
this.unit = Measurement.correctUnitName(unit); this.unit = Measurement.correctUnitName(unit);
this.isConvertible = _.includes(Measurement.CONVERTABLE_UNITS, this.unit);
} }
/** /**
@ -22,7 +31,7 @@ class Measurement {
case 'KPa': case 'KPa':
return 'kPa'; return 'kPa';
case 'kmple': case 'kmple':
return 'km/l(e)'; // TODO check on this return 'km/l(e)';
case 'volts': case 'volts':
case 'Volts': case 'Volts':
return 'V'; return 'V';
@ -36,16 +45,50 @@ class Measurement {
} }
} }
// TODO this may not be required. Check consuming application. /**
/*static convertToImperial(value, unit) { *
switch(unit) { * @param {string|number} value
case 'Cel': * @param {string} unit
const val = convert(value).from('C').to('F'); * @returns {string|number}
return new Measurement(val, 'F'); */
default: static convertValue(value, unit) {
return new Measurement(value, unit); switch (unit) {
case '°C':
value = convert(value).from('C').to('F');
break;
case 'km':
value = convert(value).from('km').to('mi');
break;
case 'kPa':
value = convert(value).from('kPa').to('psi');
break;
case 'km/l(e)':
// km/L = (1.609344 / 3.785411784) * MPG
value = value / (1.609344 / 3.785411784);
break;
} }
}*/ return value;
}
/**
*
* @param {string} unit
* @returns {string}
*/
static convertUnit(unit) {
switch (unit) {
case '°C':
return '°F';
case 'km':
return 'mi';
case 'kPa':
return 'psi';
case 'km/l(e)':
return 'mpg(e)';
default:
return unit;
}
}
toString() { toString() {
return `${this.value}${this.unit}`; return `${this.value}${this.unit}`;

View File

@ -191,17 +191,26 @@ class MQTT {
return this.mapSensorConfigPayload(diag, diagEl, 'voltage'); return this.mapSensorConfigPayload(diag, diagEl, 'voltage');
case 'HYBRID BATTERY MINIMUM TEMPERATURE': case 'HYBRID BATTERY MINIMUM TEMPERATURE':
case 'AMBIENT AIR TEMPERATURE': case 'AMBIENT AIR TEMPERATURE':
case 'AMBIENT AIR TEMPERATURE F':
return this.mapSensorConfigPayload(diag, diagEl, 'temperature'); return this.mapSensorConfigPayload(diag, diagEl, 'temperature');
case 'EV BATTERY LEVEL': case 'EV BATTERY LEVEL':
return this.mapSensorConfigPayload(diag, diagEl, 'battery'); return this.mapSensorConfigPayload(diag, diagEl, 'battery');
case 'TIRE PRESSURE LF': case 'TIRE PRESSURE LF':
return this.mapSensorConfigPayload(diag, diagEl, 'pressure', 'Tire Pressure: Left Front', `{{ {'recommendation': value_json.${MQTT.convertName('TIRE_PRESSURE_PLACARD_FRONT')}} | tojson }}`); return this.mapSensorConfigPayload(diag, diagEl, 'pressure', 'Tire Pressure: Left Front', `{{ {'recommendation': value_json.${MQTT.convertName('TIRE_PRESSURE_PLACARD_FRONT')}} | tojson }}`);
case 'TIRE PRESSURE LF PSI':
return this.mapSensorConfigPayload(diag, diagEl, 'pressure', 'Tire Pressure: Left Front PSI', `{{ {'recommendation': value_json.${MQTT.convertName('TIRE_PRESSURE_PLACARD_FRONT')}} | tojson }}`);
case 'TIRE PRESSURE LR': case 'TIRE PRESSURE LR':
return this.mapSensorConfigPayload(diag, diagEl, 'pressure', 'Tire Pressure: Left Rear', `{{ {'recommendation': value_json.${MQTT.convertName('TIRE_PRESSURE_PLACARD_REAR')}} | tojson }}`); return this.mapSensorConfigPayload(diag, diagEl, 'pressure', 'Tire Pressure: Left Rear', `{{ {'recommendation': value_json.${MQTT.convertName('TIRE_PRESSURE_PLACARD_REAR')}} | tojson }}`);
case 'TIRE PRESSURE LR PSI':
return this.mapSensorConfigPayload(diag, diagEl, 'pressure', 'Tire Pressure: Left Rear PSI', `{{ {'recommendation': value_json.${MQTT.convertName('TIRE_PRESSURE_PLACARD_REAR')}} | tojson }}`);
case 'TIRE PRESSURE RF': case 'TIRE PRESSURE RF':
return this.mapSensorConfigPayload(diag, diagEl, 'pressure', 'Tire Pressure: Right Front', `{{ {'recommendation': value_json.${MQTT.convertName('TIRE_PRESSURE_PLACARD_FRONT')}} | tojson }}`); return this.mapSensorConfigPayload(diag, diagEl, 'pressure', 'Tire Pressure: Right Front', `{{ {'recommendation': value_json.${MQTT.convertName('TIRE_PRESSURE_PLACARD_FRONT')}} | tojson }}`);
case 'TIRE PRESSURE RF PSI':
return this.mapSensorConfigPayload(diag, diagEl, 'pressure', 'Tire Pressure: Right Front PSI', `{{ {'recommendation': value_json.${MQTT.convertName('TIRE_PRESSURE_PLACARD_FRONT')}} | tojson }}`);
case 'TIRE PRESSURE RR': case 'TIRE PRESSURE RR':
return this.mapSensorConfigPayload(diag, diagEl, 'pressure', 'Tire Pressure: Right Rear', `{{ {'recommendation': value_json.${MQTT.convertName('TIRE_PRESSURE_PLACARD_REAR')}} | tojson }}`); return this.mapSensorConfigPayload(diag, diagEl, 'pressure', 'Tire Pressure: Right Rear', `{{ {'recommendation': value_json.${MQTT.convertName('TIRE_PRESSURE_PLACARD_REAR')}} | tojson }}`);
case 'TIRE PRESSURE RR PSI':
return this.mapSensorConfigPayload(diag, diagEl, 'pressure', 'Tire Pressure: Right Rear PSI', `{{ {'recommendation': value_json.${MQTT.convertName('TIRE_PRESSURE_PLACARD_REAR')}} | tojson }}`);
// binary sensor // binary sensor
case 'EV PLUG STATE': // unplugged/plugged case 'EV PLUG STATE': // unplugged/plugged
return this.mapBinarySensorConfigPayload(diag, diagEl, 'plug'); return this.mapBinarySensorConfigPayload(diag, diagEl, 'plug');

View File

@ -1,7 +1,7 @@
const assert = require('assert'); const assert = require('assert');
const _ = require('lodash'); const _ = require('lodash');
const { Diagnostic } = require('../src/diagnostic'); const { Diagnostic, DiagnosticElement } = require('../src/diagnostic');
const apiResponse = require('./diagnostic.sample.json'); const apiResponse = require('./diagnostic.sample.json');
describe('Diagnostics', () => { describe('Diagnostics', () => {
@ -12,13 +12,13 @@ describe('Diagnostics', () => {
it('should parse a diagnostic response', () => { it('should parse a diagnostic response', () => {
assert.strictEqual(d.name, 'AMBIENT AIR TEMPERATURE'); assert.strictEqual(d.name, 'AMBIENT AIR TEMPERATURE');
assert.strictEqual(d.diagnosticElements.length, 1); assert.strictEqual(d.diagnosticElements.length, 2);
}); });
it('should toString() correctly', () => { it('should toString() correctly', () => {
const output = d.toString().trimEnd(); const output = d.toString().trimEnd();
const lines = output.split(/\r\n|\r|\n/); const lines = output.split(/\r\n|\r|\n/);
assert.strictEqual(lines.length, 2); assert.strictEqual(lines.length, 3);
assert.strictEqual(lines[0], 'AMBIENT AIR TEMPERATURE:'); assert.strictEqual(lines[0], 'AMBIENT AIR TEMPERATURE:');
}); });
}); });
@ -28,15 +28,19 @@ describe('Diagnostics', () => {
it('should parse a diagnostic element', () => { it('should parse a diagnostic element', () => {
assert.strictEqual(d.name, 'TIRE PRESSURE'); assert.strictEqual(d.name, 'TIRE PRESSURE');
assert.ok(_.isArray(d.diagnosticElements)); assert.ok(_.isArray(d.diagnosticElements));
assert.strictEqual(d.diagnosticElements.length, 6); assert.strictEqual(d.diagnosticElements.length, 12);
}); });
it('should toString() correctly', () => { it('should toString() correctly', () => {
const output = d.toString().trimEnd(); const output = d.toString().trimEnd();
const lines = output.split(/\r\n|\r|\n/); const lines = output.split(/\r\n|\r|\n/);
assert.strictEqual(lines.length, 7); assert.strictEqual(lines.length, 13);
assert.strictEqual(lines[0], 'TIRE PRESSURE:'); assert.strictEqual(lines[0], 'TIRE PRESSURE:');
assert.strictEqual(lines[1], ' TIRE PRESSURE LF: 240.0kPa'); assert.strictEqual(lines[1], ' TIRE PRESSURE LF: 240.0kPa');
}); });
it('should strip non-alpha chars', () => {
assert.strictEqual(DiagnosticElement.convertName('TEMP', '°F'), 'TEMP F');
});
}); });
}); });

View File

@ -86,10 +86,31 @@ describe('MQTT', () => {
unit_of_measurement: '°C', unit_of_measurement: '°C',
value_template: '{{ value_json.ambient_air_temperature }}' value_template: '{{ value_json.ambient_air_temperature }}'
}); });
assert.deepStrictEqual(mqtt.getConfigPayload(d, d.diagnosticElements[1]), {
availability_topic: 'homeassistant/XXX/available',
device: {
identifiers: [
'XXX'
],
manufacturer: 'foo',
model: 2020,
name: '2020 foo bar'
},
device_class: 'temperature',
json_attributes_template: undefined,
name: 'Ambient Air Temperature F',
payload_available: 'true',
payload_not_available: 'false',
state_topic: 'homeassistant/sensor/XXX/ambient_air_temperature/state',
json_attributes_topic: undefined,
unit_of_measurement: '°F',
value_template: '{{ value_json.ambient_air_temperature_f }}'
});
}); });
it('should generate state payloads', () => { it('should generate state payloads', () => {
assert.deepStrictEqual(mqtt.getStatePayload(d), { assert.deepStrictEqual(mqtt.getStatePayload(d), {
ambient_air_temperature: 15 ambient_air_temperature: 15,
ambient_air_temperature_f: 59
}); });
}); });
}); });