diff --git a/packages/nodes-base/nodes/HomeAssistant/CameraProxyDescription.ts b/packages/nodes-base/nodes/HomeAssistant/CameraProxyDescription.ts index 6ff8c3d966..302ba4b141 100644 --- a/packages/nodes-base/nodes/HomeAssistant/CameraProxyDescription.ts +++ b/packages/nodes-base/nodes/HomeAssistant/CameraProxyDescription.ts @@ -33,7 +33,10 @@ export const cameraProxyFields: INodeProperties[] = [ { displayName: 'Camera Entity ID', name: 'cameraEntityId', - type: 'string', + type: 'options', + typeOptions: { + loadOptionsMethod: 'getCameraEntities', + }, default: '', required: true, displayOptions: { diff --git a/packages/nodes-base/nodes/HomeAssistant/GenericFunctions.ts b/packages/nodes-base/nodes/HomeAssistant/GenericFunctions.ts index 796b03ee51..673caab7c3 100644 --- a/packages/nodes-base/nodes/HomeAssistant/GenericFunctions.ts +++ b/packages/nodes-base/nodes/HomeAssistant/GenericFunctions.ts @@ -4,15 +4,17 @@ import { import { IExecuteFunctions, + ILoadOptionsFunctions, } from 'n8n-core'; import { IDataObject, + INodePropertyOptions, NodeApiError, NodeOperationError, } from 'n8n-workflow'; -export async function homeAssistantApiRequest(this: IExecuteFunctions, method: string, resource: string, body: IDataObject = {}, qs: IDataObject = {}, uri?: string, option: IDataObject = {}) { +export async function homeAssistantApiRequest(this: IExecuteFunctions | ILoadOptionsFunctions, method: string, resource: string, body: IDataObject = {}, qs: IDataObject = {}, uri?: string, option: IDataObject = {}) { const credentials = await this.getCredentials('homeAssistantApi'); if (credentials === undefined) { @@ -35,8 +37,51 @@ export async function homeAssistantApiRequest(this: IExecuteFunctions, method: s delete options.body; } try { - return await this.helpers.request(options); + if (this.helpers.request) { + return await this.helpers.request(options); + } } catch (error) { throw new NodeApiError(this.getNode(), error); } } + +export async function getHomeAssistantEntities(this: IExecuteFunctions | ILoadOptionsFunctions, domain = '') { + const returnData: INodePropertyOptions[] = []; + const entities = await homeAssistantApiRequest.call(this, 'GET', '/states'); + for (const entity of entities) { + const entityId = entity.entity_id as string; + if (domain === '' || domain && entityId.startsWith(domain)) { + const entityName = entity.attributes.friendly_name as string || entityId; + returnData.push({ + name: entityName, + value: entityId, + }); + } + } + return returnData; +} + +export async function getHomeAssistantServices(this: IExecuteFunctions | ILoadOptionsFunctions, domain = '') { + const returnData: INodePropertyOptions[] = []; + const services = await homeAssistantApiRequest.call(this, 'GET', '/services'); + if (domain === '') { + // If no domain specified return domains + const domains = services.map(({ domain }: IDataObject) => domain as string).sort(); + returnData.push(...domains.map((service: string) => ({ name: service, value: service }))); + return returnData; + } else { + // If we have a domain, return all relevant services + const domainServices = services.filter((service: IDataObject) => service.domain === domain); + for (const domainService of domainServices) { + for (const [serviceID, value] of Object.entries(domainService.services)) { + const serviceProperties = value as IDataObject; + const serviceName = serviceProperties.description || serviceID; + returnData.push({ + name: serviceName as string, + value: serviceID, + }); + } + } + } + return returnData; +} diff --git a/packages/nodes-base/nodes/HomeAssistant/HomeAssistant.node.ts b/packages/nodes-base/nodes/HomeAssistant/HomeAssistant.node.ts index 1a9ef184b2..2689233b38 100644 --- a/packages/nodes-base/nodes/HomeAssistant/HomeAssistant.node.ts +++ b/packages/nodes-base/nodes/HomeAssistant/HomeAssistant.node.ts @@ -6,7 +6,9 @@ import { ICredentialsDecrypted, ICredentialTestFunctions, IDataObject, + ILoadOptionsFunctions, INodeExecutionData, + INodePropertyOptions, INodeType, INodeTypeDescription, NodeCredentialTestResult, @@ -52,6 +54,8 @@ import { } from './CameraProxyDescription'; import { + getHomeAssistantEntities, + getHomeAssistantServices, homeAssistantApiRequest, } from './GenericFunctions'; @@ -169,9 +173,29 @@ export class HomeAssistant implements INodeType { status: 'OK', message: 'Authentication successful!', }; - }, }, + + loadOptions: { + async getAllEntities(this: ILoadOptionsFunctions): Promise { + return await getHomeAssistantEntities.call(this); + }, + async getCameraEntities(this: ILoadOptionsFunctions): Promise { + return await getHomeAssistantEntities.call(this, 'camera'); + }, + async getDomains(this: ILoadOptionsFunctions): Promise { + return await getHomeAssistantServices.call(this); + }, + async getDomainServices(this: ILoadOptionsFunctions): Promise { + const currentDomain = this.getCurrentNodeParameter('domain') as string; + if (currentDomain) { + return await getHomeAssistantServices.call(this, currentDomain); + } else { + return []; + } + }, + }, + }; async execute(this: IExecuteFunctions): Promise { diff --git a/packages/nodes-base/nodes/HomeAssistant/ServiceDescription.ts b/packages/nodes-base/nodes/HomeAssistant/ServiceDescription.ts index a6cde1a24d..946eab2d2d 100644 --- a/packages/nodes-base/nodes/HomeAssistant/ServiceDescription.ts +++ b/packages/nodes-base/nodes/HomeAssistant/ServiceDescription.ts @@ -83,7 +83,10 @@ export const serviceFields: INodeProperties[] = [ { displayName: 'Domain', name: 'domain', - type: 'string', + type: 'options', + typeOptions: { + loadOptionsMethod: 'getDomains', + }, default: '', required: true, displayOptions: { @@ -100,7 +103,13 @@ export const serviceFields: INodeProperties[] = [ { displayName: 'Service', name: 'service', - type: 'string', + type: 'options', + typeOptions: { + loadOptionsDependsOn: [ + 'domain', + ], + loadOptionsMethod: 'getDomainServices', + }, default: '', required: true, displayOptions: { diff --git a/packages/nodes-base/nodes/HomeAssistant/StateDescription.ts b/packages/nodes-base/nodes/HomeAssistant/StateDescription.ts index ba0dd384cc..dd58042eb3 100644 --- a/packages/nodes-base/nodes/HomeAssistant/StateDescription.ts +++ b/packages/nodes-base/nodes/HomeAssistant/StateDescription.ts @@ -43,7 +43,10 @@ export const stateFields: INodeProperties[] = [ { displayName: 'Entity ID', name: 'entityId', - type: 'string', + type: 'options', + typeOptions: { + loadOptionsMethod: 'getAllEntities', + }, displayOptions: { show: { operation: [ @@ -110,7 +113,10 @@ export const stateFields: INodeProperties[] = [ { displayName: 'Entity ID', name: 'entityId', - type: 'string', + type: 'options', + typeOptions: { + loadOptionsMethod: 'getAllEntities', + }, displayOptions: { show: { operation: [