### Install and Run Webhook Example (Bash) Source: https://github.com/smartcar/typescript-backend-sdks/blob/master/packages/example/README.md Commands to install dependencies, set up environment variables, and run the development server or build and start the application. Requires Node.js and npm. ```bash npm install cp .env.example .env # Edit .env with your webhook secret npm run dev npm run build npm start ``` -------------------------------- ### Setup Smartcar TypeScript Backend SDKs Project Source: https://github.com/smartcar/typescript-backend-sdks/blob/master/README.md Provides commands for setting up the Smartcar TypeScript Backend SDKs project, including cloning the repository, installing dependencies, generating types, and building packages. ```bash # Clone the repository git clone https://github.com/smartcar/typescript-backend-sdks.git cd typescript-backend-sdks # Install dependencies npm install # Generate types from OpenAPI specs npm run gen # Build all packages npm run build ``` -------------------------------- ### Install Smartcar SDKs Source: https://github.com/smartcar/typescript-backend-sdks/blob/master/README.md Installs the necessary Smartcar webhooks and signals SDKs using npm. ```bash npm install @smartcar/webhooks @smartcar/signals ``` -------------------------------- ### Contributor Quick Start: Testing and Release Source: https://github.com/smartcar/typescript-backend-sdks/blob/master/README.md Commands for contributors to run the test suite and perform a dry run of the release process. These steps are essential for validating changes and ensuring the release process functions correctly before actual deployment. ```bash npm test npm run release:dry-run ``` -------------------------------- ### Get Climate Control Status with Custom Signal Names (TypeScript) Source: https://github.com/smartcar/typescript-backend-sdks/blob/master/packages/signals/README.md This TypeScript example shows how to utilize `getSignalGroup` to consolidate climate control related signals under custom names like 'insideTemp' and 'hvacActive'. It processes signals to provide structured data on current temperatures, target temperatures, and HVAC system status. The function also calculates the temperature difference between inside and outside and determines if any climate systems are active. ```typescript 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(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) }; } ``` -------------------------------- ### Conventional Commits Example (Bash) Source: https://github.com/smartcar/typescript-backend-sdks/blob/master/CONTRIBUTING.md Demonstrates the format for Conventional Commits, including type, scope, description, and optional body/footers. This convention is used for automated release generation and changelog creation. ```bash # Feature for signals package feat(signals): add new signal parsing function # Bug fix for webhooks package fix(webhooks): resolve signature validation issue # Breaking change (note the !) feat(signals)!: change signal code format # Documentation update docs: update README with new examples # Chore (no release) chore: update dependencies ``` -------------------------------- ### Install @smartcar/webhooks SDK Source: https://github.com/smartcar/typescript-backend-sdks/blob/master/packages/webhooks/README.md Installs the @smartcar/webhooks SDK using npm. This is the first step to using the SDK in your TypeScript backend project. ```bash npm install @smartcar/webhooks ``` -------------------------------- ### Contributor Quick Start: Git Workflow Source: https://github.com/smartcar/typescript-backend-sdks/blob/master/README.md A sequence of git commands for contributors to fork the repository, create a feature branch, make changes, add tests, commit using Conventional Commits, and push to their fork. This outlines the standard development workflow for contributing to the project. ```bash git checkout -b feature/my-feature git commit -am 'feat(signals): add new feature' git push origin feature/my-feature ``` -------------------------------- ### Complete Express.js Server for Webhook Handling Source: https://context7.com/smartcar/typescript-backend-sdks/llms.txt A full Express.js server implementation that demonstrates how to handle Smartcar webhooks. It includes signature verification, challenge response for verification events, and processing of vehicle state and error events. This example requires the `@smartcar/webhooks` and `@smartcar/signals` packages. ```typescript import "dotenv/config"; import express, { Request, Response } from "express"; import { verifySignature, hashChallenge, parseEnvelope, type WebhookDataPayload } from "@smartcar/webhooks"; import { getSignalByCode, type ChargeAmperage, type TractionBatteryStateOfCharge, type Signals } from "@smartcar/signals"; const app = express(); const PORT = Number(process.env.PORT || 3000); const WEBHOOK_SECRET = process.env.WEBHOOK_SECRET!; // Health check endpoint app.get("/health", (_req: Request, res: Response) => { res.json({ ok: true }); }); // Webhook endpoint - use raw body parser for signature verification app.post("/webhooks", express.raw({ type: "application/json" }), (req: Request, res: Response) => { const signature = req.headers["sc-signature"] as string; // Step 1: Verify webhook signature const verifyResult = verifySignature(req.body, signature, WEBHOOK_SECRET); if (!verifyResult.ok) { console.error("Webhook signature verification failed:", verifyResult.reason); return res.status(401).json({ error: "Invalid signature" }); } // Step 2: Parse the webhook payload let payload: WebhookDataPayload; try { payload = parseEnvelope(req.body); } catch (error) { return res.status(400).json({ error: "Invalid payload" }); } // Step 3: Handle verification challenge if (payload.eventType === "VERIFY") { const challenge = payload.data.challenge; if (typeof challenge !== "string") { return res.status(400).json({ error: "Missing challenge" }); } const hash = hashChallenge(WEBHOOK_SECRET, challenge); return res.json({ challenge: hash }); } // Step 4: Handle vehicle state updates if (payload.eventType === "VEHICLE_STATE") { const signals = payload.data.signals as Signals | undefined; if (signals) { // Extract specific signals with type safety const amperage = getSignalByCode(signals, "charge-amperage"); const battery = getSignalByCode(signals, "tractionbattery-stateofcharge"); if (amperage?.status.value === "SUCCESS") { console.log(`Vehicle ${payload.data.vehicle?.id} charging at ${amperage.body.value}${amperage.body.unit}`); } if (battery?.status.value === "SUCCESS") { console.log(`Battery level: ${battery.body.value}%`); } } } // Step 5: Handle vehicle errors if (payload.eventType === "VEHICLE_ERROR") { console.error("Vehicle error received:", payload.data.errors); } res.status(204).end(); }); app.listen(PORT, () => { console.log(`Smartcar webhook server listening on port ${PORT}`); }); ``` -------------------------------- ### Process Signals with Validation (TypeScript) Source: https://github.com/smartcar/typescript-backend-sdks/blob/master/packages/signals/README.md This example demonstrates how to process signals using `getSignalsByCodes` from the Smartcar SDK. It includes validation for critical signal availability and checks for data staleness based on timestamps. The function returns processed data, validation status, and timestamps. ```typescript import { getSignalsByCodes, type Signals, type MotionCurrentSpeed, type TractionBatteryStateOfCharge } from '@smartcar/signals'; function processSignalsWithValidation(signals: Signals) { type CriticalSignals = { speed: MotionCurrentSpeed; battery: TractionBatteryStateOfCharge; }; const data = getSignalsByCodes(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) } }; } ``` -------------------------------- ### Create Driver Dashboard Signals with Custom Names (TypeScript) Source: https://github.com/smartcar/typescript-backend-sdks/blob/master/packages/signals/README.md This example demonstrates how to use `getSignalGroup` to create a custom signal group for a driver dashboard. It maps friendly names like 'speed' and 'odometer' to their corresponding Smartcar signal codes. The function processes raw signals and returns structured data for display, including speed, distance, energy levels, and location. It also calculates the last updated timestamp for all dashboard-related signals. ```typescript 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(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 ) }; } ``` -------------------------------- ### Integrate @smartcar/webhooks with @smartcar/signals in TypeScript Source: https://github.com/smartcar/typescript-backend-sdks/blob/master/packages/webhooks/README.md Illustrates how to combine the @smartcar/webhooks SDK with the @smartcar/signals package for type-safe processing of vehicle signals received via webhooks. It includes an example of retrieving a specific signal like 'tractionbattery-stateofcharge'. ```typescript import { getSignalByCode } from '@smartcar/signals'; import type { TractionBatteryStateOfCharge } from '@smartcar/signals'; // Note: Webhook payload signals may need transformation // to match the signal types structure const batterySignal = getSignalByCode( transformedSignals, 'tractionbattery-stateofcharge' ); ``` -------------------------------- ### Get Multiple Signals by Codes in TypeScript Source: https://github.com/smartcar/typescript-backend-sdks/blob/master/packages/signals/README.md Retrieves multiple signals from a collection using an array of their codes. This is efficient for gathering related data points, like all battery-related signals for a dashboard. It aggregates data and provides a consolidated view, handling missing signals gracefully. ```typescript 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(signals, [ 'charge-ischarging', 'charge-amperage', 'lowvoltagebattery-stateofcharge', 'lowvoltagebattery-status', 'tractionbattery-stateofcharge' ]); return { isCharging: batteryData.chargingStatus?.body.value ?? false, chargingRate: batteryData.chargingAmperage ? `${batteryData.chargingAmperage.body.value} ${batteryData.chargingAmperage.body.unit}` : 'N/A', lowVoltageBattery: { stateOfCharge: batteryData.lowVoltageSOC?.body.value ?? null, status: batteryData.lowVoltageStatus?.body.value ?? 'unknown', unit: batteryData.lowVoltageSOC?.body.unit ?? '' }, mainBattery: { stateOfCharge: batteryData.mainBatterySOC?.body.value ?? null, unit: batteryData.mainBatterySOC?.body.unit ?? '%' }, allSystemsHealthy: [ batteryData.charging?.status, batteryData.lowVoltageSOC?.status, batteryData.lowVoltageStatus?.status, batteryData.mainBatterySOC?.status ].every(status => status === 'success') }; } ``` ```typescript 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(signals, [ 'batteryConditioning', 'hvBattery', 'oilLife', 'tirePressure', 'checkEngine' ]); const issues = []; const warnings = []; 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) ) }; } ``` -------------------------------- ### Get Single Signal by Code in TypeScript Source: https://github.com/smartcar/typescript-backend-sdks/blob/master/packages/signals/README.md Retrieves a single signal from a collection of signals using its unique code. This function is useful for accessing specific pieces of vehicle data, such as charging amperage. It handles cases where the signal might be missing or its status is not 'SUCCESS'. ```typescript import { getSignalByCode, type Signals, type ChargeAmperage } from '@smartcar/signals'; function getCurrentChargingAmperage(signals: Signals) { const amperageSignal: ChargeAmperage | undefined = getSignalByCode(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' }; } ``` -------------------------------- ### Run Test Suite using npm Source: https://github.com/smartcar/typescript-backend-sdks/blob/master/README.md This command executes the project's test suite using npm. It's a crucial step for contributors to ensure their changes do not introduce regressions and adhere to the project's quality standards. ```bash npm test ``` -------------------------------- ### Run Package-Specific Build Command Source: https://github.com/smartcar/typescript-backend-sdks/blob/master/README.md Builds a specific package within the Smartcar TypeScript Backend SDKs monorepo, such as '@smartcar/signals' or '@smartcar/webhooks'. ```bash # In packages/signals or packages/webhooks npm run build # Build the package ``` -------------------------------- ### Handle Smartcar Webhooks and Signals in TypeScript Source: https://github.com/smartcar/typescript-backend-sdks/blob/master/README.md Demonstrates basic usage of Smartcar's webhooks and signals SDKs in a TypeScript Express application. It includes parsing webhook payloads, verifying signatures, handling verification challenges, and extracting typed vehicle signals. ```typescript import express from "express"; import { verifySignature, hashChallenge, parseEnvelope } from "@smartcar/webhooks"; import { getSignalByCode, type ChargeAmperage } from "@smartcar/signals"; const app = express(); const WEBHOOK_SECRET = process.env.WEBHOOK_SECRET!; app.post("/webhooks", (req, res) => { // Parse the webhook payload const payload = parseEnvelope(req.body); // Handle webhook verification challenge if (payload.eventType === "VERIFY") { const hash = hashChallenge(WEBHOOK_SECRET, payload.data.challenge); return res.json({ challenge: hash }); } // Handle vehicle state updates if (payload.eventType === "VEHICLE_STATE") { const signals = payload.data.signals; // Extract specific signal with type safety const amperage = getSignalByCode(signals, 'charge-amperage'); if (amperage?.status.value === "SUCCESS") { console.log(`Charge amperage: ${amperage.body.value} ${amperage.body.unit}`); } } res.status(200).end(); }); ``` -------------------------------- ### TypeScript Type Safety for Signals and Webhooks Source: https://github.com/smartcar/typescript-backend-sdks/blob/master/README.md Demonstrates how the SDK provides strong typing for vehicle signals and webhook payloads. It shows how to access strongly typed signal data and parse webhook data with proper typing, ensuring data integrity and developer productivity. ```typescript import { ChargeAmperage, getSignalByCode, WebhookDataPayload, parseEnvelope } from '@smartcar/types'; // Signals are strongly typed const amperage: ChargeAmperage | undefined = getSignalByCode( signals, 'charge-amperage' ); // Webhook payloads have proper typing const payload: WebhookDataPayload = parseEnvelope(buffer); ``` -------------------------------- ### Clean Build Artifacts Source: https://github.com/smartcar/typescript-backend-sdks/blob/master/README.md Removes generated files and build artifacts from the Smartcar TypeScript Backend SDKs project or individual packages. ```bash # Clean all packages npm run clean # Clean a specific package # In packages/signals or packages/webhooks npm run clean ``` -------------------------------- ### Smartcar Signal Type Categories in TypeScript Source: https://context7.com/smartcar/typescript-backend-sdks/llms.txt Demonstrates the import statements for various signal type categories provided by the `@smartcar/signals` package. These categories help in organizing and accessing different types of vehicle data with type safety. ```typescript // Charging signals import type { ChargeAmperage, ChargeIsCharging, ChargeVoltage, ChargeWattage, ChargeTimeToComplete, ChargeEnergyAdded, ChargeIsChargingCableConnected } from "@smartcar/signals"; // Battery signals import type { TractionBatteryStateOfCharge, TractionBatteryEstimatedRange, TractionBatteryRange, LowVoltageBatteryStateOfCharge, LowVoltageBatteryStatus } from "@smartcar/signals"; // Location signals import type { LocationPreciseLocation, LocationIsAtHome } from "@smartcar/signals"; // Climate & HVAC signals import type { ClimateInternalTemperature, ClimateExternalTemperature, HVACCabinTargetTemperature, HVACIsCabinHVACActive, HVACIsFrontDefrosterActive, HVACIsRearDefrosterActive } from "@smartcar/signals"; // Vehicle identification import type { VehicleIdentificationVIN, VehicleIdentificationBrand, VehicleIdentificationModel, VehicleIdentificationYear } from "@smartcar/signals"; // Diagnostics signals import type { DiagnosticsMIL, DiagnosticsDTCList, DiagnosticsTirePressure, DiagnosticsOilLife, DiagnosticsEVHVBattery } from "@smartcar/signals"; // Motion & odometer import type { MotionCurrentSpeed, OdometerTraveledDistance } from "@smartcar/signals"; // Closures & security import type { ClosureDoors, ClosureWindows, ClosureIsLocked, ClosureFrontTrunk, ClosureRearTrunk } from "@smartcar/signals"; ``` -------------------------------- ### Parse and Verify Webhook Payload in TypeScript Source: https://github.com/smartcar/typescript-backend-sdks/blob/master/packages/webhooks/README.md Demonstrates how to set up an Express.js webhook handler to parse and verify incoming Smartcar webhook payloads. It emphasizes security by verifying the signature before parsing the payload and handling different event types. ```typescript import { parseEnvelope, verifySignature, hashChallenge } from '@smartcar/webhooks'; import type { WebhookDataPayload } from '@smartcar/webhooks'; // Express.js webhook handler example app.post('/webhooks', (req, res) => { const signature = req.headers['SC-Signature'] as string; const rawPayload = req.body; const webhookSecret = process.env.WEBHOOK_SECRET!; // 1. Verify the webhook signature (recommended for security) if (!verifySignature(webhookSecret, rawPayload, signature)) { return res.status(401).json({ error: 'Invalid signature' }); } // 2. Parse the webhook payload const payload: WebhookDataPayload = parseEnvelope(rawPayload); // 3. Handle different event types handleWebhookEvent(payload, webhookSecret, res); }); ``` -------------------------------- ### Handle Different Webhook Event Types in TypeScript Source: https://github.com/smartcar/typescript-backend-sdks/blob/master/packages/webhooks/README.md Provides a function to handle various Smartcar webhook event types, including 'VERIFY' for challenge-response verification, 'VEHICLE_STATE' for state changes, and 'VEHICLE_ERROR' for error events. It uses helper functions like `hashChallenge`. ```typescript function handleWebhookEvent(payload: WebhookDataPayload, secret: string, res: any) { // Check if this is a webhook verification event if (payload.eventType === 'VERIFY') { const challenge = payload.data.challenge; if (typeof challenge === 'string') { const hash = hashChallenge(secret, challenge); return res.json({ challenge: hash }); } return res.status(400).json({ error: 'Invalid challenge' }); } // Handle vehicle state events if (payload.eventType === 'VEHICLE_STATE') { handleVehicleStateEvent(payload); } // Handle vehicle error events if (payload.eventType === 'VEHICLE_ERROR') { handleVehicleErrorEvent(payload); } res.status(204).end(); } ``` -------------------------------- ### Webhook Processing API Source: https://github.com/smartcar/typescript-backend-sdks/blob/master/packages/webhooks/README.md This section details the core functions for parsing and verifying Smartcar webhook payloads using the TypeScript SDK. ```APIDOC ## Webhook Processing Functions ### `parseEnvelope(buffer: Buffer): WebhookDataPayload` **Description**: Parses a raw webhook payload buffer into a structured object. **Method**: N/A (Function) **Endpoint**: N/A (Function) ### `verifySignature(secret: string, payload: Buffer, signature: string): boolean` **Description**: Verifies the webhook signature for security. This is a critical step to ensure the authenticity of incoming webhooks. **Method**: N/A (Function) **Endpoint**: N/A (Function) ### `hashChallenge(secret: string, challenge: string): string` **Description**: Creates a hash response for webhook verification challenges. Used during the initial webhook setup and verification process. **Method**: N/A (Function) **Endpoint**: N/A (Function) ### Request Example (Webhook Handler) ```typescript import { parseEnvelope, verifySignature, hashChallenge } from '@smartcar/webhooks'; import type { WebhookDataPayload } from '@smartcar/webhooks'; // Express.js webhook handler example app.post('/webhooks', (req, res) => { const signature = req.headers['SC-Signature'] as string; const rawPayload = req.body; const webhookSecret = process.env.WEBHOOK_SECRET!; // 1. Verify the webhook signature (recommended for security) if (!verifySignature(webhookSecret, rawPayload, signature)) { return res.status(401).json({ error: 'Invalid signature' }); } // 2. Parse the webhook payload const payload: WebhookDataPayload = parseEnvelope(rawPayload); // 3. Handle different event types handleWebhookEvent(payload, webhookSecret, res); }); ``` ### Response Example (Webhook Verification) ```typescript // For VERIFY event type res.json({ challenge: hash }); ``` ### Types The package exports TypeScript types for all webhook payload structures, including: - `WebhookDataPayload` - `VehicleStateEvent` - `VehicleErrorEvent` - `WebhookVerifyEvent` **Example Usage**: ```typescript import type { WebhookDataPayload } from '@smartcar/webhooks'; // ... inside your handler const payload: WebhookDataPayload = parseEnvelope(rawPayload); ``` ``` -------------------------------- ### Group Signals with Custom Naming using TypeScript Source: https://context7.com/smartcar/typescript-backend-sdks/llms.txt The `getSignalGroup` function allows for extracting a group of signals and assigning them custom, descriptive key names. This is achieved by providing a mapping object where friendly names correspond to specific signal codes. It's ideal for creating domain-specific views of vehicle data with easily understandable property names. ```typescript import { getSignalGroup, type Signals, type MotionCurrentSpeed, type OdometerTraveledDistance, type ClimateInternalTemperature, type ClimateExternalTemperature, type HVACIsCabinHVACActive } from "@smartcar/signals"; // Define typed signal group type DriverDashboardSignals = { speed: MotionCurrentSpeed; odometer: OdometerTraveledDistance; insideTemp: ClimateInternalTemperature; outsideTemp: ClimateExternalTemperature; hvacActive: HVACIsCabinHVACActive; }; function createDriverDashboard(signals: Signals) { // Map friendly names to signal codes const dashboardSignals = getSignalGroup(signals, { speed: "motion-currentspeed", odometer: "odometer-traveleddistance", insideTemp: "climate-internaltemperature", outsideTemp: "climate-externaltemperature", hvacActive: "hvac-iscabinhvacactive" }); // Build dashboard display const display = { currentSpeed: dashboardSignals.speed?.status.value === "SUCCESS" ? `${dashboardSignals.speed.body.value} ${dashboardSignals.speed.body.unit}` : "-- mph", totalMiles: dashboardSignals.odometer?.status.value === "SUCCESS" ? dashboardSignals.odometer.body.value.toLocaleString() : "---", climate: { inside: dashboardSignals.insideTemp?.body.value, outside: dashboardSignals.outsideTemp?.body.value, unit: dashboardSignals.insideTemp?.body.unit ?? "C", hvacRunning: dashboardSignals.hvacActive?.body.value ?? false } }; console.log("Driver Dashboard:", display); // Output: // Driver Dashboard: { // currentSpeed: "65 mph", // totalMiles: "45,230", // climate: { inside: 22, outside: 18, unit: "C", hvacRunning: true } // } return display; } ``` -------------------------------- ### Hash Smartcar Webhook Challenge (TypeScript) Source: https://context7.com/smartcar/typescript-backend-sdks/llms.txt Generates an HMAC-SHA256 hash response for Smartcar webhook verification challenges. When registering a new webhook endpoint, Smartcar sends a VERIFY event containing a challenge string that must be hashed with your webhook secret and returned to confirm endpoint ownership. Requires the webhook secret and the challenge string from the payload. ```typescript import { hashChallenge, parseEnvelope } from "@smartcar/webhooks"; const WEBHOOK_SECRET = process.env.WEBHOOK_SECRET!; app.post("/webhooks", express.raw({ type: "application/json" }), (req, res) => { const payload = parseEnvelope(req.body); // Handle webhook verification challenge if (payload.eventType === "VERIFY") { const challenge = payload.data.challenge; if (typeof challenge !== "string") { return res.status(400).json({ error: "Missing or invalid challenge" }); } // Hash the challenge with your webhook secret const hashedResponse = hashChallenge(WEBHOOK_SECRET, challenge); // Return the hash to complete verification return res.json({ challenge: hashedResponse }); } res.status(204).end(); }); // Example challenge: "abc123xyz" // Example hashed response: "7f83b1657ff1fc53b92dc18148a1d65dfc2d4b1fa3d677284addd200126d9069" ``` -------------------------------- ### Import TypeScript Types for Webhook Payloads Source: https://github.com/smartcar/typescript-backend-sdks/blob/master/packages/webhooks/README.md Shows how to import specific TypeScript types from the @smartcar/webhooks package for better type safety when working with webhook data structures like `WebhookDataPayload`, `VehicleStateEvent`, and `VehicleErrorEvent`. ```typescript import type { WebhookDataPayload, VehicleStateEvent, VehicleErrorEvent, WebhookVerifyEvent } from '@smartcar/webhooks'; ``` -------------------------------- ### Parse Webhook Payload with TypeScript Source: https://context7.com/smartcar/typescript-backend-sdks/llms.txt Parses raw webhook payload data (Buffer or string) into a strongly-typed WebhookDataPayload object. Handles JSON parsing and provides TypeScript types for various webhook event structures. Requires the '@smartcar/webhooks' package. ```typescript import { parseEnvelope, type WebhookDataPayload } from "@smartcar/webhooks"; app.post("/webhooks", express.raw({ type: "application/json" }), (req, res) => { try { // Parse the raw webhook payload const payload: WebhookDataPayload = parseEnvelope(req.body); // Access common webhook properties console.log("Event ID:", payload.eventId); console.log("Event Type:", payload.eventType); console.log("Webhook ID:", payload.meta.webhookId); console.log("Delivery ID:", payload.meta.deliveryId); console.log("Mode:", payload.meta.mode); // "LIVE" or "TEST" // Handle different event types switch (payload.eventType) { case "VERIFY": // Handle verification challenge console.log("Challenge:", payload.data.challenge); break; case "VEHICLE_STATE": // Access vehicle information console.log("Vehicle ID:", payload.data.vehicle?.id); console.log("Vehicle Make:", payload.data.vehicle?.make); console.log("Vehicle Model:", payload.data.vehicle?.model); console.log("User ID:", payload.data.user?.id); console.log("Signal Count:", payload.meta.signalCount); console.log("Signals:", payload.data.signals); break; case "VEHICLE_ERROR": // Handle error events console.log("Errors:", payload.data.errors); break; } res.status(204).end(); } catch (error) { // Throws Error("invalid_json") on parse failure res.status(400).json({ error: "Invalid webhook payload" }); } }); // Example VEHICLE_STATE payload output: // Event ID: 123e4567-e89b-12d3-a456-426614174000 // Event Type: VEHICLE_STATE // Vehicle ID: vehicle123 // Vehicle Make: Tesla // Vehicle Model: Model S // Signal Count: 2 ``` -------------------------------- ### Retrieve Multiple Signals by Codes with TypeScript Source: https://context7.com/smartcar/typescript-backend-sdks/llms.txt The `getSignalsByCodes` function retrieves multiple vehicle signals efficiently by their codes in a single API call. It returns an object where signal codes are mapped to their typed values, with `undefined` for any signals not found. This is particularly useful for fetching a predefined set of related signals for display or processing. ```typescript import { getSignalsByCodes, type Signals, type ChargeIsCharging, type ChargeAmperage, type TractionBatteryStateOfCharge, type TractionBatteryEstimatedRange } from "@smartcar/signals"; // Define the shape of signals you want to extract type ChargingSignals = { isCharging: ChargeIsCharging; amperage: ChargeAmperage; batteryLevel: TractionBatteryStateOfCharge; estimatedRange: TractionBatteryEstimatedRange; }; function getChargingDashboard(signals: Signals) { // Extract multiple signals at once const chargingData = getSignalsByCodes(signals, [ "charge-ischarging", "charge-amperage", "tractionbattery-stateofcharge", "tractionbattery-estimatedrange" ]); // Build dashboard with null-safe access const dashboard = { isCharging: chargingData.isCharging?.body.value ?? false, chargingRate: chargingData.amperage?.status.value === "SUCCESS" ? `${chargingData.amperage.body.value} ${chargingData.amperage.body.unit}` : "N/A", batteryLevel: chargingData.batteryLevel?.status.value === "SUCCESS" ? `${chargingData.batteryLevel.body.value}%` : "Unknown", estimatedRange: chargingData.estimatedRange?.status.value === "SUCCESS" ? `${chargingData.estimatedRange.body.value} ${chargingData.estimatedRange.body.unit}` : "Unknown", lastUpdated: Math.max( chargingData.isCharging?.meta?.recordedAt ?? 0, chargingData.amperage?.meta?.recordedAt ?? 0, chargingData.batteryLevel?.meta?.recordedAt ?? 0 ) }; console.log("Charging Dashboard:", dashboard); // Output: // Charging Dashboard: { // isCharging: true, // chargingRate: "32 A", // batteryLevel: "75%", // estimatedRange: "180 mi", // lastUpdated: 1633094400000 // } return dashboard; } ``` -------------------------------- ### Extract Vehicle Signal by Code with TypeScript Generics Source: https://context7.com/smartcar/typescript-backend-sdks/llms.txt Extracts a single signal from a signals array by its code, providing full TypeScript generic support. This function enables type-safe access to specific vehicle signals like battery level or location. Requires the '@smartcar/signals' package. ```typescript import { getSignalByCode, type Signals, type ChargeAmperage, type TractionBatteryStateOfCharge, type LocationPreciseLocation } from "@smartcar/signals"; function processVehicleSignals(signals: Signals) { // Get charging amperage with type safety const amperageSignal = getSignalByCode(signals, "charge-amperage"); if (amperageSignal) { if (amperageSignal.status.value === "SUCCESS") { console.log(`Charging at ${amperageSignal.body.value} ${amperageSignal.body.unit}`); // Output: Charging at 32 A } else if (amperageSignal.status.value === "ERROR") { console.log("Signal error:", amperageSignal.status.error); } } // Get battery state of charge const batterySignal = getSignalByCode( signals, "tractionbattery-stateofcharge" ); if (batterySignal?.status.value === "SUCCESS") { console.log(`Battery level: ${batterySignal.body.value}${batterySignal.body.unit}`); // Output: Battery level: 75% } // Get vehicle location const locationSignal = getSignalByCode( signals, "location-preciselocation" ); if (locationSignal?.status.value === "SUCCESS") { const { latitude, longitude } = locationSignal.body.value; console.log(`Vehicle location: ${latitude}, ${longitude}`); // Output: Vehicle location: 37.7749, -122.4194 } return { isCharging: amperageSignal?.body.value > 0, batteryPercent: batterySignal?.body.value, location: locationSignal?.body.value }; } ``` -------------------------------- ### Verify Smartcar Webhook Signature (TypeScript) Source: https://context7.com/smartcar/typescript-backend-sdks/llms.txt Verifies the authenticity of incoming Smartcar webhook payloads using HMAC-SHA256 signature validation. This function compares the provided signature header against a computed hash of the payload body to ensure the webhook originated from Smartcar and wasn't tampered with in transit. Requires the webhook secret and the raw request body. ```typescript import { verifySignature } from "@smartcar/webhooks"; const WEBHOOK_SECRET = process.env.WEBHOOK_SECRET!; app.post("/webhooks", express.raw({ type: "application/json" }), (req, res) => { const signature = req.headers["sc-signature"] as string; const rawBody = req.body; // Verify the webhook signature const result = verifySignature(rawBody, signature, WEBHOOK_SECRET); if (!result.ok) { console.error("Signature verification failed:", result.reason); // result.reason is either "missing_signature" or "mismatch" return res.status(401).json({ error: "Invalid webhook signature" }); } // Signature verified - proceed with processing console.log("Webhook signature verified successfully"); res.status(200).end(); }); // Output on invalid signature: // Signature verification failed: mismatch // Output on missing signature: // Signature verification failed: missing_signature ``` === COMPLETE CONTENT === This response contains all available snippets from this library. No additional content exists. Do not make further requests.