Skip to content
This repository was archived by the owner on Oct 18, 2025. It is now read-only.

Commit 1e31a93

Browse files
ccutrerTim Harper
authored andcommitted
Add smoke and CO detectors to homekit (#2)
1 parent 2a8a326 commit 1e31a93

File tree

5 files changed

+203
-2
lines changed

5 files changed

+203
-2
lines changed

addons/io/org.openhab.io.homekit/README.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,8 @@ A full list of supported accessory types can be found in the table below.
9191
| OccupancySensor | | Switch, ContactSensor | Occupancy sensor. ON / OPEN state means occupancy detected. |
9292
| Valve | | Switch | Simple open/close valve. Assumes liquid is flowing when valve is open. |
9393
| WindowCovering | | Rollershutter | Simple window covering with support for setting target position / current position support. |
94+
| SmokeSensor | | Switch, ContactSensor | Smoke detector. ON / OPEN state means smokes detected. |
95+
| CarbonMonoxideSensor | | Switch, ContactSensor | CO detector. ON / OPEN state means smoke detected (it currently appears that Home.app on iOS doesn't distinguish between a smoke and CO detector). |
9496

9597

9698
See the sample below for example items:
@@ -119,6 +121,8 @@ The following devices support report low battery status:
119121

120122
* LeakSensor
121123
* MotionSensor
124+
* SmokeSensor
125+
* CarbonMonoxideSensor
122126
* OccupancySensor
123127

124128
Battery status can be reported via a Number item (0 - 100) tagged as `homekit:BatteryLevel`, or via a Switch item (where ON == battery is low) tagged as `homekit:BatteryLowStatus`. The battery status item must be grouped in with the sensor in question so it can be associated as a composite device. Here's what it looks like to configure a leak sensor with a BatteryLevel:

addons/io/org.openhab.io.homekit/src/main/java/org/openhab/io/homekit/internal/HomekitAccessoryType.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,8 @@ public enum HomekitAccessoryType {
3232
OCCUPANCY_SENSOR("OccupancySensor"),
3333
CONTACT_SENSOR("ContactSensor"),
3434
WINDOW_COVERING("WindowCovering"),
35+
SMOKE_SENSOR("SmokeSensor"),
36+
CARBON_MONOXIDE_SENSOR("CarbonMonoxideSensor"),
3537
@Deprecated()
3638
BLINDS("Blinds");
3739

addons/io/org.openhab.io.homekit/src/main/java/org/openhab/io/homekit/internal/accessories/HomekitAccessoryFactory.java

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@
1111
import java.util.Collections;
1212
import java.util.Map;
1313
import java.util.Optional;
14-
import java.util.stream.Collectors;
1514

1615
import org.eclipse.smarthome.core.items.GroupItem;
1716
import org.eclipse.smarthome.core.items.Item;
@@ -109,6 +108,22 @@ public static HomekitAccessory create(HomekitTaggedItem taggedItem, ItemRegistry
109108
case BLINDS:
110109
case WINDOW_COVERING:
111110
return new HomekitWindowCoveringImpl(taggedItem, itemRegistry, updater);
111+
case SMOKE_SENSOR:
112+
HomekitTaggedItem smokeSensorAccessory = getPrimaryAccessory(taggedItem,
113+
HomekitAccessoryType.SMOKE_SENSOR, itemRegistry).orElseThrow(
114+
() -> new Exception("Smoke accessory group should have a smoke sensor in it"));
115+
116+
return new HomekitSmokeSensorImpl(smokeSensorAccessory, itemRegistry, updater,
117+
BatteryStatus.getFromCharacteristics(characteristicItems));
118+
case CARBON_MONOXIDE_SENSOR:
119+
HomekitTaggedItem carbonMonoxideSensorAccessory = getPrimaryAccessory(taggedItem,
120+
HomekitAccessoryType.CARBON_MONOXIDE_SENSOR, itemRegistry)
121+
.orElseThrow(() -> new Exception(
122+
"Carbon monoxide accessory group should have a carbon monoxide sensor in it"));
123+
124+
return new HomekitSmokeSensorImpl(carbonMonoxideSensorAccessory, itemRegistry, updater,
125+
BatteryStatus.getFromCharacteristics(characteristicItems));
126+
112127
}
113128

114129
throw new Exception("Unknown homekit type: " + taggedItem.getAccessoryType());
@@ -117,7 +132,7 @@ public static HomekitAccessory create(HomekitTaggedItem taggedItem, ItemRegistry
117132
/**
118133
* Given an accessory group, return the item in the group tagged as an accessory.
119134
*
120-
* @param taggedItem The group item containing our item, or, the accessory item.
135+
* @param taggedItem The group item containing our item, or, the accessory item.
121136
* @param accessoryType The accessory type for which we're looking
122137
* @return
123138
*/
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
/**
2+
* Copyright (c) 2010-2018 by the respective copyright holders.
3+
*
4+
* All rights reserved. This program and the accompanying materials
5+
* are made available under the terms of the Eclipse Public License v1.0
6+
* which accompanies this distribution, and is available at
7+
* http://www.eclipse.org/legal/epl-v10.html
8+
*/
9+
package org.openhab.io.homekit.internal.accessories;
10+
11+
import java.util.concurrent.CompletableFuture;
12+
13+
import org.eclipse.jdt.annotation.NonNull;
14+
import org.eclipse.smarthome.core.items.GenericItem;
15+
import org.eclipse.smarthome.core.items.ItemRegistry;
16+
import org.eclipse.smarthome.core.library.types.OnOffType;
17+
import org.eclipse.smarthome.core.library.types.OpenClosedType;
18+
import org.openhab.io.homekit.internal.HomekitAccessoryUpdater;
19+
import org.openhab.io.homekit.internal.HomekitTaggedItem;
20+
import org.openhab.io.homekit.internal.battery.BatteryStatus;
21+
22+
import com.beowulfe.hap.HomekitCharacteristicChangeCallback;
23+
import com.beowulfe.hap.accessories.BatteryStatusAccessory;
24+
import com.beowulfe.hap.accessories.CarbonMonoxideSensor;
25+
import com.beowulfe.hap.accessories.properties.CarbonMonoxideDetectedState;
26+
27+
/**
28+
*
29+
* @author Cody Cutrer - Initial implementation
30+
*/
31+
public class HomekitCarbonMonoxideSensorImpl extends AbstractHomekitAccessoryImpl<GenericItem>
32+
implements CarbonMonoxideSensor, BatteryStatusAccessory {
33+
34+
@NonNull
35+
private BatteryStatus batteryStatus;
36+
37+
private BooleanItemReader carbonMonoxideDetectedReader;
38+
39+
public HomekitCarbonMonoxideSensorImpl(HomekitTaggedItem taggedItem, ItemRegistry itemRegistry,
40+
HomekitAccessoryUpdater updater, BatteryStatus batteryStatus) {
41+
super(taggedItem, itemRegistry, updater, GenericItem.class);
42+
43+
this.carbonMonoxideDetectedReader = new BooleanItemReader(taggedItem.getItem(), OnOffType.ON,
44+
OpenClosedType.OPEN);
45+
this.batteryStatus = batteryStatus;
46+
}
47+
48+
@Override
49+
public CompletableFuture<CarbonMonoxideDetectedState> getCarbonMonoxideDetectedState() {
50+
Boolean state = this.carbonMonoxideDetectedReader.getValue();
51+
if (state == null) {
52+
return CompletableFuture.completedFuture(null);
53+
}
54+
return CompletableFuture
55+
.completedFuture(state ? CarbonMonoxideDetectedState.ABNORMAL : CarbonMonoxideDetectedState.NORMAL);
56+
}
57+
58+
@Override
59+
public void subscribeCarbonMonoxideDetectedState(HomekitCharacteristicChangeCallback callback) {
60+
getUpdater().subscribe(getItem(), callback);
61+
}
62+
63+
@Override
64+
public void unsubscribeCarbonMonoxideDetectedState() {
65+
getUpdater().unsubscribe(getItem());
66+
}
67+
68+
@Override
69+
public CompletableFuture<Boolean> getLowBatteryState() {
70+
return CompletableFuture.completedFuture(batteryStatus.isLow());
71+
}
72+
73+
@Override
74+
public void subscribeLowBatteryState(HomekitCharacteristicChangeCallback callback) {
75+
batteryStatus.subscribe(getUpdater(), callback);
76+
}
77+
78+
@Override
79+
public void unsubscribeLowBatteryState() {
80+
batteryStatus.unsubscribe(getUpdater());
81+
}
82+
83+
static HomekitLeakSensorImpl createForTaggedItem(HomekitTaggedItem taggedItem, ItemRegistry itemRegistry,
84+
HomekitAccessoryUpdater updater) {
85+
86+
if (taggedItem.isMemberOfAccessoryGroup()) {
87+
88+
}
89+
return null;
90+
}
91+
}
Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
/**
2+
* Copyright (c) 2010-2018 by the respective copyright holders.
3+
*
4+
* All rights reserved. This program and the accompanying materials
5+
* are made available under the terms of the Eclipse Public License v1.0
6+
* which accompanies this distribution, and is available at
7+
* http://www.eclipse.org/legal/epl-v10.html
8+
*/
9+
package org.openhab.io.homekit.internal.accessories;
10+
11+
import java.util.concurrent.CompletableFuture;
12+
13+
import org.eclipse.jdt.annotation.NonNull;
14+
import org.eclipse.smarthome.core.items.GenericItem;
15+
import org.eclipse.smarthome.core.items.ItemRegistry;
16+
import org.eclipse.smarthome.core.library.types.OnOffType;
17+
import org.eclipse.smarthome.core.library.types.OpenClosedType;
18+
import org.openhab.io.homekit.internal.HomekitAccessoryUpdater;
19+
import org.openhab.io.homekit.internal.HomekitTaggedItem;
20+
import org.openhab.io.homekit.internal.battery.BatteryStatus;
21+
22+
import com.beowulfe.hap.HomekitCharacteristicChangeCallback;
23+
import com.beowulfe.hap.accessories.BatteryStatusAccessory;
24+
import com.beowulfe.hap.accessories.SmokeSensor;
25+
import com.beowulfe.hap.accessories.properties.SmokeDetectedState;
26+
27+
/**
28+
*
29+
* @author Cody Cutrer - Initial implementation
30+
*/
31+
public class HomekitSmokeSensorImpl extends AbstractHomekitAccessoryImpl<GenericItem>
32+
implements SmokeSensor, BatteryStatusAccessory {
33+
34+
@NonNull
35+
private BatteryStatus batteryStatus;
36+
37+
private BooleanItemReader smokeDetectedReader;
38+
39+
public HomekitSmokeSensorImpl(HomekitTaggedItem taggedItem, ItemRegistry itemRegistry,
40+
HomekitAccessoryUpdater updater, BatteryStatus batteryStatus) {
41+
super(taggedItem, itemRegistry, updater, GenericItem.class);
42+
43+
this.smokeDetectedReader = new BooleanItemReader(taggedItem.getItem(), OnOffType.ON, OpenClosedType.OPEN);
44+
this.batteryStatus = batteryStatus;
45+
}
46+
47+
@Override
48+
public CompletableFuture<SmokeDetectedState> getSmokeDetectedState() {
49+
Boolean state = this.smokeDetectedReader.getValue();
50+
if (state == null) {
51+
return CompletableFuture.completedFuture(null);
52+
}
53+
return CompletableFuture.completedFuture(state ? SmokeDetectedState.DETECTED : SmokeDetectedState.NOT_DETECTED);
54+
}
55+
56+
@Override
57+
public void subscribeSmokeDetectedState(HomekitCharacteristicChangeCallback callback) {
58+
getUpdater().subscribe(getItem(), callback);
59+
}
60+
61+
@Override
62+
public void unsubscribeSmokeDetectedState() {
63+
getUpdater().unsubscribe(getItem());
64+
}
65+
66+
@Override
67+
public CompletableFuture<Boolean> getLowBatteryState() {
68+
return CompletableFuture.completedFuture(batteryStatus.isLow());
69+
}
70+
71+
@Override
72+
public void subscribeLowBatteryState(HomekitCharacteristicChangeCallback callback) {
73+
batteryStatus.subscribe(getUpdater(), callback);
74+
}
75+
76+
@Override
77+
public void unsubscribeLowBatteryState() {
78+
batteryStatus.unsubscribe(getUpdater());
79+
}
80+
81+
static HomekitLeakSensorImpl createForTaggedItem(HomekitTaggedItem taggedItem, ItemRegistry itemRegistry,
82+
HomekitAccessoryUpdater updater) {
83+
84+
if (taggedItem.isMemberOfAccessoryGroup()) {
85+
86+
}
87+
return null;
88+
}
89+
}

0 commit comments

Comments
 (0)