-
Notifications
You must be signed in to change notification settings - Fork 55
WIP: Alternate implementation to only trigger on changes #79
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
This looks much better - thanks for updating with the new approach. While it's helpful to compute the changes and passing them to the trigger function, that will become unnecessary when On the multi-trigger question, I think this strikes the right balance. The principle is this: each For "trigger on any change", how about this:
The third form could also include the state value changes too. I could go either way, although I slightly prefer that it only means attribute changes, since if you want both value and attributes you can specify both in the same trigger. Thoughts? I don't think we need to have any additional I'm happy to accept this PR as a stepping stone, and I'll start working on the states as objects, which I now have some idea about how to approach. Then we can update after that. BTW, the test fails because the trigger functions have new arguments which the tests aren't expecting. The output is very verbose, but there are particular lines that show which |
I agree that growing the function arguments isn't ideal. Perhaps I can set some additional *.old variables and then cycle through them in trigger.py. I'll give that a shot. I'll take a swing at implementing domain.entity.* as well as you described. And, while I'm not too opinionated on whether domain.entity.* should include the state as well, I can't imagine a situation where someone would want updates on all the attributes but not also the state. It seems like they'd either want to pick individual things or they'd just want EVERYTHING so they can sort it out themselves in code. I'll dig through the test output again and see if I can correct it. Though, if I succeed at handling this change, it won't even be needed. |
@craigbarratt I've updated this branch to attempt this in a new way. My limited testing shows that it works just fine. However, I made a change to |
@@ -217,9 +217,16 @@ async def state_changed(event): | |||
"value": new_val, | |||
"old_value": old_val, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
With f7169ac, "value"
can be StateVar(event.data.get('new_state'))
, or None
if new_state
is not provided (eg, when state variable is deleted. Same for "old_value"
.
if old_val is not None: | ||
for attribute in event.data["old_state"].attributes: | ||
new_vars[f"{var_name}.{attribute}.old"] = event.data["old_state"].attributes[attribute] | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
These lines shouldn't be necessary now, since the value
and old_value
contain the full states.
} | ||
if func_args['var_name'] in self.state_trig_ident: | ||
for var in self.state_trig_ident: | ||
if new_vars.get(var) != new_vars.get(f"{var}.old"): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You'll need to check if var
is domain.entity
(if so, just compare value
with old_value
) or domain.entity.attr
(if so, compare getattr(value, attr)
with getattr(old_value, attr)
).
@@ -115,12 +115,12 @@ async def update(cls, new_vars, func_args): | |||
@classmethod | |||
def notify_var_get(cls, var_names, new_vars): | |||
"""Return the most recent value of a state variable change.""" | |||
notify_vars = {} | |||
notify_vars = new_vars |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This looks like a good simplification. One small change: it's probably better to do
notify_vars = new_vars.copy()
so that new_vars
isn't modified in-place.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I honestly don't know why this wasn't working the way it was. I made this change just to be SURE it was what I expected it to be, and it fixed the whole thing. I guess cls.notify had something in it I didn't expect. But I'm not sure how.
elif var_name in new_vars: | ||
notify_vars[var_name] = new_vars[var_name] | ||
# elif var_name in new_vars: | ||
# notify_vars[var_name] = new_vars[var_name] | ||
elif 1 <= var_name.count(".") <= 2 and not cls.exist(var_name): | ||
notify_vars[var_name] = None |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This line is going to need some modifications. I still haven't fully though this through. The motivation is to avoid exceptions due to undefined variables (eg: undefined state variable or attribute) by setting all variables that are not found in cls.notify_var_last
or new_vars
to None
, so they don't generate an exception when the expression is evaluated. The logic is bit trickier now: if, say, domain.entity.attr
is in var_names
, and domain.entity
has been set to its StateVar
, then there's no need to set domain.entity.attr
to None
since attr
is now defined in StateVar
.
Not sure if that makes sense. I'm happy to deal with this after merge.
I made my "magic state object" commit f7169ac. Turned out to be very simple and elegant. Now you can do things like:
I added some comments to the PR about how to use these new objects. Hopefully they make sense. The idea is that |
Oooh. This new commit of yours will make this a lot easier. Excellent! I'll rework this whole thing. And I can probably put state.notify_var_get back the way it was. |
replaced by #82 |
This is an alternate method of detecting changes to PR #76.
It provides
changes
,old_state
, andnew_state
tofunc_args
.It uses
state_trig_ident
to determine what the trigger cares about and only fires the function if one of these has changed.It only fires once per state_changed event, since
State.update()
is only called once per state_changed event.It does NOT fire if domain.entity.attribute has changed when only domain.entity is included in state_trigger.
This PR could be extended to provide
only_on_change=False
kwarg functionality like #76 did.This PR could be extended to provide
any_attribute_change=True
kwarg to allow any attribute change to fire the function.This PR could also be extended to provide domain.entity.old.attribute pseudo-variables for checking in state_trigger.