-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathrituals-auth.js
More file actions
117 lines (97 loc) · 4.67 KB
/
rituals-auth.js
File metadata and controls
117 lines (97 loc) · 4.67 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
module.exports = function(RED) {
const axios = require('axios');
const BASE_URL = 'https://rituals.apiv2.sense-company.com';
const AUTH_URL = `${BASE_URL}/apiv2/account/token`;
const HUBS_URL = `${BASE_URL}/apiv2/account/hubs`;
function RitualsAuthNode(config) {
RED.nodes.createNode(this, config);
const node = this;
node.on('input', async function(msg) {
try {
node.status({ fill: 'blue', shape: 'dot', text: 'authentificating...' });
// Get config node
const ritualsConfig = RED.nodes.getNode(config.config);
if (!ritualsConfig) {
throw new Error('Missing Rituals configuration');
}
// Authenticate and get token
node.log('Authentificating with Rituals API...');
const authResponse = await axios.post(AUTH_URL, {
email: ritualsConfig.email,
password: ritualsConfig.credentials.password
}, {
headers: {
'Accept': '*/*',
'Content-Type': 'application/json'
},
timeout: 10000
});
if (!authResponse.data || !authResponse.data.success) {
throw new Error(authResponse.data.message || 'Authentification failed');
}
const token = authResponse.data.success;
node.log('Authentification successful!');
// Get devices
node.status({ fill: 'blue', shape: 'dot', text: 'getting devices...' });
const devicesResponse = await axios.get(HUBS_URL, {
headers: {
'Accept': '*/*',
'Authorization': token
},
timeout: 10000
});
const devices = devicesResponse.data;
node.log(`Found ${devices.length} device(s)`);
// Calculate token expiry (24 hours from now)
const expiryTime = Date.now() + (24 * 60 * 60 * 1000);
// Store in context
node.context().global.set('ritualsToken', token);
node.context().global.set('ritualsTokenExpiry', expiryTime);
node.context().global.set('ritualsDevices', devices);
node.context().flow.set('ritualsToken', token);
node.context().flow.set('ritualsTokenExpiry', expiryTime);
node.context().flow.set('ritualsDevices', devices);
// Create simplified device list
const deviceList = devices.map(d => ({
hash: d.hash,
hublot: d.hublot,
name: d.attributeValues?.roomnamec || 'Unnamed',
room: d.attributeValues?.fspacenamec || '',
isOnline: d.status === 1,
isOn: d.attributeValues?.fanc === '1'
}));
// Output - store full token and devices in msg properties
msg.ritualsToken = token; // Full token here
msg.ritualsTokenExpiry = expiryTime; // Expiry timestamp
msg.ritualsDevices = devices; // Full device data here
const expiryDate = new Date(expiryTime);
msg.payload = {
authenticated: true,
tokenLength: token.length,
deviceCount: devices.length,
devices: deviceList,
expiresAt: expiryDate.toLocaleString(),
expiresIn: '24 hours',
storage: 'persistent (file)',
message: 'Token stored in persistent context - will survive Node-RED restarts'
};
node.status({ fill: 'green', shape: 'dot', text: `authenticated - ${devices.length} device(s)` });
node.send(msg);
// Clear status after 5 seconds
setTimeout(() => node.status({}), 5000);
} catch (error) {
node.status({ fill: 'red', shape: 'ring', text: error.message });
node.error('Authentification failed: ' + error.message, msg);
msg.payload = {
authenticated: false,
error: error.message
};
node.send(msg);
}
});
node.on('close', function() {
node.status({});
});
}
RED.nodes.registerType('rituals-auth', RitualsAuthNode);
};