magico13 / PyEmVue

Python Library for the Emporia Vue Energy Monitor
MIT License
185 stars 36 forks source link

Result timezone == UTC? #72

Closed waldner closed 3 months ago

waldner commented 3 months ago

Talking about solar production here.

Regarding requests for Scale.MINUTE.value and Scale.HOUR.value:

My understanding is that the start_time returned by vue.get_chart_usage() is in UTC, and so working out the (UTC) timestamp for the values in the returned time series is just a matter of adding the corresponding amount of minutes or hours to the start_time, is this correct?

Regarding requests for Scale.DAY.value and greater (let's assume days for the example):

Are those numbers relative to a single day in UTC time or in the timezone of the device? In other words, if I request daily values starting from eg 2024-02-01 10:00:00, does each returned value represent:

or, assuming that in this case the hms part of the date is ignored and replaced with 00:00:00

- data from 00:00:00 of one day to 23:59:59 of the same day, in UTC time - data from 00:00:00 of one day to 23:59:59 of the same day, in the device local time

?

I can't find documentation about this.

EDIT: partially answering myself, I see that the returned start_date for days has the hour set to the same hour I requested, but 00:00 for minute and second, so I guess it must be one of the first two cases above.

magico13 commented 3 months ago

The returned start_time should have the correct date and time in UTC, so you just apply the correct offset to get back to local time. You can get the device's timezone from the get_devices call and use some fairly standard python libraries to convert back to local.

With regards to the DAY scale, the API will generally give you midnight to midnight in the device's local time. For me in US Eastern Time, which is currently UTC-4, it would give me 04:00:00 to 04:00:00 UTC if I ask for today, or 05:00:00 for last week because our offset just changed yesterday morning. Note that it completely ignores the time component that you pass in, it will always start at midnight.

Here's a quick test script and its output for me for the DAY scale. Note that I'm using the Pytz module and that's not a built-in. I think there are newer ways you can do this with built-ins but this was quicker/easier and gets the point across just as well.

from pyemvue.pyemvue import PyEmVue, Scale, Unit
import datetime
import pytz

vue = PyEmVue()
vue.login(token_storage_file='keys.json')
print('Logged in.')
print()

devices = vue.get_devices()

now = datetime.datetime.utcnow().replace(tzinfo=pytz.utc) # The replace is not strictly necessary but converts it from a timezone unaware datetime to an aware one. The returned start_time will aready be an aware time.
last_week = now - datetime.timedelta(days=7)
device_tz = pytz.timezone(devices[0].time_zone)

usage_list, start_time = vue.get_chart_usage(devices[0].channels[0], last_week, now, Scale.DAY.value, Unit.KWH.value)
print(f"Start_time as returned is {start_time}")
print(f"Start_time in device's time is {start_time.astimezone(device_tz)}")
print(f"Now in UTC is {now}")
print(f"Now in device's time is {now.astimezone(device_tz)}")
print(f'Usage for channel {devices[0].channels[0].channel_num} from {start_time} to {now}:')
for day in range(len(usage_list)):
    print(f'Days since start {day}: {usage_list[day]} kWh')

Outputs

Logged in.

Start_time as returned is 2024-03-04 05:00:00+00:00
Start_time in device's time is 2024-03-04 00:00:00-05:00
Now in UTC is 2024-03-11 19:27:26.511203+00:00
Now in device's time is 2024-03-11 15:27:26.511203-04:00
Usage for channel 1,2,3 from 2024-03-04 05:00:00+00:00 to 2024-03-11 19:27:26.511203+00:00:
Days since start 0: 12.32390235936059 kWh
Days since start 1: 4.278248247468737 kWh
Days since start 2: 28.242229826486373 kWh
Days since start 3: 25.675924009585913 kWh
Days since start 4: 24.668426762945387 kWh
Days since start 5: 32.93037320100149 kWh
Days since start 6: 57.99112540553622 kWh
Days since start 7: 42.29736035642412 kWh
waldner commented 3 months ago

The returned start_time should have the correct date and time in UTC, so you just apply the correct offset to get back to local time. You can get the device's timezone from the get_devices call and use some fairly standard python libraries to convert back to local.

Ah well, but in the end I DO want all timestamps for minutes and hours to be UTC, regardless of the device time zone, so this is perfect. Just add the offset to start_time.

With regards to the DAY scale, the API will generally give you midnight to midnight in the device's local time. For me in US Eastern Time, which is currently UTC-4, it would give me 04:00:00 to 04:00:00 UTC if I ask for today, or 05:00:00 for last week because our offset just changed yesterday morning. Note that it completely ignores the time component that you pass in, it will always start at midnight.

Correct, after doing some more tests (inspired by your code) that seems indeed to be the case. For a device in a UTC-5 timezone, it always starts from 05:00:00. For another in the UTC-4, it starts from 04:00:00 (modulo DST), etc. Which again is fine I guess, as when storing a daily value you don't think about UTC and whatnot, you just want a number "for the day", and if that day happens to be in the device's timezone, then I think it's the most representative value.

Thanks!

Closing this as I consider my questions answered.