The workflow JSON
Copy or download the full n8n JSON below. Paste it into a new n8n workflow, add your credentials, activate. Full import guide →
{
"name": "hass_control",
"nodes": [
{
"parameters": {
"httpMethod": "POST",
"path": "hass_control",
"responseMode": "responseNode",
"options": {}
},
"id": "webhook",
"name": "Webhook",
"type": "n8n-nodes-base.webhook",
"typeVersion": 2,
"position": [
240,
300
],
"notes": "Control Home Assistant devices. Parameters: action (required: turn_on, turn_off, volume_up, volume_down, set_volume, mute, unmute, pause, play, next, previous), target (required: device name), value (optional: for set_volume 0-100)."
},
{
"parameters": {
"resource": "state",
"operation": "getAll",
"returnAll": true
},
"id": "get-states",
"name": "Get All States",
"type": "n8n-nodes-base.homeAssistant",
"typeVersion": 1,
"position": [
460,
300
],
"credentials": {
"homeAssistantApi": {
"name": "<your credential>"
}
}
},
{
"parameters": {
"jsCode": "const states = $input.all().map(i => i.json);\nconst body = $('Webhook').item.json.body || {};\nconst action = (body.action || '').toLowerCase();\nconst target = (body.target || '').toLowerCase();\nconst value = body.value;\n\nif (!action || !target) {\n return { error: true, message: \"Missing action or target parameter.\" };\n}\n\n// Find matching entity by friendly_name (fuzzy match)\nlet match = null;\nlet matchScore = 0;\n\nfor (const entity of states) {\n const friendlyName = (entity.attributes?.friendly_name || '').toLowerCase();\n const entityId = entity.entity_id.toLowerCase();\n \n // Exact match on friendly name\n if (friendlyName === target) {\n match = entity;\n matchScore = 100;\n break;\n }\n \n // Partial match on friendly name\n if (friendlyName.includes(target) || target.includes(friendlyName)) {\n const score = friendlyName.includes(target) ? 80 : 60;\n if (score > matchScore) {\n match = entity;\n matchScore = score;\n }\n }\n \n // Match on entity_id\n if (entityId.includes(target.replace(/\\s+/g, '_'))) {\n if (50 > matchScore) {\n match = entity;\n matchScore = 50;\n }\n }\n}\n\nif (!match) {\n // List available devices for helpful error\n const available = states\n .filter(e => ['switch', 'light', 'media_player', 'fan', 'cover'].includes(e.entity_id.split('.')[0]))\n .map(e => e.attributes?.friendly_name || e.entity_id)\n .slice(0, 10);\n return { \n error: true, \n message: `Couldn't find \"${body.target}\". Available devices: ${available.join(', ')}.`\n };\n}\n\n// Determine domain and service based on action\nconst domain = match.entity_id.split('.')[0];\nlet service = '';\nlet serviceData = { entity_id: match.entity_id };\n\nswitch (action) {\n case 'turnon':\n case 'turn_on':\n case 'on':\n service = 'turn_on';\n break;\n case 'turnoff':\n case 'turn_off':\n case 'off':\n service = 'turn_off';\n break;\n case 'toggle':\n service = 'toggle';\n break;\n case 'volumeup':\n case 'volume_up':\n service = 'volume_up';\n break;\n case 'volumedown':\n case 'volume_down':\n service = 'volume_down';\n break;\n case 'setvolume':\n case 'set_volume':\n case 'volume':\n service = 'volume_set';\n serviceData.volume_level = (value || 50) / 100;\n break;\n case 'mute':\n service = 'volume_mute';\n serviceData.is_volume_muted = true;\n break;\n case 'unmute':\n service = 'volume_mute';\n serviceData.is_volume_muted = false;\n break;\n case 'pause':\n service = 'media_pause';\n break;\n case 'play':\n service = 'media_play';\n break;\n case 'next':\n service = 'media_next_track';\n break;\n case 'previous':\n case 'prev':\n service = 'media_previous_track';\n break;\n default:\n return { error: true, message: `Unknown action \"${action}\". Use: TurnOn, TurnOff, VolumeUp, VolumeDown, SetVolume, Mute, Unmute, Pause, Play, Next, Previous.` };\n}\n\n// For switches, only turn_on/turn_off/toggle are valid\nif (domain === 'switch' && !['turn_on', 'turn_off', 'toggle'].includes(service)) {\n return { error: true, message: `Can't ${action} a switch. Use TurnOn or TurnOff.` };\n}\n\nreturn {\n error: false,\n domain: domain,\n service: service,\n serviceData: serviceData,\n friendlyName: match.attributes?.friendly_name || match.entity_id,\n action: body.action\n};"
},
"id": "find-entity",
"name": "Find Entity & Build Service",
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
680,
300
]
},
{
"parameters": {
"conditions": {
"options": {
"caseSensitive": true,
"leftValue": "",
"typeValidation": "strict",
"version": 2
},
"conditions": [
{
"id": "error-check",
"leftValue": "={{ $json.error }}",
"rightValue": true,
"operator": {
"type": "boolean",
"operation": "equals"
}
}
],
"combinator": "and"
},
"options": {}
},
"id": "if-error",
"name": "Check Error",
"type": "n8n-nodes-base.if",
"typeVersion": 2.2,
"position": [
900,
300
]
},
{
"parameters": {
"resource": "service",
"operation": "call",
"domain": "={{ $json.domain }}",
"service": "={{ $json.service }}",
"serviceAttributes": {
"attributes": [
{
"name": "entity_id",
"value": "={{ $json.serviceData.entity_id }}"
},
{
"name": "volume_level",
"value": "={{ $json.serviceData.volume_level }}"
},
{
"name": "is_volume_muted",
"value": "={{ $json.serviceData.is_volume_muted }}"
}
]
}
},
"id": "call-service",
"name": "Call HA Service",
"type": "n8n-nodes-base.homeAssistant",
"typeVersion": 1,
"position": [
1120,
200
],
"credentials": {
"homeAssistantApi": {
"name": "<your credential>"
}
}
},
{
"parameters": {
"jsCode": "const prev = $('Find Entity & Build Service').item.json;\nconst friendlyName = prev.friendlyName;\nconst action = prev.action;\n\n// Build natural response\nlet verb = '';\nswitch (action.toLowerCase()) {\n case 'turnon':\n case 'turn_on':\n case 'on':\n verb = 'Turned on';\n break;\n case 'turnoff':\n case 'turn_off':\n case 'off':\n verb = 'Turned off';\n break;\n case 'toggle':\n verb = 'Toggled';\n break;\n case 'volumeup':\n case 'volume_up':\n verb = 'Turned up volume on';\n break;\n case 'volumedown':\n case 'volume_down':\n verb = 'Turned down volume on';\n break;\n case 'setvolume':\n case 'set_volume':\n case 'volume':\n verb = 'Set volume on';\n break;\n case 'mute':\n verb = 'Muted';\n break;\n case 'unmute':\n verb = 'Unmuted';\n break;\n case 'pause':\n verb = 'Paused';\n break;\n case 'play':\n verb = 'Playing';\n break;\n case 'next':\n verb = 'Skipped to next on';\n break;\n case 'previous':\n case 'prev':\n verb = 'Went back on';\n break;\n default:\n verb = action;\n}\n\nreturn {\n message: `${verb} ${friendlyName}.`\n};"
},
"id": "format-success",
"name": "Format Success",
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
1340,
200
]
},
{
"parameters": {
"respondWith": "json",
"responseBody": "={{ $json }}",
"options": {}
},
"id": "respond-success",
"name": "Respond Success",
"type": "n8n-nodes-base.respondToWebhook",
"typeVersion": 1.1,
"position": [
1560,
200
]
},
{
"parameters": {
"respondWith": "json",
"responseBody": "={{ $json }}",
"options": {}
},
"id": "respond-error",
"name": "Respond Error",
"type": "n8n-nodes-base.respondToWebhook",
"typeVersion": 1.1,
"position": [
1120,
400
]
}
],
"connections": {
"Webhook": {
"main": [
[
{
"node": "Get All States",
"type": "main",
"index": 0
}
]
]
},
"Get All States": {
"main": [
[
{
"node": "Find Entity & Build Service",
"type": "main",
"index": 0
}
]
]
},
"Find Entity & Build Service": {
"main": [
[
{
"node": "Check Error",
"type": "main",
"index": 0
}
]
]
},
"Check Error": {
"main": [
[
{
"node": "Respond Error",
"type": "main",
"index": 0
}
],
[
{
"node": "Call HA Service",
"type": "main",
"index": 0
}
]
]
},
"Call HA Service": {
"main": [
[
{
"node": "Format Success",
"type": "main",
"index": 0
}
]
]
},
"Format Success": {
"main": [
[
{
"node": "Respond Success",
"type": "main",
"index": 0
}
]
]
}
},
"settings": {
"availableInMCP": true
}
}
Credentials you'll need
Each integration node will prompt for credentials when you import. We strip credential IDs before publishing — you'll add your own.
homeAssistantApi
For the full experience including quality scoring and batch install features for each workflow upgrade to Pro
About this workflow
hass_control. Uses homeAssistant. Webhook trigger; 8 nodes.
Source: https://github.com/SleepyMoz/CAAL-main/blob/16219a8e954dbb0d266aae0e581f091f5879e506/n8n-workflows/hass_control.json — original creator credit. Request a take-down →
Related workflows
Workflows that share integrations, category, or trigger type with this one. All free to copy and import.
A production-ready authentication workflow implementing secure user registration, login, token verification, and refresh token mechanisms. Perfect for adding authentication to any application without
Portfolio Orchestrator. Uses httpRequest. Webhook trigger; 59 nodes.
This n8n template demonstrates how a simple Multi-Layer Perceptron (MLP) neural network can predict housing prices. The prediction is based on four key features, processed through a three-layer model.