var chart = null var chart2 = null function initCharts() { chart = c3.generate({ bindto: '#humidity-temp', data: { x: 'time', columns: [ ['time', 1, 2, 3, 4], ['temp', 24, 24.5, 24.3, 24.6], ['humidity', 83.1, 99.5, 74.3, 84.6], ], axes: { temp: 'y', humidity: 'y2' } }, axis: { x: { tick: { format: (x) => new Date(x).toISOString() } }, y2: { show: true } }, zoom: { enabled: true } }) chart2 = c3.generate({ bindto: '#volts', data: { x: 'time', columns: [ ['time', 1, 2, 3, 4], ['voltage', 24, 24.5, 24.3, 24.6], ['voltage2', 24, 24.5, 24.3, 24.6], ], axes: { voltage: 'y', voltage2: 'y', } }, axis: { x: { tick: { format: (x) => new Date(x).toISOString() } }, }, zoom: { enabled: true } }) } var max_interval_millis = 5*60*1000 var decimation_rate = 1 var cur_time_millis = 0 var time = [] var temp = [] var humd = [] var volts = [] var volts2 = [] function status(msg) { document.getElementById('status').textContent = msg } var autoupdate = true var chart_updater = null async function updateCharts() { status("loading data...") const latest = await fetch("/api/latest") const last_time_millis = await latest.json() if (last_time_millis > cur_time_millis) { // we need to collect more data to catch up // time is in milliseconds so we divide by 1000 var diff = (last_time_millis - cur_time_millis) / 1000 // worst case load only the last two weeks of data diff = Math.min(diff, max_interval_millis/1000) var path = "/api/last/weeks/" var offset = diff if (diff < 60) { path = "/api/last/seconds/" offset = Math.ceil(diff) } else if (diff < 60*60) { path = "/api/last/minutes/" offset = Math.ceil(diff/60) } else if (diff < 24*60*60) { path = "/api/last/hours/" offset = Math.ceil(diff/(60*60)) } else if (diff < 7*24*60*60) { path = "/api/last/days/" offset = Math.ceil(diff/(24*60*60)) } else { path = "/api/last/weeks/" offset = Math.ceil(diff/(7*24*60*60)) } const new_data = await fetch(path + offset) const new_data_json = await new_data.json() // copy the data into the arrays new_data_json.sort((a, b) => a.t - b.t) new_data_json.forEach((v) => { var last_time = time[time.length-1] if (time.length == 0) { last_time = 0 } if (v.t > last_time) { time.push(v.t) temp.push(v.temp) humd.push(v.hum) volts.push(v.hv) volts2.push(v.hv2) } }) // truncate all the lists as necessary // using the time array to determine the splicing points cur_time_millis = time[time.length - 1] const min_time = cur_time_millis - max_interval_millis var slice_idx = 0 for (var i = 0; i < time.length; i++) { if (time[i] >= min_time) { slice_idx = i break } } //console.log("slice idx ", slice_idx) temp = temp.slice(slice_idx) humd = humd.slice(slice_idx) volts = volts.slice(slice_idx) volts2 = volts2.slice(slice_idx) time = time.slice(slice_idx) const temp_d = temp.filter((_, idx) => (idx % decimation_rate) == 0) const humd_d = humd.filter((_, idx) => (idx % decimation_rate) == 0) const volts_d = volts.filter((_, idx) => (idx % decimation_rate) == 0) const volts2_d = volts2.filter((_, idx) => (idx % decimation_rate) == 0) const time_d = time.filter((_, idx) => (idx % decimation_rate) == 0) chart.load({ columns: [ ["time"].concat(time_d), ["temp"].concat(temp_d), ["humidity"].concat(humd_d), ] }) chart2.load({ columns: [ ["time"].concat(time_d), ["voltage"].concat(volts_d), ["voltage2"].concat(volts2_d), ] }) status("charts updated") } if (autoupdate) { chartupdater() } } const sleep = (ms) => new Promise(r => setTimeout(r, ms)) var chart_update_millis = 2000 async function chartupdater() { if (chart_updater) return chart_updater = true try { while (autoupdate) { await sleep(chart_update_millis) // wait at least two seconds to avoid wasting a lot of bandwidth const resp = await fetch("/api/update") updateCharts() } } finally { chart_updater = false } } var status_updater = false async function updateStatus() { const status_resp = await fetch("/api/status") const status = await status_resp.json() //console.log(status) const humidifier_state = (status.humidifier ? "on" : "off") const manual_mode_state = (status.manual_mode ? "on" : "off") //console.log(humidifier_state) document.getElementById("device-status").textContent = "connected: " + status.connected + ", manual mode: " + manual_mode_state + ", humidifier: " + humidifier_state if (autoupdate) { waitThenUpdateStatus() } } async function waitThenUpdateStatus() { if (status_updater) return status_updater = true try { while (autoupdate) { await fetch("/api/status_update") updateStatus() } } finally { status_updater = false } } async function testAdminMode() { const msg = JSON.stringify({ auth: "password", data: { set_params: { name: "target_lower", value: 0.87 } } }) await fetch("/api/admin", { method: "POST", body: msg }) } window.onload = () => { initCharts() updateCharts() updateStatus() document.getElementById('update').addEventListener('click', (e) => { updateCharts() updateStatus() }) document.getElementById('autoupdate').addEventListener('click', (e) => { autoupdate = document.getElementById('autoupdate').checked }) document.getElementsByName('decim').forEach((elem) => { elem.addEventListener('click', (e) => { if (elem.checked) { decimation_rate = parseInt(elem.value) } }) }) document.getElementsByName('update').forEach((elem) => { elem.addEventListener('click', (e) => { if (elem.checked) { chart_update_millis = parseInt(elem.value) } }) }) document.getElementsByName('duration').forEach((elem) => { elem.addEventListener('click', (e) => { const old_millis = max_interval_millis if (elem.checked) { max_interval_millis = parseInt(elem.value)*1000*60 if (max_interval_millis != old_millis) { // reset the chart data to force a reload on the next update cur_time_millis = 0 time = [] temp = [] humd = [] volts = [] volts2 = [] } } }) }) /* document.getElementById('test-admin').addEventListener('click', (e) => { testAdminMode() }) */ }