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,65 @@
import type {
JsonObject,
IDataObject,
IExecuteFunctions,
ILoadOptionsFunctions,
IHttpRequestMethods,
IRequestOptions,
} from 'n8n-workflow';
import { NodeApiError } from 'n8n-workflow';
export async function philipsHueApiRequest(
this: IExecuteFunctions | ILoadOptionsFunctions,
method: IHttpRequestMethods,
resource: string,
body: any = {},
qs: IDataObject = {},
uri?: string,
headers: IDataObject = {},
): Promise<any> {
const options: IRequestOptions = {
headers: {
'Content-Type': 'application/json',
},
method,
body,
qs,
uri: uri || `https://api.meethue.com/route${resource}`,
json: true,
};
try {
if (Object.keys(headers).length !== 0) {
options.headers = Object.assign({}, options.headers, headers);
}
if (Object.keys(body as IDataObject).length === 0) {
delete options.body;
}
if (Object.keys(qs).length === 0) {
delete options.qs;
}
const response = await this.helpers.requestOAuth2.call(this, 'philipsHueOAuth2Api', options, {
tokenType: 'Bearer',
});
return response;
} catch (error) {
throw new NodeApiError(this.getNode(), error as JsonObject);
}
}
export async function getUser(this: IExecuteFunctions | ILoadOptionsFunctions): Promise<any> {
const { whitelist } = await philipsHueApiRequest.call(this, 'GET', '/api/0/config', {}, {});
//check if there is a n8n user
for (const user of Object.keys(whitelist as IDataObject)) {
if (whitelist[user].name === 'n8n') {
return user;
}
}
// n8n user was not fount then create the user
await philipsHueApiRequest.call(this, 'PUT', '/api/0/config', { linkbutton: true });
const { success } = await philipsHueApiRequest.call(this, 'POST', '/api', { devicetype: 'n8n' });
return success.username;
}
@@ -0,0 +1,328 @@
import type { INodeProperties } from 'n8n-workflow';
export const lightOperations: INodeProperties[] = [
{
displayName: 'Operation',
name: 'operation',
type: 'options',
noDataExpression: true,
displayOptions: {
show: {
resource: ['light'],
},
},
options: [
{
name: 'Delete',
value: 'delete',
description: 'Delete a light',
action: 'Delete a light',
},
{
name: 'Get',
value: 'get',
description: 'Retrieve a light',
action: 'Get a light',
},
{
name: 'Get Many',
value: 'getAll',
description: 'Retrieve many lights',
action: 'Get many lights',
},
{
name: 'Update',
value: 'update',
description: 'Update a light',
action: 'Update a light',
},
],
default: 'update',
},
];
export const lightFields: INodeProperties[] = [
/* -------------------------------------------------------------------------- */
/* light:delete */
/* -------------------------------------------------------------------------- */
{
displayName: 'Light ID',
name: 'lightId',
type: 'string',
required: true,
displayOptions: {
show: {
operation: ['delete'],
resource: ['light'],
},
},
default: '',
},
/* -------------------------------------------------------------------------- */
/* light:getAll */
/* -------------------------------------------------------------------------- */
{
displayName: 'Return All',
name: 'returnAll',
type: 'boolean',
displayOptions: {
show: {
operation: ['getAll'],
resource: ['light'],
},
},
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: ['light'],
returnAll: [false],
},
},
typeOptions: {
minValue: 1,
maxValue: 500,
},
default: 100,
description: 'Max number of results to return',
},
/* -------------------------------------------------------------------------- */
/* light:get */
/* -------------------------------------------------------------------------- */
{
displayName: 'Light ID',
name: 'lightId',
type: 'string',
required: true,
displayOptions: {
show: {
operation: ['get'],
resource: ['light'],
},
},
default: '',
},
/* -------------------------------------------------------------------------- */
/* light:update */
/* -------------------------------------------------------------------------- */
{
displayName: 'Light Name or ID',
name: 'lightId',
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: 'getLights',
},
required: true,
displayOptions: {
show: {
operation: ['update'],
resource: ['light'],
},
},
default: '',
},
{
displayName: 'On',
name: 'on',
type: 'boolean',
required: true,
displayOptions: {
show: {
operation: ['update'],
resource: ['light'],
},
},
default: true,
// eslint-disable-next-line n8n-nodes-base/node-param-description-boolean-without-whether
description: 'On/Off state of the light',
},
{
displayName: 'Additional Fields',
name: 'additionalFields',
type: 'collection',
placeholder: 'Add Field',
displayOptions: {
show: {
resource: ['light'],
operation: ['update'],
},
},
default: {},
options: [
{
displayName: 'Alert Effect',
name: 'alert',
type: 'options',
options: [
{
name: 'None',
value: 'none',
description: 'The light is not performing an alert effect',
},
{
name: 'Select',
value: 'select',
description: 'The light is performing one breathe cycle',
},
{
name: 'LSelect',
value: 'lselect',
description:
'The light is performing breathe cycles for 15 seconds or until an "alert": "none" command is received',
},
],
default: '',
description: 'The alert effect, is a temporary change to the bulbs state',
},
{
displayName: 'Brightness',
name: 'bri',
type: 'number',
typeOptions: {
minValue: 1,
maxValue: 254,
},
default: 100,
description:
'The brightness value to set the light to. Brightness is a scale from 1 (the minimum the light is capable of) to 254 (the maximum).',
},
{
displayName: 'Brightness Increments',
name: 'bri_inc',
type: 'number',
typeOptions: {
minValue: -254,
maxValue: 254,
},
default: 0,
description:
'Increments or decrements the value of the brightness. This value is ignored if the Brightness attribute is provided.',
},
{
displayName: 'Color Temperature',
name: 'ct',
type: 'number',
default: 0,
description:
'The Mired color temperature of the light. 2012 connected lights are capable of 153 (6500K) to 500 (2000K).',
},
{
displayName: 'Color Temperature Increments',
name: 'ct_inc',
type: 'number',
typeOptions: {
minValue: -65534,
maxValue: 65534,
},
default: 0,
description:
'Increments or decrements the value of the ct. ct_inc is ignored if the ct attribute is provided.',
},
{
displayName: 'Coordinates',
name: 'xy',
type: 'string',
default: '',
placeholder: '0.64394,0.33069',
description:
'The x and y coordinates of a color in CIE color space. The first entry is the x coordinate and the second entry is the y coordinate. Both x and y are between 0 and 1',
},
{
displayName: 'Coordinates Increments',
name: 'xy_inc',
type: 'string',
default: '',
placeholder: '0.5,0.5',
description:
'Increments or decrements the value of the xy. This value is ignored if the Coordinates attribute is provided. Any ongoing color transition is stopped. Max value [0.5, 0.5]',
},
{
displayName: 'Dynamic Effect',
name: 'effect',
type: 'options',
options: [
{
name: 'None',
value: 'none',
},
{
name: 'Color Loop',
value: 'colorloop',
},
],
default: '',
description: 'The dynamic effect of the light',
},
{
displayName: 'Hue',
name: 'hue',
type: 'number',
typeOptions: {
minValue: 0,
maxValue: 65535,
},
default: 0,
description:
'The hue value to set light to.The hue value is a wrapping value between 0 and 65535. Both 0 and 65535 are red, 25500 is green and 46920 is blue.',
},
{
displayName: 'Hue Increments',
name: 'hue_inc',
type: 'number',
typeOptions: {
minValue: -65534,
maxValue: 65534,
},
default: 0,
description:
'Increments or decrements the value of the hue. Hue Increments is ignored if the Hue attribute is provided.',
},
{
displayName: 'Saturation',
name: 'sat',
type: 'number',
typeOptions: {
minValue: 0,
maxValue: 254,
},
default: 0,
description:
'Saturation of the light. 254 is the most saturated (colored) and 0 is the least saturated (white).',
},
{
displayName: 'Saturation Increments',
name: 'sat_inc',
type: 'number',
typeOptions: {
minValue: -254,
maxValue: 254,
},
default: 0,
description:
'Increments or decrements the value of the sat. This value is ignored if the Saturation attribute is provided.',
},
{
displayName: 'Transition Time',
name: 'transitiontime',
type: 'number',
typeOptions: {
minVale: 1,
},
default: 4,
description:
'The duration in seconds of the transition from the lights current state to the new state',
},
],
},
];
@@ -0,0 +1,18 @@
{
"node": "n8n-nodes-base.philipsHue",
"nodeVersion": "1.0",
"codexVersion": "1.0",
"categories": ["Miscellaneous"],
"resources": {
"credentialDocumentation": [
{
"url": "https://docs.n8n.io/integrations/builtin/credentials/philipshue/"
}
],
"primaryDocumentation": [
{
"url": "https://docs.n8n.io/integrations/builtin/app-nodes/n8n-nodes-base.philipshue/"
}
]
}
}
@@ -0,0 +1,187 @@
import type {
IDataObject,
IExecuteFunctions,
ILoadOptionsFunctions,
INodeExecutionData,
INodePropertyOptions,
INodeType,
INodeTypeDescription,
} from 'n8n-workflow';
import { NodeConnectionTypes } from 'n8n-workflow';
import { getUser, philipsHueApiRequest } from './GenericFunctions';
import { lightFields, lightOperations } from './LightDescription';
export class PhilipsHue implements INodeType {
description: INodeTypeDescription = {
displayName: 'Philips Hue',
name: 'philipsHue',
// eslint-disable-next-line n8n-nodes-base/node-class-description-icon-not-svg
icon: 'file:philipshue.png',
group: ['input'],
version: 1,
subtitle: '={{$parameter["operation"] + ": " + $parameter["resource"]}}',
description: 'Consume Philips Hue API',
defaults: {
name: 'Philips Hue',
},
usableAsTool: true,
inputs: [NodeConnectionTypes.Main],
outputs: [NodeConnectionTypes.Main],
credentials: [
{
name: 'philipsHueOAuth2Api',
required: true,
},
],
properties: [
{
displayName: 'Resource',
name: 'resource',
type: 'options',
noDataExpression: true,
options: [
{
name: 'Light',
value: 'light',
},
],
default: 'light',
},
...lightOperations,
...lightFields,
],
};
methods = {
loadOptions: {
// Get all the lights to display them to user so that they can
// select them easily
async getLights(this: ILoadOptionsFunctions): Promise<INodePropertyOptions[]> {
const returnData: INodePropertyOptions[] = [];
const user = await getUser.call(this);
const lights = await philipsHueApiRequest.call(this, 'GET', `/api/${user}/lights`);
const groups = await philipsHueApiRequest.call(this, 'GET', `/api/${user}/groups`);
for (const light of Object.keys(lights as IDataObject)) {
let lightName = lights[light].name;
const lightId = light;
for (const groupId of Object.keys(groups as IDataObject)) {
if (groups[groupId].type === 'Room' && groups[groupId].lights.includes(lightId)) {
lightName = `${groups[groupId].name}: ${lightName}`;
}
}
returnData.push({
name: lightName,
value: lightId,
});
}
return returnData;
},
},
};
async execute(this: IExecuteFunctions): Promise<INodeExecutionData[][]> {
const items = this.getInputData();
const returnData: INodeExecutionData[] = [];
const length = items.length;
let responseData;
const resource = this.getNodeParameter('resource', 0);
const operation = this.getNodeParameter('operation', 0);
for (let i = 0; i < length; i++) {
if (resource === 'light') {
if (operation === 'update') {
const lightId = this.getNodeParameter('lightId', i) as string;
const on = this.getNodeParameter('on', i) as boolean;
const additionalFields = this.getNodeParameter('additionalFields', i);
const body = {
on,
};
if (additionalFields.transitiontime) {
additionalFields.transitiontime = (additionalFields.transitiontime as number) * 100;
}
if (additionalFields.xy) {
additionalFields.xy = (additionalFields.xy as string)
.split(',')
.map((e: string) => parseFloat(e));
}
if (additionalFields.xy_inc) {
additionalFields.xy_inc = (additionalFields.xy_inc as string)
.split(',')
.map((e: string) => parseFloat(e));
}
Object.assign(body, additionalFields);
const user = await getUser.call(this);
const data = await philipsHueApiRequest.call(
this,
'PUT',
`/api/${user}/lights/${lightId}/state`,
body,
);
responseData = {};
for (const response of data) {
Object.assign(responseData, response.success);
}
}
if (operation === 'delete') {
const lightId = this.getNodeParameter('lightId', i) as string;
const user = await getUser.call(this);
responseData = await philipsHueApiRequest.call(
this,
'DELETE',
`/api/${user}/lights/${lightId}`,
);
}
if (operation === 'getAll') {
const returnAll = this.getNodeParameter('returnAll', i);
const user = await getUser.call(this);
const lights = await philipsHueApiRequest.call(this, 'GET', `/api/${user}/lights`);
responseData = Object.values(lights as IDataObject);
if (!returnAll) {
const limit = this.getNodeParameter('limit', i);
responseData = responseData.splice(0, limit);
}
}
if (operation === 'get') {
const lightId = this.getNodeParameter('lightId', i) as string;
const user = await getUser.call(this);
responseData = await philipsHueApiRequest.call(
this,
'GET',
`/api/${user}/lights/${lightId}`,
);
}
}
const executionData = this.helpers.constructExecutionMetaData(
this.helpers.returnJsonArray(responseData as IDataObject[]),
{ itemData: { item: i } },
);
returnData.push(...executionData);
}
return [returnData];
}
}
@@ -0,0 +1,163 @@
{
"type": "object",
"properties": {
"capabilities": {
"type": "object",
"properties": {
"certified": {
"type": "boolean"
},
"control": {
"type": "object",
"properties": {
"colorgamut": {
"type": "array",
"items": {
"type": "array",
"items": {
"type": "number"
}
}
},
"colorgamuttype": {
"type": "string"
},
"ct": {
"type": "object",
"properties": {
"max": {
"type": "integer"
},
"min": {
"type": "integer"
}
}
},
"maxlumen": {
"type": "integer"
},
"mindimlevel": {
"type": "integer"
}
}
},
"streaming": {
"type": "object",
"properties": {
"proxy": {
"type": "boolean"
},
"renderer": {
"type": "boolean"
}
}
}
}
},
"config": {
"type": "object",
"properties": {
"archetype": {
"type": "string"
},
"direction": {
"type": "string"
},
"function": {
"type": "string"
},
"startup": {
"type": "object",
"properties": {
"configured": {
"type": "boolean"
},
"mode": {
"type": "string"
}
}
}
}
},
"manufacturername": {
"type": "string"
},
"modelid": {
"type": "string"
},
"name": {
"type": "string"
},
"productid": {
"type": "string"
},
"productname": {
"type": "string"
},
"state": {
"type": "object",
"properties": {
"alert": {
"type": "string"
},
"bri": {
"type": "integer"
},
"colormode": {
"type": "string"
},
"ct": {
"type": "integer"
},
"effect": {
"type": "string"
},
"hue": {
"type": "integer"
},
"mode": {
"type": "string"
},
"on": {
"type": "boolean"
},
"reachable": {
"type": "boolean"
},
"sat": {
"type": "integer"
},
"xy": {
"type": "array",
"items": {
"type": "number"
}
}
}
},
"swconfigid": {
"type": "string"
},
"swupdate": {
"type": "object",
"properties": {
"lastinstall": {
"type": "string"
},
"state": {
"type": "string"
}
}
},
"swversion": {
"type": "string"
},
"type": {
"type": "string"
},
"uniqueid": {
"type": "string"
}
},
"version": 1
}
@@ -0,0 +1,160 @@
{
"type": "object",
"properties": {
"capabilities": {
"type": "object",
"properties": {
"certified": {
"type": "boolean"
},
"control": {
"type": "object",
"properties": {
"colorgamut": {
"type": "array",
"items": {
"type": "array",
"items": {
"type": "number"
}
}
},
"colorgamuttype": {
"type": "string"
},
"ct": {
"type": "object",
"properties": {
"max": {
"type": "integer"
},
"min": {
"type": "integer"
}
}
},
"maxlumen": {
"type": "integer"
},
"mindimlevel": {
"type": "integer"
}
}
},
"streaming": {
"type": "object",
"properties": {
"proxy": {
"type": "boolean"
},
"renderer": {
"type": "boolean"
}
}
}
}
},
"config": {
"type": "object",
"properties": {
"archetype": {
"type": "string"
},
"direction": {
"type": "string"
},
"function": {
"type": "string"
},
"startup": {
"type": "object",
"properties": {
"configured": {
"type": "boolean"
},
"mode": {
"type": "string"
}
}
}
}
},
"manufacturername": {
"type": "string"
},
"modelid": {
"type": "string"
},
"name": {
"type": "string"
},
"productid": {
"type": "string"
},
"productname": {
"type": "string"
},
"state": {
"type": "object",
"properties": {
"alert": {
"type": "string"
},
"bri": {
"type": "integer"
},
"colormode": {
"type": "string"
},
"ct": {
"type": "integer"
},
"effect": {
"type": "string"
},
"hue": {
"type": "integer"
},
"mode": {
"type": "string"
},
"on": {
"type": "boolean"
},
"reachable": {
"type": "boolean"
},
"sat": {
"type": "integer"
},
"xy": {
"type": "array",
"items": {
"type": "number"
}
}
}
},
"swconfigid": {
"type": "string"
},
"swupdate": {
"type": "object",
"properties": {
"state": {
"type": "string"
}
}
},
"swversion": {
"type": "string"
},
"type": {
"type": "string"
},
"uniqueid": {
"type": "string"
}
},
"version": 1
}
@@ -0,0 +1,8 @@
{
"type": "object",
"properties": {
"/lights/3/state/on": {
"type": "boolean"
}
}
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 KiB

@@ -0,0 +1,108 @@
export const getLightsResponse = {
'1': {
state: {
on: false,
bri: 1,
hue: 33761,
sat: 254,
effect: 'none',
xy: [0.3171, 0.3366],
ct: 159,
alert: 'none',
colormode: 'xy',
mode: 'homeautomation',
reachable: true,
},
swupdate: {
state: 'noupdates',
lastinstall: '2018-01-02T19:24:20',
},
type: 'Extended color light',
name: 'Hue color lamp 7',
modelid: 'LCT007',
manufacturername: 'Philips',
productname: 'Hue color lamp',
capabilities: {
certified: true,
control: {
mindimlevel: 5000,
maxlumen: 600,
colorgamuttype: 'B',
colorgamut: [
[0.675, 0.322],
[0.409, 0.518],
[0.167, 0.04],
],
ct: {
min: 153,
max: 500,
},
},
streaming: {
renderer: true,
proxy: false,
},
},
config: {
archetype: 'sultanbulb',
function: 'mixed',
direction: 'omnidirectional',
},
uniqueid: '00:17:88:01:00:bd:c7:b9-0b',
swversion: '5.105.0.21169',
},
};
export const getConfigResponse = {
name: 'Philips hue',
zigbeechannel: 15,
mac: '00:17:88:00:00:00',
dhcp: true,
ipaddress: '192.168.1.7',
netmask: '255.255.255.0',
gateway: '192.168.1.1',
proxyaddress: 'none',
proxyport: 0,
UTC: '2014-07-17T09:27:35',
localtime: '2014-07-17T11:27:35',
timezone: 'Europe/Madrid',
whitelist: {
ffffffffe0341b1b376a2389376a2389: {
'last use date': '2014-07-17T07:21:38',
'create date': '2014-04-08T08:55:10',
name: 'PhilipsHueAndroidApp#TCT ALCATEL ONE TOU',
},
pAtwdCV8NZId25Gk: {
'last use date': '2014-05-07T18:28:29',
'create date': '2014-04-09T17:29:16',
name: 'n8n',
},
},
swversion: '01012917',
apiversion: '1.3.0',
swupdate: {
updatestate: 0,
url: '',
text: '',
notify: false,
},
linkbutton: false,
portalservices: false,
portalconnection: 'connected',
portalstate: {
signedon: true,
incoming: false,
outgoing: true,
communication: 'disconnected',
},
};
export const updateLightResponse = [
{ success: { '/lights/1/state/bri': 200 } },
{ success: { '/lights/1/state/on': true } },
{ success: { '/lights/1/state/hue': 50000 } },
];
export const deleteLightResponse = {
success: '/lights/1 deleted.',
};
@@ -0,0 +1,227 @@
{
"name": "[TEST] Hue",
"nodes": [
{
"parameters": {},
"type": "n8n-nodes-base.manualTrigger",
"typeVersion": 1,
"position": [0, 0],
"id": "9e687c5d-e3a5-436d-a96d-7bfde1110f40",
"name": "When clicking Execute workflow"
},
{
"parameters": {
"operation": "getAll",
"returnAll": true
},
"type": "n8n-nodes-base.philipsHue",
"typeVersion": 1,
"position": [260, 0],
"id": "063bcf31-3055-42f1-904f-d517f85db182",
"name": "Philips Hue",
"credentials": {
"philipsHueOAuth2Api": {
"id": "s6tRicNTKQoa9TDq",
"name": "PhilipHue account"
}
}
},
{
"parameters": {},
"type": "n8n-nodes-base.noOp",
"typeVersion": 1,
"position": [480, 0],
"id": "d6c4f99a-d660-4113-aa08-a7bcc9391236",
"name": "No Operation, do nothing"
},
{
"parameters": {
"lightId": "=1",
"additionalFields": {
"bri": 200,
"hue": 50000
}
},
"type": "n8n-nodes-base.philipsHue",
"typeVersion": 1,
"position": [260, 200],
"id": "c8311112-d24e-4787-80b1-cc5397aea98e",
"name": "Philips Hue1",
"credentials": {
"philipsHueOAuth2Api": {
"id": "s6tRicNTKQoa9TDq",
"name": "PhilipHue account"
}
}
},
{
"parameters": {},
"type": "n8n-nodes-base.noOp",
"typeVersion": 1,
"position": [480, 200],
"id": "c438bf28-0cf8-459a-bd3a-28f6c30b3f2f",
"name": "No Operation, do nothing1"
},
{
"parameters": {
"operation": "delete",
"lightId": "=1"
},
"type": "n8n-nodes-base.philipsHue",
"typeVersion": 1,
"position": [280, 400],
"id": "fd197627-9221-4e6d-ab51-53d262004db8",
"name": "Philips Hue2",
"credentials": {
"philipsHueOAuth2Api": {
"id": "s6tRicNTKQoa9TDq",
"name": "PhilipHue account"
}
}
},
{
"parameters": {},
"type": "n8n-nodes-base.noOp",
"typeVersion": 1,
"position": [500, 400],
"id": "19796164-8527-462b-b01b-71d7f60c94af",
"name": "No Operation, do nothing2"
}
],
"pinData": {
"No Operation, do nothing": [
{
"json": {
"state": {
"on": false,
"bri": 1,
"hue": 33761,
"sat": 254,
"effect": "none",
"xy": [0.3171, 0.3366],
"ct": 159,
"alert": "none",
"colormode": "xy",
"mode": "homeautomation",
"reachable": true
},
"swupdate": {
"state": "noupdates",
"lastinstall": "2018-01-02T19:24:20"
},
"type": "Extended color light",
"name": "Hue color lamp 7",
"modelid": "LCT007",
"manufacturername": "Philips",
"productname": "Hue color lamp",
"capabilities": {
"certified": true,
"control": {
"mindimlevel": 5000,
"maxlumen": 600,
"colorgamuttype": "B",
"colorgamut": [[0.675, 0.322], [0.409, 0.518], [0.167, 0.04]],
"ct": {
"min": 153,
"max": 500
}
},
"streaming": {
"renderer": true,
"proxy": false
}
},
"config": {
"archetype": "sultanbulb",
"function": "mixed",
"direction": "omnidirectional"
},
"uniqueid": "00:17:88:01:00:bd:c7:b9-0b",
"swversion": "5.105.0.21169"
}
}
],
"No Operation, do nothing1": [
{
"json": {
"/lights/1/state/bri": 200,
"/lights/1/state/on": true,
"/lights/1/state/hue": 50000
}
}
],
"No Operation, do nothing2": [
{
"json": {
"success": "/lights/1 deleted."
}
}
]
},
"connections": {
"When clicking Execute workflow": {
"main": [
[
{
"node": "Philips Hue",
"type": "main",
"index": 0
},
{
"node": "Philips Hue1",
"type": "main",
"index": 0
},
{
"node": "Philips Hue2",
"type": "main",
"index": 0
}
]
]
},
"Philips Hue": {
"main": [
[
{
"node": "No Operation, do nothing",
"type": "main",
"index": 0
}
]
]
},
"Philips Hue1": {
"main": [
[
{
"node": "No Operation, do nothing1",
"type": "main",
"index": 0
}
]
]
},
"Philips Hue2": {
"main": [
[
{
"node": "No Operation, do nothing2",
"type": "main",
"index": 0
}
]
]
}
},
"active": false,
"settings": {
"executionOrder": "v1"
},
"versionId": "2c19891b-ba8e-4026-b8c2-b5f3f508b54a",
"meta": {
"instanceId": "0fa937d34dcabeff4bd6480d3b42cc95edf3bc20e6810819086ef1ce2623639d"
},
"id": "LAngRPRzm3OGrfh0",
"tags": []
}
@@ -0,0 +1,42 @@
import { NodeTestHarness } from '@nodes-testing/node-test-harness';
import nock from 'nock';
import {
deleteLightResponse,
getLightsResponse,
getConfigResponse,
updateLightResponse,
} from './apiResponses';
describe('PhilipsHue', () => {
const credentials = {
philipsHueOAuth2Api: {
grantType: 'authorizationCode',
appId: 'APPID',
authUrl: 'https://api.meethue.com/v2/oauth2/authorize',
accessTokenUrl: 'https://api.meethue.com/v2/oauth2/token',
authQueryParameters: 'appid=APPID',
scope: '',
authentication: 'header',
oauthTokenData: {
access_token: 'ACCESSTOKEN',
refresh_token: 'REFRESHTOKEN',
scope: '',
token_type: 'bearer',
expires_in: 86400,
},
},
};
describe('Run workflow', () => {
beforeAll(() => {
const mock = nock('https://api.meethue.com/route');
mock.persist().get('/api/0/config').reply(200, getConfigResponse);
mock.get('/api/pAtwdCV8NZId25Gk/lights').reply(200, getLightsResponse);
mock.put('/api/pAtwdCV8NZId25Gk/lights/1/state').reply(200, updateLightResponse);
mock.delete('/api/pAtwdCV8NZId25Gk/lights/1').reply(200, deleteLightResponse);
});
new NodeTestHarness().setupTests({ credentials });
});
});