cnt = cnt + 1
I feel like I need to try to implment a python_script in HA to find all the pitfalls. Iâm not sure why the exec() doesnât like cnt += 1. I can run it here w/o issueâŚ
exec(string) and
foo returns 1
bingo!
funny this works now, i tried it before, without luck. Apparently this change and your alterations and reorganization combined do the trick:
THANK you!
(now for the big question: could these changes be applied in a way my summary.py lights, switches and appliances load at startup⌠and have the automation have a last_triggered.
btw, i tried leaving out the initial: on so the automations last_changed would be ârestoredâ at startup, but that didnât make a difference.
Yes, it can be done. I suggest you take a few beginner python lessons for creating methods/functions. The counter was the easy one, and you can use it as an example. YOu need to figure out what variables you would pass to the method. Then you need to write the method to behave the same way. Also, avoid naming variables âswitches_descâ, name it âdescriptionâ. Stuff like that.
âŚ
point taken.
first worry now is to find a way to test for Null at startup. This is the main cause for unwanted results of the script that works just fine when initiated.
as a test I rebooted without an appliance being on, which gave me this error I hadnât experienced before:
Error executing script: must be str, not datetime.timedelta
Traceback (most recent call last):
File "/usr/lib/python3.6/site-packages/homeassistant/components/python_script.py", line 166, in execute
exec(compiled.code, restricted_globals, local)
File "summary.py", line 354, in <module>
TypeError: must be str, not datetime.timedelta
350-354 being:
if hass.states.get('automation.sense_appliances_change').attributes.get('last_triggered'):
for entity_id in hass.states.get(appliances_group).attributes['entity_id']:
dt = hass.states.get('automation.sense_appliances_change').attributes.get('last_triggered')
dt = dt + datetime.timedelta(hours= 2)
automation isnt yet triggered results in these errors, if dt = null the addition fails. Following your instructions I believed to test in existence with line 350 'if has.states.get etc etc'
Must not be enoughâŚ
cheers and thanks again,
Marius
isinstance(dt, str)
Try to change this one on your own. look up isinstance python on google and get used to the python documentation and stackoverflow. You can write a check to verify that dt is a str or a datetime object.
ill dive into that.
The being said, I still think the issue at hand lies mainly elsewhere: it is not so much the python script at fault (though the Null check should be implemented), as it is the fact the same automations are behaving differently. Why is the lights automation triggered, even when 0 lights are on, while switches and appliances arenât.
Could it be these components behave differently and need special care somehow.
Maybe the easiest way would be to switch on a light, switch and appliance at startup, and have the scripts run with a small delayâŚ
Still that would be a second best option,(adding the fact it would change the real time of last_triggered to time of startupâŚ) if it could be handled in a more fundamental way needed because of the change in components or entitiesâŚ
Cheers,
Marius
Right, but you can build in a while statement that checks the dt until it returns anything other than a string. Then it doesnât matter if they behave differently.
Chances are that they donât behave differently and you are just seeing a result of a multi thread software package.
really, you think that could be at hand? in that case the Null check would be the only possible cureâŚ
just asking: would the script run at all, if a check would be in place and the dt still is a str.?
You donât need to check for null, the error you are getting is referencing a string, not null. Python does not have null, it has None. You are not getting None:
This line is causing problems:
dt = dt + datetime.timedelta(hours= 2)
This is your error:
TypeError: must be str, not datetime.timedelta
And its basically saying you are trying to add a string to a datetime, which is not possible. The first argument is dt, so this must be a string, so itâs expecting the second datetime.timedelta to be a string. Itâs not, its a datetime.timedelta object.
So, that means that this line is returning a string when it cannot find âlast_triggeredâ:
dt = hass.states.get('automation.sense_appliances_change').attributes.get('last_triggered')
This is typical for dictionaries. When it canât find something, it returns an emtpy string.
So You need to check for a STR, not Null or None.
isisntance is a function that checks for the type of any object. It is what you want to use.
yes, sorry about the Null, meant None Which is what i had a lot if in the first iterations, have been taken out by replacing it with if state:
now this isinstance should in fact check the dt being a datetime object shouldnât it?
this is what is mentioned about the same issue ive been running into all this time. Hope checking with isinstance wont fail because of the same issueâŚ
isinstance wonât work well with datetime.timedelta, it will work with datetime. So yes, you could use that.
isinstance(dt, datetime)
well, i feared as much:
Error executing script: isinstance() arg 2 must be a type or tuple of types
Traceback (most recent call last):
File "/usr/lib/python3.6/site-packages/homeassistant/components/python_script.py", line 166, in execute
exec(compiled.code, restricted_globals, local)
File "summary.py", line 354, in <module>
TypeError: isinstance() arg 2 must be a type or tuple of types
with:
if hass.states.get('automation.sense_appliances_change').attributes.get('last_triggered'):
for entity_id in hass.states.get(appliances_group).attributes['entity_id']:
dt = hass.states.get('automation.sense_appliances_change').attributes.get('last_triggered')
if isinstance(dt, datetime):
dt = dt + datetime.timedelta(hours= 2)
time = '%02d:%02d' % (dt.hour, dt.minute)
whoops, was a bit and tried it anyway:
dt = hass.states.get('automation.sense_appliances_change').attributes.get('last_triggered')
if isinstance(dt, datetime.timedelta):
dt = dt + datetime.timedelta(hours= 2)
time = '%02d:%02d' % (dt.hour, dt.minute)
makes the script run againâŚalbeit with a wrong timeâŚpff. Its taking the time of a setting above the section in the script, apparently the check fails, time isnt set accordingly, and it reads a time set above this (switches).
I wonder if i add this check in all 3 places, if a time gets set at all, because the check wont return true anywhereâŚ
It doesnt. The script is finding the first time set above this, and sets the time for all sections below it accordinglyâŚ
Taking out the if isinstance(dt, datetime.timedelta):
condition restores the correct timings again in all placesâŚ
So i must find some other way to have the script restore correctly at startup with no entities on. Time selection itself is correct.
Then check if its a string. Itâs safer because str is builtin anyways. attributes.get should only return a float, str, int. So you could look for that.
if not isinstance(dt, (str, unicode, float, bool, int)):
but thatâs interesting, never realized that. so im getting strings all over my script and trying to add datetime âŚstill, the time is showing correctly, or at least it seems that way, it could of course be an int disguised as a time
Shouldnât a conversion be made then somehow? and then add the datetime.timedelta? Then again, how come it works just fine after initializationâŚ
it works after init because the object exists, before init the object doesnât exist so the dictionary returns whatever the default is.
As i said before, you need to learn python. This is all basic level python knowledge that you would learn with a tutorial on dictionaries.
Also, this is not necessarily true. It depends on how the dictionary was created. Most people assign simple types inside a dictionaries value
Hi @petro, this was your quote, not mine
trying to symplify here, and adhering to the fine working code in the other sections of my script, ive tried to change into this:
state = hass.states.get('automation.sense_lights_change')
#if hass.states.get('automation.sense_lights_change').attributes.get('last_triggered'):
if state:
for entity_id in hass.states.get(lights_group).attributes['entity_id']:
dt = state.attributes.get('last_triggered')
# dt = hass.states.get('automation.sense_lights_change').attributes.get('last_triggered')
# if isinstance(dt, datetime.timedelta):
dt = dt + datetime.timedelta(hours=2)
time = '%02d:%02d' % (dt.hour, dt.minute)
this is probably the exact same, but clearer to the eye, and using the same method as the other parts. Hope this makes it at least more maintainable before i learn better coding⌠(where to find the time)
Yes, itâs the same and it will have the same problem if the last_triggered attribute is empty or doesnât exist. With this direction, that scenario would be less likely to occur.