Add routes to get location and sensor by name
Added viewdata using chart.js
This commit is contained in:
parent
fadabf3451
commit
c268e1d33d
@ -1,4 +1,5 @@
|
|||||||
const {locationModel} = require("../database/model/locationModel");
|
const {locationModel} = require("../database/model/locationModel");
|
||||||
|
const {Op} = require("sequelize");
|
||||||
|
|
||||||
async function getLocation() {
|
async function getLocation() {
|
||||||
const location = await locationModel.findAll();
|
const location = await locationModel.findAll();
|
||||||
@ -35,7 +36,32 @@ async function deleteLocation(id) {
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
/*
|
||||||
|
const { Op } = require("sequelize");
|
||||||
|
|
||||||
|
var options = {
|
||||||
|
where: {
|
||||||
|
[Op.or]: [
|
||||||
|
{ 'subject': { [Op.like]: '%' + query + '%' } },
|
||||||
|
{ '$Comment.body$': { [Op.like]: '%' + query + '%' } }
|
||||||
|
]
|
||||||
|
},
|
||||||
|
include: [{ model: Comment }]
|
||||||
|
};
|
||||||
|
*/
|
||||||
|
|
||||||
|
async function getLocationByName(name) {
|
||||||
|
const location = await locationModel.findAll({
|
||||||
|
where: {
|
||||||
|
[Op.or]: [
|
||||||
|
{name: {[Op.like]: "%" + name + "%"}},
|
||||||
|
{added_by: {[Op.like]: "%" + name + "%"}},
|
||||||
|
{description: {[Op.like]: "%" + name + "%"}},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
});
|
||||||
|
return location;
|
||||||
|
}
|
||||||
|
|
||||||
async function getLocationById(id) {
|
async function getLocationById(id) {
|
||||||
const location = await locationModel.findAll({
|
const location = await locationModel.findAll({
|
||||||
@ -51,5 +77,6 @@ module.exports = {
|
|||||||
addLocation,
|
addLocation,
|
||||||
updateLocation,
|
updateLocation,
|
||||||
deleteLocation,
|
deleteLocation,
|
||||||
|
getLocationByName,
|
||||||
getLocationById,
|
getLocationById,
|
||||||
};
|
};
|
@ -1,4 +1,5 @@
|
|||||||
const { sensorModel } = require("../database/model/sensorModel");
|
const { sensorModel } = require("../database/model/sensorModel");
|
||||||
|
const {Op} = require("sequelize");
|
||||||
|
|
||||||
async function getSensor() {
|
async function getSensor() {
|
||||||
const sensor = await sensorModel.findAll();
|
const sensor = await sensorModel.findAll();
|
||||||
@ -55,6 +56,33 @@ async function deleteSensor(id) {
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
/*
|
||||||
|
async function getLocationByName(name) {
|
||||||
|
const location = await locationModel.findAll({
|
||||||
|
where: {
|
||||||
|
[Op.or]: [
|
||||||
|
{name: {[Op.like]: "%" + name + "%"}},
|
||||||
|
{added_by: {[Op.like]: "%" + name + "%"}},
|
||||||
|
{description: {[Op.like]: "%" + name + "%"}},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
});
|
||||||
|
return location;
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
async function getSensorByName(name) {
|
||||||
|
const sensor = await sensorModel.findAll({
|
||||||
|
where: {
|
||||||
|
[Op.or]: [
|
||||||
|
{name: {[Op.like]: "%" + name + "%"}},
|
||||||
|
{added_by: {[Op.like]: "%" + name + "%"}},
|
||||||
|
{mac_address: {[Op.like]: "%" + name + "%"}},
|
||||||
|
{description: {[Op.like]: "%" + name + "%"}},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
});
|
||||||
|
return sensor;
|
||||||
|
}
|
||||||
|
|
||||||
async function getSensorById(id) {
|
async function getSensorById(id) {
|
||||||
const sensor = await sensorModel.findAll({
|
const sensor = await sensorModel.findAll({
|
||||||
@ -70,5 +98,6 @@ module.exports = {
|
|||||||
addSensor,
|
addSensor,
|
||||||
updateSensor,
|
updateSensor,
|
||||||
deleteSensor,
|
deleteSensor,
|
||||||
|
getSensorByName,
|
||||||
getSensorById,
|
getSensorById,
|
||||||
};
|
};
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
//getting button from DOM id
|
//getting button from DOM id
|
||||||
const buttons = document.querySelectorAll(".button-container button");
|
const buttons = document.querySelectorAll(".button-container button");
|
||||||
const queryButton = document.getElementById("querybutton-container");
|
const queryButton = document.getElementById("querybutton-container");
|
||||||
const ctx = document.getElementById("dataChart").getContext("2d");
|
|
||||||
|
|
||||||
$(document).ready(async function () {
|
$(document).ready(async function () {
|
||||||
//https://stackoverflow.com/questions/9045868/javascript-date-getweek
|
//https://stackoverflow.com/questions/9045868/javascript-date-getweek
|
||||||
@ -12,143 +11,109 @@ $(document).ready(async function () {
|
|||||||
return Math.ceil(dayOfYear / 7);
|
return Math.ceil(dayOfYear / 7);
|
||||||
};
|
};
|
||||||
let date = new Date();
|
let date = new Date();
|
||||||
var week = date.getWeek();
|
var week = date.getWeek() - 1; // Subtracting 1 to get the actual week number
|
||||||
var today = date.getDate();
|
var today = date.getDate();
|
||||||
var month = date.getMonth() + 1; // Adding 1 to get the actual month number
|
var month = date.getMonth() + 1; // Adding 1 to get the actual month number
|
||||||
var year = date.getFullYear();
|
var year = date.getFullYear();
|
||||||
//year data
|
|
||||||
app.api.get("sensor-data/data?year=" + year, function (error, data) {
|
|
||||||
//console.log(data);
|
|
||||||
});
|
|
||||||
//monthly data
|
|
||||||
app.api.get("sensor-data/data?month=" + month, function (error, data) {
|
|
||||||
//console.log(data);
|
|
||||||
});
|
|
||||||
//weekly data
|
|
||||||
app.api.get(
|
|
||||||
"sensor-data/data?month=" + month + "&week=" + week,
|
|
||||||
function (error, data) {
|
|
||||||
//console.log(data);
|
|
||||||
}
|
|
||||||
);
|
|
||||||
//daily data
|
|
||||||
app.api.get(
|
|
||||||
"sensor-data/data?month=" + month + "&week=" + week + "&day=" + today,
|
|
||||||
function (error, data) {
|
|
||||||
for (let rows of data) {
|
|
||||||
console.log(rows);
|
|
||||||
getDate(rows.CreatedAt);
|
|
||||||
|
|
||||||
}
|
// Initialize initialData for chart
|
||||||
}
|
const initialData = {
|
||||||
);
|
labels: [], // Array to store timestamps
|
||||||
});
|
datasets: [
|
||||||
|
{
|
||||||
|
label: "Average MeasurementData",
|
||||||
|
data: [], // Array to store measurements objects
|
||||||
|
backgroundColor: "green",
|
||||||
|
borderColor: "green",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
|
||||||
function getDate(date){
|
// Create Chart.js chart
|
||||||
console.log(date);
|
const ctx = document.getElementById("DailyDataChart").getContext("2d");
|
||||||
|
const chart = new Chart(ctx, {
|
||||||
}
|
type: "bar",
|
||||||
|
data: initialData,
|
||||||
/*
|
options: {
|
||||||
|
responsive: true,
|
||||||
(async function() {
|
title: {
|
||||||
const data = [
|
display: true,
|
||||||
{ year: 2010, count: 10 },
|
text: "Average measurement metric data by Hour",
|
||||||
{ year: 2011, count: 20 },
|
|
||||||
{ year: 2012, count: 15 },
|
|
||||||
{ year: 2013, count: 25 },
|
|
||||||
{ year: 2014, count: 22 },
|
|
||||||
{ year: 2015, count: 30 },
|
|
||||||
{ year: 2016, count: 28 },
|
|
||||||
];
|
|
||||||
|
|
||||||
new Chart(
|
|
||||||
document.getElementById('acquisitions'),
|
|
||||||
{
|
|
||||||
type: 'bar',
|
|
||||||
data: {
|
|
||||||
labels: data.map(row => row.year),
|
|
||||||
datasets: [
|
|
||||||
{
|
|
||||||
label: 'Acquisitions by year',
|
|
||||||
data: data.map(row => row.count)
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
);
|
|
||||||
})();
|
|
||||||
*/
|
|
||||||
// Initial dataset (AQI)
|
|
||||||
const initialData = {
|
|
||||||
//date and time
|
|
||||||
labels: [ date.map(row => row.CreatedAt)],
|
|
||||||
datasets:[
|
|
||||||
{
|
|
||||||
//data like psi
|
|
||||||
label: "Average PSI Data",
|
|
||||||
data: [],
|
|
||||||
backgroundColor: "green",
|
|
||||||
borderColor: "green",
|
|
||||||
},
|
|
||||||
],
|
|
||||||
};
|
|
||||||
|
|
||||||
//setting chart
|
|
||||||
const chart = new Chart(ctx, {
|
|
||||||
type: "bar",
|
|
||||||
data: initialData,
|
|
||||||
options: {
|
|
||||||
responsive: true,
|
|
||||||
title: {
|
|
||||||
display: true,
|
|
||||||
text: "HISTORICAL",
|
|
||||||
},
|
|
||||||
subtitle: {
|
|
||||||
display: true,
|
|
||||||
text: "Historic air quality graph for Singapore",
|
|
||||||
},
|
|
||||||
legend: {
|
|
||||||
display: false,
|
|
||||||
},
|
|
||||||
tooltips: {
|
|
||||||
mode: "index",
|
|
||||||
intersect: false,
|
|
||||||
callbacks: {
|
|
||||||
label: function (tooltipItem, data) {
|
|
||||||
const label = data.labels[tooltipItem.index];
|
|
||||||
return label + ": " + data.datasets[0].data[tooltipItem.index];
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
scales: {
|
});
|
||||||
xAxes: [
|
|
||||||
{
|
// Function to update chart data based on button clicked
|
||||||
barPercentage: 0.6,
|
function updateChart(metric) {
|
||||||
categoryPercentage: 0.7,
|
const queryParams = `sensor-data/data?month=${month}&week=${week}&day=${today}`;
|
||||||
ticks: {
|
app.api.get(queryParams, function (error, data) {
|
||||||
autoSkip: true,
|
// Clear previous data
|
||||||
},
|
initialData.labels = []; //timestamp
|
||||||
maxRotation: 0,
|
initialData.datasets[0].data = []; //measurement data dependinbg on metric
|
||||||
minRotation: 0,
|
|
||||||
},
|
// Group data by hour and calculate average value
|
||||||
],
|
const hourlyData = {};
|
||||||
yAxes: [
|
for (let row of data) {
|
||||||
{
|
//each row contains a timestamp and measurement data of each sensor and location
|
||||||
title: {
|
const createdAt = new Date(row.createdAt); //set to local time
|
||||||
display: true,
|
const hourString = new Date(
|
||||||
text: "Value",
|
createdAt.getFullYear(),
|
||||||
},
|
createdAt.getMonth(),
|
||||||
},
|
createdAt.getDate(),
|
||||||
],
|
createdAt.getHours()
|
||||||
},
|
).toISOString();
|
||||||
},
|
if (!hourlyData[hourString]) {
|
||||||
|
hourlyData[hourString] = [];
|
||||||
|
}
|
||||||
|
hourlyData[hourString].push(row.measurement[metric]); //pushing measurement data into hourlyData
|
||||||
|
}
|
||||||
|
|
||||||
|
// Calculate average value for each hour
|
||||||
|
//console.log(hourlyData); //24 values for each hour of the day
|
||||||
|
for (let hourString in hourlyData) {
|
||||||
|
const averageValue =
|
||||||
|
hourlyData[hourString].reduce((acc, val) => acc + val, 0) /
|
||||||
|
hourlyData[hourString].length;
|
||||||
|
initialData.labels.push(
|
||||||
|
new Date(hourString).toLocaleString("en-US", {
|
||||||
|
timeZone: "UTC",
|
||||||
|
hour12: false,
|
||||||
|
})
|
||||||
|
);
|
||||||
|
initialData.datasets[0].data.push(averageValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update chart
|
||||||
|
chart.update();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Event listeners for buttons
|
||||||
|
document.getElementById("psiButton").addEventListener("click", function () {
|
||||||
|
updateChart("psi");
|
||||||
|
});
|
||||||
|
|
||||||
|
document.getElementById("tempButton").addEventListener("click", function () {
|
||||||
|
updateChart("temperature");
|
||||||
|
});
|
||||||
|
|
||||||
|
document.getElementById("humButton").addEventListener("click", function () {
|
||||||
|
updateChart("humidity");
|
||||||
|
});
|
||||||
|
|
||||||
|
document.getElementById("o3Button").addEventListener("click", function () {
|
||||||
|
updateChart("o3");
|
||||||
|
});
|
||||||
|
|
||||||
|
document.getElementById("no2Button").addEventListener("click", function () {
|
||||||
|
updateChart("no2");
|
||||||
|
});
|
||||||
|
|
||||||
|
document.getElementById("so2Button").addEventListener("click", function () {
|
||||||
|
updateChart("so2");
|
||||||
|
});
|
||||||
|
|
||||||
|
document.getElementById("coButton").addEventListener("click", function () {
|
||||||
|
updateChart("co");
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
queryButton.addEventListener("click", (event) => {
|
|
||||||
const clickedButton = event.target;
|
|
||||||
//console.log(clickedButton.id);
|
|
||||||
|
|
||||||
//make it switch bar chart based on query button
|
|
||||||
});
|
|
||||||
|
|
||||||
|
@ -2,6 +2,7 @@ const {
|
|||||||
addLocation,
|
addLocation,
|
||||||
getLocation,
|
getLocation,
|
||||||
getLocationById,
|
getLocationById,
|
||||||
|
getLocationByName,
|
||||||
updateLocation,
|
updateLocation,
|
||||||
deleteLocation,
|
deleteLocation,
|
||||||
} = require("../functions/location");
|
} = require("../functions/location");
|
||||||
@ -58,6 +59,19 @@ router.delete("/delete", async (req, res, next) => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
//get location by name
|
||||||
|
router.get("/name/:name", async (req, res, next) => {
|
||||||
|
try {
|
||||||
|
//get params
|
||||||
|
const { name } = req.params;
|
||||||
|
const location = await getLocationByName(name);
|
||||||
|
res.status(200).json(location);
|
||||||
|
} catch (error) {
|
||||||
|
console.error(error);
|
||||||
|
next(error);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//get location by id
|
//get location by id
|
||||||
|
@ -3,6 +3,7 @@ const {
|
|||||||
addSensor,
|
addSensor,
|
||||||
updateSensor,
|
updateSensor,
|
||||||
deleteSensor,
|
deleteSensor,
|
||||||
|
getSensorByName,
|
||||||
getSensorById
|
getSensorById
|
||||||
} = require("../functions/sensor.js");
|
} = require("../functions/sensor.js");
|
||||||
|
|
||||||
@ -52,6 +53,16 @@ router.delete("/delete", async (req, res, next) => {
|
|||||||
next(error);
|
next(error);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
router.get("/name/:name", async (req, res, next) => {
|
||||||
|
try {
|
||||||
|
const sensor = await getSensorByName(req.params.name);
|
||||||
|
res.status(200).json(sensor);
|
||||||
|
} catch (error) {
|
||||||
|
console.error(error);
|
||||||
|
next(error);
|
||||||
|
}
|
||||||
|
});
|
||||||
router.get("/:id", async (req, res, next) => {
|
router.get("/:id", async (req, res, next) => {
|
||||||
try {
|
try {
|
||||||
const sensor = await getSensorById(req.params.id);
|
const sensor = await getSensorById(req.params.id);
|
||||||
|
@ -42,12 +42,13 @@
|
|||||||
</script>
|
</script>
|
||||||
<!-- socket.io scriot -->
|
<!-- socket.io scriot -->
|
||||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/socket.io/4.2.0/socket.io.min.js"></script>
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/socket.io/4.2.0/socket.io.min.js"></script>
|
||||||
|
<!-- fancy table cdn -->
|
||||||
|
<!-- <script src="https://cdn.jsdelivr.net/npm/jquery.fancytable/dist/fancyTable.min.js"></script> -->
|
||||||
|
|
||||||
<!-- jq-repeat -->
|
<!-- jq-repeat -->
|
||||||
<script src="js/jq-repeat.js"></script>
|
<script src="js/jq-repeat.js"></script>
|
||||||
|
|
||||||
<!-- jquery app.js -->
|
<!-- jquery public app.js -->
|
||||||
<script src="js/app.js"></script>
|
<script src="js/app.js"></script>
|
||||||
</head>
|
</head>
|
||||||
<!-- javascript function to check if user is auth -->
|
<!-- javascript function to check if user is auth -->
|
||||||
@ -99,12 +100,6 @@
|
|||||||
<li class="nav-item">
|
<li class="nav-item">
|
||||||
<a class="nav-link" href="/contact">Contact</a>
|
<a class="nav-link" href="/contact">Contact</a>
|
||||||
</li>
|
</li>
|
||||||
<!--
|
|
||||||
<li class="nav-item">
|
|
||||||
<a class="nav-link" href="/api">API Doc</a>
|
|
||||||
</li>
|
|
||||||
|
|
||||||
-->
|
|
||||||
<!-- profile button -->
|
<!-- profile button -->
|
||||||
<div class="form-inline mt-2 mt-md-0">
|
<div class="form-inline mt-2 mt-md-0">
|
||||||
<a id="cl-viewdata-button" class="btn btn-outline-info btn-sm my-2 my-sm-0" href="/viewdata"
|
<a id="cl-viewdata-button" class="btn btn-outline-info btn-sm my-2 my-sm-0" href="/viewdata"
|
||||||
|
@ -5,6 +5,26 @@
|
|||||||
<script type="text/javascript">
|
<script type="text/javascript">
|
||||||
// Require login to see this page.
|
// Require login to see this page.
|
||||||
app.auth.forceLogin()
|
app.auth.forceLogin()
|
||||||
|
|
||||||
|
//get Location data
|
||||||
|
app.api.get('location', function (error, data) {
|
||||||
|
for (let row in data){
|
||||||
|
//format time to local time than push
|
||||||
|
data[row].createdAt = new Date(data[row].createdAt).toLocaleString();
|
||||||
|
data[row].updatedAt = new Date(data[row].updatedAt).toLocaleString();
|
||||||
|
$.scope.LocationTable.push(data[row]);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
//get sensor data
|
||||||
|
app.api.get('sensor', function (error, data) {
|
||||||
|
for (let row in data){
|
||||||
|
//format time to local time than push
|
||||||
|
data[row].createdAt = new Date(data[row].createdAt).toLocaleString();
|
||||||
|
data[row].updatedAt = new Date(data[row].updatedAt).toLocaleString();
|
||||||
|
$.scope.SensorTable.push(data[row]);
|
||||||
|
}
|
||||||
|
})
|
||||||
</script>
|
</script>
|
||||||
<br>
|
<br>
|
||||||
<br>
|
<br>
|
||||||
@ -12,17 +32,15 @@
|
|||||||
<body>
|
<body>
|
||||||
|
|
||||||
<div class="air-quality-container">
|
<div class="air-quality-container">
|
||||||
<div class="querybutton-container" id="querybutton-container">
|
<!-- header -->
|
||||||
<!-- <button id="yearButton">Year</button>
|
<div class="header-container">
|
||||||
<button id="monthButton">Month</button>
|
<h1>Daily Air Quality Data Chart</h1>
|
||||||
<button id="weekButton">Week</button> -->
|
|
||||||
<button id="dayButton">Day</button>
|
|
||||||
</div>
|
</div>
|
||||||
<br>
|
<br>
|
||||||
<div class="chart-container">
|
<div class="chart-container">
|
||||||
<canvas id="dataChart"></canvas>
|
<canvas id="DailyDataChart"></canvas>
|
||||||
</div>
|
</div>
|
||||||
<!-- <div class="button-container">
|
<div class="button-container">
|
||||||
<button id="psiButton">PSI</button>
|
<button id="psiButton">PSI</button>
|
||||||
<button id="tempButton">Temperature</button>
|
<button id="tempButton">Temperature</button>
|
||||||
<button id="humButton">Humidity</button>
|
<button id="humButton">Humidity</button>
|
||||||
@ -31,16 +49,70 @@
|
|||||||
<button id="so2Button">SO2</button>
|
<button id="so2Button">SO2</button>
|
||||||
<button id="coButton">CO</button>
|
<button id="coButton">CO</button>
|
||||||
</div>
|
</div>
|
||||||
-->
|
<br>
|
||||||
|
<div class="header-container">
|
||||||
|
<h1>Location Table</h1>
|
||||||
|
</div>
|
||||||
|
<table id="LocationTable" class="table table-striped LocationTable">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>ID</th>
|
||||||
|
<th>Location Name</th>
|
||||||
|
<th>Added_By</th>
|
||||||
|
<th>Description</th>
|
||||||
|
<th>CreatedAt</th>
|
||||||
|
<th>UpdatedAt</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<!-- Table Content Goes Here -->
|
||||||
|
<tr jq-repeat="LocationTable">
|
||||||
|
<td>{{id}}</td>
|
||||||
|
<td>{{name}}</td>
|
||||||
|
<td>{{added_by}}</td>
|
||||||
|
<td>{{description}}</td>
|
||||||
|
<td>{{createdAt}}</td>
|
||||||
|
<td>{{updatedAt}}</td>
|
||||||
|
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
<div class="header-container">
|
||||||
|
<h1>Sensor Table</h1>
|
||||||
|
</div>
|
||||||
|
<table id="sensorTable" class="table table-striped sensorTable">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>ID</th>
|
||||||
|
<th>Sensor Name</th>
|
||||||
|
<th>Added_By</th>
|
||||||
|
<th>Mac Address</th>
|
||||||
|
<th>Description</th>
|
||||||
|
<th>Location ID</th>
|
||||||
|
<th>CreatedAt</th>
|
||||||
|
<th>UpdatedAt</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<!-- Table Content Goes Here -->
|
||||||
|
<tr jq-repeat="SensorTable">
|
||||||
|
<td>{{id}}</td>
|
||||||
|
<td>{{name}}</td>
|
||||||
|
<td>{{added_by}}</td>
|
||||||
|
<td>{{mac_address}}</td>
|
||||||
|
<td>{{description}}</td>
|
||||||
|
<td>{{locationid}}</td>
|
||||||
|
<td>{{createdAt}}</td>
|
||||||
|
<td>{{updatedAt}}</td>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
<br>
|
<br>
|
||||||
<div class="download-container">
|
<div class="download-container">
|
||||||
<p>Download data here:</p>
|
<p>Download sensor data here:</p>
|
||||||
<button id="downloadCSVButton" onclick="downloadCSV()">Download CSV</button>
|
<button id="downloadCSVButton" onclick="downloadCSV()">Download CSV</button>
|
||||||
<br><br>
|
<br><br>
|
||||||
<button id="downloadCSVButton" onclick="downloadExcel()">Download Excel Sheet</button>
|
<button id="downloadCSVButton" onclick="downloadExcel()">Download Excel Sheet</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</body>
|
</body>
|
||||||
<br>
|
<br>
|
||||||
<br>
|
<br>
|
||||||
|
Loading…
x
Reference in New Issue
Block a user