Solar Array and home energy dashboard.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 

167 lines
4.1 KiB

  1. import arrow
  2. import random
  3. import pprint
  4. import json
  5. from datetime import datetime, timedelta
  6. rand = random.Random('a seed')
  7. tz = 'US/Pacific'
  8. startdate = arrow.Arrow(2019, 11, 1, tzinfo=tz)
  9. enddate = arrow.Arrow(2019, 12, 10, tzinfo=tz)
  10. meanwhprod = 20000
  11. sigwhprod = 2000
  12. def drange(s, e, interval):
  13. cmpfun = lambda s, e, ts, te: te < e
  14. if e < s:
  15. interval = -interval
  16. cmpfun = lambda s, e, ts, te: te > e
  17. ts = s.clone()
  18. te = ts + interval
  19. #print('dr:', repr((s, e, ts, te, cmpfun(s, e, ts, te), interval)))
  20. while cmpfun(s, e, ts, te):
  21. yield ts + (te - ts)
  22. ts, te = te, te + interval
  23. # idea:
  24. # first hour linear ramp up (25% in first half, 75% in second half)
  25. # middle 5 hours near constant generation
  26. # first/tailing linear is equiv of an hour total, so total power / 6
  27. # approx 7 hours time
  28. def makestartend(t):
  29. s = t.replace(hour=9).shift(minutes=rand.gauss(30, 10))
  30. e = t.replace(hour=16).shift(minutes=rand.gauss(30, 10))
  31. return (s, e)
  32. def normdist(small, big, amount, minsize):
  33. '''Distribute most twoards the big side, total distribute amount
  34. over the entire range, [small, big].
  35. '''
  36. ret = []
  37. timediff = abs(big - small)
  38. if timediff < minsize:
  39. scaledamount = amount * (timedelta(hours=1) / timediff)
  40. midpnt = small + (big - small) / 2
  41. #print('ndf:', repr((small, big, midpnt, amount, scaledamount)))
  42. return [ (midpnt, scaledamount) ]
  43. #print('nd:', repr((small, big, amount)))
  44. dist = big - small
  45. halfpoint = small + (dist / 9 * 5)
  46. ret.extend(normdist(small, halfpoint, amount / 2, minsize))
  47. ret.extend(normdist(halfpoint, big, amount / 2, minsize))
  48. #print('ndr:')
  49. #pprint.pprint(ret)
  50. return ret
  51. def linramp(start, end, wtarget, minsize):
  52. mid = start + (end - start) / 2
  53. timediff = abs(end - start)
  54. ret = []
  55. ndates = abs((end - start) / minsize)
  56. #print('lr', ndates)
  57. for x, i in enumerate(drange(start, end, minsize)):
  58. #print(repr((x, i)))
  59. yield (i, x / ndates * wtarget)
  60. def makeconsumption(s, mean, sig, interval):
  61. s = s.replace(hour=0, minute=0, second=0)
  62. e = (s + timedelta(days=1)) - interval
  63. ret = [ (i, rand.gauss(mean, sig)) for i in drange(s, e, interval) ]
  64. return ret
  65. def distribute(s, e, prod, minsize):
  66. onehour = timedelta(hours=1)
  67. totaltime = e - s
  68. mid = s + totaltime / 2
  69. startrampend = s + onehour
  70. endrampstart = e - onehour
  71. # prod == wh
  72. wtarget = prod / ((totaltime + onehour).seconds / 60 / 60)
  73. ret = []
  74. #print('d:', repr((s, e)))
  75. ret.extend(linramp(s, startrampend, wtarget, minsize))
  76. for i in drange(startrampend, endrampstart, minsize):
  77. ret.append((i, wtarget))
  78. ret.extend(linramp(e, endrampstart, wtarget, minsize))
  79. ret.sort()
  80. #pprint.pprint(ret)
  81. return ret
  82. #print('start')
  83. prodpoints = []
  84. prodindex = []
  85. conspoints = []
  86. consindex = []
  87. def serializearrowasmili(obj):
  88. if not isinstance(obj, arrow.Arrow):
  89. raise TypeError
  90. return int(obj.float_timestamp*1000)
  91. for i in arrow.Arrow.range('day', startdate, enddate):
  92. whprod = rand.gauss(meanwhprod, sigwhprod)
  93. s, e = makestartend(i)
  94. noon = i.replace(hour=12)
  95. prodindex.append((noon, whprod))
  96. #print(repr(i), whprod)
  97. dist = []
  98. tmppoints = makeconsumption(s, rand.uniform(400,800), 100, timedelta(minutes=1))
  99. consindex.append((noon, sum((i[1] / 60 for i in tmppoints), 0)))
  100. conspoints.extend(tmppoints)
  101. # zero points for non-production
  102. import sys
  103. dist.extend(distribute(i, s, 0, timedelta(minutes=5)))
  104. # production
  105. dist.extend(distribute(s, e, whprod, timedelta(seconds=20)))
  106. # zero points for non-production
  107. eod = i.replace(hour=23, minute=55)
  108. dist.extend(distribute(e, eod, 0, timedelta(minutes=5)))
  109. # print timestamps as miliseconds
  110. if False:
  111. dist = ((int(a.float_timestamp*1000), b) for a, b in dist)
  112. # print space delimited time, else json
  113. if False:
  114. print('\n'.join('%s %s' % (a, b) for a, b in dist))
  115. else:
  116. #print(json.dumps(tuple(dist), indent=2))
  117. prodpoints.extend(dist)
  118. for i in { 'prodpoints', 'prodindex', 'conspoints', 'consindex',}:
  119. v = locals()[i]
  120. v = [ (x, int(y)) for x, y in v ]
  121. locals()[i] = v
  122. #conspoints = [ (x, -y) for x, y in conspoints ]
  123. print('fakedata =', json.dumps(dict(production=prodpoints, prodindex=prodindex, consumption=conspoints, consindex=consindex), default=serializearrowasmili, indent=1))