main.ts 2.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115
  1. import {firebaseConfig} from './config';
  2. import {initializeApp} from 'firebase/app';
  3. import {getDatabase, onValue, orderByChild, query, ref, startAt} from 'firebase/database';
  4. import {Reading} from './types';
  5. import {createGauges, createGraphs} from './graphs';
  6. import {processData} from './processData';
  7. import {HOUR_OPTIONS, HOURS_DEFAULT, HOURS_QUERY, MAX_HOURS_AGO} from './consts';
  8. import {getWindowTime} from './utils';
  9. const app = initializeApp(firebaseConfig);
  10. const rtdb = getDatabase(app);
  11. document.body.onload = init;
  12. async function init() {
  13. const hoursAgo = getHoursAgo();
  14. const readings:Reading[] = await getRecentData(hoursAgo);
  15. showLatestReading(readings);
  16. // Force a 15-minute window for the gauges, to ensure it shows the most
  17. // up-to-date value it can (that's had spikes filtered out)
  18. const gaugeValues = processData(
  19. readings.slice(-10),
  20. { alignWindow: 10 }
  21. );
  22. createGauges(
  23. gaugeValues,
  24. document.getElementById('gauges')
  25. );
  26. // Have graphs use a potentially longer window time, so they don't show as
  27. // many elements when a longer time is selected.
  28. const graphValues = processData(readings, {
  29. alignWindow: getWindowTime(hoursAgo)
  30. });
  31. createGraphs(
  32. graphValues,
  33. document.getElementById('time-series-graphs')
  34. );
  35. showHourSelection();
  36. }
  37. function getHoursAgo():number {
  38. const query = new URLSearchParams(document.location.search)
  39. .get(HOURS_QUERY);
  40. if(!query) {
  41. return HOURS_DEFAULT;
  42. }
  43. let hours = parseInt(query);
  44. if(isNaN(hours) || hours > MAX_HOURS_AGO) {
  45. return HOURS_DEFAULT;
  46. }
  47. return hours;
  48. }
  49. function showLatestReading(readings:Reading[]) {
  50. const span = document.getElementById('last-update-value');
  51. span.textContent = new Date(readings.pop().time).toLocaleTimeString();
  52. }
  53. function showHourSelection() {
  54. const options:HTMLSelectElement =
  55. document.getElementById('hours-ago') as HTMLSelectElement;
  56. const current = getHoursAgo();
  57. for(let h of HOUR_OPTIONS) {
  58. const opt = document.createElement('option');
  59. opt.value = h.toString();
  60. opt.textContent = `Last ${h} hours`;
  61. if(h === current) {
  62. opt.selected = true;
  63. }
  64. options.add(opt);
  65. }
  66. options.addEventListener('change', () => {
  67. const selection = options.value;
  68. const newUrl = `${document.location.href.split('?')[0]}?hours=${selection}`;
  69. document.location.href = newUrl;
  70. });
  71. }
  72. async function getRecentData(
  73. hoursAgo:number = 2
  74. ): Promise<Reading[]> {
  75. const since = new Date().getTime() - (hoursAgo * 3600 * 1000);
  76. const outdoors = ref(rtdb, 'outdoor');
  77. const search = query(
  78. outdoors,
  79. orderByChild('time'),
  80. startAt(since)
  81. );
  82. return new Promise<Reading[]>((res, err) => {
  83. let values:Reading[] = [];
  84. onValue(search, snapshot => {
  85. snapshot.forEach(child => {
  86. values.push(child.val());
  87. });
  88. res(values);
  89. }, {
  90. onlyOnce: true
  91. });
  92. });
  93. }