first commit
Security: Sync from Public / sync-from-public (push) Has been cancelled
Test: Benchmark Nightly / build (push) Has been cancelled
Test: Benchmark Nightly / Notify Cats on failure (push) Has been cancelled
CI: Python / Checks (push) Has been cancelled
Test: Evals Python / Workflow Comparison Python (push) Has been cancelled
Util: Check Docs URLs / check-docs-urls (push) Has been cancelled
Test: Visual Storybook / Cloudflare Pages (push) Has been cancelled
Test: E2E Performance / build-and-test-performance (push) Has been cancelled
Test: Workflows Nightly / Run Workflow Tests (push) Has been cancelled
Util: Cleanup CI Docker Images / Delete stale CI images (push) Has been cancelled
Test: Benchmark Destroy Env / build (push) Has been cancelled
Util: Update Node Popularity / update-popularity (push) Has been cancelled
Test: E2E Coverage Weekly / Coverage Tests (push) Has been cancelled

This commit is contained in:
2026-03-17 16:22:57 +03:30
commit 3d5eaf9445
15349 changed files with 2847338 additions and 0 deletions
@@ -0,0 +1,62 @@
import type { INodeProperties } from 'n8n-workflow';
export const cameraProxyOperations: INodeProperties[] = [
{
displayName: 'Operation',
name: 'operation',
type: 'options',
noDataExpression: true,
displayOptions: {
show: {
resource: ['cameraProxy'],
},
},
options: [
{
name: 'Get Screenshot',
value: 'getScreenshot',
description: 'Get the camera screenshot',
action: 'Get a screenshot',
},
],
default: 'getScreenshot',
},
];
export const cameraProxyFields: INodeProperties[] = [
/* -------------------------------------------------------------------------- */
/* cameraProxy:getScreenshot */
/* -------------------------------------------------------------------------- */
{
displayName: 'Camera Entity Name or ID',
name: 'cameraEntityId',
type: 'options',
description:
'Choose from the list, or specify an ID using an <a href="https://docs.n8n.io/code/expressions/">expression</a>',
typeOptions: {
loadOptionsMethod: 'getCameraEntities',
},
default: '',
required: true,
displayOptions: {
show: {
operation: ['getScreenshot'],
resource: ['cameraProxy'],
},
},
},
{
displayName: 'Put Output File in Field',
name: 'binaryPropertyName',
type: 'string',
required: true,
default: 'data',
displayOptions: {
show: {
operation: ['getScreenshot'],
resource: ['cameraProxy'],
},
},
hint: 'The name of the output binary field to put the file in',
},
];
@@ -0,0 +1,30 @@
import type { INodeProperties } from 'n8n-workflow';
export const configOperations: INodeProperties[] = [
{
displayName: 'Operation',
name: 'operation',
type: 'options',
noDataExpression: true,
displayOptions: {
show: {
resource: ['config'],
},
},
options: [
{
name: 'Get',
value: 'get',
description: 'Get the configuration',
action: 'Get the config',
},
{
name: 'Check Configuration',
value: 'check',
description: 'Check the configuration',
action: 'Check the config',
},
],
default: 'get',
},
];
@@ -0,0 +1,123 @@
import type { INodeProperties } from 'n8n-workflow';
export const eventOperations: INodeProperties[] = [
{
displayName: 'Operation',
name: 'operation',
type: 'options',
noDataExpression: true,
displayOptions: {
show: {
resource: ['event'],
},
},
options: [
{
name: 'Create',
value: 'create',
description: 'Create an event',
action: 'Create an event',
},
{
name: 'Get Many',
value: 'getAll',
description: 'Get many events',
action: 'Get many events',
},
],
default: 'getAll',
},
];
export const eventFields: INodeProperties[] = [
/* -------------------------------------------------------------------------- */
/* event:getAll */
/* -------------------------------------------------------------------------- */
{
displayName: 'Return All',
name: 'returnAll',
type: 'boolean',
displayOptions: {
show: {
operation: ['getAll'],
resource: ['event'],
},
},
default: false,
description: 'Whether to return all results or only up to a given limit',
},
{
displayName: 'Limit',
name: 'limit',
type: 'number',
displayOptions: {
show: {
operation: ['getAll'],
resource: ['event'],
returnAll: [false],
},
},
typeOptions: {
minValue: 1,
maxValue: 100,
},
default: 50,
description: 'Max number of results to return',
},
/* -------------------------------------------------------------------------- */
/* event:create */
/* -------------------------------------------------------------------------- */
{
displayName: 'Event Type',
name: 'eventType',
type: 'string',
displayOptions: {
show: {
operation: ['create'],
resource: ['event'],
},
},
required: true,
default: '',
description: 'The Entity ID for which an event will be created',
},
{
displayName: 'Event Attributes',
name: 'eventAttributes',
type: 'fixedCollection',
typeOptions: {
multipleValues: true,
},
placeholder: 'Add Attribute',
default: {},
displayOptions: {
show: {
resource: ['event'],
operation: ['create'],
},
},
options: [
{
displayName: 'Attributes',
name: 'attributes',
values: [
{
displayName: 'Name',
name: 'name',
type: 'string',
default: '',
description: 'Name of the attribute',
},
{
displayName: 'Value',
name: 'value',
type: 'string',
default: '',
description: 'Value of the attribute',
},
],
},
],
},
];
@@ -0,0 +1,101 @@
import type {
IExecuteFunctions,
ILoadOptionsFunctions,
IDataObject,
INodePropertyOptions,
JsonObject,
IHttpRequestMethods,
IRequestOptions,
} from 'n8n-workflow';
import { NodeApiError } from 'n8n-workflow';
export async function homeAssistantApiRequest(
this: IExecuteFunctions | ILoadOptionsFunctions,
method: IHttpRequestMethods,
resource: string,
body: IDataObject = {},
qs: IDataObject = {},
uri?: string,
option: IDataObject = {},
) {
const credentials = await this.getCredentials('homeAssistantApi');
let options: IRequestOptions = {
headers: {
Authorization: `Bearer ${credentials.accessToken}`,
},
method,
qs,
body,
uri:
uri ??
`${credentials.ssl === true ? 'https' : 'http'}://${credentials.host}:${
credentials.port
}/api${resource}`,
json: true,
};
options = Object.assign({}, options, option);
if (Object.keys(options.body as IDataObject).length === 0) {
delete options.body;
}
try {
if (this.helpers.request) {
return await this.helpers.request(options);
}
} catch (error) {
throw new NodeApiError(this.getNode(), error as JsonObject);
}
}
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: service }: IDataObject) => service as string).sort();
returnData.push(
...(domains.map((service: string) => ({
name: service,
value: service,
})) as INodePropertyOptions[]),
);
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 as IDataObject)) {
const serviceProperties = value as IDataObject;
const serviceName = serviceProperties.description || serviceID;
returnData.push({
name: serviceName as string,
value: serviceID,
});
}
}
}
return returnData;
}
@@ -0,0 +1,111 @@
import type { INodeProperties } from 'n8n-workflow';
export const historyOperations: INodeProperties[] = [
{
displayName: 'Operation',
name: 'operation',
type: 'options',
noDataExpression: true,
displayOptions: {
show: {
resource: ['history'],
},
},
options: [
{
name: 'Get Many',
value: 'getAll',
description: 'Get many state changes',
action: 'Get many state changes',
},
],
default: 'getAll',
},
];
export const historyFields: INodeProperties[] = [
/* -------------------------------------------------------------------------- */
/* history:getLogbookEntries */
/* -------------------------------------------------------------------------- */
{
displayName: 'Return All',
name: 'returnAll',
type: 'boolean',
displayOptions: {
show: {
operation: ['getAll'],
resource: ['history'],
},
},
default: false,
description: 'Whether to return all results or only up to a given limit',
},
{
displayName: 'Limit',
name: 'limit',
type: 'number',
displayOptions: {
show: {
operation: ['getAll'],
resource: ['history'],
returnAll: [false],
},
},
typeOptions: {
minValue: 1,
maxValue: 100,
},
default: 50,
description: 'Max number of results to return',
},
{
displayName: 'Additional Fields',
name: 'additionalFields',
type: 'collection',
placeholder: 'Add Field',
default: {},
displayOptions: {
show: {
resource: ['history'],
operation: ['getAll'],
},
},
options: [
{
displayName: 'End Time',
name: 'endTime',
type: 'dateTime',
default: '',
description: 'The end of the period',
},
{
displayName: 'Entity IDs',
name: 'entityIds',
type: 'string',
default: '',
description: 'The entities IDs separated by comma',
},
{
displayName: 'Minimal Response',
name: 'minimalResponse',
type: 'boolean',
default: false,
description: 'Whether to only return <code>last_changed</code> and state for states',
},
{
displayName: 'Significant Changes Only',
name: 'significantChangesOnly',
type: 'boolean',
default: false,
description: 'Whether to only return significant state changes',
},
{
displayName: 'Start Time',
name: 'startTime',
type: 'dateTime',
default: '',
description: 'The beginning of the period',
},
],
},
];
@@ -0,0 +1,18 @@
{
"node": "n8n-nodes-base.homeAssistant",
"nodeVersion": "1.0",
"codexVersion": "1.0",
"categories": ["Miscellaneous"],
"resources": {
"credentialDocumentation": [
{
"url": "https://docs.n8n.io/integrations/builtin/credentials/homeassistant/"
}
],
"primaryDocumentation": [
{
"url": "https://docs.n8n.io/integrations/builtin/app-nodes/n8n-nodes-base.homeassistant/"
}
]
}
}
@@ -0,0 +1,460 @@
import {
type IExecuteFunctions,
type ICredentialsDecrypted,
type ICredentialTestFunctions,
type IDataObject,
type ILoadOptionsFunctions,
type INodeCredentialTestResult,
type INodeExecutionData,
type INodePropertyOptions,
type INodeType,
type INodeTypeDescription,
NodeConnectionTypes,
} from 'n8n-workflow';
import { cameraProxyFields, cameraProxyOperations } from './CameraProxyDescription';
import { configOperations } from './ConfigDescription';
import { eventFields, eventOperations } from './EventDescription';
import {
getHomeAssistantEntities,
getHomeAssistantServices,
homeAssistantApiRequest,
} from './GenericFunctions';
import { historyFields, historyOperations } from './HistoryDescription';
import { logFields, logOperations } from './LogDescription';
import { serviceFields, serviceOperations } from './ServiceDescription';
import { stateFields, stateOperations } from './StateDescription';
import { templateFields, templateOperations } from './TemplateDescription';
export class HomeAssistant implements INodeType {
description: INodeTypeDescription = {
displayName: 'Home Assistant',
name: 'homeAssistant',
icon: 'file:homeAssistant.svg',
group: ['output'],
version: 1,
subtitle: '={{$parameter["operation"] + ": " + $parameter["resource"]}}',
description: 'Consume Home Assistant API',
defaults: {
name: 'Home Assistant',
},
usableAsTool: true,
inputs: [NodeConnectionTypes.Main],
outputs: [NodeConnectionTypes.Main],
credentials: [
{
name: 'homeAssistantApi',
required: true,
testedBy: 'homeAssistantApiTest',
},
],
properties: [
{
displayName: 'Resource',
name: 'resource',
type: 'options',
noDataExpression: true,
options: [
{
name: 'Camera Proxy',
value: 'cameraProxy',
},
{
name: 'Config',
value: 'config',
},
{
name: 'Event',
value: 'event',
},
// {
// name: 'History',
// value: 'history',
// },
{
name: 'Log',
value: 'log',
},
{
name: 'Service',
value: 'service',
},
{
name: 'State',
value: 'state',
},
{
name: 'Template',
value: 'template',
},
],
default: 'config',
},
...cameraProxyOperations,
...cameraProxyFields,
...configOperations,
...eventOperations,
...eventFields,
...historyOperations,
...historyFields,
...logOperations,
...logFields,
...serviceOperations,
...serviceFields,
...stateOperations,
...stateFields,
...templateOperations,
...templateFields,
],
};
methods = {
credentialTest: {
async homeAssistantApiTest(
this: ICredentialTestFunctions,
credential: ICredentialsDecrypted,
): Promise<INodeCredentialTestResult> {
const credentials = credential.data;
const options = {
method: 'GET',
headers: {
Authorization: `Bearer ${credentials!.accessToken}`,
},
uri: `${credentials!.ssl === true ? 'https' : 'http'}://${credentials!.host}:${
credentials!.port || '8123'
}/api/`,
json: true,
timeout: 5000,
};
try {
const response = await this.helpers.request(options);
if (!response.message) {
return {
status: 'Error',
message: `Token is not valid: ${response.error}`,
};
}
} catch (error) {
return {
status: 'Error',
message: `${
error.statusCode === 401 ? 'Token is' : 'Settings are'
} not valid: ${error}`,
};
}
return {
status: 'OK',
message: 'Authentication successful!',
};
},
},
loadOptions: {
async getAllEntities(this: ILoadOptionsFunctions): Promise<INodePropertyOptions[]> {
return await getHomeAssistantEntities.call(this);
},
async getCameraEntities(this: ILoadOptionsFunctions): Promise<INodePropertyOptions[]> {
return await getHomeAssistantEntities.call(this, 'camera');
},
async getDomains(this: ILoadOptionsFunctions): Promise<INodePropertyOptions[]> {
return await getHomeAssistantServices.call(this);
},
async getDomainServices(this: ILoadOptionsFunctions): Promise<INodePropertyOptions[]> {
const currentDomain = this.getCurrentNodeParameter('domain') as string;
if (currentDomain) {
return await getHomeAssistantServices.call(this, currentDomain);
} else {
return [];
}
},
},
};
async execute(this: IExecuteFunctions): Promise<INodeExecutionData[][]> {
const items = this.getInputData();
const returnData: INodeExecutionData[] = [];
const length = items.length;
const resource = this.getNodeParameter('resource', 0);
const operation = this.getNodeParameter('operation', 0);
const qs: IDataObject = {};
let responseData;
for (let i = 0; i < length; i++) {
try {
if (resource === 'config') {
if (operation === 'get') {
responseData = await homeAssistantApiRequest.call(this, 'GET', '/config');
} else if (operation === 'check') {
responseData = await homeAssistantApiRequest.call(
this,
'POST',
'/config/core/check_config',
);
}
} else if (resource === 'service') {
if (operation === 'getAll') {
const returnAll = this.getNodeParameter('returnAll', i);
responseData = (await homeAssistantApiRequest.call(
this,
'GET',
'/services',
)) as IDataObject[];
if (!returnAll) {
const limit = this.getNodeParameter('limit', i);
responseData = responseData.slice(0, limit);
}
} else if (operation === 'call') {
const domain = this.getNodeParameter('domain', i) as string;
const service = this.getNodeParameter('service', i) as string;
const serviceAttributes = this.getNodeParameter('serviceAttributes', i) as {
attributes: IDataObject[];
};
const body: IDataObject = {};
if (Object.entries(serviceAttributes).length) {
if (serviceAttributes.attributes !== undefined) {
serviceAttributes.attributes.map((attribute) => {
body[attribute.name as string] = attribute.value;
});
}
}
responseData = await homeAssistantApiRequest.call(
this,
'POST',
`/services/${domain}/${service}`,
body,
);
if (Array.isArray(responseData) && responseData.length === 0) {
responseData = {};
}
}
} else if (resource === 'state') {
if (operation === 'getAll') {
const returnAll = this.getNodeParameter('returnAll', i);
responseData = (await homeAssistantApiRequest.call(
this,
'GET',
'/states',
)) as IDataObject[];
if (!returnAll) {
const limit = this.getNodeParameter('limit', i);
responseData = responseData.slice(0, limit);
}
} else if (operation === 'get') {
const entityId = this.getNodeParameter('entityId', i) as string;
responseData = await homeAssistantApiRequest.call(this, 'GET', `/states/${entityId}`);
} else if (operation === 'upsert') {
const entityId = this.getNodeParameter('entityId', i) as string;
const state = this.getNodeParameter('state', i) as string;
const stateAttributes = this.getNodeParameter('stateAttributes', i) as {
attributes: IDataObject[];
};
const body = {
state,
attributes: {},
};
if (Object.entries(stateAttributes).length) {
if (stateAttributes.attributes !== undefined) {
stateAttributes.attributes.map((attribute) => {
// @ts-ignore
body.attributes[attribute.name as string] = attribute.value;
});
}
}
responseData = await homeAssistantApiRequest.call(
this,
'POST',
`/states/${entityId}`,
body,
);
}
} else if (resource === 'event') {
if (operation === 'getAll') {
const returnAll = this.getNodeParameter('returnAll', i);
responseData = (await homeAssistantApiRequest.call(
this,
'GET',
'/events',
)) as IDataObject[];
if (!returnAll) {
const limit = this.getNodeParameter('limit', i);
responseData = responseData.slice(0, limit);
}
} else if (operation === 'create') {
const eventType = this.getNodeParameter('eventType', i) as string;
const eventAttributes = this.getNodeParameter('eventAttributes', i) as {
attributes: IDataObject[];
};
const body = {};
if (Object.entries(eventAttributes).length) {
if (eventAttributes.attributes !== undefined) {
eventAttributes.attributes.map((attribute) => {
// @ts-ignore
body[attribute.name as string] = attribute.value;
});
}
}
responseData = await homeAssistantApiRequest.call(
this,
'POST',
`/events/${eventType}`,
body,
);
}
} else if (resource === 'log') {
if (operation === 'getErroLogs') {
responseData = await homeAssistantApiRequest.call(this, 'GET', '/error_log');
if (responseData) {
responseData = {
errorLog: responseData,
};
}
} else if (operation === 'getLogbookEntries') {
const additionalFields = this.getNodeParameter('additionalFields', i);
let endpoint = '/logbook';
if (Object.entries(additionalFields).length) {
if (additionalFields.startTime) {
endpoint = `/logbook/${additionalFields.startTime}`;
}
if (additionalFields.endTime) {
qs.end_time = additionalFields.endTime;
}
if (additionalFields.entityId) {
qs.entity = additionalFields.entityId;
}
}
responseData = await homeAssistantApiRequest.call(this, 'GET', endpoint, {}, qs);
}
} else if (resource === 'template') {
if (operation === 'create') {
const body = {
template: this.getNodeParameter('template', i) as string,
};
responseData = await homeAssistantApiRequest.call(this, 'POST', '/template', body);
if (responseData) {
responseData = { renderedTemplate: responseData };
}
}
} else if (resource === 'history') {
if (operation === 'getAll') {
const returnAll = this.getNodeParameter('returnAll', i);
const additionalFields = this.getNodeParameter('additionalFields', i);
let endpoint = '/history/period';
if (Object.entries(additionalFields).length) {
if (additionalFields.startTime) {
endpoint = `/history/period/${additionalFields.startTime}`;
}
if (additionalFields.endTime) {
qs.end_time = additionalFields.endTime;
}
if (additionalFields.entityIds) {
qs.filter_entity_id = additionalFields.entityIds;
}
if (additionalFields.minimalResponse === true) {
qs.minimal_response = additionalFields.minimalResponse;
}
if (additionalFields.significantChangesOnly === true) {
qs.significant_changes_only = additionalFields.significantChangesOnly;
}
}
responseData = (await homeAssistantApiRequest.call(
this,
'GET',
endpoint,
{},
qs,
)) as IDataObject[];
if (!returnAll) {
const limit = this.getNodeParameter('limit', i);
responseData = responseData.slice(0, limit);
}
}
} else if (resource === 'cameraProxy') {
if (operation === 'getScreenshot') {
const cameraEntityId = this.getNodeParameter('cameraEntityId', i) as string;
const dataPropertyNameDownload = this.getNodeParameter('binaryPropertyName', i);
const endpoint = `/camera_proxy/${cameraEntityId}`;
let mimeType: string | undefined;
responseData = await homeAssistantApiRequest.call(
this,
'GET',
endpoint,
{},
{},
undefined,
{
encoding: null,
resolveWithFullResponse: true,
},
);
const newItem: INodeExecutionData = {
json: items[i].json,
binary: {},
};
if (mimeType === undefined && responseData.headers['content-type']) {
mimeType = responseData.headers['content-type'];
}
if (items[i].binary !== undefined && newItem.binary) {
// Create a shallow copy of the binary data so that the old
// data references which do not get changed still stay behind
// but the incoming data does not get changed.
Object.assign(newItem.binary, items[i].binary);
}
items[i] = newItem;
const data = Buffer.from(responseData.body as string);
items[i].binary![dataPropertyNameDownload] = await this.helpers.prepareBinaryData(
data,
'screenshot.jpg',
mimeType,
);
}
}
} catch (error) {
if (this.continueOnFail()) {
if (resource === 'cameraProxy' && operation === 'get') {
items[i].json = { error: error.message };
} else {
const executionData = this.helpers.constructExecutionMetaData(
this.helpers.returnJsonArray({ error: error.message }),
{ itemData: { item: i } },
);
returnData.push(...executionData);
}
continue;
}
throw error;
}
const executionData = this.helpers.constructExecutionMetaData(
this.helpers.returnJsonArray(responseData as IDataObject[]),
{ itemData: { item: i } },
);
returnData.push(...executionData);
}
if (resource === 'cameraProxy' && operation === 'getScreenshot') {
return [items];
} else {
return [returnData];
}
}
}
@@ -0,0 +1,71 @@
import type { INodeProperties } from 'n8n-workflow';
export const logOperations: INodeProperties[] = [
{
displayName: 'Operation',
name: 'operation',
type: 'options',
noDataExpression: true,
displayOptions: {
show: {
resource: ['log'],
},
},
options: [
{
name: 'Get Error Logs',
value: 'getErroLogs',
description: 'Get a log for a specific entity',
action: 'Get a log for an entity',
},
{
name: 'Get Logbook Entries',
value: 'getLogbookEntries',
description: 'Get all logs',
action: 'Get all logs for an entity',
},
],
default: 'getErroLogs',
},
];
export const logFields: INodeProperties[] = [
/* -------------------------------------------------------------------------- */
/* log:getLogbookEntries */
/* -------------------------------------------------------------------------- */
{
displayName: 'Additional Fields',
name: 'additionalFields',
type: 'collection',
placeholder: 'Add Field',
default: {},
displayOptions: {
show: {
resource: ['log'],
operation: ['getLogbookEntries'],
},
},
options: [
{
displayName: 'End Time',
name: 'endTime',
type: 'dateTime',
default: '',
description: 'The end of the period',
},
{
displayName: 'Entity ID',
name: 'entityId',
type: 'string',
default: '',
},
{
displayName: 'Start Time',
name: 'startTime',
type: 'dateTime',
default: '',
description: 'The beginning of the period',
},
],
},
];
@@ -0,0 +1,146 @@
import type { INodeProperties } from 'n8n-workflow';
export const serviceOperations: INodeProperties[] = [
{
displayName: 'Operation',
name: 'operation',
type: 'options',
noDataExpression: true,
displayOptions: {
show: {
resource: ['service'],
},
},
options: [
{
name: 'Call',
value: 'call',
description: 'Call a service within a specific domain',
action: 'Call a service',
},
{
name: 'Get Many',
value: 'getAll',
description: 'Get many services',
action: 'Get many services',
},
],
default: 'getAll',
},
];
export const serviceFields: INodeProperties[] = [
/* -------------------------------------------------------------------------- */
/* service:getAll */
/* -------------------------------------------------------------------------- */
{
displayName: 'Return All',
name: 'returnAll',
type: 'boolean',
displayOptions: {
show: {
operation: ['getAll'],
resource: ['service'],
},
},
default: false,
description: 'Whether to return all results or only up to a given limit',
},
{
displayName: 'Limit',
name: 'limit',
type: 'number',
displayOptions: {
show: {
operation: ['getAll'],
resource: ['service'],
returnAll: [false],
},
},
typeOptions: {
minValue: 1,
maxValue: 100,
},
default: 50,
description: 'Max number of results to return',
},
/* -------------------------------------------------------------------------- */
/* service:Call */
/* -------------------------------------------------------------------------- */
{
displayName: 'Domain Name or ID',
name: 'domain',
type: 'options',
description:
'Choose from the list, or specify an ID using an <a href="https://docs.n8n.io/code/expressions/">expression</a>',
typeOptions: {
loadOptionsMethod: 'getDomains',
},
default: '',
required: true,
displayOptions: {
show: {
resource: ['service'],
operation: ['call'],
},
},
},
{
displayName: 'Service Name or ID',
name: 'service',
type: 'options',
description:
'Choose from the list, or specify an ID using an <a href="https://docs.n8n.io/code/expressions/">expression</a>',
typeOptions: {
loadOptionsDependsOn: ['domain'],
loadOptionsMethod: 'getDomainServices',
},
default: '',
required: true,
displayOptions: {
show: {
resource: ['service'],
operation: ['call'],
},
},
},
{
displayName: 'Service Attributes',
name: 'serviceAttributes',
type: 'fixedCollection',
typeOptions: {
multipleValues: true,
},
placeholder: 'Add Attribute',
default: {},
displayOptions: {
show: {
resource: ['service'],
operation: ['call'],
},
},
options: [
{
name: 'attributes',
displayName: 'Attributes',
values: [
{
displayName: 'Name',
name: 'name',
type: 'string',
default: '',
description: 'Name of the field',
},
{
displayName: 'Value',
name: 'value',
type: 'string',
default: '',
description: 'Value of the field',
},
],
},
],
},
];
@@ -0,0 +1,168 @@
import type { INodeProperties } from 'n8n-workflow';
export const stateOperations: INodeProperties[] = [
{
displayName: 'Operation',
name: 'operation',
type: 'options',
noDataExpression: true,
displayOptions: {
show: {
resource: ['state'],
},
},
options: [
{
name: 'Create or Update',
value: 'upsert',
description: 'Create a new record, or update the current one if it already exists (upsert)',
action: 'Create or update a state',
},
{
name: 'Get',
value: 'get',
description: 'Get a state for a specific entity',
action: 'Get a state',
},
{
name: 'Get Many',
value: 'getAll',
description: 'Get many states',
action: 'Get many states',
},
],
default: 'get',
},
];
export const stateFields: INodeProperties[] = [
/* -------------------------------------------------------------------------- */
/* state:get */
/* -------------------------------------------------------------------------- */
{
displayName: 'Entity Name or ID',
name: 'entityId',
type: 'options',
description:
'Choose from the list, or specify an ID using an <a href="https://docs.n8n.io/code/expressions/">expression</a>',
typeOptions: {
loadOptionsMethod: 'getAllEntities',
},
displayOptions: {
show: {
operation: ['get'],
resource: ['state'],
},
},
required: true,
default: '',
},
/* -------------------------------------------------------------------------- */
/* state:getAll */
/* -------------------------------------------------------------------------- */
{
displayName: 'Return All',
name: 'returnAll',
type: 'boolean',
displayOptions: {
show: {
operation: ['getAll'],
resource: ['state'],
},
},
default: false,
description: 'Whether to return all results or only up to a given limit',
},
{
displayName: 'Limit',
name: 'limit',
type: 'number',
displayOptions: {
show: {
operation: ['getAll'],
resource: ['state'],
returnAll: [false],
},
},
typeOptions: {
minValue: 1,
maxValue: 100,
},
default: 50,
description: 'Max number of results to return',
},
/* -------------------------------------------------------------------------- */
/* state:upsert */
/* -------------------------------------------------------------------------- */
{
displayName: 'Entity Name or ID',
name: 'entityId',
type: 'options',
typeOptions: {
loadOptionsMethod: 'getAllEntities',
},
displayOptions: {
show: {
operation: ['upsert'],
resource: ['state'],
},
},
required: true,
default: '',
description:
'The entity ID for which a state will be created. Choose from the list, or specify an ID using an <a href="https://docs.n8n.io/code/expressions/">expression</a>.',
},
{
displayName: 'State',
name: 'state',
type: 'string',
default: '',
required: true,
displayOptions: {
show: {
resource: ['state'],
operation: ['upsert'],
},
},
},
{
displayName: 'State Attributes',
name: 'stateAttributes',
type: 'fixedCollection',
typeOptions: {
multipleValues: true,
},
placeholder: 'Add Attribute',
default: {},
displayOptions: {
show: {
resource: ['state'],
operation: ['upsert'],
},
},
options: [
{
displayName: 'Attributes',
name: 'attributes',
values: [
{
displayName: 'Name',
name: 'name',
type: 'string',
default: '',
description: 'Name of the attribute',
},
{
displayName: 'Value',
name: 'value',
type: 'string',
default: '',
description: 'Value of the attribute',
},
],
},
],
},
];
@@ -0,0 +1,45 @@
import type { INodeProperties } from 'n8n-workflow';
export const templateOperations: INodeProperties[] = [
{
displayName: 'Operation',
name: 'operation',
type: 'options',
noDataExpression: true,
displayOptions: {
show: {
resource: ['template'],
},
},
options: [
{
name: 'Create',
value: 'create',
description: 'Create a template',
action: 'Create a template',
},
],
default: 'create',
},
];
export const templateFields: INodeProperties[] = [
/* -------------------------------------------------------------------------- */
/* template:create */
/* -------------------------------------------------------------------------- */
{
displayName: 'Template',
name: 'template',
type: 'string',
displayOptions: {
show: {
resource: ['template'],
operation: ['create'],
},
},
required: true,
default: '',
description:
'Render a Home Assistant template. <a href="https://www.home-assistant.io/docs/configuration/templating/">See template docs for more information.</a>.',
},
];
@@ -0,0 +1,46 @@
{
"type": "object",
"properties": {
"attributes": {
"type": "object",
"properties": {
"friendly_name": {
"type": "string"
},
"supported_features": {
"type": "integer"
}
}
},
"context": {
"type": "object",
"properties": {
"id": {
"type": "string"
},
"parent_id": {
"type": "null"
},
"user_id": {
"type": "string"
}
}
},
"entity_id": {
"type": "string"
},
"last_changed": {
"type": "string"
},
"last_reported": {
"type": "string"
},
"last_updated": {
"type": "string"
},
"state": {
"type": "string"
}
},
"version": 2
}
@@ -0,0 +1,40 @@
{
"type": "object",
"properties": {
"attributes": {
"type": "object",
"properties": {
"device_class": {
"type": "string"
},
"friendly_name": {
"type": "string"
}
}
},
"context": {
"type": "object",
"properties": {
"id": {
"type": "string"
}
}
},
"entity_id": {
"type": "string"
},
"last_changed": {
"type": "string"
},
"last_reported": {
"type": "string"
},
"last_updated": {
"type": "string"
},
"state": {
"type": "string"
}
},
"version": 2
}
@@ -0,0 +1,97 @@
{
"type": "object",
"properties": {
"attributes": {
"type": "object",
"properties": {
"auto_update": {
"type": "boolean"
},
"device_trackers": {
"type": "array",
"items": {
"type": "string"
}
},
"editable": {
"type": "boolean"
},
"entity_picture": {
"type": "string"
},
"friendly_name": {
"type": "string"
},
"id": {
"type": "string"
},
"in_progress": {
"type": "boolean"
},
"installed_version": {
"type": "string"
},
"latest_version": {
"type": "string"
},
"latitude": {
"type": "number"
},
"longitude": {
"type": "number"
},
"release_summary": {
"type": "null"
},
"release_url": {
"type": "string"
},
"skipped_version": {
"type": "null"
},
"source": {
"type": "string"
},
"supported_features": {
"type": "integer"
},
"title": {
"type": "string"
},
"user_id": {
"type": "string"
}
}
},
"context": {
"type": "object",
"properties": {
"id": {
"type": "string"
},
"parent_id": {
"type": "null"
},
"user_id": {
"type": "null"
}
}
},
"entity_id": {
"type": "string"
},
"last_changed": {
"type": "string"
},
"last_reported": {
"type": "string"
},
"last_updated": {
"type": "string"
},
"state": {
"type": "string"
}
},
"version": 1
}
@@ -0,0 +1,35 @@
{
"type": "object",
"properties": {
"context": {
"type": "object",
"properties": {
"id": {
"type": "string"
},
"parent_id": {
"type": "null"
},
"user_id": {
"type": "string"
}
}
},
"entity_id": {
"type": "string"
},
"last_changed": {
"type": "string"
},
"last_reported": {
"type": "string"
},
"last_updated": {
"type": "string"
},
"state": {
"type": "string"
}
},
"version": 1
}
@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="240" height="240" fill="none"><path fill="#F2F4F9" d="M240 224.762c0 8.25-6.75 15-15 15H15c-8.25 0-15-6.75-15-15v-90c0-8.25 4.77-19.769 10.61-25.609l98.78-98.78c5.83-5.83 15.38-5.83 21.21 0l98.79 98.789c5.83 5.83 10.61 17.36 10.61 25.61v90z"/><path fill="#18BCF2" d="m229.39 109.153-98.78-98.78c-5.83-5.83-15.38-5.83-21.21 0l-98.79 98.78C4.78 114.983 0 126.512 0 134.762v90c0 8.25 6.75 15 15 15h92.27l-40.63-40.63c-2.09.72-4.32 1.13-6.64 1.13-11.3 0-20.5-9.2-20.5-20.5s9.2-20.5 20.5-20.5 20.5 9.2 20.5 20.5c0 2.33-.41 4.56-1.13 6.65l31.63 31.63v-115.88c-6.8-3.34-11.5-10.32-11.5-18.39 0-11.3 9.2-20.5 20.5-20.5s20.5 9.2 20.5 20.5c0 8.07-4.7 15.05-11.5 18.39v81.27l31.46-31.46c-.62-1.96-.96-4.04-.96-6.2 0-11.3 9.2-20.5 20.5-20.5s20.5 9.2 20.5 20.5-9.2 20.5-20.5 20.5c-2.5 0-4.88-.47-7.09-1.29L129 208.892v30.88h96c8.25 0 15-6.75 15-15v-90c0-8.25-4.77-19.77-10.61-25.61z"/></svg>

After

Width:  |  Height:  |  Size: 925 B