[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:
Cody Cutrer
2023-01-13 12:25:06 -07:00
committed by GitHub
parent ee54882841
commit 0de87b15d2
7 changed files with 201 additions and 8 deletions

View File

@@ -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<>();

View File

@@ -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<>();

View File

@@ -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);
}
};

View File

@@ -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);
}
}

View File

@@ -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 -> {

View File

@@ -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;
}
}