Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CODEOWNERS
Original file line number Diff line number Diff line change
Expand Up @@ -411,6 +411,7 @@
/bundles/org.openhab.binding.tradfri/ @cweitkamp @kaikreuzer
/bundles/org.openhab.binding.tuya/ @J-N-K
/bundles/org.openhab.binding.unifi/ @mgbowman @Hilbrand
/bundles/org.openhab.binding.unifiaccess/ @digitaldan
/bundles/org.openhab.binding.unifiedremote/ @GiviMAD
/bundles/org.openhab.binding.upb/ @marcusb
/bundles/org.openhab.binding.upnpcontrol/ @mherwege
Expand Down
5 changes: 5 additions & 0 deletions bom/openhab-addons/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -2031,6 +2031,11 @@
<artifactId>org.openhab.binding.unifi</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.openhab.addons.bundles</groupId>
<artifactId>org.openhab.binding.unifiaccess</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.openhab.addons.bundles</groupId>
<artifactId>org.openhab.binding.unifiedremote</artifactId>
Expand Down
13 changes: 13 additions & 0 deletions bundles/org.openhab.binding.unifiaccess/NOTICE
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
This content is produced and maintained by the openHAB project.

* Project home: https://www.openhab.org

== Declared Project Licenses

This program and the accompanying materials are made available under the terms
of the Eclipse Public License 2.0 which is available at
https://www.eclipse.org/legal/epl-2.0/.

== Source Code

https://github.com/openhab/openhab-addons
181 changes: 181 additions & 0 deletions bundles/org.openhab.binding.unifiaccess/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,181 @@
# UniFi Access Binding

![logo](doc/logo.png)

Comment on lines +3 to +4
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Logo can be added afterwards. Please remove from the docs here.

This binding integrates Ubiquiti UniFi Access with openHAB.
It connects to your UniFi Access controller over HTTPS and listens for live door events while exposing channels to monitor and control door locks.

## Supported Things

- `unifiaccess:bridge` (Bridge): The UniFi Access controller instance.
Required to discover and manage door things.
- `unifiaccess:door`: A UniFi Access door with status and control channels.
- `unifiaccess:device`: A UniFi Access device (reader, hub, or camera) with device-level status and controls.
Comment on lines +10 to +13
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

no need to add the binding id.

Suggested change
- `unifiaccess:bridge` (Bridge): The UniFi Access controller instance.
Required to discover and manage door things.
- `unifiaccess:door`: A UniFi Access door with status and control channels.
- `unifiaccess:device`: A UniFi Access device (reader, hub, or camera) with device-level status and controls.
- `bridge` (Bridge): The UniFi Access controller instance.
Required to discover and manage door things.
- `door`: A UniFi Access door with status and control channels.
- `device`: A UniFi Access device (reader, hub, or camera) with device-level status and controls.


## Discovery

- Add the `Bridge` by entering the controller Hostname or IP and an API Token.
- Once the Bridge is ONLINE, Doors are discovered automatically and appear in the Inbox.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
- Once the Bridge is ONLINE, Doors are discovered automatically and appear in the Inbox.
- Once the `bridge` is ONLINE, `door`s are discovered automatically and appear in the Inbox.

- Approve discovered doors and devices to add them to your system, or create them manually using `deviceId`.

## Binding Configuration

There are no global binding settings.
All configuration is on the Bridge and on individual Door and Device things.

## Thing Configuration

### Bridge `unifiaccess:bridge`
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also check other places

Suggested change
### Bridge `unifiaccess:bridge`
### Bridge `bridge`


| Name | Type | Description | Default | Required | Advanced |
|-----------|------|--------------------------------------------------------|---------|----------|----------|
| host | text | Hostname or IP address of the UniFi Access controller. | N/A | yes | no |
| authToken | text | API token used for HTTPS and WebSocket authentication. | N/A | yes | no |

How to get the API Token.
Open the UniFi Access controller and create an API token with permissions suitable for reading doors and remote unlocking.
Then paste the token into the Bridge configuration.

![Create API token](doc/token.png)

![Recommended permissions](doc/permissions.png)

### Door `unifiaccess:door`

| Name | Type | Description | Default | Required | Advanced |
|----------|------|----------------------------------------------------------|---------|----------|----------|
| deviceId | text | Unique door identifier from the UniFi Access controller. | N/A | yes | no |

