As we know, Home Assistant has a huge ecosystem of integrations — far more than openHAB currently supports. Many of us quietly run HA alongside openHAB just to fill those gaps.
So I wrote a new binding: Home Assistant Link (HassLink).
I’m looking for people who already use Home Assistant to augment openHAB’s device support and would like to help test this binding, try it with real setups, and report any issues or edge cases.
Plan:
Once this is somewhat stable, I plan to submit it to the openhab project to become an official addon
Status:
Discovery works, updates flow, commands round‑trip, and most entity types are supported.
But I don’t have a lot of devices on my Home Assistant instance, so my testing has been limited. So far I have tested:
- Navimow HA integration — the HA integration shows the map image!
- Bambulab Integration with P1S — toggle chamber light, see status, see camera
- Mammotion Mower integration
I’m specifically looking for people who have more diverse HA setups and can help validate the binding across a wider range of integrations and entity types.
README - Click to expand
Home Assistant Link Binding
The Home Assistant Link binding (hasslink) connects to a Home Assistant instance via its native WebSocket API, exposing Home Assistant devices and entities as native openHAB Things, Channels, and Items with real-time, bi-directional synchronization.
While openHAB offers robust scripting, flexible rule engines, and tailored UIs, Home Assistant’s massive contributor base rapidly adds support for new smart home hardware and niche integrations.
This binding bridges that gap—allowing you to use Home Assistant as an integration backend for hard-to-integrate devices while keeping openHAB as your primary home automation platform.
[[toc]]
Features
- Vast Ecosystem Expansion: Instantly extends openHAB compatibility to thousands of Home Assistant-supported integrations and hardware.
- Native WebSocket Communication: Connects directly to Home Assistant’s built-in
/api/websocket—no extra integrations, add-ons, or Home Assistant-side modifications required. - Automatic Unit Conversion: Maps Home Assistant units of measurement (
°C,°F,W,kWh,hPa, etc.) directly to openHABQuantityTypeitems (Number:Temperature,Number:Power, etc.). - Dynamic Channel Generation: Dynamically creates channels based on entity state attributes upon discovery or configuration.
- Unified Channel Naming: Predictable channel structure using
domain-entity_namefor primary channels anddomain-entity_name#attributefor secondary attributes. - Bi-directional Control: Full command mapping for switches, dimmers, colors, climate controls, covers, fans, media players, and more.
- Rule Engine Actions: Execute Home Assistant service calls, trigger buttons, run scripts, and activate scenes directly from openHAB automation rules.
Prerequisites & Setup
1. Home Assistant Instance
You need a running Home Assistant instance on your local network. If you do not already have one, it can be run as a lightweight container alongside openHAB.
For full installation options, see the official Home Assistant Installation Guide.
Minimal Docker Compose Example
If running in Docker, a basic setup requires network_mode: host to allow mDNS discovery and local API connectivity:
version: '3.8'
services:
homeassistant:
container_name: homeassistant
image: ghcr.io/home-assistant/home-assistant:stable
volumes:
- /opt/homeassistant/config:/config
- /etc/localtime:/etc/localtime:ro
restart: unless-stopped
network_mode: host
2. Generate a Long-Lived Access Token
openHAB authenticates with Home Assistant using a token:
- Open your Home Assistant UI in a web browser.
- Click your user profile icon (bottom-left corner).
- Select the Security tab at the top of the profile page.
- Scroll down to the Long-Lived Access Tokens section.
- Select Create Token, enter a name (e.g.,
openHAB Link), and copy the generated token string. - Store this token securely. You will provide it when configuring the
hasslink:serverThing in openHAB.
Discovery
The binding supports automatic discovery for both the Home Assistant server itself and its connected entities.
Server Bridge Discovery (mDNS)
If Home Assistant is running on your local network with mDNS enabled, openHAB will automatically discover the Home Assistant server instance:
- Go to Settings → Things → Inbox in openHAB.
- Choose the discovered Home Assistant server from the Inbox.
- Enter your Long-Lived Access Token in the Bridge configuration and save.
Entity & Device Discovery
Once the server Bridge is online, device discovery runs automatically:
- The binding queries the Home Assistant WebSocket API and device/entity registries.
- Discovered physical and logical hardware devices appear in your openHAB Inbox as Things (e.g.,
hasslink:device:XXXXX:XXXXX). - Adding a Thing from the Inbox automatically generates its primary channel and any mapped attribute channels.
- Real-Time Additions: Devices added to Home Assistant while openHAB is running are automatically detected and added to the Inbox without requiring a restart.
Filtered Discovery & Manual Scans:
To prevent cluttering your openHAB Inbox with hundreds of virtual helpers, automations, scripts, persons, and system domains that are typically of lower direct interest, auto-discovery focuses exclusively on physical and logical devices by default.
Skipped entities and independent items can still be added manually or configured via discovery filter options on the Bridge.If you add or rename entities in Home Assistant and want to refresh the Inbox immediately, go to Settings → Things → + (Add), select Home Assistant Link Binding, and trigger a manual scan.
Thing Configuration
Bridge (server)
The server Bridge manages the persistent WebSocket connection to Home Assistant Core and controls global Inbox discovery filters. Standard wildcard glob patterns (*, ?) are supported across area, domain, and label filters.
| Parameter | Type | Default | Required | Description |
|---|---|---|---|---|
host |
Text | N/A | Yes | Hostname or IP address of Home Assistant |
port |
Integer | 8123 |
No | WebSocket/HTTP port |
token |
Text | N/A | Yes | Home Assistant Long-Lived Access Token |
secure |
Boolean | false |
No | Use TLS/SSL (wss://) |
ignoreIndependentEntities |
Boolean | true |
No | Exclude entities without a parent device (helpers, automations, person tracking) from Inbox discovery |
includedAreas |
Text (Multiple) | [] |
No | Whitelist of Home Assistant Area IDs to discover (supports glob patterns, e.g., *bedroom*) |
excludedAreas |
Text (Multiple) | [] |
No | Blacklist of Home Assistant Area IDs to exclude from discovery (supports glob patterns) |
includedDomains |
Text (Multiple) | [] |
No | Whitelist of Home Assistant domains to discover (supports glob patterns, e.g., light*) |
excludedDomains |
Text (Multiple) | See desc. | No | Blacklist of Home Assistant domains to exclude from discovery (supports glob patterns). Default: automation,conversation,counter,media_source,persistent_notification,stt,sun,timer,tts,zone |
includedLabels |
Text (Multiple) | [] |
No | Whitelist of Home Assistant Labels/Tags to discover (supports glob patterns, e.g., openhab*) |
excludedLabels |
Text (Multiple) | [] |
No | Blacklist of Home Assistant Labels/Tags to ignore during discovery (supports glob patterns) |
Device / Entity Container (device)
The device Thing maps one or more Home Assistant entities to openHAB channels and provides per-Thing channel creation filters. Standard wildcard glob patterns (*, ?) are supported across all filter fields.
| Parameter | Type | Default | Required | Description |
|---|---|---|---|---|
deviceId |
Text | N/A | No | Home Assistant device registry identifier |
entityIds |
Text (Multiple) | [] |
No | Explicitly defined entity IDs or glob patterns; bypasses all device exclusions and filter rules |
includedDomains |
Text (Multiple) | [] |
No | Whitelist of Home Assistant domains to restrict channels to (supports glob patterns, e.g., sensor*) |
excludedDomains |
Text (Multiple) | [] |
No | Blacklist of Home Assistant domains to exclude from creating channels (supports glob patterns, e.g., update*) |
includedLabels |
Text (Multiple) | [] |
No | Whitelist of Home Assistant Labels/Tags for child entities (supports glob patterns) |
excludedLabels |
Text (Multiple) | [] |
No | Blacklist of Home Assistant Labels/Tags for child entities (supports glob patterns) |
includedEntities |
Text (Multiple) | [] |
No | Specific Entity IDs or glob patterns (e.g., sensor.*bed*, *_temperature) to explicitly allow as channels |
excludedEntities |
Text (Multiple) | [] |
No | Specific Entity IDs or glob patterns (e.g., *battery*, *_rssi) to exclude from channel creation |
Note: At least one of
deviceIdorentityIdsmust be defined.
For full evaluation semantics and details on composite filtering, see filtering.
::: tip
The device ID can be obtained from Settings → Devices & Services → Devices.
Click on the device you want to use.
The device ID is the final segment of the URL.
For example:
https://homeassistant.local/config/devices/device/5a471c1a7211f02d76869838fe451d68
The device ID is:
5a471c1a7211f02d76869838fe451d68
:::
Supported Entity Domains
The binding includes dedicated mapping logic for over 35 Home Assistant entity domains:
- Lighting & Power:
light,switch,button,input_button,scene,script - Sensors:
sensor,binary_sensor,device_tracker,geolocation,event - Climate & Environment:
climate,humidifier,water_heater,weather,fan - Covers & Locks:
cover,lock,valve - Media & Remotes:
media_player,remote,camera,image - Controls & Inputs:
number,input_number,select,input_select,text,input_text,date,time,datetime - Robotics & Vacuum:
vacuum,lawn_mower,siren - System & Infrastructure:
update,alarm_control_panel,infrared,radio_frequency
Channel Types & Event Handling
The binding utilizes standard openHAB channel types mapped dynamically across entities:
- Standard State Channels:
color,contact,datetime,dimmer,image,location,number,player,rollershutter,string,switch - Event Channel (
event): Used exclusively for the Home Assistantevententity domain.
Event Channel Behavior (event Domain)
For Home Assistant event entities, event acts purely as an openHAB Trigger channel:
- Rule Triggers: Incoming Home Assistant events fire openHAB channel trigger events directly (e.g.,
channel "hasslink:..." triggered). - Item Linking: If you wish to post commands or update Item states from incoming events, link the channel to an Item using standard openHAB Profiles or rules.
Weather Forecast Channels (weather Domain)
When supported by the underlying integration, forecast channels generated for weather entities update openHAB channels with time-series data.
Channel Naming Conventions
Channels are generated dynamically using the following structure:
-
Primary Channel:
domain-entity_name(e.g.,light-living_room_light,sensor-living_room_temperature)
Exposes the primary state of the entity (e.g.,ON/OFF, numeric values, or string states). -
Domain Attribute Channel:
domain-entity_name#attribute_name(e.g.,light-living_room_light#color_temp,climate-hallway#target_temperature)
Exposes first-class, strongly typed mapped attributes explicitly handled by specific entity implementations. -
Generic Attribute Channel (Sensor Entities):
sensor-entity_name#attribute_name(e.g.,sensor-weather_station#pressure_trend)
Auto-discovered fallback attributes generated exclusively for sensor entities from the Home Assistant state payload when not mapped as primary channels.
List and map attributes are provided as JSON strings. -
Raw JSON Attributes Channel:
domain-entity_name#json_attributes
Exposes the entire Home Assistant entity attributes payload as a serialized JSON string.Note: This channel is not created automatically.
To opt in, you must manually create a custom channel with the following configuration parameters:entityId: <entity_id>attribute: json_attributes
Once defined, payload serialization is evaluated lazily and only executes when an Item is actively linked to the channel.
Home Assistant to openHAB Conversion Quirks
Cover Entity
Percentage values for cover entities are inverted in openHAB to comply with standard openHAB roller shutter/cover conventions.
| Meaning | Home Assistant | openHAB |
|---|---|---|
| open | 100% | 0% (UpDownType.UP) |
| closed | 0% | 100% (UpDownType.DOWN) |
Light Entity
The Home Assistant light entity is mapped into openHAB channels based on the light’s supported capabilities.
The primary channel adapts automatically, and additional attribute channels are created only when the device exposes the corresponding features.
| Channel | openHAB Item Type | Notes |
|---|---|---|
Primary |
Color / Dimmer / Switch |
Color if any color modes are supported; Dimmer if brightness is supported; otherwise Switch. |
#color_temp |
Number:Temperature |
Created when the light supports color temperature mode. Always expressed in Kelvin, even if HA reports mireds. |
#color_temp_percent |
Dimmer |
Normalized color temperature (0–100%), where 0% = Cool and 100% = Warm. |
#effect |
String |
Created when the light supports effect selection. |
Primary Channel Behavior
-
Color Lights (
ColorItem):
A single channel controls color, brightness, and power state usingHSBType,PercentType, orOnOffType.
The same channel may also be linked to aDimmerorSwitchitem if separate controls are desired. -
Dimmable Lights (
DimmerItem):
Controls brightness (converted between HA’s 0–255 scale and openHAB’s 0–100%) and power state (OnOffTypeorPercentType).
May also be linked to aSwitchitem for independent on/off control. -
Binary Lights (
SwitchItem):
Controls basic on/off state (OnOffType). -
Brightness Commands:
Sending aPercentTypeto the primary channel sets the HAbrightnessparameter when supported. -
Power Commands:
Sending anOnOffTypeto the primary channel turns the light on or off. -
Attribute Channels:
Sending a command to any attribute channel (e.g.,#color_temp,#effect) will turn the light on,
except when sendingOnOffType.OFFto the primary channel.
MediaPlayer Entity
- Home Assistant
volume(0.0-1.0) is converted into openHAB PercentType (0-100%).
Image / Camera Entity
Images and Snapshots are fetched asynchronously through HTTP whenever an image update event was received via WebSocket.
Home Assistant does not send image update events in real-time to conserve processing power and network bandwidth.
However, Image channels accept and process the REFRESH command and will fetch an updated image without waiting for an event from Home Assistant.
Image Entity
| Channel | Item Type | Description |
|---|---|---|
| Primary | Image |
Image data as RawType |
Camera Entity
| Channel | Item Type | Description |
|---|---|---|
| Primary | String |
Camera status as a StringType |
#image |
Image |
Camera image snapshots as RawType |
#power |
Switch |
Turns the camera on/off, if the camera entity supports ON_OFF feature |
#stream_url |
String |
The camera stream source URL, if the camera entity supports STREAM feature |
Rule Actions
The binding exposes actions scoped to the server Bridge (hasslink:server).
Actions can be retrieved in openHAB automation rules using the action identifier "hasslink" and the bridge UID.
Action Summary
| Method | Description |
|---|---|
callService(String domain, String service, String entityId, Map<String, Object> payload) |
Invokes any native Home Assistant service via the WebSocket connection. |
pressButton(String entityId) |
Triggers a Home Assistant button entity (button or input_button). |
runScript(String scriptId, Map<String, Object> variables) |
Executes a Home Assistant script directly by service name. |
activateScene(String sceneId) |
Activates a Home Assistant scene using the generic scene.turn_on service. |
Action Details
callService
callService(String domain, String service, String entityId, Map<String, Object> payload)
Invokes any native Home Assistant service via the WebSocket connection.
domain(String, required): The target Home Assistant service domain (e.g."light","notify","climate").service(String, required): The target service action (e.g."turn_on","persistent_notification","set_temperature").entityId(String, optional): Fully qualified entity ID ("domain.object_id") ornullif the service does not target a specific entity.payload(Map<String, Object>, optional): Key-value parameters passed to the service payload ornull.
pressButton
Syntax: pressButton(String entityId)
Triggers a Home Assistant button entity (button or input_button).
entityId(String, required): The target entity ID. Accepts a fully qualified entity ID (e.g.,"button.reboot","input_button.reset") or a shorthand object ID (e.g.,"reboot"), which automatically defaults to thebuttondomain ("button.reboot").
runScript
Syntax: runScript(String scriptId, Map<String, Object> variables)
Executes a Home Assistant script directly by service name.
scriptId(String, required): Fully qualified script ID (e.g.,"script.welcome_home") or simple script name (e.g.,"welcome_home").variables(Map<String, Object>, optional): Execution variables passed to the script payload ornull.
activateScene
Syntax: activateScene(String sceneId)
Activates a Home Assistant scene using the generic scene.turn_on service.
sceneId(String, required): Fully qualified scene ID (e.g.,"scene.movie_night") or simple scene name (e.g.,"movie_night").
Rule Examples
:::: tabs
::: tab DSL
rule "Trigger Home Assistant Script & Actions"
when
Item LivingRoom_SceneSwitch received command ON
then
val actions = getActions("hasslink", "hasslink:server:homeassistant")
if (actions === null) {
logWarn("rules", "HassLink actions not available")
return;
}
// Activate a scene (shorthand or full entity ID)
actions.activateScene("movie_night")
// Press a button
actions.pressButton("button.reboot_router")
// Run a script with variables
actions.runScript("welcome_home", Map.of("brightness", 80))
// Call arbitrary HA service
actions.callService("notify", "persistent_notification", null, Map.of(
"title", "openHAB Alert",
"message", "Motion detected in hallway"
))
end
:::
::: tab JS
const actions = actions.get("hasslink", "hasslink:server:homeassistant");
if (actions) {
// Activate a scene
actions.activateScene("movie_night");
// Trigger a button entity
actions.pressButton("reboot_router");
// Run script with parameters
actions.runScript("script.welcome_home", { brightness: 100, color: "red" });
// Custom service call
actions.callService("light", "turn_on", "light.living_room", { brightness_pct: 75 });
}
:::
::: tab JRuby
thing = things["hasslink:server:homeassistant"]
# Activate scene
thing.activate_scene("movie_night")
# Press button
thing.press_button("button.reboot_router")
# Run script with parameters
thing.run_script("welcome_home", { "brightness" => 80 })
# Generic service call
thing.call_service("climate", "set_temperature", "climate.hallway", { "temperature" => 21.5 })
:::
:::
Textual Configuration Examples
things/homeassistant.things
Bridge hasslink:server:homeassistant "Home Assistant Core" [
host="192.168.1.100",
port=8123,
token="eyJhbGciOiJIUzI1NiIsInR...",
secure=false,
ignoreIndependentEntities=true,
includedLabels="openhab*"
] {
// Single entity mapping
Thing device desk_light "Desk Lamp" [
entityIds="light.desk_lamp"
]
// Physical device mapping via Home Assistant Device ID with wildcard exclusions for static noise
Thing device lawn_mower "Lawn Mower" [
deviceId="3a4b5c6d7e8f90123456789abcdef012",
excludedEntities="*_version", "*_mac", "*_fw_*"
]
// Grouping multiple standalone entities into one Thing
Thing device office_environment "Office Environment" [
entityIds=
"sensor.office_temperature",
"sensor.office_humidity",
"switch.office_fan"
]
}
items/homeassistant.items
// Light Controls
Switch DeskLamp_Power "Desk Lamp Power" <light> { channel="hasslink:device:homeassistant:desk_light:light-desk_lamp" }
Dimmer DeskLamp_Brightness "Desk Lamp Brightness" <light> { channel="hasslink:device:homeassistant:desk_light:light-desk_lamp#brightness" }
// Environment Sensors (Automated QuantityType)
Number:Temperature Office_Temp "Office Temperature [%.1f %unit%]" <temperature> { channel="hasslink:device:homeassistant:office_environment:sensor-office_temperature" }
Number:Dimensionless Office_Humidity "Office Humidity [%.0f %%]" <humidity> { channel="hasslink:device:homeassistant:office_environment:sensor-office_humidity" }
// Climate Controls
String AC_Mode "AC Mode" <climate> { channel="hasslink:device:homeassistant:living_room_ac:climate-living_room_ac" }
Number:Temperature AC_TargetTemp "Target Temperature [%.1f %unit%]" <temperature> { channel="hasslink:device:homeassistant:living_room_ac:climate-living_room_ac#target_temperature" }
If you’re running HA + openHAB together and want a cleaner, more native way to bridge the two systems, I’d really appreciate your help testing this.
Downloads:
v0.0.15 - 2026-10-03
- Allow discovery to include services (non hardware) when they satisfy filter criteria
v0.0.14 - 2026-10-03
Contains fixes up to b48e762
- Support glob in config.entityIds
- Don’t call listeners if json contains errors
- remove setting target_temperature_low|high in WaterHeaterEntity
- ensure absolute url matches homeassistant base url
- reject mismatched domain/entity id in action methods
- add datetime,location,player to extensible channel type list
- copy channel’s AutoUpdatePolicy during disambiguation
- add ClimateEntity swing_horizontal_mode to parseState
- WaterHeaterEntity: handle quantitytype for temperature
- make uri replacement from ws → http ipv6 compatible
- clear lastKnownStates when receiving entitiesSnapshot
- reset all internal states in stopConnection()
- remove unsupported input_type, input_select, input_number, input_button, etc from docs
- Add GeoLocation and RadioFrequency entities to EntityTypeRegistry
v0.0.13 - 2026-09-27
- Exclude entities that are disabled in HA. Its states are never sent so it will forever be in an indeterminate state anyway.
- Fix a race condition where an orderly websocket closure could prevent Thing from coming online.
- Improve origin check before sending bearer token when fetching images
- Add Gson as an explicit dependency
v0.0.12 - 2026-09-25
- Exclude disabled entities (disabled in HA)
- Fix repeated warnings about missing entities (because they were disabled in HA)
- Sensor channels (primary + attributes) are set to have AutoUpdatePolicy.VETO
- Removed VETO from the channel config - it was unnecessary
- Add readonly stateDescription for sensor channels
v0.0.11 - 2026-09-24
This release primarily revolves around better type inferencing, some minor fixes in attribute names mentioned below.
- Improvements on type inferencing
- Add DynamicCommandDescription for some channels that report their status differently than the command
- Set up test HA environment and extract demo payload for test harness
- Fix incorrect attribute names in Humidifier, Remote, WaterHeater
- Fix Date/Time/Datetime parsing and dispatching
- Fix DeviceTrackerEntity battery attribute name
- Humidifier: Add StateDescription for humidity and mode
- MediaPlayer: Add sound_mode, shuffle, repeat
- Remove infrared entity - it has no demo payload
- Fix Siren state
- Fix incorrect Valve entity position attribute
- Fix WaterHeater entity’s target_temp_min/max attributes
v0.0.10 - 2026-09-23
Starting work on creating unit tests and finding a few more bugs.
- Fix QuantityType parsing ISE on non-standard units
- Fetch HA system wide config, including standard units on websocket connection
- Fallback to HA-system-wide unit for standard units (temperature, length, area, mass, pressure, volume, windspeed, accumulated precipitation, volume)
- Add StateDescription for sensor entity
- Fix light entity not inverting color_temp commands
- Fix weather entity channels: remove incorrect
native_prefixes. - AlarmControlPanel entity: add trigger option and arm custom bypass
- Fix waiting for entity states forever when initializing Thing with a newly added device in HA
- Fix StateMapBuilder putString
v0.0.9 - 2026-09-22
- Resolved an issue where bridge-level area and label filters (
includedLabels,includedAreas) incorrectly excluded valid Home Assistant devices. - Refactored to prevent a leak where references to disposed device handlers were kept, keeping the unregistered handlers alive.
- Fix inconsistency in Channel ID and Channel Label generation. The channel ID previous did not consider/strip the current device name prefix. This affects HA devices with inconsistent entityid prefixes.
v0.0.8 - 2026-09-21
- Fix configuration update handling. No longer need to disable/enable Thing to apply update.
- Add configurable Bridge and Device filtering.
- Bridge filters control what’s discovered in the inbox
- Device filters control what entities are included in the Thing channels.
- Supported filters: include/exclude by label/tag, by domain, entityId. Supports Glob wildcards.
v0.0.7 - 2026-09-21
- Suppress “handler is already disposed” warnings.
- Infer Binary Sensor entities as Contact items for door, window, garage_door and opening device classes.
- Improve numeric state inference.
v0.0.6 - 2026-09-20
- Fix repeating inbox removal / additions log messages
- Filter out system and helper domains from auto-discovery
- Infer datetime attributes based on common datetime naming patterns - for Sensor entities
v0.0.5 - 2026-09-20
- Remove the special
attr_prefix on Sensor entity. This makes the attribute channels uniform across all entities. Channels need to be regenerated. The easiest way is to add “X” to the end of the deviceId → save → remove that extra X → save. Then fix the missing links for the sensor items. - Improve channel label inference
v0.0.4 - 2026-09-20
- Improve representationProperty so that manually configured Bridges/Things do not show up in Inbox.
- Add Bridge Actions (callService, pressButton, runScript, activateScene)
v0.0.3 - 2026-09-19 Fix state parsing for Select,MediaPlayer,Remote and Siren entities.
v0.0.2 - 2026-09-19 LightEntity’s main channel is now Color > Dimmer > Switch. Improved connection and automatic channel building logic
v0.0.1 - 2026-09-17 First release


