main.py 3.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111
  1. # This script will find all devices in your installation and
  2. # output their per-minute usage for the previous DAYS_BACK days.
  3. # Per-minute data is stored for a maximum of 7 days.
  4. #
  5. # To change the timing, update the DAYS_BACK variable below.
  6. #
  7. # Reads credentials from `keys.json`
  8. #
  9. # To ensure data is not read for a minute that has not yet ended,
  10. # this will read up to 2 minutes ago.
  11. import pyemvue
  12. import json
  13. import time
  14. import sys
  15. from pyemvue.enums import Scale, Unit
  16. from pyemvue import PyEmVue
  17. from pprint import pprint
  18. from datetime import datetime, timezone, timedelta
  19. NOW = datetime.now(timezone.utc) - timedelta(minutes=2)
  20. DAYS_BACK = 14 # Number of days in the past to get per-minute data for.
  21. MAX_HOURS = 12 # The maxinum number of hours that can be retrieved at once.
  22. MAX_ATTEMPTS = 5
  23. def eprint(*args, **kwargs):
  24. print(*args, file=sys.stderr, **kwargs)
  25. # Get & display minutely data for all devices.
  26. def allUsageOverTime(vue: PyEmVue):
  27. devices = vue.get_devices()
  28. print(f'time\tdevice_gid\tchannel_num\twH\twatts\tmultiplier\tname')
  29. for device in devices:
  30. # print('=== Device ===')
  31. # pprint(vars(device))
  32. for channel in device.channels:
  33. # pprint(vars(channel))
  34. if not channel.name is None:
  35. channelUsageOverTime(vue, channel)
  36. def channelUsageOverTime(vue: PyEmVue, c):
  37. start_time = NOW - timedelta(days=DAYS_BACK)
  38. end_time = start_time + timedelta(hours=MAX_HOURS)
  39. while start_time < NOW:
  40. usage_over_time, start = getChartUsage(
  41. vue, c,
  42. start_time, end_time
  43. )
  44. time = start
  45. for kwh in usage_over_time:
  46. if kwh is None:
  47. continue
  48. wattHours = kwh * 1000
  49. watts = wattHours * 60
  50. print(f'{time}\t{c.device_gid}\t{c.channel_num}\t{wattHours}\t{watts}\t{c.channel_multiplier}\t{c.name}')
  51. time += timedelta(minutes=1)
  52. # Jump to the next start time.
  53. start_time = end_time
  54. end_time += timedelta(minutes=(MAX_HOURS*60))
  55. def getChartUsage(vue: PyEmVue, channel, start_time, end_time):
  56. """
  57. Get per-minute usage for the channel, with up to MAX_ATTEMPTS
  58. attempts to account for errors.
  59. """
  60. attempt = 0
  61. while True:
  62. attempt += 1
  63. try:
  64. return vue.get_chart_usage(
  65. channel,
  66. start_time, end_time,
  67. scale=Scale.MINUTE.value,
  68. unit=Unit.KWH.value
  69. )
  70. except Exception as e:
  71. if attempt < MAX_ATTEMPTS:
  72. eprint(
  73. f'Attempt {attempt} for channel {channel.name}',
  74. f'from {start_time} to {end_time}',
  75. f'failed:', e)
  76. time.sleep(5)
  77. else:
  78. eprint(f'Failed after {attempt} attempts')
  79. raise
  80. if __name__ == '__main__':
  81. with open('keys.json') as f:
  82. keys = json.load(f)
  83. vue = pyemvue.PyEmVue()
  84. if 'id_token' in keys:
  85. # Login using access tokens.
  86. vue.login(
  87. id_token=keys['id_token'],
  88. access_token=keys['access_token'],
  89. refresh_token=keys['refresh_token'],
  90. token_storage_file='keys.json'
  91. )
  92. else:
  93. # Login with username & password, and update keys.json.
  94. vue.login(
  95. username=keys['username'],
  96. password=keys['password'],
  97. token_storage_file='keys.json'
  98. )
  99. allUsageOverTime(vue)