### Device `unifiaccess:device`

| Name | Type | Description | Default | Required | Advanced |
|----------|------|------------------------------------------------------------|---------|----------|----------|
| deviceId | text | Unique device identifier from the UniFi Access controller. | N/A | yes | no |

## Channels

### Door Channels

| Channel ID | Item Type | RW | Description |
|----------------|-------------|----|-----------------------------------------------------------------------------|
| lock | Switch | RW | Lock state. ON locks the door, OFF unlocks immediately. |
| position | Contact | R | Door position sensor. OPEN when the door is open, CLOSED otherwise. |
| last-unlock | DateTime | R | Timestamp of the last unlock event. |
| last-actor | String | R | Name of the user who last unlocked the door. |
| lock-rule | String | R | Current lock rule. One of `schedule`, `custom`, `keep_unlock`, `keep_lock`. |
| keep-unlocked | Switch | W | Keep the door unlocked until changed. Send ON to apply. |
| keep-locked | Switch | W | Keep the door locked until changed. Send ON to apply. |
| unlock-minutes | Number:Time | W | Unlock for a number of minutes. Send a value in minutes. |
| door-thumbnail | Image | R | Door thumbnail. |

### Device Channels

Channels

| Channel ID | Item Type | RW | Description |
|-----------------------|-----------|----|-------------------------------------------------------------|
| nfc-enabled | Switch | RW | Enable or disable NFC access on this device. |
| pin-enabled | Switch | RW | Enable or disable PIN code access. |
| pin-shuffle | Switch | RW | Shuffle keypad digits for PIN entry. |
| face-enabled | Switch | RW | Enable or disable face unlock. |
| mobile-tap-enabled | Switch | RW | Allow mobile tap to unlock. |
| mobile-button-enabled | Switch | RW | Allow mobile app unlock button. |
| mobile-shake-enabled | Switch | RW | Allow mobile shake to unlock. |
| mobile-wave-enabled | Switch | RW | Allow mobile wave gesture to unlock. |
| wave-enabled | Switch | RW | Allow hand wave gesture to unlock. |
| qr-code-enabled | Switch | RW | Allow QR-code unlock. |
| touch-pass-enabled | Switch | RW | Allow Touch Pass unlock. |
| face-anti-spoofing | String | RW | One of `high`, `medium`, `no`. |
| face-detect-distance | String | RW | One of `near`, `medium`, `far`. |
| emergency-status | String | R | Device emergency state: `normal`, `lockdown`, `evacuation`. |
| door-sensor | Contact | R | Door position sensor. |
| doorbell-contact | Contact | R | Doorbell contact. |


Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change

Triggers

| Channel ID | Events / Payload | Description |
|-----------------|------------------------------------------------------------------------------------------------------------------------------------------------------|------------------------------------------------|
| doorbell | Events: `incoming`, `incoming-ren`, `completed` | Fires on doorbell events. |
| doorbell-status | Events: `DOORBELL_TIMED_OUT`, `ADMIN_REJECTED_UNLOCK`, `ADMIN_UNLOCK_SUCCEEDED`, `VISITOR_CANCELED_DOORBELL`, `ANSWERED_BY_ANOTHER_ADMIN`, `UNKNOWN` | Fires on doorbell status changes. |
| log-insight | Payload: JSON with `logKey`, `eventType`, `message`, `published`, `result`, common refs | Fires for insights log events for this device. |

## Full Examples (Textual Configuration)

Replace the IDs with your own thing and item names.
Examples assume a Bridge UID of `ua` and a Door UID of `frontdoor`.

### Things (`.things`)

```
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
```
```java

Bridge unifiaccess:bridge:ua "UniFi Access" [ host="192.168.1.20", authToken="YOUR_LONG_TOKEN" ] {
Thing unifiaccess:door:frontdoor [ deviceId="60546f80e4b0abcd12345678" ]
}
```

### Items (`.items`)

