[homekit] Implement IrrigationSystem Accessory (#14209)
* [homekit] Implement IrrigationSystem Fairly trivial now, except that a ServiceLabelService has to be added to the accessory. Signed-off-by: Cody Cutrer <cody@cutrer.us>
This commit is contained in:
parent
ee54882841
commit
0de87b15d2
|
@ -41,6 +41,7 @@ HomeKit integration supports following accessory types:
|
|||
- Battery
|
||||
- Filter Maintenance
|
||||
- Television
|
||||
- Irrigation System
|
||||
|
||||
## Quick start
|
||||
|
||||
|
@ -591,7 +592,7 @@ configuration for these two cases looks as follow:
|
|||
- valve with timer:
|
||||
|
||||
```xtend
|
||||
Group gValve "Valve Group" {homekit="Valve" [homekitValveType="Irrigation"]}
|
||||
Group gValve "Valve Group" {homekit="Valve" [ValveType="Irrigation"]}
|
||||
Switch valve_active "Valve active" (gValve) {homekit = "Valve.ActiveStatus, Valve.InUseStatus"}
|
||||
Number valve_duration "Valve duration" (gValve) {homekit = "Valve.Duration"}
|
||||
Number valve_remaining_duration "Valve remaining duration" (gValve) {homekit = "Valve.RemainingDuration"}
|
||||
|
@ -600,11 +601,38 @@ Number valve_remaining_duration "Valve remaining duration" (gValve)
|
|||
- valve without timer (no item for remaining duration required)
|
||||
|
||||
```xtend
|
||||
Group gValve "Valve Group" {homekit="Valve" [homekitValveType="Irrigation", homekitTimer="true"]}
|
||||
Group gValve "Valve Group" {homekit="Valve" [ValveType="Irrigation", homekitTimer="true"]}
|
||||
Switch valve_active "Valve active" (gValve) {homekit = "Valve.ActiveStatus, Valve.InUseStatus"}
|
||||
Number valve_duration "Valve duration" (gValve) {homekit = "Valve.Duration" [homekitDefaultDuration = 1800]}
|
||||
```
|
||||
|
||||
### Irrigation System
|
||||
|
||||
An irrigation system is an accessory composed of multiple valves.
|
||||
You just need to link multiple valves within an irrigation system's group.
|
||||
When part of an irrigation system, valves are required to have Duration and RemainingDuration characteristics, as well as a ServiceIndex.
|
||||
The valve's types will also automatically be set to IRRIGATION.
|
||||
|
||||
```java
|
||||
Group gIrrigationSystem "Irrigation System" { homekit="IrrigationSystem" }
|
||||
String irrigationSystemProgramMode (gIrrigationSystem) { homekit="ProgramMode" }
|
||||
Switch irrigationSystemEnabled (gIrrigationSystem) { homekit="Active" }
|
||||
Switch irrigationSystemInUse (gIrrigationSystem) { homekit="InUseStatus" }
|
||||
Group irrigationSystemTotalRemaining (gIrrigationSystem) { homekit="RemainingDuration" }
|
||||
|
||||
Group gValve1 "Valve 1" (gIrrigationSystem) { homekit="Valve"[ServiceIndex=1] }
|
||||
Switch valve1Active (gValve1) { homekit="ActiveStatus" }
|
||||
Switch valve1InUse (gValve1) { homekit="InUseStatus" }
|
||||
Number valve1SetDuration (gValve1) { homekit="Duration" }
|
||||
Number valve1RemainingDuration (gValve1) { homekit="RemainingDuration" }
|
||||
|
||||
Group gValve2 "Valve 2" (gIrrigationSystem) { homekit="Valve"[ServiceIndex=2] }
|
||||
Switch valve2Active (gValve2) { homekit="ActiveStatus" }
|
||||
Switch valve2InUse (gValve2) { homekit="InUseStatus" }
|
||||
Number valve2SetDuration (gValve2) { homekit="Duration" }
|
||||
Number valve2RemainingDuration (gValve2) { homekit="RemainingDuration" }
|
||||
```
|
||||
|
||||
### Sensors
|
||||
|
||||
Sensors have typically one mandatory characteristic, e.g. temperature or lead trigger, and several optional characteristics which are typically used for battery powered sensors and/or wireless sensors.
|
||||
|
@ -830,7 +858,7 @@ or using UI
|
|||
| | LockCurrentState | | Switch, Number | Current state of lock mechanism (1/ON=SECURED, 0/OFF=UNSECURED, 2=JAMMED, 3=UNKNOWN) |
|
||||
| | LockTargetState | | Switch | Target state of lock mechanism (ON=SECURED, OFF=UNSECURED) |
|
||||
| | | Name | String | Name of the lock |
|
||||
| Valve | | | | Valve. additional configuration: homekitValveType = ["Generic", "Irrigation", "Shower", "Faucet"] |
|
||||
| Valve | | | | Valve. additional configuration: ValveType = ["Generic", "Irrigation", "Shower", "Faucet"] |
|
||||
| | ActiveStatus | | Switch, Dimmer | Accessory current working status. A value of "ON"/"OPEN" indicates that the accessory is active and is functioning without any errors. |
|
||||
| | InUseStatus | | Switch, Dimmer | Indicates whether fluid flowing through the valve. A value of "ON"/"OPEN" indicates that fluid is flowing. |
|
||||
| | | Duration | Number | Defines how long a valve should be set to ʼIn Useʼ in second. You can define the default duration via configuration homekitDefaultDuration = <default duration in seconds> |
|
||||
|
@ -908,6 +936,13 @@ or using UI
|
|||
| | | Volume | Dimmer, Number | Current volume. min/max/step can configured at item level, e.g. minValue=10.5, maxValue=50, step=2] |
|
||||
| | | VolumeSelector | Dimmer, String | If linked do a dimmer item, will send INCREASE/DECREASE commands. If linked to a string item, will send INCREMENT and DECREMENT. |
|
||||
| | | VolumeControlType | String | The type of control available. This will default to infer based on what other items are linked. NONE = status only, no control; RELATIVE = INCREMENT/DECREMENT only, no status; RELATIVE_WITH_CURRENT = INCREMENT/DECREMENT only with status; ABSOLUTE = direct status and control. Can also be configured via metadata, e.g. [VolumeControlType="ABSOLUTE"]. |
|
||||
| IrrigationSystem | | | | An accessory that represents multiple water valves and accommodates a programmed scheduled. |
|
||||
| | Active | | Switch | If the irrigation system as a whole is enabled. This must be ON if any of the valves are also enabled. |
|
||||
| | InUseStatus | | Switch | If the irrigation system as a whole is running. This must be ON if any of the valves are ON. |
|
||||
| | ProgramMode | | String | The current program mode of the irrigation system. Possible values (NO_SCHEDULED - no programs scheduled, SCHEDULED - program scheduled, SCHEDULED_MANUAL - program scheduled, currently overriden to manual mode). |
|
||||
| | | RemainingDuration | Number | The remaining duration for all scheduled valves in the current program in seconds. |
|
||||
| | | FaultStatus | Switch, Contact | Accessory fault status. "ON"/"OPEN" value indicates that the accessory has experienced a fault that may be interfering with its intended functionality. A value of "OFF"/"CLOSED" indicates that there is no fault. |
|
||||
|
||||
|
||||
### Examples
|
||||
|
||||
|
|
|
@ -59,6 +59,7 @@ public enum HomekitAccessoryType {
|
|||
INPUT_SOURCE("InputSource"),
|
||||
TELEVISION_SPEAKER("TelevisionSpeaker"),
|
||||
ACCESSORY_GROUP("AccessoryGroup"),
|
||||
IRRIGATION_SYSTEM("IrrigationSystem"),
|
||||
DUMMY("Dummy");
|
||||
|
||||
private static final Map<String, HomekitAccessoryType> TAG_MAP = new HashMap<>();
|
||||
|
|
|
@ -141,7 +141,11 @@ public enum HomekitCharacteristicType {
|
|||
TARGET_VISIBILITY_STATE("TargetVisibilityState"),
|
||||
|
||||
VOLUME_SELECTOR("VolumeSelector"),
|
||||
VOLUME_CONTROL_TYPE("VolumeControlType");
|
||||
VOLUME_CONTROL_TYPE("VolumeControlType"),
|
||||
|
||||
PROGRAM_MODE("ProgramMode"),
|
||||
SERVICE_LABEL("ServiceLabel"),
|
||||
SERVICE_INDEX("ServiceIndex");
|
||||
|
||||
private static final Map<String, HomekitCharacteristicType> TAG_MAP = new HashMap<>();
|
||||
|
||||
|
|
|
@ -108,6 +108,7 @@ public class HomekitAccessoryFactory {
|
|||
put(TELEVISION, new HomekitCharacteristicType[] { ACTIVE });
|
||||
put(INPUT_SOURCE, new HomekitCharacteristicType[] {});
|
||||
put(TELEVISION_SPEAKER, new HomekitCharacteristicType[] { MUTE });
|
||||
put(IRRIGATION_SYSTEM, new HomekitCharacteristicType[] { ACTIVE, INUSE_STATUS, PROGRAM_MODE });
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -150,6 +151,7 @@ public class HomekitAccessoryFactory {
|
|||
put(TELEVISION, HomekitTelevisionImpl.class);
|
||||
put(INPUT_SOURCE, HomekitInputSourceImpl.class);
|
||||
put(TELEVISION_SPEAKER, HomekitTelevisionSpeakerImpl.class);
|
||||
put(IRRIGATION_SYSTEM, HomekitIrrigationSystemImpl.class);
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
@ -0,0 +1,130 @@
|
|||
/**
|
||||
* Copyright (c) 2010-2023 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.io.homekit.internal.accessories;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.openhab.core.library.types.OnOffType;
|
||||
import org.openhab.io.homekit.internal.HomekitAccessoryUpdater;
|
||||
import org.openhab.io.homekit.internal.HomekitCharacteristicType;
|
||||
import org.openhab.io.homekit.internal.HomekitSettings;
|
||||
import org.openhab.io.homekit.internal.HomekitTaggedItem;
|
||||
|
||||
import io.github.hapjava.accessories.IrrigationSystemAccessory;
|
||||
import io.github.hapjava.characteristics.HomekitCharacteristicChangeCallback;
|
||||
import io.github.hapjava.characteristics.impl.common.ActiveEnum;
|
||||
import io.github.hapjava.characteristics.impl.common.InUseEnum;
|
||||
import io.github.hapjava.characteristics.impl.common.ProgramModeEnum;
|
||||
import io.github.hapjava.characteristics.impl.common.ServiceLabelNamespaceCharacteristic;
|
||||
import io.github.hapjava.characteristics.impl.common.ServiceLabelNamespaceEnum;
|
||||
import io.github.hapjava.services.impl.IrrigationSystemService;
|
||||
import io.github.hapjava.services.impl.ServiceLabelService;
|
||||
|
||||
/**
|
||||
* Implements an Irrigation System accessory.
|
||||
*
|
||||
* To be a complete accessory, the user must configure individual valves linked
|
||||
* to this primary service. This class also adds the ServiceLabelService
|
||||
* automatically.
|
||||
*
|
||||
* @author Cody Cutrer - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault({})
|
||||
public class HomekitIrrigationSystemImpl extends AbstractHomekitAccessoryImpl implements IrrigationSystemAccessory {
|
||||
private BooleanItemReader inUseReader;
|
||||
private Map<ProgramModeEnum, String> programModeMap;
|
||||
private static final String SERVICE_LABEL = "ServiceLabel";
|
||||
|
||||
public HomekitIrrigationSystemImpl(HomekitTaggedItem taggedItem, List<HomekitTaggedItem> mandatoryCharacteristics,
|
||||
HomekitAccessoryUpdater updater, HomekitSettings settings) throws IncompleteAccessoryException {
|
||||
super(taggedItem, mandatoryCharacteristics, updater, settings);
|
||||
inUseReader = createBooleanReader(HomekitCharacteristicType.INUSE_STATUS);
|
||||
programModeMap = HomekitCharacteristicFactory
|
||||
.createMapping(getCharacteristic(HomekitCharacteristicType.PROGRAM_MODE).get(), ProgramModeEnum.class);
|
||||
getServices().add(new IrrigationSystemService(this));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void init() {
|
||||
String serviceLabelNamespaceConfig = getAccessoryConfiguration(SERVICE_LABEL, "ARABIC_NUMERALS");
|
||||
ServiceLabelNamespaceEnum serviceLabelEnum;
|
||||
|
||||
try {
|
||||
serviceLabelEnum = ServiceLabelNamespaceEnum.valueOf(serviceLabelNamespaceConfig.toUpperCase());
|
||||
} catch (IllegalArgumentException e) {
|
||||
serviceLabelEnum = ServiceLabelNamespaceEnum.ARABIC_NUMERALS;
|
||||
}
|
||||
final var finalEnum = serviceLabelEnum;
|
||||
var serviceLabelNamespace = getCharacteristic(ServiceLabelNamespaceCharacteristic.class).orElseGet(
|
||||
() -> new ServiceLabelNamespaceCharacteristic(() -> CompletableFuture.completedFuture(finalEnum)));
|
||||
getServices().add(new ServiceLabelService(serviceLabelNamespace));
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<ActiveEnum> getActive() {
|
||||
OnOffType state = getStateAs(HomekitCharacteristicType.ACTIVE, OnOffType.class);
|
||||
return CompletableFuture.completedFuture(state == OnOffType.ON ? ActiveEnum.ACTIVE : ActiveEnum.INACTIVE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<Void> setActive(ActiveEnum value) {
|
||||
getCharacteristic(HomekitCharacteristicType.ACTIVE).ifPresent(tItem -> {
|
||||
tItem.send(value == ActiveEnum.ACTIVE ? OnOffType.ON : OnOffType.OFF);
|
||||
});
|
||||
return CompletableFuture.completedFuture(null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<InUseEnum> getInUse() {
|
||||
return CompletableFuture.completedFuture(inUseReader.getValue() ? InUseEnum.IN_USE : InUseEnum.NOT_IN_USE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<ProgramModeEnum> getProgramMode() {
|
||||
return CompletableFuture.completedFuture(getKeyFromMapping(HomekitCharacteristicType.PROGRAM_MODE,
|
||||
programModeMap, ProgramModeEnum.NO_SCHEDULED));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void subscribeActive(HomekitCharacteristicChangeCallback callback) {
|
||||
subscribe(HomekitCharacteristicType.ACTIVE, callback);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void unsubscribeActive() {
|
||||
unsubscribe(HomekitCharacteristicType.ACTIVE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void subscribeInUse(HomekitCharacteristicChangeCallback callback) {
|
||||
subscribe(HomekitCharacteristicType.INUSE_STATUS, callback);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void unsubscribeInUse() {
|
||||
unsubscribe(HomekitCharacteristicType.INUSE_STATUS);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void subscribeProgramMode(HomekitCharacteristicChangeCallback callback) {
|
||||
subscribe(HomekitCharacteristicType.PROGRAM_MODE, callback);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void unsubscribeProgramMode() {
|
||||
unsubscribe(HomekitCharacteristicType.PROGRAM_MODE);
|
||||
}
|
||||
}
|
|
@ -39,6 +39,7 @@ import io.github.hapjava.characteristics.impl.common.IdentifierCharacteristic;
|
|||
import io.github.hapjava.characteristics.impl.common.IsConfiguredCharacteristic;
|
||||
import io.github.hapjava.characteristics.impl.common.IsConfiguredEnum;
|
||||
import io.github.hapjava.characteristics.impl.common.NameCharacteristic;
|
||||
import io.github.hapjava.characteristics.impl.common.ServiceLabelIndexCharacteristic;
|
||||
import io.github.hapjava.characteristics.impl.heatercooler.CurrentHeaterCoolerStateCharacteristic;
|
||||
import io.github.hapjava.characteristics.impl.heatercooler.CurrentHeaterCoolerStateEnum;
|
||||
import io.github.hapjava.characteristics.impl.heatercooler.TargetHeaterCoolerStateCharacteristic;
|
||||
|
@ -90,6 +91,7 @@ public class HomekitMetadataCharacteristicFactory {
|
|||
put(INPUT_SOURCE_TYPE, HomekitMetadataCharacteristicFactory::createInputSourceTypeCharacteristic);
|
||||
put(NAME, HomekitMetadataCharacteristicFactory::createNameCharacteristic);
|
||||
put(PICTURE_MODE, HomekitMetadataCharacteristicFactory::createPictureModeCharacteristic);
|
||||
put(SERVICE_INDEX, HomekitMetadataCharacteristicFactory::createServiceIndexCharacteristic);
|
||||
put(SLEEP_DISCOVERY_MODE, HomekitMetadataCharacteristicFactory::createSleepDiscoveryModeCharacteristic);
|
||||
put(TARGET_HEATER_COOLER_STATE,
|
||||
HomekitMetadataCharacteristicFactory::createTargetHeaterCoolerStateCharacteristic);
|
||||
|
@ -249,6 +251,10 @@ public class HomekitMetadataCharacteristicFactory {
|
|||
});
|
||||
}
|
||||
|
||||
private static Characteristic createServiceIndexCharacteristic(Object value) {
|
||||
return new ServiceLabelIndexCharacteristic(getInteger(value));
|
||||
}
|
||||
|
||||
private static Characteristic createSleepDiscoveryModeCharacteristic(Object value) {
|
||||
return new SleepDiscoveryModeCharacteristic(getEnum(value, SleepDiscoveryModeEnum.class,
|
||||
SleepDiscoveryModeEnum.ALWAYS_DISCOVERABLE, SleepDiscoveryModeEnum.NOT_DISCOVERABLE), v -> {
|
||||
|
|
|
@ -38,6 +38,7 @@ import org.openhab.io.homekit.internal.HomekitTaggedItem;
|
|||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import io.github.hapjava.accessories.HomekitAccessory;
|
||||
import io.github.hapjava.accessories.ValveAccessory;
|
||||
import io.github.hapjava.characteristics.HomekitCharacteristicChangeCallback;
|
||||
import io.github.hapjava.characteristics.impl.common.ActiveEnum;
|
||||
|
@ -53,7 +54,8 @@ import io.github.hapjava.services.impl.ValveService;
|
|||
*/
|
||||
public class HomekitValveImpl extends AbstractHomekitAccessoryImpl implements ValveAccessory {
|
||||
private final Logger logger = LoggerFactory.getLogger(HomekitValveImpl.class);
|
||||
private static final String CONFIG_VALVE_TYPE = "homekitValveType";
|
||||
private static final String CONFIG_VALVE_TYPE = "ValveType";
|
||||
private static final String CONFIG_VALVE_TYPE_DEPRECATED = "homekitValveType";
|
||||
public static final String CONFIG_DEFAULT_DURATION = "homekitDefaultDuration";
|
||||
private static final String CONFIG_TIMER = "homekitTimer";
|
||||
|
||||
|
@ -70,6 +72,7 @@ public class HomekitValveImpl extends AbstractHomekitAccessoryImpl implements Va
|
|||
private final ScheduledExecutorService timerService = Executors.newSingleThreadScheduledExecutor();
|
||||
private ScheduledFuture<?> valveTimer;
|
||||
private final boolean homekitTimer;
|
||||
private ValveTypeEnum valveType;
|
||||
|
||||
public HomekitValveImpl(HomekitTaggedItem taggedItem, List<HomekitTaggedItem> mandatoryCharacteristics,
|
||||
HomekitAccessoryUpdater updater, HomekitSettings settings) throws IncompleteAccessoryException {
|
||||
|
@ -82,6 +85,10 @@ public class HomekitValveImpl extends AbstractHomekitAccessoryImpl implements Va
|
|||
if (homekitTimer) {
|
||||
addRemainingDurationCharacteristic(taggedItem, updater, service);
|
||||
}
|
||||
String valveTypeConfig = getAccessoryConfiguration(CONFIG_VALVE_TYPE, "GENERIC");
|
||||
valveTypeConfig = getAccessoryConfiguration(CONFIG_VALVE_TYPE_DEPRECATED, valveTypeConfig);
|
||||
var valveType = CONFIG_VALVE_TYPE_MAPPING.get(valveTypeConfig.toUpperCase());
|
||||
this.valveType = valveType != null ? valveType : ValveTypeEnum.GENERIC;
|
||||
}
|
||||
|
||||
private void addRemainingDurationCharacteristic(HomekitTaggedItem taggedItem, HomekitAccessoryUpdater updater,
|
||||
|
@ -191,9 +198,7 @@ public class HomekitValveImpl extends AbstractHomekitAccessoryImpl implements Va
|
|||
|
||||
@Override
|
||||
public CompletableFuture<ValveTypeEnum> getValveType() {
|
||||
final String valveType = getAccessoryConfiguration(CONFIG_VALVE_TYPE, "GENERIC");
|
||||
ValveTypeEnum type = CONFIG_VALVE_TYPE_MAPPING.get(valveType.toUpperCase());
|
||||
return CompletableFuture.completedFuture(type != null ? type : ValveTypeEnum.GENERIC);
|
||||
return CompletableFuture.completedFuture(valveType);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -205,4 +210,14 @@ public class HomekitValveImpl extends AbstractHomekitAccessoryImpl implements Va
|
|||
public void unsubscribeValveType() {
|
||||
// nothing changes here
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isLinkable(HomekitAccessory parentAccessory) {
|
||||
// When part of an irrigation system, the valve type _must_ be irrigation.
|
||||
if (parentAccessory instanceof HomekitIrrigationSystemImpl) {
|
||||
valveType = ValveTypeEnum.IRRIGATION;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue