|
| 1 | +"use strict"; |
| 2 | + |
| 3 | +let loc = { |
| 4 | + hostname: "www.bannister.id.au", |
| 5 | + port:443, |
| 6 | + useSSL:true |
| 7 | +}; |
| 8 | + |
| 9 | +// location options https://developer.mozilla.org/en-US/docs/Web/API/Geolocation/getCurrentPosition |
| 10 | +const locationOptions = { |
| 11 | + enableHighAccuracy: true, |
| 12 | + maximumAge: 10000, // milliseconds |
| 13 | + timeout: 700 // milliseconds |
| 14 | +}; |
| 15 | + |
| 16 | +var zoomLevel = 17; |
| 17 | + |
| 18 | +var mqtt_client, map, breadCrumbLine, locationCircle, locationChart, temperatureChart; |
| 19 | + |
| 20 | +var mqtt_message_count = 0; |
| 21 | + |
| 22 | + |
| 23 | +let statusText = document.getElementById("status"); |
| 24 | +let locationText = document.getElementById("location") |
| 25 | + |
| 26 | +let npositions = 0; |
| 27 | + |
| 28 | + |
| 29 | +function dashboard_init() { |
| 30 | + |
| 31 | + mqtt_init(); |
| 32 | + map_init(); |
| 33 | + |
| 34 | + temp_chart_init(); |
| 35 | + location_chart_init(); |
| 36 | + |
| 37 | + |
| 38 | +} |
| 39 | + |
| 40 | + |
| 41 | + |
| 42 | +function mqtt_init() { |
| 43 | + // Create a client instance |
| 44 | + mqtt_client = new Paho.MQTT.Client(loc.hostname, Number(loc.port), "clientId"); |
| 45 | + |
| 46 | + // set callback handlers |
| 47 | + mqtt_client.onConnectionLost = onConnectionLost; |
| 48 | + mqtt_client.onMessageArrived = onMessageArrived; |
| 49 | +} |
| 50 | + |
| 51 | + |
| 52 | + |
| 53 | +function setStatus(msg) { |
| 54 | + // statusText.textContent = msg; |
| 55 | + console.log("Status changed:", msg); |
| 56 | +} |
| 57 | + |
| 58 | + |
| 59 | +function mqtt_login() { |
| 60 | + let username = document.getElementById("username").value; |
| 61 | + let password = document.getElementById("password").value; |
| 62 | + // For more options, see here: https://www.eclipse.org/paho/files/jsdoc/Paho.MQTT.Client.html |
| 63 | + let options = { |
| 64 | + timeout:3, |
| 65 | + userName:username, |
| 66 | + password:password, |
| 67 | + onSuccess:onConnect, |
| 68 | + onFailure:onConnectionLost, |
| 69 | + useSSL:loc.useSSL, |
| 70 | + //reconnect:true |
| 71 | + } |
| 72 | + |
| 73 | + // setStatus("Connecting..."); |
| 74 | + $('#debug_logs').append("MQTT Client connecting..."); |
| 75 | + mqtt_client.connect(options); |
| 76 | + return false |
| 77 | + |
| 78 | +} |
| 79 | + |
| 80 | +// called when the mqtt_client connects |
| 81 | +function onConnect() { |
| 82 | + // Once a connection has been made, make a subscription and send a message. |
| 83 | + console.log("onConnect"); |
| 84 | + mqtt_client.subscribe("ekayak"); |
| 85 | + // let message = new Paho.MQTT.Message("Hello"); |
| 86 | + // message.destinationName = "World"; |
| 87 | + // mqtt_client.send(message); |
| 88 | + // setStatus("Connected"); |
| 89 | + $('#debug_logs').append("MQTT Client Connected!"); |
| 90 | + $('#login_icon').removeClass('text-red-500'); |
| 91 | + $('#login_icon').addClass('text-green-500'); |
| 92 | +} |
| 93 | + |
| 94 | +// called when the mqtt_client loses its connection |
| 95 | +function onConnectionLost(responseObject) { |
| 96 | + // setStatus(`Connection lost: ${responseObject.errorMessage}`); |
| 97 | + if (responseObject.errorCode !== 0) { |
| 98 | + console.log("onConnectionLost:"+responseObject.errorMessage); |
| 99 | + } |
| 100 | + $('#debug_logs').append("onConnectionLost:"+responseObject.errorMessage); |
| 101 | + $('#login_icon').removeClass('text-green-500'); |
| 102 | + $('#login_icon').addClass('text-red-500'); |
| 103 | +} |
| 104 | + |
| 105 | + |
| 106 | + |
| 107 | +function onMessageArrived(message) { |
| 108 | + console.log("onMessageArrived:",message.payloadString); |
| 109 | + console.log(message) |
| 110 | + console.log("Destionation", message.destinationName); |
| 111 | + console.log("QoS", message.qos); |
| 112 | + console.log("retained", message.retained); |
| 113 | + console.log("Duplicate", message.duplicate); |
| 114 | + |
| 115 | + // let out = document.getElementById("lastmessage"); |
| 116 | + // out.textContent = message.payloadString; |
| 117 | + $('#debug_logs').append(message.payloadString); |
| 118 | + |
| 119 | + let data = JSON.parse(message.payloadString); |
| 120 | + |
| 121 | + addData(temperatureChart, mqtt_message_count, [data.internal_temp, data.external_temp, data.relative_humidity]) |
| 122 | + |
| 123 | + $('#battery_voltage').html("- V"); |
| 124 | + $('#controller_temp').html(`${data.external_temp} Deg.`); |
| 125 | + |
| 126 | + mqtt_message_count += 1; |
| 127 | + |
| 128 | +} |
| 129 | + |
| 130 | + |
| 131 | + |
| 132 | + |
| 133 | +function map_init() { |
| 134 | + map = L.map('map').fitWorld(); |
| 135 | + L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', { |
| 136 | + maxZoom: 21, |
| 137 | + attribution: '© OpenStreetMap' |
| 138 | + }).addTo(map); |
| 139 | + breadCrumbLine = L.polyline([]).addTo(map); |
| 140 | + locationCircle = L.circle([0,0]).addTo(map); |
| 141 | +} |
| 142 | + |
| 143 | + |
| 144 | +function temp_chart_init() { |
| 145 | + const temperatureData = { |
| 146 | + labels: [], |
| 147 | + datasets: [ |
| 148 | + { |
| 149 | + label: 'Internal Temp', |
| 150 | + data: [], |
| 151 | + borderColor: 'rgba(54, 162, 235, 1)', |
| 152 | + backgroundColor: 'rgba(54, 162, 235, 0.2)', |
| 153 | + yAxisID: 'y', |
| 154 | + }, |
| 155 | + { |
| 156 | + label: 'External Temp', |
| 157 | + data: [], |
| 158 | + borderColor: 'rgba(0, 99, 182, 1)', |
| 159 | + backgroundColor: 'rgba(0, 99, 182, 0.2)', |
| 160 | + yAxisID: 'y', |
| 161 | + }, |
| 162 | + { |
| 163 | + label: 'Humidity', |
| 164 | + data: [], |
| 165 | + borderColor: 'rgba(255, 99, 132, 1)', |
| 166 | + backgroundColor: 'rgba(255, 99, 132, 0.2)', |
| 167 | + yAxisID: 'y1', |
| 168 | + } |
| 169 | + ] |
| 170 | + }; |
| 171 | + |
| 172 | + |
| 173 | + const temperatureConfig = { |
| 174 | + type: 'line', |
| 175 | + data: temperatureData, |
| 176 | + options: { |
| 177 | + responsive: true, |
| 178 | + interaction: { |
| 179 | + mode: 'index', |
| 180 | + intersect: false, |
| 181 | + }, |
| 182 | + stacked: false, |
| 183 | + plugins: { |
| 184 | + title: { |
| 185 | + display: true, |
| 186 | + text: 'Temperature & humidity' |
| 187 | + } |
| 188 | + }, |
| 189 | + scales: { |
| 190 | + y: { |
| 191 | + type: 'linear', |
| 192 | + display: true, |
| 193 | + position: 'left', |
| 194 | + title: { |
| 195 | + display:true, |
| 196 | + text: 'Temperature (deg)' |
| 197 | + } |
| 198 | + }, |
| 199 | + y1: { |
| 200 | + type: 'linear', |
| 201 | + display: true, |
| 202 | + position: 'right', |
| 203 | + title: { |
| 204 | + display:true, |
| 205 | + text: 'Relative humidity (%)' |
| 206 | + }, |
| 207 | + |
| 208 | + // grid line settings |
| 209 | + grid: { |
| 210 | + drawOnChartArea: false, // only want the grid lines for one axis to show up |
| 211 | + }, |
| 212 | + }, |
| 213 | + } |
| 214 | + }, |
| 215 | + }; |
| 216 | + |
| 217 | + temperatureChart = new Chart('temperatureChart', temperatureConfig); |
| 218 | + |
| 219 | +} |
| 220 | + |
| 221 | + |
| 222 | + |
| 223 | + |
| 224 | +function location_chart_init() { |
| 225 | + const locationData = { |
| 226 | + labels: [], |
| 227 | + datasets: [ |
| 228 | + { |
| 229 | + label: 'Speed', |
| 230 | + data: [], |
| 231 | + borderColor: 'rgba(54, 162, 235, 1)', |
| 232 | + backgroundColor: 'rgba(54, 162, 235, 0.2)', |
| 233 | + yAxisID: 'y', |
| 234 | + }, |
| 235 | + { |
| 236 | + label: 'Heading', |
| 237 | + data: [], |
| 238 | + borderColor: 'rgba(255, 99, 132, 1)', |
| 239 | + backgroundColor: 'rgba(255, 99, 132, 0.2)', |
| 240 | + yAxisID: 'y1', |
| 241 | + } |
| 242 | + ] |
| 243 | + }; |
| 244 | + |
| 245 | + |
| 246 | + const locationConfig = { |
| 247 | + type: 'line', |
| 248 | + data: locationData, |
| 249 | + options: { |
| 250 | + responsive: true, |
| 251 | + interaction: { |
| 252 | + mode: 'index', |
| 253 | + intersect: false, |
| 254 | + }, |
| 255 | + stacked: false, |
| 256 | + plugins: { |
| 257 | + title: { |
| 258 | + display: true, |
| 259 | + text: 'Location' |
| 260 | + } |
| 261 | + }, |
| 262 | + scales: { |
| 263 | + y: { |
| 264 | + type: 'linear', |
| 265 | + display: true, |
| 266 | + position: 'left', |
| 267 | + title: { |
| 268 | + display:true, |
| 269 | + text: 'Speed (m/s)' |
| 270 | + } |
| 271 | + }, |
| 272 | + y1: { |
| 273 | + type: 'linear', |
| 274 | + display: true, |
| 275 | + position: 'right', |
| 276 | + title: { |
| 277 | + display:true, |
| 278 | + text: 'Heading (deg)' |
| 279 | + }, |
| 280 | + |
| 281 | + // grid line settings |
| 282 | + grid: { |
| 283 | + drawOnChartArea: false, // only want the grid lines for one axis to show up |
| 284 | + }, |
| 285 | + }, |
| 286 | + } |
| 287 | + }, |
| 288 | + }; |
| 289 | + |
| 290 | + locationChart = new Chart('locationChart', locationConfig); |
| 291 | + |
| 292 | +} |
| 293 | + |
| 294 | + |
| 295 | + |
| 296 | + |
| 297 | +function locationSuccess(position) { |
| 298 | + const c = position.coords; |
| 299 | + // locationText.textContent = `lat/long:${c.latitude}/${c.longitude} speed:${c.speed} heading:${c.heading} altitude:${c.altitude} accuracy:${c.accuracy} altaccuracy:${c.altitudeAccuracy} timestamp:${position.timestamp}`; |
| 300 | + const latlng = [c.latitude, c.longitude]; |
| 301 | + if (npositions == 0) { |
| 302 | + //map.flyTo(latlng, Number(zoomLevel), {animate:true}); // Flying takes too long and stops when the next location comes in |
| 303 | + map.setView(latlng, zoomLevel); |
| 304 | + } else { |
| 305 | + map.panTo(latlng); |
| 306 | + } |
| 307 | + breadCrumbLine.addLatLng(latlng); |
| 308 | + locationCircle.setLatLng(latlng); |
| 309 | + locationCircle.setRadius(c.accuracy); |
| 310 | + |
| 311 | + npositions = npositions + 1; |
| 312 | + |
| 313 | + $('#gps_speed').html(c.speed); |
| 314 | + $('#current_trip').html('0 km'); |
| 315 | + |
| 316 | + addData(locationChart, npositions, [c.speed, c.heading]) |
| 317 | + |
| 318 | +} |
| 319 | + |
| 320 | +function locationError(error) { |
| 321 | + // locationText.textContent = `GEOLOCATION ERROR(${error.code}): ${error.message}`; |
| 322 | + console.log(`GEOLOCATION ERROR(${error.code}): ${error.message}`) |
| 323 | +} |
| 324 | + |
| 325 | +// setup geolocation |
| 326 | +if ('geolocation' in navigator) { |
| 327 | + const watchID = navigator.geolocation.watchPosition(locationSuccess, locationError, locationOptions); |
| 328 | +} else { |
| 329 | + // locationText.textContent = "Geolocation not supported in this browser"; |
| 330 | +} |
| 331 | + |
| 332 | + |
| 333 | + |
| 334 | + |
| 335 | +// Chart update functions: https://www.chartjs.org/docs/latest/developers/updates.html |
| 336 | +function addData(chart, label, data) { |
| 337 | + chart.data.labels.push(label); |
| 338 | + chart.data.datasets.forEach((dataset, i) => { |
| 339 | + dataset.data.push(data[i]); |
| 340 | + }); |
| 341 | + chart.update(); |
| 342 | +} |
| 343 | + |
| 344 | +function removeData(chart) { |
| 345 | + chart.data.labels.pop(); |
| 346 | + chart.data.datasets.forEach((dataset) => { |
| 347 | + dataset.data.pop(); |
| 348 | + }); |
| 349 | + chart.update(); |
| 350 | +} |
| 351 | + |
| 352 | + |
| 353 | + |
| 354 | + |
| 355 | +// On Apline ready |
| 356 | +document.addEventListener('alpine:init', () => { |
| 357 | + dashboard_init(); |
| 358 | + |
| 359 | +}); |
| 360 | + |
| 361 | + |
| 362 | + |
0 commit comments