Skip to content

Commit 66cde86

Browse files
authored
[unifiaccess] Unifi access binding initial contribution (#19412)
* Unifi Access Binding Signed-off-by: Dan Cunningham <dan@digitaldan.com>
1 parent b5d753b commit 66cde86

51 files changed

Lines changed: 6489 additions & 0 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

CODEOWNERS

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -418,6 +418,7 @@
418418
/bundles/org.openhab.binding.tradfri/ @cweitkamp @kaikreuzer
419419
/bundles/org.openhab.binding.tuya/ @J-N-K
420420
/bundles/org.openhab.binding.unifi/ @mgbowman @Hilbrand
421+
/bundles/org.openhab.binding.unifiaccess/ @digitaldan
421422
/bundles/org.openhab.binding.unifiedremote/ @GiviMAD
422423
/bundles/org.openhab.binding.unifiprotect/ @digitaldan
423424
/bundles/org.openhab.binding.upb/ @marcusb

bom/openhab-addons/pom.xml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2071,6 +2071,11 @@
20712071
<artifactId>org.openhab.binding.unifi</artifactId>
20722072
<version>${project.version}</version>
20732073
</dependency>
2074+
<dependency>
2075+
<groupId>org.openhab.addons.bundles</groupId>
2076+
<artifactId>org.openhab.binding.unifiaccess</artifactId>
2077+
<version>${project.version}</version>
2078+
</dependency>
20742079
<dependency>
20752080
<groupId>org.openhab.addons.bundles</groupId>
20762081
<artifactId>org.openhab.binding.unifiedremote</artifactId>
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
This content is produced and maintained by the openHAB project.
2+
3+
* Project home: https://www.openhab.org
4+
5+
== Declared Project Licenses
6+
7+
This program and the accompanying materials are made available under the terms
8+
of the Eclipse Public License 2.0 which is available at
9+
https://www.eclipse.org/legal/epl-2.0/.
10+
11+
== Source Code
12+
13+
https://github.com/openhab/openhab-addons

bundles/org.openhab.binding.unifiaccess/README.md

Lines changed: 288 additions & 0 deletions
Large diffs are not rendered by default.
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
3+
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
4+
5+
<modelVersion>4.0.0</modelVersion>
6+
7+
<parent>
8+
<groupId>org.openhab.addons.bundles</groupId>
9+
<artifactId>org.openhab.addons.reactor.bundles</artifactId>
10+
<version>5.2.0-SNAPSHOT</version>
11+
</parent>
12+
13+
<artifactId>org.openhab.binding.unifiaccess</artifactId>
14+
15+
<name>openHAB Add-ons :: Bundles :: UnifiAccess Binding</name>
16+
17+
</project>
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<features name="org.openhab.binding.unifiaccess-${project.version}" xmlns="http://karaf.apache.org/xmlns/features/v1.4.0">
3+
<repository>mvn:org.openhab.core.features.karaf/org.openhab.core.features.karaf.openhab-core/${ohc.version}/xml/features</repository>
4+
5+
<feature name="openhab-binding-unifiaccess" description="UnifiAccess Binding" version="${project.version}">
6+
<feature>openhab-runtime-base</feature>
7+
<bundle start-level="80">mvn:org.openhab.addons.bundles/org.openhab.binding.unifiaccess/${project.version}</bundle>
8+
</feature>
9+
</features>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
/*
2+
* Copyright (c) 2010-2026 Contributors to the openHAB project
3+
*
4+
* See the NOTICE file(s) distributed with this work for additional
5+
* information.
6+
*
7+
* This program and the accompanying materials are made available under the
8+
* terms of the Eclipse Public License 2.0 which is available at
9+
* http://www.eclipse.org/legal/epl-2.0
10+
*
11+
* SPDX-License-Identifier: EPL-2.0
12+
*/
13+
package org.openhab.binding.unifiaccess.internal;
14+
15+
import org.eclipse.jdt.annotation.NonNullByDefault;
16+
import org.openhab.core.thing.ThingTypeUID;
17+
18+
/**
19+
* The {@link UnifiAccessBindingConstants} class defines common constants, which are
20+
* used across the whole binding.
21+
*
22+
* @author Dan Cunningham - Initial contribution
23+
*/
24+
@NonNullByDefault
25+
public class UnifiAccessBindingConstants {
26+
27+
public static final String BINDING_ID = "unifiaccess";
28+
29+
public static final ThingTypeUID BRIDGE_THING_TYPE = new ThingTypeUID(BINDING_ID, "bridge");
30+
public static final ThingTypeUID DOOR_THING_TYPE = new ThingTypeUID(BINDING_ID, "door");
31+
public static final ThingTypeUID DEVICE_THING_TYPE = new ThingTypeUID(BINDING_ID, "device");
32+
33+
public static final String CONFIG_DEVICE_ID = "deviceId";
34+
35+
public static final String CHANNEL_LOCK = "lock";
36+
public static final String CHANNEL_DOOR_POSITION = "position";
37+
public static final String CHANNEL_LAST_UNLOCK = "last-unlock";
38+
public static final String CHANNEL_LAST_ACTOR = "last-actor";
39+
public static final String CHANNEL_LOCK_RULE = "lock-rule";
40+
41+
public static final String CHANNEL_KEEP_UNLOCKED = "keep-unlocked";
42+
public static final String CHANNEL_KEEP_LOCKED = "keep-locked";
43+
public static final String CHANNEL_UNLOCK_MINUTES = "unlock-minutes";
44+
public static final String CHANNEL_UNLOCK_UNTIL = "unlock-until";
45+
public static final String CHANNEL_DOOR_THUMBNAIL = "thumbnail";
46+
47+
// Door trigger channels
48+
public static final String CHANNEL_DOOR_ACCESS_ATTEMPT_SUCCESS = "access-attempt-success";
49+
public static final String CHANNEL_DOOR_ACCESS_ATTEMPT_FAILURE = "access-attempt-failure";
50+
public static final String CHANNEL_DOOR_REMOTE_UNLOCK = "remote-unlock";
51+
public static final String CHANNEL_DOORBELL_STATUS = "doorbell-status";
52+
53+
// Device channels
54+
public static final String CHANNEL_DEVICE_NFC_ENABLED = "nfc-enabled";
55+
public static final String CHANNEL_DEVICE_PIN_ENABLED = "pin-enabled";
56+
public static final String CHANNEL_DEVICE_PIN_SHUFFLE = "pin-shuffle";
57+
public static final String CHANNEL_DEVICE_FACE_ENABLED = "face-enabled";
58+
public static final String CHANNEL_DEVICE_MOBILE_TAP_ENABLED = "mobile-tap-enabled";
59+
public static final String CHANNEL_DEVICE_MOBILE_BUTTON_ENABLED = "mobile-button-enabled";
60+
public static final String CHANNEL_DEVICE_MOBILE_SHAKE_ENABLED = "mobile-shake-enabled";
61+
public static final String CHANNEL_DEVICE_MOBILE_WAVE_ENABLED = "mobile-wave-enabled";
62+
public static final String CHANNEL_DEVICE_WAVE_ENABLED = "wave-enabled";
63+
public static final String CHANNEL_DEVICE_DOOR_SENSOR = "door-sensor";
64+
public static final String CHANNEL_DEVICE_DOORBELL_CONTACT = "doorbell-contact";
65+
public static final String CHANNEL_DEVICE_DOORBELL_TRIGGER = "doorbell";
66+
public static final String CHANNEL_DEVICE_QR_CODE_ENABLED = "qr-code-enabled";
67+
public static final String CHANNEL_DEVICE_TOUCH_PASS_ENABLED = "touch-pass-enabled";
68+
public static final String CHANNEL_DEVICE_FACE_ANTI_SPOOFING = "face-anti-spoofing";
69+
public static final String CHANNEL_DEVICE_FACE_DETECT_DISTANCE = "face-detect-distance";
70+
public static final String CHANNEL_DEVICE_ONLINE = "online";
71+
72+
// Door alarm channel
73+
public static final String CHANNEL_DOOR_ALARM = "alarm";
74+
75+
// Bridge channels
76+
public static final String CHANNEL_BRIDGE_EMERGENCY_STATUS = "emergency-status";
77+
public static final String CHANNEL_BRIDGE_LOG_INSIGHT = "log-insight";
78+
public static final String CHANNEL_BRIDGE_LOG = "log";
79+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
/*
2+
* Copyright (c) 2010-2026 Contributors to the openHAB project
3+
*
4+
* See the NOTICE file(s) distributed with this work for additional
5+
* information.
6+
*
7+
* This program and the accompanying materials are made available under the
8+
* terms of the Eclipse Public License 2.0 which is available at
9+
* http://www.eclipse.org/legal/epl-2.0
10+
*
11+
* SPDX-License-Identifier: EPL-2.0
12+
*/
13+
package org.openhab.binding.unifiaccess.internal;
14+
15+
import java.util.List;
16+
import java.util.Map;
17+
import java.util.Set;
18+
19+
import org.eclipse.jdt.annotation.NonNullByDefault;
20+
import org.openhab.binding.unifiaccess.internal.api.UniFiAccessApiClient;
21+
import org.openhab.binding.unifiaccess.internal.dto.Device;
22+
import org.openhab.binding.unifiaccess.internal.dto.Door;
23+
import org.openhab.binding.unifiaccess.internal.dto.UniFiAccessApiException;
24+
import org.openhab.binding.unifiaccess.internal.handler.UnifiAccessBridgeHandler;
25+
import org.openhab.core.config.discovery.AbstractThingHandlerDiscoveryService;
26+
import org.openhab.core.config.discovery.DiscoveryResult;
27+
import org.openhab.core.config.discovery.DiscoveryResultBuilder;
28+
import org.openhab.core.thing.ThingUID;
29+
import org.openhab.core.thing.binding.ThingHandler;
30+
import org.osgi.service.component.annotations.Component;
31+
import org.osgi.service.component.annotations.ServiceScope;
32+
import org.slf4j.Logger;
33+
import org.slf4j.LoggerFactory;
34+
35+
/**
36+
* Discovery service for UniFi Access Door things.
37+
*
38+
* @author Dan Cunningham - Initial contribution
39+
*/
40+
@Component(scope = ServiceScope.PROTOTYPE, service = UnifiAccessDiscoveryService.class)
41+
@NonNullByDefault
42+
public class UnifiAccessDiscoveryService extends AbstractThingHandlerDiscoveryService<UnifiAccessBridgeHandler> {
43+
44+
private final Logger logger = LoggerFactory.getLogger(UnifiAccessDiscoveryService.class);
45+
46+
public UnifiAccessDiscoveryService() {
47+
super(UnifiAccessBridgeHandler.class,
48+
Set.of(UnifiAccessBindingConstants.DOOR_THING_TYPE, UnifiAccessBindingConstants.DEVICE_THING_TYPE), 30,
49+
false);
50+
}
51+
52+
@Override
53+
public void setThingHandler(ThingHandler handler) {
54+
if (handler instanceof UnifiAccessBridgeHandler childDiscoveryHandler) {
55+
childDiscoveryHandler.setDiscoveryService(this);
56+
this.thingHandler = childDiscoveryHandler;
57+
}
58+
}
59+
60+
@Override
61+
protected void startScan() {
62+
removeOlderResults(getTimestampOfLastScan());
63+
final UniFiAccessApiClient client = thingHandler.getApiClient();
64+
if (client == null) {
65+
return;
66+
}
67+
try {
68+
List<Door> doors = client.getDoors();
69+
discoverDoors(doors);
70+
List<Device> devices = client.getDevices();
71+
discoverDevices(devices);
72+
} catch (UniFiAccessApiException e) {
73+
logger.debug("Error discovering doors: {}", e.getMessage());
74+
}
75+
}
76+
77+
public void discoverDoors(List<Door> doors) {
78+
for (Door d : doors) {
79+
ThingUID uid = new ThingUID(UnifiAccessBindingConstants.DOOR_THING_TYPE, thingHandler.getThing().getUID(),
80+
d.id);
81+
Map<String, Object> props = Map.of(UnifiAccessBindingConstants.CONFIG_DEVICE_ID, d.id);
82+
DiscoveryResult result = DiscoveryResultBuilder.create(uid).withBridge(thingHandler.getThing().getUID())
83+
.withThingType(UnifiAccessBindingConstants.DOOR_THING_TYPE).withProperties(props)
84+
.withRepresentationProperty(UnifiAccessBindingConstants.CONFIG_DEVICE_ID)
85+
.withLabel("UniFi Door: " + (d.fullName != null ? d.fullName : d.name)).build();
86+
thingDiscovered(result);
87+
}
88+
}
89+
90+
public void discoverDevices(List<Device> devices) {
91+
for (Device d : devices) {
92+
String devId = d.id;
93+
if (devId == null || d.isHub()) {
94+
continue;
95+
}
96+
ThingUID uid = new ThingUID(UnifiAccessBindingConstants.DEVICE_THING_TYPE, thingHandler.getThing().getUID(),
97+
devId);
98+
Map<String, Object> props = Map.of(UnifiAccessBindingConstants.CONFIG_DEVICE_ID, devId);
99+
String label = "UniFi Device: " + (d.type != null ? d.type : "") + " - "
100+
+ (d.alias != null ? d.alias : (d.name != null ? d.name : devId));
101+
DiscoveryResult result = DiscoveryResultBuilder.create(uid).withBridge(thingHandler.getThing().getUID())
102+
.withThingType(UnifiAccessBindingConstants.DEVICE_THING_TYPE).withProperties(props)
103+
.withRepresentationProperty(UnifiAccessBindingConstants.CONFIG_DEVICE_ID).withLabel(label).build();
104+
thingDiscovered(result);
105+
}
106+
}
107+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
/*
2+
* Copyright (c) 2010-2026 Contributors to the openHAB project
3+
*
4+
* See the NOTICE file(s) distributed with this work for additional
5+
* information.
6+
*
7+
* This program and the accompanying materials are made available under the
8+
* terms of the Eclipse Public License 2.0 which is available at
9+
* http://www.eclipse.org/legal/epl-2.0
10+
*
11+
* SPDX-License-Identifier: EPL-2.0
12+
*/
13+
package org.openhab.binding.unifiaccess.internal;
14+
15+
import static org.openhab.binding.unifiaccess.internal.UnifiAccessBindingConstants.*;
16+
17+
import java.util.Set;
18+
19+
import org.eclipse.jdt.annotation.NonNullByDefault;
20+
import org.eclipse.jdt.annotation.Nullable;
21+
import org.openhab.binding.unifiaccess.internal.handler.UnifiAccessBridgeHandler;
22+
import org.openhab.binding.unifiaccess.internal.handler.UnifiAccessDeviceHandler;
23+
import org.openhab.binding.unifiaccess.internal.handler.UnifiAccessDoorHandler;
24+
import org.openhab.core.io.net.http.HttpClientFactory;
25+
import org.openhab.core.thing.Bridge;
26+
import org.openhab.core.thing.Thing;
27+
import org.openhab.core.thing.ThingTypeUID;
28+
import org.openhab.core.thing.binding.BaseThingHandlerFactory;
29+
import org.openhab.core.thing.binding.ThingHandler;
30+
import org.openhab.core.thing.binding.ThingHandlerFactory;
31+
import org.osgi.service.component.annotations.Activate;
32+
import org.osgi.service.component.annotations.Component;
33+
import org.osgi.service.component.annotations.Reference;
34+
35+
/**
36+
* The {@link UnifiAccessHandlerFactory} is responsible for creating things and thing
37+
* handlers.
38+
*
39+
* @author Dan Cunningham - Initial contribution
40+
*/
41+
@NonNullByDefault
42+
@Component(configurationPid = "binding.unifiaccess", service = ThingHandlerFactory.class)
43+
public class UnifiAccessHandlerFactory extends BaseThingHandlerFactory {
44+
45+
private static final Set<ThingTypeUID> SUPPORTED_THING_TYPES_UIDS = Set.of(BRIDGE_THING_TYPE, DOOR_THING_TYPE,
46+
DEVICE_THING_TYPE);
47+
48+
private final HttpClientFactory httpClientFactory;
49+
50+
@Activate
51+
public UnifiAccessHandlerFactory(@Reference HttpClientFactory httpClientFactory) {
52+
this.httpClientFactory = httpClientFactory;
53+
}
54+
55+
@Override
56+
public boolean supportsThingType(ThingTypeUID thingTypeUID) {
57+
return SUPPORTED_THING_TYPES_UIDS.contains(thingTypeUID);
58+
}
59+
60+
@Override
61+
protected @Nullable ThingHandler createHandler(Thing thing) {
62+
ThingTypeUID thingTypeUID = thing.getThingTypeUID();
63+
64+
if (BRIDGE_THING_TYPE.equals(thingTypeUID)) {
65+
return new UnifiAccessBridgeHandler((Bridge) thing, httpClientFactory);
66+
}
67+
68+
if (DOOR_THING_TYPE.equals(thingTypeUID)) {
69+
return new UnifiAccessDoorHandler(thing);
70+
}
71+
72+
if (DEVICE_THING_TYPE.equals(thingTypeUID)) {
73+
return new UnifiAccessDeviceHandler(thing);
74+
}
75+
76+
return null;
77+
}
78+
}

0 commit comments

Comments
 (0)