[homekit] fix for battery charging state (#12959)

* fix for battery charging state

Signed-off-by: Eugen Freiter <freiter@gmx.de>
This commit is contained in:
eugen 2022-06-22 11:20:36 +02:00 committed by GitHub
parent cf94687ad6
commit 565dc19508
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 62 additions and 39 deletions

View File

@ -473,6 +473,7 @@ For example, ceiling fans often include lighting functionality. Such fans can be
- two separate HomeKit accessories - fan **and** light.
iOS home app would show them as **two tiles** that can be controlled directly from home screen.
![ios_fan_and_light_home_screen.png](doc/ios_fan_and_light_home_screen.png)
- one complex accessory - fan **with** light.
@ -517,12 +518,17 @@ Group FanWithLight "Fan with Light" {
![ui_fan_with_light_primary.png](doc/ui_fan_with_light_primary.png)
Similarly, you can create a sensor with battery
![ui_sensor_with_battery.png](doc/ui_sensor_with_battery.png)
However, home app does not support changing of tiles for already added accessory.
If you want to change the tile after the accessory was added, you need either to rename the group, if you use textual item configuration, or to delete and to create a new group with a different name, if you use UI for configuration.
You can combine more than two accessories as well as accessories linked to different physical devices.
You can also do unusually combinations, e.g. you can combine temperature sensor with blinds and light.
It will be represented by home app as follows
![ios_complex_accessory_detail_screen.png](doc/ios_complex_accessory_detail_screen.png)

Binary file not shown.

After

Width:  |  Height:  |  Size: 131 KiB

View File

@ -153,4 +153,9 @@ public enum HomekitCharacteristicType {
public static Optional<HomekitCharacteristicType> valueOfTag(String tag) {
return Optional.ofNullable(TAG_MAP.get(tag));
}
@Override
public String toString() {
return tag;
}
}

View File

@ -282,7 +282,7 @@ public class HomekitTaggedItem {
}
public String toString() {
return "Item:" + proxyItem.getItem() + " HomeKit type:" + homekitAccessoryType + " HomeKit characteristic:"
+ homekitCharacteristicType;
return "Item:" + proxyItem.getItem() + " HomeKit type: '" + homekitAccessoryType.getTag()
+ "' characteristic: '" + homekitCharacteristicType.getTag() + "'";
}
}

View File

@ -141,8 +141,21 @@ public class HomekitAccessoryFactory {
}
};
private static List<HomekitCharacteristicType> getRequiredCharacteristics(HomekitTaggedItem taggedItem) {
if (taggedItem.getAccessoryType() == BATTERY) {
final String isChargeable = taggedItem.getConfiguration(HomekitBatteryImpl.BATTERY_TYPE, "false");
if ("true".equalsIgnoreCase(isChargeable) || "yes".equalsIgnoreCase(isChargeable)) {
final List<HomekitCharacteristicType> characteristics = new ArrayList<>();
characteristics.addAll(Arrays.asList(MANDATORY_CHARACTERISTICS.get(taggedItem.getAccessoryType())));
characteristics.add(BATTERY_CHARGING_STATE);
return characteristics;
}
}
return Arrays.asList(MANDATORY_CHARACTERISTICS.get(taggedItem.getAccessoryType()));
}
/**
* creates HomeKit accessory for a openhab item.
* creates HomeKit accessory for an openhab item.
*
* @param taggedItem openhab item tagged as HomeKit item
* @param metadataRegistry openhab metadata registry required to get item meta information
@ -158,12 +171,12 @@ public class HomekitAccessoryFactory {
HomekitAccessoryUpdater updater, HomekitSettings settings) throws HomekitException {
final HomekitAccessoryType accessoryType = taggedItem.getAccessoryType();
logger.trace("Constructing {} of accessory type {}", taggedItem.getName(), accessoryType.getTag());
final List<HomekitTaggedItem> requiredCharacteristics = getMandatoryCharacteristics(taggedItem,
final List<HomekitTaggedItem> foundCharacteristics = getMandatoryCharacteristicsFromItem(taggedItem,
metadataRegistry);
final HomekitCharacteristicType[] mandatoryCharacteristics = MANDATORY_CHARACTERISTICS.get(accessoryType);
if ((mandatoryCharacteristics != null) && (requiredCharacteristics.size() < mandatoryCharacteristics.length)) {
final List<HomekitCharacteristicType> mandatoryCharacteristics = getRequiredCharacteristics(taggedItem);
if (foundCharacteristics.size() < mandatoryCharacteristics.size()) {
logger.warn("Accessory of type {} must have following characteristics {}. Found only {}",
accessoryType.getTag(), mandatoryCharacteristics, requiredCharacteristics);
accessoryType.getTag(), mandatoryCharacteristics, foundCharacteristics);
throw new HomekitException("Missing mandatory characteristics");
}
AbstractHomekitAccessoryImpl accessoryImpl;
@ -171,10 +184,9 @@ public class HomekitAccessoryFactory {
final @Nullable Class<? extends AbstractHomekitAccessoryImpl> accessoryImplClass = SERVICE_IMPL_MAP
.get(accessoryType);
if (accessoryImplClass != null) {
accessoryImpl = accessoryImplClass
.getConstructor(HomekitTaggedItem.class, List.class, HomekitAccessoryUpdater.class,
HomekitSettings.class)
.newInstance(taggedItem, requiredCharacteristics, updater, settings);
accessoryImpl = accessoryImplClass.getConstructor(HomekitTaggedItem.class, List.class,
HomekitAccessoryUpdater.class, HomekitSettings.class)
.newInstance(taggedItem, foundCharacteristics, updater, settings);
addOptionalCharacteristics(taggedItem, accessoryImpl, metadataRegistry);
return accessoryImpl;
} else {
@ -255,7 +267,7 @@ public class HomekitAccessoryFactory {
* @param metadataRegistry meta data registry
* @return list of mandatory
*/
private static List<HomekitTaggedItem> getMandatoryCharacteristics(HomekitTaggedItem taggedItem,
private static List<HomekitTaggedItem> getMandatoryCharacteristicsFromItem(HomekitTaggedItem taggedItem,
MetadataRegistry metadataRegistry) {
List<HomekitTaggedItem> collectedCharacteristics = new ArrayList<>();
if (taggedItem.isGroup()) {
@ -265,8 +277,7 @@ public class HomekitAccessoryFactory {
} else {
addMandatoryCharacteristics(taggedItem, collectedCharacteristics, taggedItem.getItem(), metadataRegistry);
}
logger.trace("Mandatory characteristics for item {} characteristics {}", taggedItem.getName(),
collectedCharacteristics);
logger.trace("Mandatory characteristics: {}", collectedCharacteristics);
return collectedCharacteristics;
}
@ -285,9 +296,8 @@ public class HomekitAccessoryFactory {
private static void addMandatoryCharacteristics(HomekitTaggedItem mainItem, List<HomekitTaggedItem> characteristics,
Item item, MetadataRegistry metadataRegistry) {
// get list of mandatory characteristics
HomekitCharacteristicType[] mandatoryCharacteristics = MANDATORY_CHARACTERISTICS
.get(mainItem.getAccessoryType());
if ((mandatoryCharacteristics == null) || (mandatoryCharacteristics.length == 0)) {
List<HomekitCharacteristicType> mandatoryCharacteristics = getRequiredCharacteristics(mainItem);
if (mandatoryCharacteristics.isEmpty()) {
// no mandatory characteristics linked to accessory type of mainItem. we are done
return;
}
@ -299,13 +309,12 @@ public class HomekitAccessoryFactory {
for (Entry<HomekitAccessoryType, HomekitCharacteristicType> accessory : getAccessoryTypes(item,
metadataRegistry)) {
// if the item has only accessory tag, e.g. TemperatureSensor,
// the we will link all mandatory characteristic to this item,
// then we will link all mandatory characteristic to this item,
// e.g. we will link CurrentTemperature in case of TemperatureSensor.
if (isRootAccessory(accessory)) {
Arrays.stream(mandatoryCharacteristics)
.forEach(c -> characteristics.add(new HomekitTaggedItem(itemProxy, accessory.getKey(), c,
mainItem.isGroup() ? (GroupItem) mainItem.getItem() : null,
HomekitAccessoryFactory.getItemConfiguration(item, metadataRegistry))));
mandatoryCharacteristics.forEach(c -> characteristics.add(new HomekitTaggedItem(itemProxy,
accessory.getKey(), c, mainItem.isGroup() ? (GroupItem) mainItem.getItem() : null,
HomekitAccessoryFactory.getItemConfiguration(item, metadataRegistry))));
} else {
// item has characteristic tag on it, so, adding it as that characteristic.
@ -313,7 +322,7 @@ public class HomekitAccessoryFactory {
// check whether it is a mandatory characteristic. optional will be added later by another method.
if (belongsToType(mainItem.getAccessoryType(), accessory)
&& isMandatoryCharacteristic(mainItem.getAccessoryType(), characteristic)) {
&& isMandatoryCharacteristic(mainItem, characteristic)) {
characteristics.add(new HomekitTaggedItem(itemProxy, accessory.getKey(), characteristic,
mainItem.isGroup() ? (GroupItem) mainItem.getItem() : null,
HomekitAccessoryFactory.getItemConfiguration(item, metadataRegistry)));
@ -373,30 +382,28 @@ public class HomekitAccessoryFactory {
GroupItem groupItem = (GroupItem) taggedItem.getItem();
groupItem.getMembers().forEach(item -> getAccessoryTypes(item, metadataRegistry).stream()
.filter(c -> !isRootAccessory(c)).filter(c -> belongsToType(taggedItem.getAccessoryType(), c))
.filter(c -> !isMandatoryCharacteristic(taggedItem.getAccessoryType(), c.getValue()))
.filter(c -> !isMandatoryCharacteristic(taggedItem, c.getValue()))
.forEach(characteristic -> characteristicItems.put(characteristic.getValue(), (GenericItem) item)));
} else {
getAccessoryTypes(taggedItem.getItem(), metadataRegistry).stream().filter(c -> !isRootAccessory(c))
.filter(c -> !isMandatoryCharacteristic(taggedItem.getAccessoryType(), c.getValue()))
.filter(c -> !isMandatoryCharacteristic(taggedItem, c.getValue()))
.forEach(characteristic -> characteristicItems.put(characteristic.getValue(),
(GenericItem) taggedItem.getItem()));
}
logger.trace("Optional characteristics for item {} characteristics {}", taggedItem.getName(),
characteristicItems);
logger.trace("Optional characteristics for item {}: {}", taggedItem.getName(), characteristicItems.values());
return Collections.unmodifiableMap(characteristicItems);
}
/**
* return true is characteristic is a mandatory characteristic for the accessory.
*
* @param accessory accessory
* @param item item
* @param characteristic characteristic
* @return true if characteristic is mandatory, false if not mandatory
*/
private static boolean isMandatoryCharacteristic(HomekitAccessoryType accessory,
HomekitCharacteristicType characteristic) {
return MANDATORY_CHARACTERISTICS.containsKey(accessory)
&& Arrays.asList(MANDATORY_CHARACTERISTICS.get(accessory)).contains(characteristic);
private static boolean isMandatoryCharacteristic(HomekitTaggedItem item, HomekitCharacteristicType characteristic) {
return MANDATORY_CHARACTERISTICS.containsKey(item.getAccessoryType())
&& getRequiredCharacteristics(item).contains(characteristic);
}
/**

View File

@ -37,7 +37,7 @@ import io.github.hapjava.services.impl.BatteryService;
* @author Eugen Freiter - Initial contribution
*/
public class HomekitBatteryImpl extends AbstractHomekitAccessoryImpl implements BatteryAccessory {
private static final String BATTERY_TYPE = "chargeable";
public static final String BATTERY_TYPE = "chargeable";
private final BooleanItemReader lowBatteryReader;
private BooleanItemReader chargingBatteryReader;
@ -87,7 +87,9 @@ public class HomekitBatteryImpl extends AbstractHomekitAccessoryImpl implements
@Override
public void subscribeBatteryChargingState(final HomekitCharacteristicChangeCallback callback) {
subscribe(BATTERY_CHARGING_STATE, callback);
if (isChargeable) {
subscribe(BATTERY_CHARGING_STATE, callback);
}
}
@Override
@ -102,6 +104,8 @@ public class HomekitBatteryImpl extends AbstractHomekitAccessoryImpl implements
@Override
public void unsubscribeBatteryChargingState() {
unsubscribe(BATTERY_CHARGING_STATE);
if (isChargeable) {
unsubscribe(BATTERY_CHARGING_STATE);
}
}
}

View File

@ -184,13 +184,14 @@ public class HomekitCharacteristicFactory {
public static Characteristic createCharacteristic(HomekitTaggedItem item, HomekitAccessoryUpdater updater)
throws HomekitException {
final @Nullable HomekitCharacteristicType type = item.getCharacteristicType();
logger.trace("CreateCharacteristic, type {} item {}", type, item);
logger.trace("Create characteristic {}", item);
if (optional.containsKey(type)) {
return optional.get(type).apply(item, updater);
}
logger.warn("Unsupported optional characteristic. Accessory type {}, characteristic type {}",
item.getAccessoryType(), type);
throw new HomekitException("Unsupported optional characteristic. Characteristic type \"" + type + "\"");
item.getAccessoryType(), type.getTag());
throw new HomekitException(
"Unsupported optional characteristic. Characteristic type \"" + type.getTag() + "\"");
}
// METHODS TO CREATE SINGLE CHARACTERISTIC FROM OH ITEM

View File

@ -24,7 +24,7 @@ public class IncompleteAccessoryException extends Exception {
private static final long serialVersionUID = 8595808359805444177L;
public IncompleteAccessoryException(HomekitCharacteristicType missingType) {
super(String.format("Missing accessory type %s", missingType.getTag()));
super(String.format("Missing accessory characteristic %s", missingType.getTag()));
}
public IncompleteAccessoryException(String message) {