Substitute hardcoded timedifference with dynamic in python script?

HI,

using this now:

timeDifference = 2 #adapt according to the continental time differences for Summer/Winter time

and

if state:
    dt = state.last_changed + datetime.timedelta(hours= timeDifference)
    time = '%02d:%02d' % (dt.hour, dt.minute)

worked fine for almost a year.
I knew that for a specific period in the year, my local timezone and the python time differ only 1 hour. thought I’d adjust that if and when that happens. Never would have thought it to cause my system to go bonkers, which apparently it did today…

Had some serious mindtwists, until I saw my presence sensors being off for 1 hour and then it became clear it was that time of the year…

so, manually edited the timeDifference to be 1, and the system calmed down again.

Since I don’t want that to happen again, I was wondering if I could somehow automate the process.
for that I would need to:

  • test for the local timedifference and python time
  • create a sensor of some kind (preferably since I use the timeDifference in several python scripts), or, if must be, a way to calculate that difference in the python script itself.

Could be the timeDifference being Off for 2 hours is the exception, and 1 hour is normal, but that doesn’t make any difference for the logic does it. We’re +1 CET throughout the year, except for the period CET doesn’t follow daylight saving time. Now how to check that…

@petro and @pnbruckner maybe you would know how I can do that?
thanks!

{{ now().utcoffset().total_seconds()/3600 }}

o yes, that would be it!
making that {{ (now().utcoffset().total_seconds()/3600)|int }} to get it to 1 (and not 1.0). Although according to the docs, a float is allowed too?

How do I use this in a python script directly, to replace the 1 in the above timeDifference = 1 ?

Or would you advise me to create the template sensor, and use the value of that in the python, like:

utc_offset = hass.states.get('sensor.utc_offset').state
timeDifference = float(utc_offset)

the latter does work alright, not sure if this is the best way to do so.
An external sensor has the advantage of availability for all other components and scripts, only have to create it once. Disadvantage is the dependency of course.

I guess I’m not sure what you’re ultimately trying to do. E.g., what you showed in your OP, where is that used? Is that a “frontend script”? A Python script of some sort?

If you’re just trying to show last_changed in the local timezone, there should be an easier way to do that then trying to maintain a timeDifference variable. But again, I’d need to better understand the context before recommending something.

yes, in several python_scripts. I use this:

    for entity_id in hass.states.get(lightEntities).attributes['entity_id']:
        dt = state.last_updated + datetime.timedelta(hours=timeDifference)
        time = '%02d:%02d' % (dt.hour, dt.minute)

before, I had:

dt = state.last_updated + datetime.timedelta(hours=1) or
dt = state.last_updated + datetime.timedelta(hours=2)
depending on the actual difference between local and CET time.

I substituted that with the timeDifference, (which occurs on more than 1 place in several scripts) so had to only adjust the

timeDifference = 1 (or 2…) manually every once in a while, and only in that one place in the script.

because I ran into trouble yesterday, I thought it to be best to do so automatically, based on a sensor (you now provided me with, thanks again!)

It is working as shown above, but maybe it can be done better. And even if this would be a good solution, I would love to know how to do it natively in the Python script itself, without depending on the external sensor and use hass.states.get().

That for loop is a bit unexpected. It seems like you’re iterating through a list of entity_id’s, yet in the body of the loop you’re not referencing entity_id, but rather some variable named state.

In any case, assuming you do have a state.last_updated, which is, of course, in UTC, you could do:

state.last_updated.astimezone().strftime('%H:%M')

yes I will try that. this might be a simpler example:

mode_desc = ''
## Get Mode description
state = hass.states.get('input_select.mode')

