How precisely is a predicate evaluated for an HMEventTrigger?

Perhaps I am just stupid, but I can't quite grok that. What object is the predicate evaluated over, how, and at what moment?

Originally I've thought when some event causes the trigger to fire, the predicate would just be evaluated over that event; but that can't be that simple, see please details below.

I've set (in a 3rd party application; Home, far as I can say, does not allow that) a predicate “only when both of a pair of lights are off”. The predicate works perfectly, and the event really performs its action set only when both of the lights are off, so far so good.

Nevertheless, I've dumped the predicate, and it looks like this (the first line is my own dump which is a bit more concise; the 2nd one is the standard description just to make sure that my own dump works properly):

(((self.characteristic == 'Power State' of 'Lustr O') && (self.characteristicValue == 0)) && ((self.characteristic == 'Power State' of 'Knihovna') && (self.characteristicValue == 0)))

(characteristic == <HMCharacteristic, Unique ID: 2E046807-9127-565F-B9CB-10BC0F2EE6D2, Type: 00000025, Instance ID: 9> AND characteristicValue == 0) AND (characteristic == <HMCharacteristic, Unique ID: 7444CCCE-1C1B-5234-8CAE-01DA4D811B8B, Type: 00000025, Instance ID: 9> AND characteristicValue == 0)

I can't understand that at all :(

I must be missing something of great importance, but it seems to me that a predicate which can be just as well[1] written as self.characteristic==A && self.characteristic==B && (some other conditions) can't ever evaluate to YES when A and B differ (which they self-evidently do in this case).

The “to evaluate before executing the scene” of the documentation does not really help. What do I miss, can anybody explain that? Thanks!

[1] Correct me please if I am wrong, but I do believe the logical AND is both an associative and an commutative operator, which makes this re-write fully equivalent to the original.

Perhaps I am just stupid, but I can't quite grok that.

So, to be perfectly honest, this area of the API was never particularly well documented nor has it been heavily used. That's since been exacerbated by the fact that Home.app has implemented features like it's shortcuts integration which are both more powerful and not accessible through the public API.

In any case:

What object is the predicate evaluated over, how, and at what moment?

I think the best way to understand this is that its being evaluated against the entire home configuration.

So this is:

(characteristic == <HMCharacteristic, Unique ID: 2E046807-9127-565F-B9CB-10BC0F2EE6D2, Type: 00000025, Instance ID: 9> AND characteristicValue == 0)

Means "Does a characteristic with an ID value of UID and a value == 0 exist?", while the other is asking the same question about the a different UID. Each of those statements are separately evaluated, then those two results are then AND'd together.

__
Kevin Elliott
DTS Engineer, CoreOS/Hardware

Thanks a lot!

Far as I understand, this would most probably mean the HMEventTrigger never uses[1] the standard evaluateWithObject: service. Instead it parses the predicate and evaluates it sort of “manually“, among others, whenever it encounters a nested compound predicate like the ones above, it evaluates it a special way, finding explicitly the appropriate characteristic in the current HomeKit, instead of evaluating it over same object as the other parts.

Which alas opens more questions it answers. How could I create such predicates trusting they would work right? What would happen e.g., if the characteristic became unreachable or blocked, or even the accessory which contained it was removed from the HK meantime?

The original reason I've started studying this was a need to set up a predicate which would compare values of two different characteristics: “perform the scene only if the current value of characteristic A is greater than the current value of characteristic B“. How do I do that (if possible at all)?

Of course I could try to create different predicates and test whether they can be set up to triggers (and if so, whether they work right), but without a proper documentation that's not good, for even if something happens to work now, if not explicitly documented, it could stop working in the next system version :(

[1] Or perhaps it first creates a different predicate essentially copying the original, but replacing those nested characteristic-based ones by constant boolean values, and then does use evaluateWithObject: on this “copy” to reuse the standard code for evaluating all those potentially complex boolean expressions and all the non-characteristic-based parts, which is probably how would I implement it myself. Which though boils down to essentially the same result and the questions open above remain.