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')
);
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() {
@ -24,6 +27,29 @@ class Diagnostic {
}
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) {
this._name = ele.name;
this.measurement = new Measurement(ele.value, ele.unit);
@ -41,6 +67,10 @@ class DiagnosticElement {
return this.measurement.unit;
}
get isConvertible() {
return this.measurement.isConvertible;
}
toString() {
return `${this.name}: ${this.measurement.toString()}`;
}

View File

@ -5,7 +5,7 @@ const _ = require('lodash');
const Vehicle = require('./vehicle');
const {Diagnostic} = require('./diagnostic');
const MQTT = require('./mqtt');
const {Commands} = require('./commands');
const Commands = require('./commands');
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 {
static CONVERTABLE_UNITS = [
'°C',
'km',
'kPa',
'km/l(e)'
];
constructor(value, unit) {
this.value = value;
this.unit = Measurement.correctUnitName(unit);
this.isConvertible = _.includes(Measurement.CONVERTABLE_UNITS, this.unit);
}
/**
@ -22,7 +31,7 @@ class Measurement {
case 'KPa':
return 'kPa';
case 'kmple':
return 'km/l(e)'; // TODO check on this
return 'km/l(e)';
case 'volts':
case 'Volts':
return 'V';
@ -36,16 +45,50 @@ class Measurement {
}
}
// TODO this may not be required. Check consuming application.
/*static convertToImperial(value, unit) {
switch(unit) {
case 'Cel':
const val = convert(value).from('C').to('F');
return new Measurement(val, 'F');
default:
return new Measurement(value, unit);
/**
*
* @param {string|number} value
* @param {string} unit
* @returns {string|number}
*/
static convertValue(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() {
return `${this.value}${this.unit}`;

View File

@ -191,17 +191,26 @@ class MQTT {
return this.mapSensorConfigPayload(diag, diagEl, 'voltage');
case 'HYBRID BATTERY MINIMUM TEMPERATURE':
case 'AMBIENT AIR TEMPERATURE':
case 'AMBIENT AIR TEMPERATURE F':
return this.mapSensorConfigPayload(diag, diagEl, 'temperature');
case 'EV BATTERY LEVEL':
return this.mapSensorConfigPayload(diag, diagEl, 'battery');
case 'TIRE PRESSURE LF':
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':
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':
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':
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
case 'EV PLUG STATE': // unplugged/plugged
return this.mapBinarySensorConfigPayload(diag, diagEl, 'plug');

View File

@ -1,7 +1,7 @@
const assert = require('assert');
const _ = require('lodash');
const { Diagnostic } = require('../src/diagnostic');
const { Diagnostic, DiagnosticElement } = require('../src/diagnostic');
const apiResponse = require('./diagnostic.sample.json');
describe('Diagnostics', () => {
@ -12,13 +12,13 @@ describe('Diagnostics', () => {
it('should parse a diagnostic response', () => {
assert.strictEqual(d.name, 'AMBIENT AIR TEMPERATURE');
assert.strictEqual(d.diagnosticElements.length, 1);
assert.strictEqual(d.diagnosticElements.length, 2);
});
it('should toString() correctly', () => {
const output = d.toString().trimEnd();
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:');
});
});
@ -28,15 +28,19 @@ describe('Diagnostics', () => {
it('should parse a diagnostic element', () => {
assert.strictEqual(d.name, 'TIRE PRESSURE');
assert.ok(_.isArray(d.diagnosticElements));
assert.strictEqual(d.diagnosticElements.length, 6);
assert.strictEqual(d.diagnosticElements.length, 12);
});
it('should toString() correctly', () => {
const output = d.toString().trimEnd();
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[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',
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', () => {
assert.deepStrictEqual(mqtt.getStatePayload(d), {
ambient_air_temperature: 15
ambient_air_temperature: 15,
ambient_air_temperature_f: 59
});
});
});