```
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
```
```java

// Door status
Switch UA_FrontDoor_Lock "Front Door Locked" { channel="unifiaccess:door:ua:frontdoor:lock" }
Contact UA_FrontDoor_Position "Front Door Position [%s]" { channel="unifiaccess:door:ua:frontdoor:position" }
DateTime UA_FrontDoor_LastUnlock "Last Unlock [%1$ta %1$tF %1$tR]" { channel="unifiaccess:door:ua:frontdoor:last-unlock" }
String UA_FrontDoor_LastActor "Last Actor [%s]" { channel="unifiaccess:door:ua:frontdoor:last-actor" }
String UA_FrontDoor_LockRule "Lock Rule [%s]" { channel="unifiaccess:door:ua:frontdoor:lock-rule" }

// Door controls
Switch UA_FrontDoor_KeepUnlocked "Keep Unlocked" { channel="unifiaccess:door:ua:frontdoor:keep-unlocked" }
Switch UA_FrontDoor_KeepLocked "Keep Locked" { channel="unifiaccess:door:ua:frontdoor:keep-locked" }
Number:Time UA_FrontDoor_UnlockMins "Unlock Minutes [%.0f min]" { channel="unifiaccess:door:ua:frontdoor:unlock-minutes" }
```

### Sitemap (`.sitemap`)

```
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
```
```perl

sitemap home label="Home" {
Frame label="Front Door" {
Switch item=UA_FrontDoor_Lock
Text item=UA_FrontDoor_Position
Text item=UA_FrontDoor_LastActor
Text item=UA_FrontDoor_LastUnlock
Text item=UA_FrontDoor_LockRule
Switch item=UA_FrontDoor_KeepUnlocked
Switch item=UA_FrontDoor_KeepLocked
Setpoint item=UA_FrontDoor_UnlockMins minValue=1 maxValue=120 step=1
}
}
```

### Rules

```javascript
rules.when().channel('unifiaccess:door:ua:frontdoor:access-attempt-success').triggered().then( e => {
const jsonData = JSON.parse(e.payload.event);
console.log("Door Access Attempt Success: ", jsonData);
}).build('Unifi Access Door Access Attempt Success');

rules.when().channel('unifiaccess:door:ua:frontdoor:access-attempt-failure').triggered().then( e => {
const jsonData = JSON.parse(e.payload.event);
console.log("Door Access Attempt Failure: ", jsonData);
}).build('Unifi Access Door Access Attempt Failure');

rules.when().channel('unifiaccess:door:ua:frontdoor:remote-unlock').triggered().then( e => {
const jsonData = JSON.parse(e.payload.event);
console.log("Door Remote Unlock: ", jsonData);
}).build('Unifi Access Door Remote Unlock');

rules.when().channel('unifiaccess:door:ua:frontdoor:doorbell-status').triggered().then( e => {
const data = e.payload.event;
console.log("Doorbell Status: ", e);
}).build('Unifi Access Doorbell Status');

rules.when().channel('unifiaccess:bridge:ua:log-insight').triggered().then( e => {
const jsonData = JSON.parse(e.payload.event);
console.log("Bridge Log Insight: ", jsonData);
}).build('Unifi Access Bridge Log Insight');

rules.when().channel('unifiaccess:bridge:ua:log').triggered().then( e => {
const jsonData = JSON.parse(e.payload.event);
console.log("Bridge Log: ", jsonData);
}).build('Unifi Access Bridge Log');
```
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See other comment, binding logo can be added afterwards

Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are you able to provide better quality screenshots (same applies to other image) with less file size?

Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
17 changes: 17 additions & 0 deletions bundles/org.openhab.binding.unifiaccess/pom.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">

<modelVersion>4.0.0</modelVersion>

<parent>
<groupId>org.openhab.addons.bundles</groupId>
<artifactId>org.openhab.addons.reactor.bundles</artifactId>
<version>5.1.0-SNAPSHOT</version>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
<version>5.1.0-SNAPSHOT</version>
<version>5.2.0-SNAPSHOT</version>

</parent>

<artifactId>org.openhab.binding.unifiaccess</artifactId>

<name>openHAB Add-ons :: Bundles :: UnifiAccess Binding</name>

</project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<?xml version="1.0" encoding="UTF-8"?>
<features name="org.openhab.binding.unifiaccess-${project.version}" xmlns="http://karaf.apache.org/xmlns/features/v1.4.0">
<repository>mvn:org.openhab.core.features.karaf/org.openhab.core.features.karaf.openhab-core/${ohc.version}/xml/features</repository>

