@gravypower/node-red-franklinwh
Version:
Node-RED node to control FranklinWH gateway
102 lines (87 loc) • 3.94 kB
JavaScript
const franklinwh = require('franklinwh');
module.exports = function(RED) {
function FranklinWHConfigNode(config) {
RED.nodes.createNode(this, config);
const node = this;
// Unified properties for config fields
this.host = config.host || "";
this.serialNumber = config.serialNumber || "";
this.username = this.credentials?.username;
this.password = this.credentials?.password;
// migrate/compatibility: keep gateway as either host or serialNumber (old flows may expect gateway)
this.gateway = this.serialNumber || config.gateway || this.host;
// Debug logging to aid diagnosing node registration
node.debug(`FranklinWHConfigNode initialized. host: ${this.host}, serialNumber: ${this.serialNumber}, username: ${this.username ? '[set]' : '[not set]'}, password: ${this.password ? '[set]' : '[not set]'}`);
// Check if we have all required fields
if (!this.host || !this.serialNumber || !this.username || !this.password) {
node.error("Missing configuration: host, serialNumber, username and password are all required.");
return;
}
this.api = null;
this.connecting = null; // Track ongoing connection attempts
this.lastConnectionError = null;
// Initialize connection with proper Promise handling
const connect = async (force = false) => {
if (this.api && !force) {
return this.api;
}
// If there's already a connection attempt in progress, wait for it
if (this.connecting) {
return this.connecting;
}
// Start new connection attempt
this.connecting = (async () => {
try {
node.debug("Initiating connection to FranklinWH gateway...");
// franklinwh(username, password, gateway, base)
this.api = await franklinwh(
this.username,
this.password,
this.serialNumber || this.gateway,
this.host
);
node.log("Successfully connected to FranklinWH gateway");
this.lastConnectionError = null;
return this.api;
} catch (error) {
this.api = null;
this.lastConnectionError = error;
node.error("Connection failed: " + error.message);
throw error;
} finally {
this.connecting = null;
}
})();
return this.connecting;
};
// Initial connection attempt
connect().catch(() => {
// Errors are already logged in connect()
node.status({fill: "red", shape: "ring", text: "Connection failed"});
});
this.getApi = async function(needsNewAuth = false) {
try {
// If we need new auth or last attempt failed, force new connection
const forceNewConnection = needsNewAuth || this.lastConnectionError;
return await connect(forceNewConnection);
} catch (error) {
const errorMsg = needsNewAuth ?
"Failed to refresh connection: " :
"Failed to connect: ";
throw new Error(errorMsg + error.message);
}
};
node.on('close', function(done) {
node.api = null;
node.connecting = null;
node.lastConnectionError = null;
done();
});
}
RED.nodes.registerType("franklinwh-config", FranklinWHConfigNode, {
credentials: {
username: {type: "text", required: true},
password: {type: "password", required: true}
}
});
}