TimeZones and locale awareness

Morning,

I’m having fun and games with timezones between HA and AppDaemon. I’ve got a work-around going for my scenario, but I’m not convinced I should have to be putting in all the effort I’m currently having to.

Both HA and AppDaemon are configured to timezones of Europe/London, which should now be in DST.

The scenario is that we have some smart blinds that my son decided ought to rise and fall according to the current elevation of the sun. We get the solar noon from HAS (sun.sun / next_noon) but that seems to be returning a UTC datetime (so currently it says 2020-04-15T12:03:37+00:00 and will says that until 13:04 local time). Nothing I can seem to use will parse that datetime as anything other than 12:03.

The problem is that if the time is between the UTC solar_noon and the actual DST solar_noon then the blinds try to adjust to more than 100% of open :smile:, so my workaround is to parse the date and then check if the current date falls between DST and so adjust the solar noon time by the daylight savings hour.

But, as I say, this seems overly fiddly. Surely there’s a better solution? Can someone tell me what I’m missing, please?

Below is the code that does the work, including my workaround, which may clarify anything that my explanation didn’t :wink:

  if weather_state == "sunny":
      if datetime.datetime.now().minute == 0 or current_position == 100:
          noon = self.get_state("sun.sun", attribute="next_noon")
          noon_date = datetime.datetime.fromisoformat(noon)
          # as noon_date is given back as a UTC value, but the sun's elevation is being given as
          # correct for the locale-specific time, we may need to correct the noon_date for DST
          # if appropriate, so as to give the correct time for the maximum elevation of the sun
          last_sunday_march = max(week[-1] for week in calendar.monthcalendar(noon_date.year, 3))
          last_sunday_october = max(week[-1] for week in calendar.monthcalendar(noon_date.year, 10))
          if (noon_date.dst() == None or noon_date.dst() == 0) and \
             (datetime.datetime(noon_date.year, 3, last_sunday_march, 1, tzinfo=noon_date.tzinfo) < noon_date < datetime.datetime(noon_date.year, 10, last_sunday_october, 2, tzinfo=noon_date.tzinfo)):
              noon_date += datetime.timedelta(hours=1)
          positions = suncalc.getPosition(noon_date, 52.480003, -0.897213)
          highest = positions['altitude'] * 180 / math.pi
          new_blind_position = self.get_state("sun.sun", attribute="elevation") / highest * 100

          self.log("sunny, updating blinds to: " + str(new_blind_position))
          self.call_service("cover/set_cover_position", entity_id = blind_entity, position = new_blind_position)
                
      else:
          self.log("sunny, but not on the hour")

  else:
      if current_position < 100:
          self.log("not sunny and blinds not fully open: opening")
          self.call_service("cover/open_cover", entity_id = blind_entity)
      else:
          self.log("not sunny and blinds already fully open")

why not use the azimuth attribute of sun.sun? I use that to lift some blinds when the sun is “behind the house”.

Thanks, I’ll look into that :+1: