I've just bumped into a weird problem. I've created a pretty simple and perfectly working event trigger with a non-trivial predicate which is just an AND of two characteristic-value ones.
Then I've renamed it in the Home application, and it stopped working. A dump suggests the cause is a broken predicate, for it looks now rather differently — one of those two characteristic-value predicates seems broken (details below).
Is that possible? Could please someone check whether the problem is really a Home ugly bug, or whether I do something wrong at my side? Versions are
- macOS 27.0.1 (26A434)
- Home 11.0 (1241.1.7.4.3)
I would gladly post a bugreport, but at this moment I am far from being sure I do not overlook something of importance myself. Thanks!
My code — for clarity stripped of all the error checking and reporting — looks like this:
HMHome *home=...;
HMCharacteristic *knihovna=..., *stul=..., *sklenik=...; // light chars, found by names
HMActionSet *switchOff=...; // trivial switch-off for another light, found by name
HMEventTrigger *trigger=...; // found by name if exists
if (trigger) { // if found, the predicate is logged out and that's all
NSLog(@"=== predicate found: %@", trigger.predicate);
return;
}
// otherwise, the trigger and predicate are created this way:
HMCharacteristicEvent *evt=[[HMCharacteristicEvent alloc] initWithCharacteristic:sklenik triggerValue:@1];
NSPredicate *p1=[HMEventTrigger predicateForEvaluatingTriggerWithCharacteristic:knihovna relatedBy:NSEqualToPredicateOperatorType toValue:@1];
NSPredicate *p2=[HMEventTrigger predicateForEvaluatingTriggerWithCharacteristic:stul relatedBy:NSEqualToPredicateOperatorType toValue:@1];
NSPredicate *p=[[NSCompoundPredicate alloc] initWithType:NSAndPredicateType subpredicates:@[p1, p2]];
NSLog(@"=== predicate created: %@", p);
HMEventTrigger *et=[[HMEventTrigger alloc] initWithName:@"TEST-ONLY" events:@[evt] predicate:p];
[home addTrigger:et completionHandler:^(NSError *error) {
if (!error) [et addActionSet:switchOff completionHandler:^(NSError *error) {
if (!error) [et enable:YES completionHandler:^(NSError *error) {
if (!error) NSLog(@"=== success!");
}];
}];
}];
As already mentioned, the trigger works perfectly (is trivial enough, would be weird if not). When created and also later, when found by name, the predicate looks like this:
=== predicate found: (characteristic == <HMCharacteristic, Unique ID: 7444CCCE-1C1B-5234-8CAE-01DA4D811B8B, Type: 00000025, Instance ID: 9> AND characteristicValue == 1) AND (characteristic == <HMCharacteristic, Unique ID: 2E046807-9127-565F-B9CB-10BC0F2EE6D2, Type: 00000025, Instance ID: 9> AND characteristicValue == 1)
Then, I just go to the Home application and rename the trigger, nothing else. It immediately stops working properly, and from that moment on, the predicate looks like this — note the difference:
=== predicate found: characteristic == <HMCharacteristic, Unique ID: 2E046807-9127-565F-B9CB-10BC0F2EE6D2, Type: 00000025, Instance ID: 9> AND (characteristic == <HMCharacteristic, Unique ID: 7444CCCE-1C1B-5234-8CAE-01DA4D811B8B, Type: 00000025, Instance ID: 9> AND characteristicValue == 1)
I would gladly post a bug report, but at this moment I am far from being sure I do not overlook something of importance myself. Thanks!
So, in general, it's better to file a bug report than not. Even if there is some mistake on your part, there's value in simply documenting the fact that you had trouble with the API.
However, in this case:
Is that possible? Could someone please check whether the problem is really a Home ugly bug?
Yes, this is very likely to be a problem with Home.app. I don't know what the specific issue might be, but architecturally, the HomeKit API is capable of creating predicates that are much more complex than Home.app is capable of displaying in its interface. To a large extent, this is a side effect of HomeKit's development history— the original predicate API was created as an abstract, flexible API that tried to give developers as much power as possible.
The problem with that approach is that ACTUALLY creating a viewer/editor that's capable of displaying everything that's possible is HIDEOUSLY complicated and, in fact, I'm not sure any app has actually written one. In practice, that means that most (likely "all") HomeKit apps that interact with the predicate system have predicate configurations they can't actually create or display.
As originally envisioned, this wasn't necessarily a problem— the expectation (and reality) was that most users would pick a primary controller app they "liked" and use that to create whatever automations they wanted, which means they'd never notice that there are configurations that particular app couldn't handle.
The problem then becomes "What should a controller do when presented with a configuration it can't represent?“— leave it alone or alter the predicate to something it can represent? The standard answer is to leave it alone; however, that means you're basically "isolating" automations where the predicate can't be modified. That might sound unlikely, but keep in mind that the shared home architecture means users can create automations for "each other.” So it's entirely possible for one user to create an automation that another user couldn't display/edit without paying money for an entirely new app.
Now, one thing to understand here is that all of this is true for any controller app, not just Home.app. All Home.app really changes here is that it’s become the controller app used by the vast majority of users.
In any case, the last time I looked at this, Home.app avoided modifying predicates; however, the problem here is this:
Then I've renamed it in the Home application, and it stopped working.
I'd have to look at the code, but it's very likely that HomeKit is doing a save of the entire automation, not just the name, which could mean that it then overwrote the previous predicate with “its own.” Arguably that's a bug; however, the downside of preserving automations is that you then need additional UI so that the user can "reclaim” a predicate Home.app doesn't recognize. That's not impossible to imagine; however, Home.app has generally done everything it can to minimize complexity, and that's significant extra complexity for functionality that isn't in very wide use.
or whether I do something wrong at my side?
Not in terms of code. I thought I said this in a different thread, but my main advice is to avoid modifying complex automation outside of the app that created them.
__
Kevin Elliott
DTS Engineer, CoreOS/Hardware