diff --git a/main.js b/main.js index 77edf689f7882aca8d391c8b83b871f56067cd4a..a9ba658cde9bb9b3be77b16331ae77dcdb94568d 100644 --- a/main.js +++ b/main.js @@ -10,6 +10,8 @@ class DoorInfo { state = null; opener = null; lastChange = null; + history = []; + historyEnabled = false; } const web_host = process.env.HTTP_BIND; @@ -44,6 +46,36 @@ let doors = { 'uwaes-door': new DoorInfo(), }; +doors['ucc-door'].historyEnabled = true; +doors['uwaes-door'].historyEnabled = false; +doors['unisfa-door'].historyEnabled = false; + +// Load history from file if it exists +const loadHistory = () => { + try { + const history = JSON.parse(fs.readFileSync(`${__dirname}/door_history.json`, 'utf8')); + for (const [door, info] of Object.entries(history)) { + if (!doors[door]) continue; + doors[door].history = info.history || []; + } + console.log('Loaded door history from file'); + } catch (err) { + console.log('No history file found, starting fresh'); + } +}; + +// Save history to file +const saveHistory = () => { + const history = {}; + for (const [door, info] of Object.entries(doors)) { + history[door] = { + history: info.history + }; + } + fs.writeFileSync(`${__dirname}/door_history.json`, JSON.stringify(history, null, 2)); + console.log('Saved door history to file'); +}; + const update_door_state = (door, state) => { if (doors[door] === undefined) { console.log(`Tried to update state for unknown door ${door}`); @@ -70,7 +102,23 @@ const update_door_state = (door, state) => { // Only update lastChange if the state actually changed if (oldState !== doors[door].state) { - doors[door].lastChange = new Date().toISOString(); + const timestamp = new Date().toISOString(); + doors[door].lastChange = timestamp; + + // Add to history + doors[door].history.unshift({ + timestamp, + state: doors[door].state, + opener: doors[door].opener + }); + + // Keep only last 1000 changes + if (doors[door].history.length > 1000) { + doors[door].history = doors[door].history.slice(0, 1000); + } + + // Save history to file + saveHistory(); } }; @@ -83,6 +131,12 @@ const update_door_opener = (door, opener) => { if (opener) { console.log(`Marked door ${door} as opened by ${opener}`); doors[door].opener = opener; + + if (doors[door].state && doors[door].history.length > 0) { + doors[door].history[0].timestamp = new Date().toISOString(); + doors[door].history[0].opener = opener; + saveHistory(); + } } }; @@ -130,6 +184,61 @@ const send_file = (res, code, fname, ctype) => { res.end(data); }; +const handleHistoryRequest = (req, res, door) => { + const n = parseInt(req.url.split('/').pop()); + if (isNaN(n) || n <= 0) { + return send_json(res, 400, { error: 'Invalid number of changes requested' }); + } + + const history = doors[door].history.slice(0, n); + return send_json(res, 200, history); +}; + +const handleLastChangeRequest = (res, door) => { + if (doors[door].lastChange === null) { + return send_json(res, 500, null); + } + + return send_json(res, 200, { + lastChange: doors[door].lastChange, + state: doors[door].state + }); +}; + +const handleStateRequest = (req, res) => { + const parts = req.url.split('/').filter(Boolean); + if (parts.length < 2) return send_error(res, 404); + + const door = parts[1]; + const doorId = `${door}-door`; + + if (!doors[doorId]) { + return send_error(res, 404); + } + + if (parts.length === 2) { + // handle /state/{door} + if (doors[doorId].state === null) { + return send_json(res, 500, null); + } + return send_json(res, 200, doors[doorId].state); + } + + if (parts[2] === 'lastchange') { + return handleLastChangeRequest(res, doorId); + } + + if (parts[2] === 'history') { + if (!doors[doorId].historyEnabled) { + return send_error(res, 404); + } + if (parts.length !== 4) return send_error(res, 404); + return handleHistoryRequest(req, res, doorId); + } + + return send_error(res, 404); +}; + // Serve state on http const server = http.createServer((req, res) => { console.log(`Serving request for ${req.url}`); @@ -169,28 +278,8 @@ const server = http.createServer((req, res) => { return send_file(res, 200, fname, 'text/html'); } - if (req.url === '/state/ucc') { - if (doors['ucc-door'].state === null) { - return send_json(res, 500, null); - } - - return send_json(res, 200, doors['ucc-door'].state); - } - - if (req.url === '/state/unisfa') { - if (doors['unisfa-door'].state === null) { - return send_json(res, 500, null); - } - - return send_json(res, 200, doors['unisfa-door'].state); - } - - if (req.url === '/state/uwaes') { - if (doors['uwaes-door'].state === null) { - return send_json(res, 500, null); - } - - return send_json(res, 200, doors['uwaes-door'].state); + if (req.url.startsWith('/state/')) { + return handleStateRequest(req, res); } if (req.url === '/opener/ucc') { @@ -233,42 +322,12 @@ const server = http.createServer((req, res) => { return send_file(res, 200, './static/darkmode.js', 'text/javascript'); } - if (req.url === '/state/ucc/lastchange') { - if (doors['ucc-door'].lastChange === null) { - return send_json(res, 500, null); - } - - return send_json(res, 200, { - lastChange: doors['ucc-door'].lastChange, - state: doors['ucc-door'].state - }); - } - - if (req.url === '/state/unisfa/lastchange') { - if (doors['unisfa-door'].lastChange === null) { - return send_json(res, 500, null); - } - - return send_json(res, 200, { - lastChange: doors['unisfa-door'].lastChange, - state: doors['unisfa-door'].state - }); - } - - if (req.url === '/state/uwaes/lastchange') { - if (doors['uwaes-door'].lastChange === null) { - return send_json(res, 500, null); - } - - return send_json(res, 200, { - lastChange: doors['uwaes-door'].lastChange, - state: doors['uwaes-door'].state - }); - } - return send_error(res, 404); }); +// Load history when server starts +loadHistory(); + // Run the server server.listen(web_port, web_host, () => { console.log(`HTTP server started on http://${web_host}:${web_port}`);