UNPKG

n8n-nodes-bluelinky

Version:
235 lines (234 loc) 11.6 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.BlueLinky = void 0; const n8n_workflow_1 = require("n8n-workflow"); const bluelinky_1 = require("bluelinky"); async function initClient() { const credentials = await this.getCredentials('blueLinkyApi'); if (!credentials) { throw new n8n_workflow_1.NodeOperationError(this.getNode(), 'Missing BlueLinky credentials'); } // @ts-ignore -- brand is missing, only required in v10 of Bluelinky const client = new bluelinky_1.BlueLinky({ username: credentials.username, password: credentials.password, region: credentials.region, language: (credentials.language || 'en'), pin: (credentials.pin || undefined), }); await client.login(); return client; } async function resolveVehicle(client, vin, index) { const vehicles = await client.getVehicles(); if (!vehicles || vehicles.length === 0) { throw new Error('No vehicles found for this BlueLinky account.'); } if (vin) { const match = vehicles.find((v) => { var _a; return v.vin === vin || ((_a = v.vehicleConfig) === null || _a === void 0 ? void 0 : _a.vin) === vin; }); if (!match) { throw new Error(`Vehicle with VIN ${vin} not found.`); } return match; } const i = typeof index === 'number' ? index : 0; if (i < 0 || i >= vehicles.length) { throw new Error(`Vehicle index ${i} is out of bounds (found ${vehicles.length} vehicles).`); } return vehicles[i]; } class BlueLinky { constructor() { this.description = { displayName: 'BlueLinky', name: 'blueLinky', icon: 'file:bluelinky.svg', group: ['transform'], version: 1, description: 'Control Hyundai/Kia vehicles via the bluelinky library', defaults: { name: 'BlueLinky', }, inputs: ['main'], outputs: ['main'], credentials: [ { name: 'blueLinkyApi', required: true, }, ], properties: [ { displayName: 'Operation', name: 'operation', type: 'options', noDataExpression: true, options: [ { name: 'List Vehicles', value: 'listVehicles', description: 'Get a list of vehicles' }, { name: 'Get Vehicle Status', value: 'getStatus', description: 'Fetch current vehicle status' }, { name: 'Get Location', value: 'getLocation', description: 'Fetch current vehicle location' }, { name: 'Get Odometer', value: 'getOdometer', description: 'Fetch current vehicle odometer' }, { name: 'Lock', value: 'lock', description: 'Lock the vehicle' }, { name: 'Unlock', value: 'unlock', description: 'Unlock the vehicle' }, { name: 'Start Engine/Climate', value: 'startEngine', description: 'Start engine or climate with options' }, { name: 'Stop Engine/Climate', value: 'stopEngine', description: 'Stop engine or climate' }, ], default: 'listVehicles', }, // Vehicle selection { displayName: 'VIN', name: 'vin', type: 'string', default: '', placeholder: 'Optional VIN to target a specific vehicle', description: 'If empty, vehicle index will be used', displayOptions: { show: { operation: ['getStatus', 'getLocation', 'getOdometer', 'lock', 'unlock', 'startEngine', 'stopEngine'], }, }, }, { displayName: 'Vehicle Index', name: 'vehicleIndex', type: 'number', typeOptions: { minValue: 0 }, default: 0, description: 'Index of the vehicle to use when VIN is not provided', displayOptions: { show: { operation: ['getStatus', 'getLocation', 'getOdometer', 'lock', 'unlock', 'startEngine', 'stopEngine'], }, }, }, // Start options { displayName: 'Start Options', name: 'startOptions', type: 'collection', default: {}, placeholder: 'Add Option', options: [ { displayName: 'Air Control', name: 'airCtrl', type: 'boolean', default: true, description: 'Enable climate control', }, { displayName: 'Defrost', name: 'defrost', type: 'boolean', default: false, }, { displayName: 'Heating Temperature (°C)', name: 'heatingTemp', type: 'number', typeOptions: { minValue: 16, maxValue: 32 }, default: 22, description: 'Target temperature for climate start (if supported)', }, { displayName: 'Duration (minutes)', name: 'igniOnDuration', type: 'number', typeOptions: { minValue: 1, maxValue: 30 }, default: 10, description: 'How long to run the engine/climate', }, ], displayOptions: { show: { operation: ['startEngine'], }, }, }, ], }; } async execute() { var _a, _b, _c, _d; const returnData = []; const operation = this.getNodeParameter('operation', 0); const client = await initClient.call(this); try { if (operation === 'listVehicles') { const vehicles = await client.getVehicles(); const data = vehicles.map((v) => { var _a, _b, _c, _d, _e; return ({ vin: (_a = v.vin()) !== null && _a !== void 0 ? _a : (_b = v.vehicleConfig) === null || _b === void 0 ? void 0 : _b.vin, id: (_c = v.id()) !== null && _c !== void 0 ? _c : null, nickname: (_e = (_d = v.nickname()) !== null && _d !== void 0 ? _d : v.name()) !== null && _e !== void 0 ? _e : null, }); }); returnData.push(...data.map((d) => ({ json: d }))); } else if (operation === 'getStatus') { const vin = this.getNodeParameter('vin', 0, ''); const index = this.getNodeParameter('vehicleIndex', 0, 0); const vehicle = await resolveVehicle(client, vin || undefined, index); // Many setups support a status method with parsed/refresh flags const status = (_b = await ((_a = vehicle.status) === null || _a === void 0 ? void 0 : _a.call(vehicle, { parsed: true, refresh: true }))) !== null && _b !== void 0 ? _b : await ((_c = vehicle.fullStatus) === null || _c === void 0 ? void 0 : _c.call(vehicle, { parsed: true, refresh: true })); returnData.push({ json: status !== null && status !== void 0 ? status : { message: 'No status returned' } }); } else if (operation === 'getLocation') { const vin = this.getNodeParameter('vin', 0, ''); const index = this.getNodeParameter('vehicleIndex', 0, 0); const vehicle = await resolveVehicle(client, vin || undefined, index); const location = await ((_d = vehicle.location) === null || _d === void 0 ? void 0 : _d.call(vehicle)); returnData.push({ json: location !== null && location !== void 0 ? location : { message: 'No location returned' } }); } else if (operation === 'getOdometer') { const vin = this.getNodeParameter('vin', 0, ''); const index = this.getNodeParameter('vehicleIndex', 0, 0); const vehicle = await resolveVehicle(client, vin || undefined, index); let odo = await vehicle.odometer(); returnData.push({ json: odo !== null && odo !== void 0 ? odo : { message: 'No odometer returned' } }); } else if (operation === 'lock') { const vin = this.getNodeParameter('vin', 0, ''); const index = this.getNodeParameter('vehicleIndex', 0, 0); const vehicle = await resolveVehicle(client, vin || undefined, index); const res = await vehicle.lock(); returnData.push({ json: res !== null && res !== void 0 ? res : { success: true } }); } else if (operation === 'unlock') { const vin = this.getNodeParameter('vin', 0, ''); const index = this.getNodeParameter('vehicleIndex', 0, 0); const vehicle = await resolveVehicle(client, vin || undefined, index); const res = await vehicle.unlock(); returnData.push({ json: res !== null && res !== void 0 ? res : { success: true } }); } else if (operation === 'startEngine') { const vin = this.getNodeParameter('vin', 0, ''); const index = this.getNodeParameter('vehicleIndex', 0, 0); const startOptions = this.getNodeParameter('startOptions', 0, {}); const vehicle = await resolveVehicle(client, vin || undefined, index); const res = await vehicle.start(startOptions); returnData.push({ json: res !== null && res !== void 0 ? res : { success: true } }); } else if (operation === 'stopEngine') { const vin = this.getNodeParameter('vin', 0, ''); const index = this.getNodeParameter('vehicleIndex', 0, 0); const vehicle = await resolveVehicle(client, vin || undefined, index); const res = await vehicle.stop(); returnData.push({ json: res !== null && res !== void 0 ? res : { success: true } }); } else { throw new n8n_workflow_1.NodeOperationError(this.getNode(), `Unknown operation: ${operation}`); } } catch (error) { if (error instanceof n8n_workflow_1.NodeOperationError) { throw error; } throw new n8n_workflow_1.NodeOperationError(this.getNode(), error.message); } return [returnData]; } } exports.BlueLinky = BlueLinky;