u
This commit is contained in:
60
server.js
60
server.js
@@ -1218,30 +1218,43 @@ app.get('/api/history', (req, res) => {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Calculate time modifiers using offset in seconds
|
// Calculate absolute start/end times (in seconds)
|
||||||
|
const nowMs = Date.now();
|
||||||
|
let startTsSec, endTsSec;
|
||||||
|
|
||||||
|
if (range === 'today') {
|
||||||
|
// align to midnight local time
|
||||||
|
const d = new Date(nowMs);
|
||||||
|
d.setHours(0, 0, 0, 0);
|
||||||
|
const midnightMs = d.getTime();
|
||||||
|
|
||||||
|
// "1 Tag" = 24h aligned to midnight
|
||||||
|
startTsSec = (midnightMs / 1000) - (off * 24 * 3600);
|
||||||
|
endTsSec = startTsSec + (24 * 3600);
|
||||||
|
} else {
|
||||||
|
// Rolling window
|
||||||
let durationSec;
|
let durationSec;
|
||||||
if (range === 'week') durationSec = 7 * 24 * 3600;
|
if (range === 'week') durationSec = 7 * 24 * 3600;
|
||||||
else if (range === 'month') durationSec = 30 * 24 * 3600;
|
else if (range === 'month') durationSec = 30 * 24 * 3600;
|
||||||
else durationSec = 24 * 3600; // day
|
else durationSec = 24 * 3600; // day
|
||||||
|
|
||||||
const endOffsetSec = off * durationSec;
|
const endMs = nowMs - (off * durationSec * 1000);
|
||||||
const startOffsetSec = (off + 1) * durationSec;
|
const startMs = endMs - (durationSec * 1000);
|
||||||
|
|
||||||
const endMod = `-${endOffsetSec} seconds`;
|
startTsSec = Math.floor(startMs / 1000);
|
||||||
const startMod = `-${startOffsetSec} seconds`;
|
endTsSec = Math.floor(endMs / 1000);
|
||||||
|
}
|
||||||
|
|
||||||
// Select raw data
|
|
||||||
// Select raw data
|
|
||||||
const stmt = db.prepare(`
|
const stmt = db.prepare(`
|
||||||
SELECT strftime('%s', timestamp) as ts, temp_c, humidity, fan_speed
|
SELECT strftime('%s', timestamp) as ts, temp_c, humidity, fan_speed
|
||||||
FROM readings
|
FROM readings
|
||||||
WHERE dev_name = ? AND port = ?
|
WHERE dev_name = ? AND port = ?
|
||||||
AND timestamp >= datetime('now', ?)
|
AND timestamp >= datetime(?, 'unixepoch')
|
||||||
AND timestamp < datetime('now', ?)
|
AND timestamp < datetime(?, 'unixepoch')
|
||||||
ORDER BY timestamp ASC
|
ORDER BY timestamp ASC
|
||||||
`);
|
`);
|
||||||
|
|
||||||
const rows = stmt.all(devName, parseInt(port, 10), startMod, endMod);
|
const rows = stmt.all(devName, parseInt(port, 10), startTsSec, endTsSec);
|
||||||
|
|
||||||
if (rows.length === 0) return res.json({ start: 0, step: bucketSize, temps: [], hums: [], levels: [] });
|
if (rows.length === 0) return res.json({ start: 0, step: bucketSize, temps: [], hums: [], levels: [] });
|
||||||
|
|
||||||
@@ -1320,27 +1333,38 @@ app.get('/api/outputs/history', (req, res) => {
|
|||||||
const { range, offset = 0 } = req.query;
|
const { range, offset = 0 } = req.query;
|
||||||
const off = parseInt(offset, 10) || 0;
|
const off = parseInt(offset, 10) || 0;
|
||||||
|
|
||||||
// Calculate duration in seconds
|
// Calculate absolute start/end times (in seconds)
|
||||||
|
const nowMs = Date.now();
|
||||||
|
let startTsSec, endTsSec;
|
||||||
|
|
||||||
|
if (range === 'today') {
|
||||||
|
const d = new Date(nowMs);
|
||||||
|
d.setHours(0, 0, 0, 0);
|
||||||
|
const midnightMs = d.getTime();
|
||||||
|
startTsSec = (midnightMs / 1000) - (off * 24 * 3600);
|
||||||
|
endTsSec = startTsSec + (24 * 3600);
|
||||||
|
} else {
|
||||||
let durationSec;
|
let durationSec;
|
||||||
if (range === 'week') durationSec = 7 * 24 * 3600;
|
if (range === 'week') durationSec = 7 * 24 * 3600;
|
||||||
else if (range === 'month') durationSec = 30 * 24 * 3600;
|
else if (range === 'month') durationSec = 30 * 24 * 3600;
|
||||||
else durationSec = 24 * 3600; // day
|
else durationSec = 24 * 3600; // day
|
||||||
|
|
||||||
const endOffsetSec = off * durationSec;
|
const endMs = nowMs - (off * durationSec * 1000);
|
||||||
const startOffsetSec = (off + 1) * durationSec;
|
const startMs = endMs - (durationSec * 1000);
|
||||||
|
|
||||||
const endMod = `-${endOffsetSec} seconds`;
|
startTsSec = Math.floor(startMs / 1000);
|
||||||
const startMod = `-${startOffsetSec} seconds`;
|
endTsSec = Math.floor(endMs / 1000);
|
||||||
|
}
|
||||||
|
|
||||||
const stmt = db.prepare(`
|
const stmt = db.prepare(`
|
||||||
SELECT timestamp, dev_name, port, state, level
|
SELECT timestamp, dev_name, port, state, level
|
||||||
FROM output_log
|
FROM output_log
|
||||||
WHERE timestamp >= datetime('now', ?)
|
WHERE timestamp >= datetime(?, 'unixepoch')
|
||||||
AND timestamp < datetime('now', ?)
|
AND timestamp < datetime(?, 'unixepoch')
|
||||||
ORDER BY timestamp ASC
|
ORDER BY timestamp ASC
|
||||||
`);
|
`);
|
||||||
|
|
||||||
const rows = stmt.all(startMod, endMod);
|
const rows = stmt.all(startTsSec, endTsSec);
|
||||||
|
|
||||||
// Compress: Group by "Dev:Port" -> [[ts, state, level], ...]
|
// Compress: Group by "Dev:Port" -> [[ts, state, level], ...]
|
||||||
const compressed = {};
|
const compressed = {};
|
||||||
|
|||||||
@@ -28,9 +28,19 @@ class Dashboard extends Component {
|
|||||||
const { range, offset } = this.state;
|
const { range, offset } = this.state;
|
||||||
|
|
||||||
const nowMs = Date.now();
|
const nowMs = Date.now();
|
||||||
|
let startMs, endMs;
|
||||||
|
|
||||||
|
if (range === 'today') {
|
||||||
|
const d = new Date(nowMs);
|
||||||
|
d.setHours(0, 0, 0, 0);
|
||||||
|
const midnightMs = d.getTime();
|
||||||
|
startMs = midnightMs - (offset * 24 * 3600 * 1000);
|
||||||
|
endMs = startMs + (24 * 3600 * 1000);
|
||||||
|
} else {
|
||||||
const durationSec = (range === 'week' ? 7 * 24 * 3600 : (range === 'month' ? 30 * 24 * 3600 : 24 * 3600));
|
const durationSec = (range === 'week' ? 7 * 24 * 3600 : (range === 'month' ? 30 * 24 * 3600 : 24 * 3600));
|
||||||
const endMs = nowMs - (offset * durationSec * 1000);
|
endMs = nowMs - (offset * durationSec * 1000);
|
||||||
const startMs = endMs - (durationSec * 1000);
|
startMs = endMs - (durationSec * 1000);
|
||||||
|
}
|
||||||
|
|
||||||
const dateOpts = { month: 'short', day: 'numeric', hour: '2-digit', minute: '2-digit' };
|
const dateOpts = { month: 'short', day: 'numeric', hour: '2-digit', minute: '2-digit' };
|
||||||
const dateRangeLabel = `${new Date(startMs).toLocaleString([], dateOpts)} - ${new Date(endMs).toLocaleString([], dateOpts)}`;
|
const dateRangeLabel = `${new Date(startMs).toLocaleString([], dateOpts)} - ${new Date(endMs).toLocaleString([], dateOpts)}`;
|
||||||
@@ -71,6 +81,12 @@ class Dashboard extends Component {
|
|||||||
>
|
>
|
||||||
{t('dashboard.hours24')}
|
{t('dashboard.hours24')}
|
||||||
</Button>
|
</Button>
|
||||||
|
<Button
|
||||||
|
onClick={() => this.setRange('today')}
|
||||||
|
color={range === 'today' ? 'primary' : 'inherit'}
|
||||||
|
>
|
||||||
|
1 Tag
|
||||||
|
</Button>
|
||||||
<Button
|
<Button
|
||||||
onClick={() => this.setRange('week')}
|
onClick={() => this.setRange('week')}
|
||||||
color={range === 'week' ? 'primary' : 'inherit'}
|
color={range === 'week' ? 'primary' : 'inherit'}
|
||||||
|
|||||||
@@ -80,11 +80,19 @@ class EnvChart extends Component {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const nowMs = Date.now();
|
const nowMs = Date.now();
|
||||||
const durationSec = (this.props.range === 'week' ? 7 * 24 * 3600 : (this.props.range === 'month' ? 30 * 24 * 3600 : 24 * 3600));
|
let startMs, endMs;
|
||||||
|
|
||||||
// Use offset to calculate exact window
|
if (this.props.range === 'today') {
|
||||||
const endMs = nowMs - ((this.props.offset || 0) * durationSec * 1000);
|
const d = new Date(nowMs);
|
||||||
const startMs = endMs - (durationSec * 1000);
|
d.setHours(0, 0, 0, 0);
|
||||||
|
const midnightMs = d.getTime();
|
||||||
|
startMs = midnightMs - ((this.props.offset || 0) * 24 * 3600 * 1000);
|
||||||
|
endMs = startMs + (24 * 3600 * 1000);
|
||||||
|
} else {
|
||||||
|
const durationSec = (this.props.range === 'week' ? 7 * 24 * 3600 : (this.props.range === 'month' ? 30 * 24 * 3600 : 24 * 3600));
|
||||||
|
endMs = nowMs - ((this.props.offset || 0) * durationSec * 1000);
|
||||||
|
startMs = endMs - (durationSec * 1000);
|
||||||
|
}
|
||||||
|
|
||||||
const options = {
|
const options = {
|
||||||
animation: false,
|
animation: false,
|
||||||
|
|||||||
@@ -70,11 +70,19 @@ class LevelChart extends Component {
|
|||||||
: { suggestedMin: 0, suggestedMax: 10, ticks: { stepSize: 1 } };
|
: { suggestedMin: 0, suggestedMax: 10, ticks: { stepSize: 1 } };
|
||||||
|
|
||||||
const nowMs = Date.now();
|
const nowMs = Date.now();
|
||||||
const durationSec = (this.props.range === 'week' ? 7 * 24 * 3600 : (this.props.range === 'month' ? 30 * 24 * 3600 : 24 * 3600));
|
let startMs, endMs;
|
||||||
|
|
||||||
// Use offset to calculate exact window
|
if (this.props.range === 'today') {
|
||||||
const endMs = nowMs - ((this.props.offset || 0) * durationSec * 1000);
|
const d = new Date(nowMs);
|
||||||
const startMs = endMs - (durationSec * 1000);
|
d.setHours(0, 0, 0, 0);
|
||||||
|
const midnightMs = d.getTime();
|
||||||
|
startMs = midnightMs - ((this.props.offset || 0) * 24 * 3600 * 1000);
|
||||||
|
endMs = startMs + (24 * 3600 * 1000);
|
||||||
|
} else {
|
||||||
|
const durationSec = (this.props.range === 'week' ? 7 * 24 * 3600 : (this.props.range === 'month' ? 30 * 24 * 3600 : 24 * 3600));
|
||||||
|
endMs = nowMs - ((this.props.offset || 0) * durationSec * 1000);
|
||||||
|
startMs = endMs - (durationSec * 1000);
|
||||||
|
}
|
||||||
|
|
||||||
const options = {
|
const options = {
|
||||||
animation: false,
|
animation: false,
|
||||||
|
|||||||
@@ -88,10 +88,19 @@ class OutputChart extends Component {
|
|||||||
const gruvboxColors = ['#fb4934', '#b8bb26', '#fabd2f', '#83a598', '#d3869b', '#8ec07c', '#fe8019', '#928374'];
|
const gruvboxColors = ['#fb4934', '#b8bb26', '#fabd2f', '#83a598', '#d3869b', '#8ec07c', '#fe8019', '#928374'];
|
||||||
|
|
||||||
const nowMs = Date.now();
|
const nowMs = Date.now();
|
||||||
const durationSec = (range === 'week' ? 7 * 24 * 3600 : (range === 'month' ? 30 * 24 * 3600 : 24 * 3600));
|
let startMs, endMs;
|
||||||
|
|
||||||
const endMs = nowMs - ((this.props.offset || 0) * durationSec * 1000);
|
if (range === 'today') {
|
||||||
const startMs = endMs - (durationSec * 1000);
|
const d = new Date(nowMs);
|
||||||
|
d.setHours(0, 0, 0, 0);
|
||||||
|
const midnightMs = d.getTime();
|
||||||
|
startMs = midnightMs - ((this.props.offset || 0) * 24 * 3600 * 1000);
|
||||||
|
endMs = startMs + (24 * 3600 * 1000);
|
||||||
|
} else {
|
||||||
|
const durationSec = (range === 'week' ? 7 * 24 * 3600 : (range === 'month' ? 30 * 24 * 3600 : 24 * 3600));
|
||||||
|
endMs = nowMs - ((this.props.offset || 0) * durationSec * 1000);
|
||||||
|
startMs = endMs - (durationSec * 1000);
|
||||||
|
}
|
||||||
|
|
||||||
// Generate datasets
|
// Generate datasets
|
||||||
const datasets = Object.keys(groupedData).map((key, index) => {
|
const datasets = Object.keys(groupedData).map((key, index) => {
|
||||||
|
|||||||
Reference in New Issue
Block a user