if state:
    dt = state.last_changed + datetime.timedelta(hours= timeDifference)
    time = '%02d:%02d' % (dt.hour, dt.minute)

    mode_desc = '{}*- {} mode selected at: {}\n'.format(summary, state.state, time)
    mode_desc_love = '{} {} mode selected at: {}\n'.format(summary, state.state, time)
    hass.states.set('sensor.mode_badge',state.state, {
         'entity_picture': '/local/modes/{''}.png'.format(state.state.replace(' ','').lower()),
         'friendly_name': time,
         'unit_of_measurement': 'Mode',
         'love': mode_desc_love
          })

I tried here: state.last_changed.astimezone().strftime('%H:%M') , bt that brings back old memories:

Error executing script: '__import__'
Traceback (most recent call last):
  File "/usr/local/lib/python3.6/site-packages/homeassistant/components/python_script.py", line 166, in execute
    exec(compiled.code, restricted_globals, local)
  File "summary.py", line 264, in <module>
KeyError: '__import__'

this is the python script environment of HA, in which a lot has been tied down. I have had long tests with @petro on this, after which we decided (were forced to decide) on the
dt = state.last_changed + datetime.timedelta(hours= 1) format…

fyi, this is the full light part of my what’s_on script:

lights_on = []

#show only lights, not light groups
#excluded = ['light.custom_group_for_group','light.custom_group_for_lights_2']

#total_lights_on = hass.states.get('input_boolean.whats_on').attributes.get('lights_on')
state = hass.states.get('automation.sense_lights_change') #hass.states.get('input_boolean.whats_on')

if state:
#    total_on = hass.states.get('input_boolean.whats_on').attributes.get('lights_on')
    for entity_id in hass.states.get(lightEntities).attributes['entity_id']:
        dt = state.last_updated + datetime.timedelta(hours=timeDifference)
        time = '%02d:%02d' % (dt.hour, dt.minute)

        if hass.states.get(entity_id).state is 'on': #and entity_id not in excluded
            lights_on.append(hass.states.get(entity_id).attributes['friendly_name'])

    if len(lights_on) > 0:
        picture = '/local/lights/hue_pl.png'
        message = ', '.join(lights_on)
        sensor_message = message
        lights_desc = '=- Lights on: {} -- {}'.format(lights_on, message)
        uom = 'Lights'

        if len(lights_on) == 1:
            uom = 'Light'
    else:
        picture = '/local/lights/bulboff.png'
        message= ''
        sensor_message= 'No lights on since {}'.format(time)
        uom = 'Lights'

    sensor_lights_desc = '{}'.format(sensor_message)

    hass.states.set('sensor.lights_badge', len(lights_on), {
        'Lights on:': sensor_message,
        'unit_of_measurement': time,
        'friendly_name': uom,
        'entity_picture': picture
         })

the state is taken off of the automation being run, and provides the time of the light change. I havent looked at that in a long while, but at the time it was the only way I could get the actual time to showup in the frontend.

HI @pnbruckner

please let me get back to this once more, with a python related question.

I can use this now successfully in my Python scripts, and declare that globally like:

    utc_offset = hass.states.get('sensor.utc_offset').state
    timeDifference = float(utc_offset)

However, in the below script (snippet) I have to declare this in the function NameMyEntities declaration, because when I do it globally, I get a name is not defined error:

Error executing script: name 'timeDifference' is not defined
Traceback (most recent call last):
  File "/usr/local/lib/python3.6/site-packages/homeassistant/components/python_script.py", line 166, in execute
    exec(compiled.code, restricted_globals, local)
  File "family_home.py", line 54, in <module>
  File "family_home.py", line 46, in NameMyEntities
NameError: name 'timeDifference' is not defined

Cant the function use a globally declared name?

##########################################################################################
# family_home.py, based on Whats_on.py, previously known as Anything_on...
# by https://community.home-assistant.io/u/Mariusthvdb/summary
##########################################################################################

