A comprehensive Home Assistant integration for Neovim
Features • Installation • Configuration • Commands • Pickers • Keymaps • Completion • Diagnostics • LSP
ha.nvim brings the power of Home Assistant directly into your Neovim editor. Edit your HA configuration files with real-time entity completion, inline diagnostics, interactive pickers, and a live WebSocket connection to your Home Assistant instance.
- Real-time Completion — Entity IDs, services, domains, integrations, and MDI icons via blink.cmp
- Interactive Pickers — Browse entities, services, integrations, and areas using snacks.nvim pickers with live preview, filtering, and multi-select insertion
- Inline Diagnostics — Validates entity IDs and service calls against your live registry
- Entity Hover — Press
Kon any entity ID to see its current state, attributes, area, and device info - LSP Integration — Optional Home Assistant Language Server with Mason auto-install
- WebSocket API — Live connection via
websocatfor real-time state and registry updates - Service Calls — Call HA services directly from Neovim
- Template Rendering — Render Jinja2 templates and see results in a dedicated output channel
- Statusline — Connection status indicator for lualine
- Workspace Detection — Auto-activates when editing HA config files
- Neovim ≥ 0.10 (0.11+ recommended for native LSP)
- websocat — WebSocket CLI client (required for API communication)
- plenary.nvim — HTTP requests
- blink.cmp — Completion engine
- snacks.nvim — Picker UI
- mason.nvim — Auto-install HA Language Server
- lualine.nvim — Statusline integration
- which-key.nvim — Keymap discovery
- Node.js — Required if using the HA Language Server
With lazy.nvim
{
"szsolt/ha.nvim",
dependencies = {
"nvim-lua/plenary.nvim",
"saghen/blink.cmp",
"folke/snacks.nvim",
},
ft = { "yaml", "home-assistant" },
cmd = { "Ha" },
event = "VeryLazy",
opts = {
-- See Configuration section below
},
config = function(_, opts)
require("ha").setup(opts)
end,
}With AstroNvim
Copy examples/astronvim.lua to your AstroNvim plugins directory (e.g., lua/plugins/home-assistant.lua). This includes keymaps, statusline, completion sources, and which-key integration out of the box.
Before using the plugin, you need to set up authentication with your Home Assistant instance:
- Run
:Ha auth setup - Enter your Home Assistant URL (e.g.,
http://homeassistant.local:8123) - Enter a Long-Lived Access Token from your HA profile
Credentials are encrypted and stored in stdpath('data')/ha_credentials.
Tip: If you have the VS Code Home Assistant extension configured, credentials will be auto-migrated on first launch.
require("ha").setup({
-- Logging: "trace", "debug", "info", "warn", "error", "fatal"
log_level = "info",
-- Language Server Protocol
lsp = {
enabled = false, -- Disable by default, toggle with <localleader>hL
auto_setup = true, -- Auto-setup when enabled in HA workspaces
server_path = nil, -- Auto-detected; set to override
dynamic = {
enabled = true, -- Enable dynamic installation via Mason
auto_install = true, -- Auto-install if server not found
},
settings = {
["vscode-home-assistant"] = {
ignoreCertificates = false,
},
},
},
-- Authentication
auth = {
auto_migrate = true, -- Auto-migrate from VS Code settings
storage_file = "ha_credentials",
},
-- UI
ui = {
statusline = { enabled = true },
notifications = { enabled = true, timeout = 5000 },
progress = {
enabled = true,
spinner_type = "braille", -- "braille", "wheel", "ascii", "none"
},
picker = {
layout = { preset = "default" },
},
},
-- Completion (blink.cmp sources)
completion = {
enabled = true,
sources = {
ha_entities = { enabled = true, priority = 100 },
ha_services = { enabled = true, priority = 90 },
ha_domains = { enabled = true, priority = 110 },
ha_mdi_icons = { enabled = true, priority = 80 },
},
},
-- Diagnostics
diagnostics = {
enabled = true,
on_change = true, -- Validate on text change (debounced)
debounce_ms = 1000,
validate_entities = true, -- Validate entity_id references
validate_services = true, -- Validate service calls
severity = "warn",
},
-- Commands
commands = {
prefix = "ha",
create_user_commands = true,
},
-- API
api = {
timeout = 10000,
retry_count = 3,
retry_delay = 1000,
},
-- Workspace detection
workspace = {
auto_detect = true,
indicators = {
"configuration.yaml",
".storage",
"home-assistant_v2.db",
"automations.yaml",
"scripts.yaml",
"scenes.yaml",
},
},
})All commands are available under the :Ha prefix.
| Command | Description |
|---|---|
:Ha |
Show command picker |
:Ha auth setup |
Interactive credential setup |
:Ha auth test |
Test connection and authentication |
:Ha auth show |
Show current auth details (token obscured) |
:Ha auth clear |
Clear stored credentials |
:Ha entities |
Open entity picker (auto-detects entity under cursor) |
:Ha entities <id> |
Open entity picker focused on specific entity |
:Ha services |
Open service picker |
:Ha integrations |
Open integration browser |
:Ha template |
Render Jinja2 template (current line or visual selection) |
:Ha template help |
Show template syntax help |
:Ha reload list |
Show reload options picker |
:Ha reload automations |
Reload automations |
:Ha reload scripts |
Reload scripts |
:Ha reload scenes |
Reload scenes |
:Ha reload all |
Reload all configuration |
:Ha restart |
Restart Home Assistant |
:Ha status |
Show connection status |
:Ha log |
Show HA error log |
:Ha open |
Open Home Assistant in browser |
:Ha diagnostics run |
Run diagnostics on current buffer |
:Ha diagnostics clear |
Clear diagnostics |
:Ha diagnostics refresh |
Refresh diagnostics for all buffers |
:Ha diagnostics stats |
Show diagnostics statistics |
:Ha lsp toggle |
Toggle HA Language Server on/off |
:Ha lsp start |
Start HA Language Server |
:Ha lsp stop |
Stop HA Language Server |
:Ha lsp restart |
Restart HA Language Server |
:Ha lsp status |
Show LSP status |
Pickers are powered by snacks.nvim and provide fuzzy search, live preview, and multi-select.
Open with :Ha entities or <localleader>he.
The entity picker shows all entities from your Home Assistant instance with:
- Fuzzy search by entity ID or friendly name
- Live preview with state, attributes, device info, and timeline
- Domain filtering — Press
<C-d>to filter by domain (e.g.,light,sensor) - Area filtering — Press
<C-l>to filter by area/room - Platform filtering — Press
<C-p>to filter by platform (e.g., MQTT, Z-Wave) - Device class filtering — Press
<C-u>to filter by device class - Toggle unavailable — Press
<C-h>to show/hide unavailable entities - Reset filters — Press
<C-r>to clear all filters - Insert into buffer — Press
<C-s>to insert selected entity ID(s) at cursor - Auto-detection — If your cursor is on an entity ID, the picker will focus it
Open with :Ha services or <localleader>hS.
- Fuzzy search by
domain.servicename - Live preview with description, fields, types, defaults, examples, and target info
- Insert into buffer — Press
<C-s>to insert selected service name(s) at cursor
Open with :Ha integrations or <localleader>hi.
- Browse all installed integrations
- Preview with manifest details, documentation links, and entity counts
Open with :Ha reload list.
- Quick access to reload automations, scripts, scenes, groups, and more
- Supports reloading all or individual domains
| Key | Action |
|---|---|
<CR> |
Confirm selection |
<Esc> |
Close picker |
<C-s> |
Insert selected item(s) into buffer at cursor |
<Tab> |
Toggle selection (multi-select) |
<C-d> |
Filter by domain (entity picker only) |
<C-l> |
Filter by area (entity picker only) |
<C-p> |
Filter by platform (entity picker only) |
<C-u> |
Filter by device class (entity picker only) |
<C-h> |
Toggle unavailable entities (entity picker only) |
<C-r> |
Reset filters (entity picker only) |
Keymaps use the <localleader>h prefix. With localleader set to ,, the keymaps are:
| Keymap | Action |
|---|---|
<localleader>h |
Show Home Assistant menu (which-key) |
<localleader>ha |
Setup authentication |
<localleader>ht |
Render template |
<localleader>hT |
Template help |
<localleader>he |
Entity picker |
<localleader>hS |
Service picker |
<localleader>hi |
Integration picker |
<localleader>hr |
Reload services |
<localleader>hR |
Restart Home Assistant |
<localleader>hs |
Show status |
<localleader>hl |
Show error log |
<localleader>ho |
Open in browser |
<localleader>hd |
Run diagnostics |
<localleader>hD |
Clear diagnostics |
<localleader>hL |
Toggle HA LSP |
K |
Hover entity info (falls back to LSP hover) |
Completion is provided through blink.cmp with the following sources:
Triggers after typing a domain prefix (e.g., light., sensor.). Shows entity IDs with friendly names and states from the live registry.
Completes domain.service names with descriptions.
Completes domain names extracted dynamically from your entity registry.
Completes integration domain names.
Triggers after mdi: prefix for Material Design Icons.
Add the sources to your blink.cmp configuration:
{
"saghen/blink.cmp",
opts = function(_, opts)
vim.list_extend(opts.sources.default, {
"ha_domains", "ha_entities", "ha_services",
"ha_integrations", "ha_mdi_icons",
})
opts.sources.providers.ha_entities = {
name = "Home Assistant Entities",
module = "ha.completion.entities",
score_offset = 100,
}
opts.sources.providers.ha_services = {
name = "Home Assistant Services",
module = "ha.completion.services",
score_offset = 90,
}
opts.sources.providers.ha_domains = {
name = "Home Assistant Domains",
module = "ha.completion.domains",
score_offset = 110,
}
opts.sources.providers.ha_mdi_icons = {
name = "Home Assistant MDI Icons",
module = "ha.completion.mdi",
score_offset = 80,
}
opts.sources.providers.ha_integrations = {
name = "Home Assistant Integrations",
module = "ha.completion.integrations",
score_offset = 85,
}
end,
}ha.nvim validates your YAML files against the live Home Assistant registry:
- Entity validation — Warns when an
entity_idreference doesn't exist in the registry - Service validation — Warns when a
servicecall references an unknown domain - Numeric values ignored — Float values like
0.5are not mistaken for entity IDs
Diagnostics run automatically on:
- Buffer enter (
BufEnter) - File save (
BufWritePost) - Text changes (
TextChanged,TextChangedI) — debounced (configurable)
Use :Ha diagnostics run to trigger manually, or :Ha diagnostics clear to dismiss.
ha.nvim integrates with the Home Assistant Language Server for schema validation, hover documentation, and completion.
The LSP is disabled by default. Toggle it with <localleader>hL or :Ha lsp toggle.
When enabled, the server is auto-detected from:
- Configured
lsp.server_path - Mason installation (
home-assistant-language-server) - Legacy locations
ha.nvim bundles a Mason registry for the Home Assistant Language Server. This pulls vscode-home-assistant from GitHub and builds it locally.
Requirements:
- Node.js — Required for building and running the language server
- npm — Comes with Node.js
Add the registry to your Mason configuration:
{
"williamboman/mason.nvim",
opts = {
registries = {
"lua:ha.mason-registry", -- Bundled with ha.nvim
"github:mason-org/mason-registry",
},
},
}Then enable auto-install in ha.nvim:
lsp = {
enabled = true,
dynamic = {
enabled = true,
auto_install = true,
},
}Press K with your cursor on an entity ID to see a floating window with:
- Friendly name and current state
- Unit of measurement
- Area and device information
- Platform
- All attributes
- Last changed timestamp
If the cursor is not on an entity ID, K falls back to standard LSP hover.
ha.nvim maintains an in-memory registry that mirrors your Home Assistant instance:
- Areas — All defined areas/rooms
- Devices — All registered devices with manufacturer, model info
- Entities — All entities enriched with device names, area resolution, and current state
- Integrations — All installed integrations
The registry is populated via WebSocket on first use and kept up-to-date with real-time subscriptions for state changes and registry updates.
Add the HA connection status to your statusline. With lualine:
{
"nvim-lualine/lualine.nvim",
opts = function(_, opts)
table.insert(opts.sections.lualine_x, {
function()
return require("ha.ui.status").get_status_text()
end,
cond = function()
return require("ha.utils").workspace.is_in_ha_environment()
end,
})
end,
}Run :checkhealth ha to verify:
- Plugin initialization
- Workspace detection
- Authentication status
- LSP client status
- Connection status
- Registry system
- Diagnostics system
- Dependencies
- Ensure you're in a Home Assistant workspace (check with
:Ha status) - Verify authentication with
:Ha auth test - Check that blink.cmp sources are registered
- Entity completion triggers after a domain prefix (e.g., type
light.)
- Ensure
websocatis installed and in your PATH - Verify your HA URL is accessible
- Check your access token is valid
- Run
:checkhealth hafor diagnostics
- Float values (e.g.,
0.5) are automatically ignored - If you see false warnings, check that your registry is initialized with
:Ha status - Use
:Ha diagnostics clearto dismiss
MIT License. See LICENSE for details.
Contributions are welcome! Please read the Contributing Guide before submitting a PR.
- Home Assistant — The amazing home automation platform
- vscode-home-assistant — Language server and inspiration
- snacks.nvim — Beautiful picker UI
- blink.cmp — Fast completion engine