<feature name="openhab-binding-unifiaccess" description="UnifiAccess Binding" version="${project.version}">
<feature>openhab-runtime-base</feature>
<bundle start-level="80">mvn:org.openhab.addons.bundles/org.openhab.binding.unifiaccess/${project.version}</bundle>
</feature>
</features>
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
/*
* Copyright (c) 2010-2025 Contributors to the openHAB project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.unifiaccess.internal;

import org.eclipse.jdt.annotation.NonNullByDefault;
import org.openhab.core.thing.ThingTypeUID;

/**
* The {@link UnifiAccessBindingConstants} class defines common constants, which are
* used across the whole binding.
*
* @author Dan Cunningham - Initial contribution
*/
@NonNullByDefault
public class UnifiAccessBindingConstants {

public static final String BINDING_ID = "unifiaccess";

public static final ThingTypeUID BRIDGE_THING_TYPE = new ThingTypeUID(BINDING_ID, "bridge");
public static final ThingTypeUID DOOR_THING_TYPE = new ThingTypeUID(BINDING_ID, "door");
public static final ThingTypeUID DEVICE_THING_TYPE = new ThingTypeUID(BINDING_ID, "device");

public static final String CONFIG_DEVICE_ID = "deviceId";

public static final String CHANNEL_LOCK = "lock";
public static final String CHANNEL_DOOR_POSITION = "position";
public static final String CHANNEL_LAST_UNLOCK = "last-unlock";
public static final String CHANNEL_LAST_ACTOR = "last-actor";
public static final String CHANNEL_LOCK_RULE = "lock-rule";

public static final String CHANNEL_KEEP_UNLOCKED = "keep-unlocked";
public static final String CHANNEL_KEEP_LOCKED = "keep-locked";
public static final String CHANNEL_UNLOCK_MINUTES = "unlock-minutes";
public static final String CHANNEL_UNLOCK_UNTIL = "unlock-until";
public static final String CHANNEL_DOOR_THUMBNAIL = "thumbnail";

// Door trigger channels
public static final String CHANNEL_DOOR_ACCESS_ATTEMPT_SUCCESS = "access-attempt-success";
public static final String CHANNEL_DOOR_ACCESS_ATTEMPT_FAILURE = "access-attempt-failure";
public static final String CHANNEL_DOOR_REMOTE_UNLOCK = "remote-unlock";
public static final String CHANNEL_DOORBELL_STATUS = "doorbell-status";

// Device channels
public static final String CHANNEL_DEVICE_NFC_ENABLED = "nfc-enabled";
public static final String CHANNEL_DEVICE_PIN_ENABLED = "pin-enabled";
public static final String CHANNEL_DEVICE_PIN_SHUFFLE = "pin-shuffle";
public static final String CHANNEL_DEVICE_FACE_ENABLED = "face-enabled";
public static final String CHANNEL_DEVICE_MOBILE_TAP_ENABLED = "mobile-tap-enabled";
public static final String CHANNEL_DEVICE_MOBILE_BUTTON_ENABLED = "mobile-button-enabled";
public static final String CHANNEL_DEVICE_MOBILE_SHAKE_ENABLED = "mobile-shake-enabled";
public static final String CHANNEL_DEVICE_MOBILE_WAVE_ENABLED = "mobile-wave-enabled";
public static final String CHANNEL_DEVICE_WAVE_ENABLED = "wave-enabled";
public static final String CHANNEL_DEVICE_EMERGENCY_STATUS = "emergency-status";
public static final String CHANNEL_DEVICE_DOOR_SENSOR = "door-sensor";
public static final String CHANNEL_DEVICE_DOORBELL_CONTACT = "doorbell-contact";
public static final String CHANNEL_DEVICE_DOORBELL_TRIGGER = "doorbell";
public static final String CHANNEL_DEVICE_QR_CODE_ENABLED = "qr-code-enabled";
public static final String CHANNEL_DEVICE_TOUCH_PASS_ENABLED = "touch-pass-enabled";
public static final String CHANNEL_DEVICE_FACE_ANTI_SPOOFING = "face-anti-spoofing";
public static final String CHANNEL_DEVICE_FACE_DETECT_DISTANCE = "face-detect-distance";

// Bridge trigger channels
public static final String CHANNEL_BRIDGE_LOG_INSIGHT = "log-insight";
public static final String CHANNEL_BRIDGE_LOG = "log";
}
Loading