familyEntities = 'group.family'
daughterEntities = 'group.daughters'
familyColor = ['rgb(128, 128, 128)',  # count_home = 0
               'rgb(128, 205, 128)',  # = 1
               'rgb(0, 0, 255)',      # = 2
               'rgb(251, 210, 41)',   # = 3
               'rgb(255, 135, 0)',    # = 4
               'rgb(255, 15, 0)',     # = 5
               'rgb(0, 137, 0)']      # = 6

familyIcon = ['mdi:account-off',      # count_home = 0
              'mdi:account',          # = 1
              'mdi:account-multiple', # = 2
              'mdi:account-multiple-check', # = 3
              'mdi:account-group']    # > 3


def CountMyEntities(hass, entity_list, entity_state):
    cnt = 0
    for entity_id in hass.states.get(entity_list).attributes['entity_id']:
        state = hass.states.get(entity_id)
        if state.state == entity_state:
            cnt = cnt + 1

    return cnt

count_home = CountMyEntities(hass, familyEntities, 'home')
count_away = CountMyEntities(hass, familyEntities, 'not_home')
daughter_count_home =  CountMyEntities(hass, daughterEntities, 'home')
daughter_count_away =  CountMyEntities(hass, daughterEntities, 'not_home')


def NameMyEntities(hass, entity_list, entity_state):
    names = []
    utc_offset = hass.states.get('sensor.utc_offset').state
    timeDifference = float(utc_offset)
    for entity_id in hass.states.get(entity_list).attributes['entity_id']:
        state = hass.states.get(entity_id)
        dt = state.last_changed + datetime.timedelta(hours= timeDifference)
        time = '%02d:%02d' % (dt.hour, dt.minute)
        if state.state == entity_state:
            name = '{} ({})'.format(state.attributes['friendly_name'], time)
            names.append(name)

    return names

I haven’t quite figured out why sometimes you need to do this, but sometimes you need to declare the variable global inside the function. So like this:

##########################################################################################
# family_home.py, based on Whats_on.py, previously known as Anything_on...
# by https://community.home-assistant.io/u/Mariusthvdb/summary
##########################################################################################

familyEntities = 'group.family'
daughterEntities = 'group.daughters'
familyColor = ['rgb(128, 128, 128)',  # count_home = 0
               'rgb(128, 205, 128)',  # = 1
               'rgb(0, 0, 255)',      # = 2
               'rgb(251, 210, 41)',   # = 3
               'rgb(255, 135, 0)',    # = 4
               'rgb(255, 15, 0)',     # = 5
               'rgb(0, 137, 0)']      # = 6

familyIcon = ['mdi:account-off',      # count_home = 0
              'mdi:account',          # = 1
              'mdi:account-multiple', # = 2
              'mdi:account-multiple-check', # = 3
              'mdi:account-group']    # > 3


def CountMyEntities(hass, entity_list, entity_state):
    cnt = 0
    for entity_id in hass.states.get(entity_list).attributes['entity_id']:
        state = hass.states.get(entity_id)
        if state.state == entity_state:
            cnt = cnt + 1

    return cnt

count_home = CountMyEntities(hass, familyEntities, 'home')
count_away = CountMyEntities(hass, familyEntities, 'not_home')
daughter_count_home =  CountMyEntities(hass, daughterEntities, 'home')
daughter_count_away =  CountMyEntities(hass, daughterEntities, 'not_home')

utc_offset = hass.states.get('sensor.utc_offset').state
timeDifference = float(utc_offset)


def NameMyEntities(hass, entity_list, entity_state):
    global timeDifference
    names = []
    for entity_id in hass.states.get(entity_list).attributes['entity_id']:
        state = hass.states.get(entity_id)
        dt = state.last_changed + datetime.timedelta(hours= timeDifference)
        time = '%02d:%02d' % (dt.hour, dt.minute)
        if state.state == entity_state:
            name = '{} ({})'.format(state.attributes['friendly_name'], time)
            names.append(name)

    return names

great, thank you. Ill investigate further, to see what could be the reason for this. Can confirm it works now, which is nice, and I don’t have to repeat that in other places in the script.