Yes that's what I've edited the last time, but like I said it returned me the error
[node-red-contrib-surepet/surepet] TypeError: Cannot convert undefined or null to object (line:55)
So I tried to add something to handle an undefined or null object in the async function like this
async function SurePetCredentials(n){
RED.nodes.createNode(this, n);
this.username = n.username || this.credentials.username;
this.password = n.password || this.credentials.password;
this.client = new SurePet.SurePetCareClient();
if (this.username && this.password){
this.log("We have a username and password, attempting to authenticiate.")
try{
await this.client.authenticate(this.username, this.password);
this.status({fill:"green",shape:"dot",text:"Connected."});
} catch (err){
this.status({fill:"red",shape:"ring",text:err});
}
if (typeof this.client.authenticate() !== 'undefined' && this.client.authenticate() !== null) {
}
else {
this.log('âď¸ Object null ');}
}
}
Then i get
[error] Error: Authentication failed: received 422 Unprocessable Entity
During the Nodered restart
Sorry, I was trying to guide you to an answer
I will be more specific. This code will never work....
Delete it and try again.
Also, could you please try the promise version I wrote here....
Steve-Mcl:
try changing this function...
function SurePetCredentials(n){
RED.nodes.createNode(this, n);
this.username = n.username || this.credentials.username;
this.password = n.password || this.credentials.password;
this.client = new SurePet.SurePetCareClient();
const node = this
if (this.username && this.password){
this.log("We have a username and password, attempting to authenticate.")
this.client.authenticate(this.username, this.password)
.then(() => node.status({fill:"green",shape:"dot",text:"Connected.")
.catch(err => node.status({fill:"red",shape:"ring",text:err}))
}
}
Exactly as written.
So after testing the code I modify the node status for .then() to be accurate, like this :
function SurePetCredentials(n){
RED.nodes.createNode(this, n);
this.username = n.username || this.credentials.username;
this.password = n.password || this.credentials.password;
this.client = new SurePet.SurePetCareClient();
const node = this
if (this.username && this.password){
this.log("We have a username and password, attempting to authenticate.")
this.client.authenticate(this.username, this.password)
.then(() => node.status({fill:"green",shape:"dot",text:"Connected."}))
.catch(err => node.status({fill:"red",shape:"ring",text:err}))
}
}
Nodered is deployed well, flows are started, I get informations from the API as usual. Now I have to make it run for a while and see if I still get crash from timeout errors.
Again @Steve-Mcl a big thank you for your help and your time on this subject, it means a lot to me.
Hi, so after two days of running, unfortunately I still get the exact same crashes from time to time
[red] Uncaught Exception:
[error] Error: Get state failed: received 504 Gateway Time-out
at SurePetCareClient.getState (/home/klose54/.node-red/node_modules/sure-pet-care-client/dist/client.js:41:31)
at runMicrotasks (<anonymous>)
at processTicksAndRejections (node:internal/process/task_queues:96:5)
So if you look in the file surepet.js
you should find a call to .getState()
and it probably doesnt have a catch block or is not handling the timeout correctly.
Can you paste exactly the full content of your surepet.js
as it is right now.
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.SurePetCareClient = void 0;
const node_fetch_1 = __importDefault(require("node-fetch"));
class SurePetCareClient {
constructor() {
this.token = '';
this.baseUrl = 'https://app.api.surehub.io';
this.authenticate = async (email, password) => {
const resp = await node_fetch_1.default(`${this.baseUrl}/api/auth/login`, {
method: 'POST',
body: JSON.stringify({ email_address: email, password, device_id: '.' }),
headers: { 'Content-Type': 'application/json' },
});
if (!resp.ok) {
switch (resp.status) {
case 401:
throw new Error('Authentication failed: invalid credentials');
default:
throw new Error(`Authentication failed: received ${resp.status} ${resp.statusText}`);
}
}
const token = resp.headers.get('Authorization');
if (token === null) {
throw new Error('Authentication failed: no authorization token in response');
}
this.token = token;
};
this.getState = async () => {
const resp = await node_fetch_1.default(`${this.baseUrl}/api/me/start`, {
headers: { 'Content-Type': 'application/json', 'Authorization': this.token },
});
if (!resp.ok) {
switch (resp.status) {
case 401:
throw new Error('Get state failed: unauthenticated');
default:
throw new Error(`Get state failed: received ${resp.status} ${resp.statusText}`);
}
}
const { data } = await resp.json();
return data;
};
this.getPets = async () => {
const state = await this.getState();
return state.pets;
};
this.getPetByName = async (name) => {
const pets = await this.getPets();
const pet = pets.find((p) => p.name.toLowerCase() === name.toLowerCase());
if (!pet) {
throw new Error('Pet not found');
}
return pet;
};
this.getPetById = async (id) => {
const pets = await this.getPets();
const pet = pets.find((p) => p.id === id);
if (!pet) {
throw new Error('Pet not found');
}
return pet;
};
this.setPetLocation = async (id, location, since = new Date()) => {
const date = since.toISOString();
const resp = await node_fetch_1.default(`${this.baseUrl}/api/pet/${id}/position`, {
method: 'POST',
body: JSON.stringify({ since: date, where: location }),
headers: { 'Content-Type': 'application/json', 'Authorization': this.token },
});
if (!resp.ok) {
switch (resp.status) {
case 401:
throw new Error('Set pet location failed failed: invalid credentials');
default:
throw new Error(`Set pet location failed failed: received ${resp.status} ${resp.statusText}`);
}
}
};
}
}
exports.SurePetCareClient = SurePetCareClient;
that is not the right file. I want to see where getState
is called by the contrib node.
It will be file node_modules/node-red-contrib-surepet/surepet.js
The one in node_modules/node-red-contrib-surepet/surepet.js
is the exact same one as /.node-red/node_modules/node-red-contrib-surepet/surepet.js
Then something is very wrong.
It should look like a node-red module. i.e. the first line will read module.exports = function(RED){
(what you pasted is a class file)
It should look like this: node-red-contrib-surepet/surepet.js at 548e3d50560bb9af4b4a38195c72e43495da02d0 ¡ jpwsutton/node-red-contrib-surepet ¡ GitHub
So just to be clear, the file at node_modules/node-red-contrib-surepet/surepet.js
is the exact same one as /.node-red/node_modules/node-red-contrib-surepet/surepet.js
Here is the following :
module.exports = function(RED){
"use strict"
var SurePet = require('sure-pet-care-client');
function SurePetNode(config){
RED.nodes.createNode(this, config);
this.surepet = RED.nodes.getNode(config.surepet);
var node = this;
//console.log(config);
node.on('input', function(msg){
if(config.mode === 'list_pets'){
this.surepet.client.getPets().then((pets) =>{
msg.payload = {
pets: pets
}
node.send(msg)
})
} else if(config.mode === "get_status"){
this.surepet.client.getState().then((state) => {
msg.payload = state;
node.send(msg);
})
}
/*
msg.payload = {
surepet: this.surepet,
pets:
}
node.send(msg);*/
});
}
RED.nodes.registerType("surepet", SurePetNode);
function SurePetCredentials(n){
RED.nodes.createNode(this, n);
this.username = n.username || this.credentials.username;
this.password = n.password || this.credentials.password;
this.client = new SurePet.SurePetCareClient();
const node = this
if (this.username && this.password){
this.log("We have a username and password, attempting to authenticiate.")
this.client.authenticate(this.username, this.password)
.then(() => node.status({fill:"green",shape:"dot",text:"ConnectĂŠ."}))
.catch(err => node.status({fill:"red",shape:"ring",text:err}))
}
}
RED.nodes.registerType("surepet-credentials",SurePetCredentials,{
credentials: {
username: {type:"text"},
password: {type:"password"}
}
});
}
Colin
9 April 2023 20:19
32
There should not be one at ~/node_modules/node-red-contrib-surepet/surepet.js. If there is that probably means that you manually installed the node from your home folder instead of from ~/.node-red. You can safely ignore the one in ~/node_modules. The one in .node-red/node_modules is the one you need to adjust.
Thanks for the explanation, I was wondering why I only get this one at two differents folders, I will delete the useless one.
So for now the file at /.node-red/node_modules/node-red-contrib-surepet/surepet.js
is the one I paste on my previous post.
@klose54
Try replacing that files content with the code below
Changes:
Ensure all promise calls have a .catch
block & that node.error()
is called upon error (so a use can catch errors with the catch node
Add "connect" msg support. This way, you can send a msg with a topic of "connect" to change (or reconnect) at runtime (pass the username
and password
in the msg
or msg.payload
module.exports = function (RED) {
"use strict"
const SurePet = require('sure-pet-care-client');
function SurePetNode(config) {
RED.nodes.createNode(this, config);
const node = this;
node.surepet = RED.nodes.getNode(config.surepet);
node.on('input', function (msg) {
// special case, if `topic` === connect, then re-authenticate
if (msg.topic === "connect") {
node.surepet.client = new SurePet.SurePetCareClient();
node.surepet.client.authenticate(msg.username || msg.payload?.username, msg.password || msg.payload?.password)
.then(() => node.status({ fill: "green", shape: "dot", text: "Connected." }))
.catch(err => {
node.error(err, msg)
node.status({ fill: "red", shape: "ring", text: err })
})
return
}
// normal operation
if (config.mode === 'list_pets') {
node.surepet.client.getPets().then((pets) => {
msg.payload = {
pets: pets
}
node.send(msg)
}).catch((err) => {
node.error(err, msg);
})
} else if (config.mode === "get_status") {
node.surepet.client.getState().then((state) => {
msg.payload = state;
node.send(msg);
}).catch((err) => {
node.error(err, msg);
})
}
});
}
RED.nodes.registerType("surepet", SurePetNode);
function SurePetCredentials(n) {
RED.nodes.createNode(this, n);
const node = this
node.username = n.username || node.credentials.username;
node.password = n.password || node.credentials.password;
node.client = new SurePet.SurePetCareClient();
if (node.username && node.password) {
node.log("We have a username and password, attempting to authenticate.")
node.client.authenticate(node.username, node.password)
.then(() => node.status({ fill: "green", shape: "dot", text: "Connected." }))
.catch(err => {
node.status({ fill: "red", shape: "ring", text: err })
node.error(err)
})
}
}
RED.nodes.registerType("surepet-credentials", SurePetCredentials, {
credentials: {
username: { type: "text" },
password: { type: "password" }
}
});
}
@Steve-Mcl Your edits deployed well, API is answering. Let's see how things goes for some time. Thank you.
@Steve-Mcl
I think the problem is now solved thank to your last edit. This afternoon I did not have a single reboot of nodered but I get some error msg in when there was server time out.
Again, thanks a lot for your help.
I will update the github with your code for other users after some more days of observation but things are getting really well.
1 Like
system
Closed
24 April 2023 17:29
37
This topic was automatically closed 14 days after the last reply. New replies are no longer allowed.