@skylord123/node-red-pebble-timeline
Version:
Node-RED nodes for interacting with the Pebble Timeline API
176 lines (150 loc) • 6.53 kB
JavaScript
const axios = require('axios');
const fs = require('fs-extra');
const path = require('path');
module.exports = function(RED) {
function PebbleTimelineDeleteNode(config) {
RED.nodes.createNode(this, config);
const node = this;
// Get the config node
const configNode = RED.nodes.getNode(config.config);
if (!configNode) {
node.error("No Pebble Timeline configuration found");
return;
}
// Make sure storage directory exists
const storageDir = path.join(RED.settings.userDir, 'pebble-timeline');
fs.ensureDirSync(storageDir);
const pinsFile = path.join(storageDir, 'timeline-pins.json');
// Load existing pins (organized by token)
let pinsData = {};
try {
if (fs.existsSync(pinsFile)) {
pinsData = JSON.parse(fs.readFileSync(pinsFile, 'utf8'));
}
} catch (error) {
node.warn(`Error loading pins file: ${error.message}`);
}
node.on('input', function(msg, send, done) {
// Backwards compatibility with Node-RED 0.x
send = send || function() { node.send.apply(node, arguments) };
// Get the pin ID to delete
let pinId;
// Process parameters in sequence
Promise.all([
new Promise(resolve => {
RED.util.evaluateNodeProperty(config.pinId, config.pinIdType, node, msg, (err, result) => {
if (err) {
node.error(`Error evaluating pin ID: ${err.message}`, msg);
if (done) done(err);
return;
}
pinId = result;
if (!pinId) {
node.error("Pin ID is required", msg);
if (done) done("Pin ID is required");
return;
}
resolve();
});
}),
// Check for server override options
new Promise(resolve => {
if (config.apiUrl) {
RED.util.evaluateNodeProperty(config.apiUrl, config.apiUrlType, node, msg, (err, result) => {
if (!err && result) {
node.apiUrlOverride = result;
}
resolve();
});
} else {
resolve();
}
}),
new Promise(resolve => {
if (config.token) {
RED.util.evaluateNodeProperty(config.token, config.tokenType, node, msg, (err, result) => {
if (!err && result) {
node.tokenOverride = result;
}
resolve();
});
} else {
resolve();
}
})
]).then(() => {
// Use overrides if provided, otherwise use config node values
const apiUrl = `${node.apiUrlOverride || configNode.apiUrl}/v1/user/pins/${pinId}`;
const timelineToken = node.tokenOverride || configNode.credentials.timelineToken;
if (!timelineToken) {
node.error("Timeline token is required", msg);
if (done) done("Timeline token is required");
return;
}
axios.delete(apiUrl, {
headers: {
'X-User-Token': timelineToken
}
})
.then(response => {
node.status({fill: "green", shape: "dot", text: "Pin deleted"});
// Remove the pin from our local storage
removePin(pinId, timelineToken);
// Prepare the output message
msg.payload = {
success: true,
pinId: pinId,
response: response.data
};
send(msg);
if (done) done();
})
.catch(error => {
node.status({fill: "red", shape: "dot", text: "Error: " + (error.response ? error.response.status : error.message)});
msg.payload = {
success: false,
pinId: pinId,
error: error.message,
response: error.response ? error.response.data : null
};
send(msg);
if (done) done(error);
});
}).catch(err => {
if (done) done(err);
});
});
// Helper to remove a pin from local storage
function removePin(pinId, timelineToken) {
// Ensure we have a valid token
if (!timelineToken) {
node.warn("Cannot remove pin: No valid timeline token provided");
return;
}
// Convert token to string to ensure it can be used as an object key
timelineToken = String(timelineToken);
// Check if this token has any pins
if (!pinsData[timelineToken]) {
return; // No pins for this token
}
// Remove the pin with the specified ID from this token's pins
const initialCount = pinsData[timelineToken].length;
pinsData[timelineToken] = pinsData[timelineToken].filter(p => p.id !== pinId);
// Note: Cleanup of old pins is handled in the add node
// Only write if we actually removed something
if (pinsData[timelineToken].length !== initialCount) {
try {
fs.writeFileSync(pinsFile, JSON.stringify(pinsData, null, 2));
} catch (error) {
node.warn(`Error saving pins to file: ${error.message}`);
}
}
}
node.on('close', function() {
// Clean up any resources
});
}
RED.nodes.registerType("pebble-timeline-delete", PebbleTimelineDeleteNode, {
credentials: {}
});
};