AutomationFlowsGeneral › Hass Control

Hass Control

hass_control. Uses homeAssistant. Webhook trigger; 8 nodes.

Webhook trigger★★★★☆ complexity8 nodesHome Assistant
General Trigger: Webhook Nodes: 8 Complexity: ★★★★☆ Added:

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 →

Download .json
{
  "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.

Pro

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 →

More General workflows → · Browse all categories →

Related workflows

Workflows that share integrations, category, or trigger type with this one. All free to copy and import.

General

Alarm. Uses homeAssistant. Webhook trigger; 10 nodes.

Home Assistant
General

Intercom 播放. Uses homeAssistant. Webhook trigger; 9 nodes.

Home Assistant
General

A production-ready authentication workflow implementing secure user registration, login, token verification, and refresh token mechanisms. Perfect for adding authentication to any application without

Crypto, Data Table, Execute Workflow Trigger
General

Portfolio Orchestrator. Uses httpRequest. Webhook trigger; 59 nodes.

HTTP Request
General

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.