@smartcar/signals
v1.1.0
Published
TypeScript SDK for parsing Smartcar vehicle signals
Readme
Signal Parsing Functions - Usage Examples
This document demonstrates how to use all the signal parsing functions available in @smartcar/signals.
Available Functions
getSignalByCode<T>(signals, code)- Get a single signal by its codegetSignalsByCodes<T>(signals, codes)- Get multiple signals by their codesgetSignalGroup<T>(signals, signalMap)- Get a group of signals with custom naming
1. Basic Single Signal Retrieval
Example: Get Current Charging Rate
import {
getSignalByCode,
type Signals,
type ChargeAmperage
} from '@smartcar/signals';
function getCurrentChargingAmperage(signals: Signals) {
const amperageSignal: ChargeAmperage | undefined = getSignalByCode<ChargeAmperage>(signals, 'charge-amperage');
if (amperageSignal) {
if (amperageSignal.status.value === 'SUCCESS') {
return {
isCharging: amperageSignal.body.value > 0,
rate: `${amperageSignal.body.value} ${amperageSignal.body.unit}`,
timestamp: amperageSignal.meta.oemUpdatedAt,
status: amperageSignal.status
}
} else {
return {
isCharging: null,
rate: null,
timestamp: amperageSignal.meta.oemUpdatedAt,
status: amperageSignal.status
}
}
}
return {
isCharging: null,
rate: null,
timestamp: null,
status: 'MISSING'
};
}2. Multiple Signals by Codes
Example: Battery Status Dashboard
import {
getSignalsByCodes,
type Signals,
type ChargeIsCharging,
type ChargeAmperage,
type LowVoltageBatteryStateOfCharge,
type LowVoltageBatteryStatus,
type TractionBatteryStateOfCharge
} from '@smartcar/signals';
type BatterySignals = {
chargingStatus: ChargeIsCharging;
chargingAmperage: ChargeAmperage;
lowVoltageSOC: LowVoltageBatteryStateOfCharge;
lowVoltageStatus: LowVoltageBatteryStatus;
mainBatterySOC: TractionBatteryStateOfCharge;
};
function getBatteryStatus(signals: Signals) {
const batteryData = getSignalsByCodes<BatterySignals>(signals, [
'charge-ischarging',
'charge-amperage',
'lowvoltagebattery-stateofcharge',
'lowvoltagebattery-status',
'tractionbattery-stateofcharge'
]);
return {
// Charging information
isCharging: batteryData.chargingStatus?.body.value ?? false,
chargingRate: batteryData.chargingAmperage ?
`${batteryData.chargingAmperage.body.value} ${batteryData.chargingAmperage.body.unit}` : 'N/A',
// Low voltage battery (12V system)
lowVoltageBattery: {
stateOfCharge: batteryData.lowVoltageSOC?.body.value ?? null,
status: batteryData.lowVoltageStatus?.body.value ?? 'unknown',
unit: batteryData.lowVoltageSOC?.body.unit ?? ''
},
// Main traction battery (EV)
mainBattery: {
stateOfCharge: batteryData.mainBatterySOC?.body.value ?? null,
unit: batteryData.mainBatterySOC?.body.unit ?? '%'
},
// Overall health check
allSystemsHealthy: [
batteryData.charging?.status,
batteryData.lowVoltageSOC?.status,
batteryData.lowVoltageStatus?.status,
batteryData.mainBatterySOC?.status
].every(status => status === 'success')
};
}Example: Vehicle Diagnostics Summary
import {
getSignalsByCodes,
type Signals,
type DiagnosticsEVBatteryConditioning,
type DiagnosticsEVHVBattery,
type DiagnosticsOilLife,
type DiagnosticsTirePressure,
type DiagnosticsMIL
} from '@smartcar/signals';
type DiagnosticSignals = {
batteryConditioning: DiagnosticsEVBatteryConditioning;
hvBattery: DiagnosticsEVHVBattery;
oilLife: DiagnosticsOilLife;
tirePressure: DiagnosticsTirePressure;
checkEngine: DiagnosticsMIL;
};
function getVehicleDiagnostics(signals: Signals) {
const diagnostics = getSignalsByCodes<DiagnosticSignals>(signals, [
'batteryConditioning',
'hvBattery',
'oilLife',
'tirePressure',
'checkEngine'
]);
const issues = [];
const warnings = [];
// Check each diagnostic signal
if (diagnostics.batteryConditioning?.body.value !== 'normal') {
issues.push('Battery conditioning system needs attention');
}
if (diagnostics.hvBattery?.body.value !== 'normal') {
issues.push('High voltage battery system alert');
}
if (diagnostics.oilLife && diagnostics.oilLife.body.value < 20) {
warnings.push(`Oil life low: ${diagnostics.oilLife.body.value}%`);
}
if (diagnostics.tirePressure?.body.value !== 'normal') {
warnings.push('Tire pressure monitoring alert');
}
if (diagnostics.checkEngine?.body.value === true) {
issues.push('Check engine light is on');
}
return {
overallHealth: issues.length === 0 ? 'good' : 'needs_attention',
criticalIssues: issues,
warnings: warnings,
lastChecked: Math.max(
...Object.values(diagnostics)
.filter(signal => signal)
.map(signal => signal!.meta.retrievedAt)
)
};
}3. Signal Groups with Custom Naming
Example: Driver Dashboard
import {
getSignalGroup,
type Signals,
type MotionCurrentSpeed,
type OdometerTraveledDistance,
type InternalCombustionEngineFuelLevel,
type TractionBatteryStateOfCharge,
type LocationPreciseLocation
} from '@smartcar/signals';
type DashboardSignals = {
speed: MotionCurrentSpeed;
odometer: OdometerTraveledDistance;
fuelLevel: InternalCombustionEngineFuelLevel;
batteryLevel: TractionBatteryStateOfCharge;
location: LocationPreciseLocation;
};
function createDriverDashboard(signals: Signals) {
// Map friendly names to actual signal codes
const dashboard = getSignalGroup<DashboardSignals>(signals, {
speed: 'motion-currentspeed',
odometer: 'odometer-traveleddistance',
fuelLevel: 'internalcombustionengine-fuellevel',
batteryLevel: 'tractionbattery-stateofcharge',
location: 'location-preciselocation'
});
return {
// Speed information
currentSpeed: dashboard.speed ? {
value: dashboard.speed.body.value,
unit: dashboard.speed.body.unit,
display: `${dashboard.speed.body.value} ${dashboard.speed.body.unit}`
} : null,
// Distance traveled
totalDistance: dashboard.odometer ? {
value: dashboard.odometer.body.value,
unit: dashboard.odometer.body.unit,
display: `${dashboard.odometer.body.value.toLocaleString()} ${dashboard.odometer.body.unit}`
} : null,
// Energy levels (fuel or battery)
energyLevel: {
fuel: dashboard.fuelLevel ? {
percentage: dashboard.fuelLevel.body.value,
unit: dashboard.fuelLevel.body.unit
} : null,
battery: dashboard.batteryLevel ? {
percentage: dashboard.batteryLevel.body.value,
unit: dashboard.batteryLevel.body.unit
} : null
},
// Current position
position: dashboard.location ? {
latitude: dashboard.location.body.latitude,
longitude: dashboard.location.body.longitude,
heading: dashboard.location.body.heading,
coordinates: `${dashboard.location.body.latitude}, ${dashboard.location.body.longitude}`
} : null,
// Data freshness
lastUpdated: Math.max(
dashboard.speed?.meta.retrievedAt ?? 0,
dashboard.odometer?.meta.retrievedAt ?? 0,
dashboard.fuelLevel?.meta.retrievedAt ?? 0,
dashboard.batteryLevel?.meta.retrievedAt ?? 0,
dashboard.location?.meta.retrievedAt ?? 0
)
};
}Example: Climate Control Status
import {
getSignalGroup,
type Signals,
type ClimateInternalTemperature,
type ClimateExternalTemperature,
type HVACCabinTargetTemperature,
type HVACIsCabinHVACActive,
type HVACIsFrontDefrosterActive,
type HVACIsRearDefrosterActive
} from '@smartcar/signals';
type ClimateSignals = {
insideTemp: ClimateInternalTemperature;
outsideTemp: ClimateExternalTemperature;
targetTemp: HVACCabinTargetTemperature;
hvacActive: HVACIsCabinHVACActive;
frontDefroster: HVACIsFrontDefrosterActive;
rearDefroster: HVACIsRearDefrosterActive;
};
function getClimateStatus(signals: Signals) {
const climate = getSignalGroup<ClimateSignals>(signals, {
insideTemp: 'climate-internaltemperature',
outsideTemp: 'climate-externaltemperature',
targetTemp: 'hvac-cabintargettemperature',
hvacActive: 'hvac-iscabinhvacactive',
frontDefroster: 'hvac-isfrontdefrosteractive',
rearDefroster: 'hvac-isreardefrosteractive'
});
return {
temperatures: {
inside: climate.insideTemp ? {
value: climate.insideTemp.body.value,
unit: climate.insideTemp.body.unit
} : null,
outside: climate.outsideTemp ? {
value: climate.outsideTemp.body.value,
unit: climate.outsideTemp.body.unit
} : null,
target: climate.targetTemp ? {
value: climate.targetTemp.body.value,
unit: climate.targetTemp.body.unit
} : null
},
systems: {
hvacRunning: climate.hvacActive?.body.value ?? false,
frontDefrosterOn: climate.frontDefroster?.body.value ?? false,
rearDefrosterOn: climate.rearDefroster?.body.value ?? false
},
// Calculate temperature difference
temperatureDelta: (climate.insideTemp && climate.outsideTemp) ?
climate.insideTemp.body.value - climate.outsideTemp.body.value : null,
// System status
systemsActive: [
climate.hvacActive?.body.value,
climate.frontDefroster?.body.value,
climate.rearDefroster?.body.value
].some(Boolean)
};
}4. Error Handling and Validation
Example: Robust Signal Processing
import {
getSignalsByCodes,
type Signals,
type MotionCurrentSpeed,
type TractionBatteryStateOfCharge
} from '@smartcar/signals';
function processSignalsWithValidation(signals: Signals) {
type CriticalSignals = {
speed: MotionCurrentSpeed;
battery: TractionBatteryStateOfCharge;
};
const data = getSignalsByCodes<CriticalSignals>(signals, ['speed', 'battery']);
// Validate signal availability
const validation = {
hasSpeed: data.speed !== undefined,
hasBattery: data.battery !== undefined,
speedStatus: data.speed?.status ?? 'missing',
batteryStatus: data.battery?.status ?? 'missing'
};
// Check for critical missing signals
const missingCritical = [];
if (!validation.hasSpeed) missingCritical.push('speed');
if (!validation.hasBattery) missingCritical.push('battery');
if (missingCritical.length > 0) {
throw new Error(`Missing critical signals: ${missingCritical.join(', ')}`);
}
// Check for stale data (older than 5 minutes)
const fiveMinutesAgo = Date.now() / 1000 - 300;
const staleSignals = [];
if (data.speed!.meta.retrievedAt < fiveMinutesAgo) {
staleSignals.push('speed');
}
if (data.battery!.meta.retrievedAt < fiveMinutesAgo) {
staleSignals.push('battery');
}
return {
data: {
speed: data.speed!.body.value,
speedUnit: data.speed!.body.unit,
batteryLevel: data.battery!.body.value,
batteryUnit: data.battery!.body.unit
},
validation: {
...validation,
hasStaleData: staleSignals.length > 0,
staleSignals,
dataQuality: staleSignals.length === 0 ? 'fresh' : 'stale'
},
timestamps: {
speed: data.speed!.meta.retrievedAt,
battery: data.battery!.meta.retrievedAt,
oldestData: Math.min(data.speed!.meta.retrievedAt, data.battery!.meta.retrievedAt)
}
};
}Usage Summary
getSignalByCode- Use when you need one specific signalgetSignalsByCodes- Use when you need multiple related signalsgetSignalGroup- Use when you want custom naming for your signals- Always handle undefined - Signals may not be available
- Check signal status - Verify data quality with
signal.status - Use timestamps - Check
signal.meta.retrievedAtfor data freshness
