@flecoufle/node-red-linky 1.3.0 → 1.4.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,3 +1,12 @@
1
+ ## 1.4.2 - 20260125
2
+ github action
3
+
4
+ ## 1.4.1 - 20260125
5
+ add publishConfig registry
6
+
7
+ ## 1.4.0 - 20260124
8
+ add options_api_daily_limit
9
+
1
10
  ## 1.3.0 - 20250321
2
11
  add debug option
3
12
 
package/README.md CHANGED
@@ -1,44 +1,122 @@
1
1
  # node-red-linky
2
- Retreive linky power consumption and production from <a href="https://github.com/bokub/conso-api#readme">bokub/conso-api</a>
3
2
 
4
- See Enedis API documentation on <a href="https://datahub-enedis.fr/services-api/data-connect/documentation/">data-connect</a>
3
+ Package Node-RED **Linky** pour récupérer les données de **consommation** et **production** d'énergie via l'API Enedis.
5
4
 
6
- ## consumption usage
7
- - <b>Récupération de la consommation énergétique quotidienne</b>: <code>daily_consumption</code>
5
+ ## Installation
8
6
 
9
- Permet de récupérer la consommation énergétique quotidienne de la période demandée [start;end[ en Wh pour le point de livraison renseigné et la personne titulaire authentifiée, ou dont l’indentifiant est donné en paramètre.
7
+ Via la palette l'éditeur: @flecoufle/node-red-linky
10
8
 
11
- Chaque valeur est datée.
12
- Un appel peut porter sur des données datant au maximum de 36 mois et 15 jours avant la date d’appel.
9
+ ou via npm:
10
+ ```bash
11
+ npm install @flecoufle/node-red-linky
12
+ ```
13
13
 
14
- - <b>Récupération de la courbe de charge en soutirage</b>: <code>consumption_load_curve</code>
14
+ ## Prérequis
15
15
 
16
- Permet de récupérer la courbe de charge en soutirage sur l’intervalle [start;end[ pour le point de livraison renseigné et la personne titulaire authentifiée, ou dont l’indentifiant est donné en paramètre.
16
+ - **Node.js**: >= 14.0.0
17
+ - **Node-RED**: >= 0.17.0
18
+ - **Dépendances**:
19
+ - `got` 13.0.0
17
20
 
18
- La profondeur de données disponible en un appel est d’une semaine.
19
- Les valeurs retournées sont des puissances moyennes de consommation en W sur l’intervalle de mesure du compteur (par défaut 30mn).
20
- Chaque valeur est horodatée.
21
- La courbe de charge s’obtient sur des journées complètes de minuit à minuit du jour suivant en heures locales.
22
- Un appel peut porter sur des données datant au maximum de 24 mois et 15 jours avant la date d’appel.
21
+ ## Mise en oeuvre
22
+ - Obtenir un accès API (le token) à votre compteur: [API Conso](https://github.com/bokub/conso-api#readme) vous redirigera vers [https://conso.boris.sh](https://conso.boris.sh/)
23
+ - [Exemples](examples/Examples.json) d'utilisation du noeud node-red-linky une fois le noeud installé
24
+ - [Documentation](https://datahub-enedis.fr/services-api/data-connect/documentation/) de Enedis API Data-Connect (pour information uniquement)
25
+
26
+ ## ⚠️ Important - Mise en cache des appels
23
27
 
24
- - <b>Récupération de la puissance maximale soutirée quotidienne</b>: <code>consumption_max_power</code>
28
+ À partir de la version **1.4.0**, le node limite les appels pour respecter les quotas de l'API et éviter le bannissement de votre IP, une mise en cache est mise en place pour permettre la continuité de service. Cette fonction est appliquée par requête unique (l'API sous-jacente elle-même renvoie déjà des données qui ne changent pas, donc cette fonction ne modifie en rien les informations retournées).
25
29
 
26
- Permet de récupérer la puissance maximale soutirée quotidienne de la période demandée [start;end[, en VA pour le point de livraison renseigné et la personne titulaire authentifiée, ou dont l’indentifiant est donné en paramètre.
30
+ Pour les utilisateurs des versions antérieures: **veuillez mettre à jour** pour bénéficier de cette protection.
27
31
 
28
- Chaque valeur est datée.
29
- Un appel peut porter sur des données datant au maximum de 36 mois et 15 jours avant la date d’appel.
32
+ ## Configuration et Paramètres
30
33
 
31
- ## production usage
32
- - <b>Récupération de la production énergétique quotidienne</b>: <code>daily_production</code>
34
+ ### Paramètres obligatoires (peuvent être passés via le payload)
33
35
 
34
- Permet de récupérer la production énergétique quotidienne de la période demandée pour le point de livraison renseigné et la personne titulaire authentifiée, ou dont l’indentifiant est donné en paramètre sur la période demandée [start;end[ en Wh. Un appel peut porter sur des données datant au maximum de 36 mois et 15 jours avant la date d’appel.
36
+ | Paramètre | Type | Description |
37
+ |-----------|------|-------------|
38
+ | `type` | string | Type de mesure (daily_consumption, daily_production, consumption_load_curve, consumption_max_power, production_load_curve) |
39
+ | `token` | string | Token d'authentification Enedis (via le payload ou noeud ou config) |
40
+ | `prm` | string | Point de livraison (numéro PRM) (via le payload ou noeud ou config)|
41
+ | `start` | string | Date de début (format YYYY-MM-DD) |
42
+ | `end` | string | Date de fin (format YYYY-MM-DD) |
35
43
 
36
- - <b>Récupération de la courbe de charge de production</b>: <code>production_load_curve</code>
44
+ ### Paramètres optionnels (uniquement via le payload)
37
45
 
38
- Permet de récupérer la courbe de charge de production sur l’intervalle [start;end[ pour le point de livraison renseigné et la personne titulaire authentifiée, ou dont l’indentifiant est donné en paramètre.
46
+ | Paramètre | Type | Défaut | Description |
47
+ |-----------|------|--------|-------------|
48
+ | `options_api_daily_limit` | number | 3 | Limite d'appels par jour pour une requête identique |
49
+ | `options_retry_limit` | number | 2 | Nombre de tentatives en cas d'erreur |
50
+ | `options_timeout_lookup` | number | 500 | Timeout lookup DNS (ms) |
51
+ | `options_timeout_connect` | number | 500 | Timeout connexion (ms) |
52
+ | `options_timeout_secureconnect` | number | 500 | Timeout connexion sécurisée (ms) |
53
+ | `options_timeout_socket` | number | 5000 | Timeout socket (ms) |
54
+ | `options_timeout_send` | number | 10000 | Timeout envoi (ms) |
55
+ | `options_timeout_response` | number | 10000 | Timeout réponse (ms) |
56
+ | `random_delay` | number | 5000 | Délai aléatoire avant exécution (ms) |
57
+ | `endpoint` | string | https://conso.boris.sh/api/ | Endpoint API à utiliser |
58
+ | `debug` | boolean | false | Afficher les détails de débogage dans les logs |
39
59
 
40
- La profondeur de données disponible en un appel est d’une semaine.
41
- Les valeurs retournées sont des puissances moyennes de production en W sur l’intervalle de mesure du compteur (par défaut 30mn).
42
- Chaque valeur est horodatée.
43
- La courbe de charge s’obtient sur des journées complètes de minuit à minuit du jour suivant en heures locales.
44
- Un appel peut porter sur des données datant au maximum de 24 mois et 15 jours avant la date d’appel.
60
+ ## Types d'API disponibles
61
+
62
+ ### Consommation
63
+
64
+ - **`daily_consumption`** - Récupération de la consommation énergétique quotidienne
65
+ - Période demandée [start;end[ en Wh
66
+ - Historique: jusqu'à 36 mois et 15 jours avant la date d'appel
67
+ - Chaque valeur est datée
68
+
69
+ - **`consumption_load_curve`** - Récupération de la courbe de charge en soutirage
70
+ - Intervalle [start;end[
71
+ - Profondeur: une semaine maximum par appel
72
+ - Valeurs: puissances moyennes de consommation en W (intervalle par défaut 30mn)
73
+ - Historique: jusqu'à 24 mois et 15 jours avant la date d'appel
74
+ - Journées complètes de minuit à minuit (heures locales)
75
+
76
+ - **`consumption_max_power`** - Récupération de la puissance maximale soutirée quotidienne
77
+ - Période demandée [start;end[ en VA
78
+ - Historique: jusqu'à 36 mois et 15 jours avant la date d'appel
79
+ - Chaque valeur est datée
80
+
81
+ ### Production
82
+
83
+ - **`daily_production`** - Récupération de la production énergétique quotidienne
84
+ - Période demandée [start;end[ en Wh
85
+ - Historique: jusqu'à 36 mois et 15 jours avant la date d'appel
86
+ - Chaque valeur est datée
87
+
88
+ - **`production_load_curve`** - Récupération de la courbe de charge de production
89
+ - Intervalle [start;end[
90
+ - Profondeur: une semaine maximum par appel
91
+ - Valeurs: puissances moyennes de production en W (intervalle par défaut 30mn)
92
+ - Historique: jusqu'à 24 mois et 15 jours avant la date d'appel
93
+ - Journées complètes de minuit à minuit (heures locales)
94
+
95
+ ## Limitation quotidienne des appels et système de cache
96
+
97
+ Un système de limitation quotidienne est mis en place pour éviter les appels API excessifs pour une même requête identique. **Les réponses sont mises en cache**, permettant de servir les données en cache quand la limite est atteinte.
98
+ Cette fonctionnalité permet d'éviter le blocage d'IP, voir "Conseils d'utilisation" sur [Conso API](https://github.com/bokub/conso-api?tab=readme-ov-file#conseils-dutilisation)
99
+
100
+ ### Fonctionnement de la limitation
101
+
102
+ - **Limite par défaut**: 3 appels par jour pour une requête identique
103
+ - **Configuration**: La limite peut être ajustée via le paramètre `options_api_daily_limit` (en cas de besoin spécifique peut être modifiée, attention au risque de bannissement)
104
+ - **Signature de requête**: Chaque requête est identifiée par une signature unique (hash SHA256) et **mis en cache** (cache de votre instance Node-RED dans une variable de contexte du noeud, donc reste chez vous) basée sur:
105
+ - Le jour de l'appel
106
+ - Le type d'API (`type`)
107
+ - Le point de livraison (`prm`)
108
+ - Les dates de début et fin (`start`, `end`)
109
+ - L'endpoint API (`endpoint`)
110
+ - Les configurations de timeout
111
+ - La limite de retry
112
+
113
+ Donc tout sauf `debug`
114
+
115
+ - **Réinitialisation automatique**: Le compteur se réinitialise automatiquement chaque jour à minuit
116
+
117
+ ### Comportement avec cache
118
+
119
+ - **Appel réussi**: La réponse est automatiquement mise en cache avec la signature de la requête
120
+ - **Limite atteinte**:
121
+ - Une réponse en cache est retournée (statut du noeud: **jaune**)
122
+ - Si aucune réponse n'a pu être mise en cache (statut du noeud: **rouge**) cas de l'API en erreurs successives "_Daily limit of X calls for this request reached. No cache available._" sera affiché dans le log
package/linky/linky.html CHANGED
@@ -108,17 +108,19 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
108
108
  "prm": PRM set in payload or in node property
109
109
  "start": start date in RFC 3339 format ("YYYY-MM-DD")
110
110
  "end": end date in RFC 3339 format ("YYYY-MM-DD")
111
- "options_retry_limit": got option in ms(default 2)
111
+ "options_api_daily_limit": daily limit of calls for identical request (default 3)
112
+ "options_retry_limit": got option (default 2)
112
113
  "options_timeout_lookup": got option in ms(default 500)
113
114
  "options_timeout_connect": got option in ms(default 500)
114
115
  "options_timeout_secureconnect": got option in ms(default 500)
115
116
  "options_timeout_socket": got option in ms(default 5000)
116
- "options_timeout_send": got option in ms(default 1000)
117
- "options_timeout_response": got option in ms(default 1000)
117
+ "options_timeout_send": got option in ms(default 10000)
118
+ "options_timeout_response": got option in ms(default 10000)
118
119
  "random_delay": fetch after a random delay in ms (default 5000)
119
120
  "endpoint": endpoint api (default "https://conso.boris.sh/api/")
120
121
  "debug": debug mode (default false)
121
122
  </code></pre></p>
123
+ <p><strong>🔄 Système de Cache:</strong> Les réponses réussies sont automatiquement mises en cache. Si la limite quotidienne est atteinte pour une requête identique, la réponse en cache est retournée (statut jaune) au lieu d'une erreur.</p>
122
124
  Available types are: <code>"daily_consumption"</code>, <code>"consumption_load_curve"</code>, <code>"consumption_max_power"</code> in consumption<br/>
123
125
  <code>"daily_production"</code>, <code>"production_load_curve"</code> in production
124
126
  </dl>
package/linky/linky.js CHANGED
@@ -23,14 +23,90 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
23
23
 
24
24
  module.exports = async function (RED) {
25
25
  const { got } = await import('got');
26
+ const crypto = await import('crypto');
26
27
 
27
28
  const nodeStatus = {
28
29
  WAIT: "Waiting...",
29
- FETCH: "Fetching...",
30
- OK: "OK",
31
- ERROR: "Error"
30
+ FETCH: "Fetching..."
32
31
  };
33
32
 
33
+ // Fonction pour obtenir la clé du jour (YYYY-MM-DD)
34
+ function getTodayKey() {
35
+ return new Date().toISOString().split('T')[0];
36
+ }
37
+
38
+ // Fonction pour créer une clé unique basée sur les paramètres de la requête
39
+ function createRequestSignature(params, options) {
40
+ // Créer un objet avec les paramètres pertinents pour la requête
41
+ const requestIdentifier = {
42
+ day: getTodayKey(), // Inclure le jour pour avoir une signature par jour
43
+ type: params.type,
44
+ prm: params.prm,
45
+ start: params.start,
46
+ end: params.end,
47
+ endpoint: params.endpoint,
48
+ // Inclure les timeouts pour distinguer les requêtes avec configs différentes
49
+ timeout: {
50
+ lookup: params.options_timeout_lookup,
51
+ connect: params.options_timeout_connect,
52
+ secureConnect: params.options_timeout_secureconnect,
53
+ socket: params.options_timeout_socket,
54
+ send: params.options_timeout_send,
55
+ response: params.options_timeout_response
56
+ },
57
+ retry_limit: params.options_retry_limit
58
+ };
59
+
60
+ // Créer un hash SHA256 pour avoir une clé courte et unique
61
+ const hash = crypto.createHash('sha256');
62
+ hash.update(JSON.stringify(requestIdentifier));
63
+ return hash.digest('hex').substring(0, 16); // Utiliser les 16 premiers caractères
64
+ }
65
+
66
+ // Fonction pour obtenir/réinitialiser le compteur si nécessaire
67
+ function getOrResetRequestCounter(node, requestSignature) {
68
+ const today = getTodayKey();
69
+ let requestHistory = node.context().get('requestHistory') || {};
70
+
71
+ if (!requestHistory[requestSignature]) {
72
+ requestHistory[requestSignature] = {
73
+ date: today,
74
+ count: 0
75
+ };
76
+ } else if (requestHistory[requestSignature].date !== today) {
77
+ // Nouveau jour, réinitialiser
78
+ requestHistory[requestSignature] = {
79
+ date: today,
80
+ count: 0
81
+ };
82
+ }
83
+
84
+ node.context().set('requestHistory', requestHistory);
85
+ return requestHistory[requestSignature];
86
+ }
87
+
88
+ // Fonction pour incrémenter et retourner le nouveau count pour une requête spécifique
89
+ function incrementAndGetRequestCount(node, requestSignature) {
90
+ let requestCounter = getOrResetRequestCounter(node, requestSignature);
91
+ requestCounter.count++;
92
+ let requestHistory = node.context().get('requestHistory');
93
+ requestHistory[requestSignature] = requestCounter;
94
+ node.context().set('requestHistory', requestHistory);
95
+ return requestCounter.count;
96
+ }
97
+
98
+ // Fonction pour obtenir les infos du compteur avec détails
99
+ function getRequestCounterInfo(node, requestSignature, dailyLimit) {
100
+ const requestCounter = getOrResetRequestCounter(node, requestSignature);
101
+ return {
102
+ count: requestCounter.count,
103
+ date: requestCounter.date,
104
+ signature: requestSignature,
105
+ remaining: Math.max(0, dailyLimit - requestCounter.count),
106
+ isLimited: requestCounter.count >= dailyLimit
107
+ };
108
+ }
109
+
34
110
  function LinkyMetering(config) {
35
111
  RED.nodes.createNode(this, config);
36
112
  var node = this;
@@ -45,16 +121,17 @@ module.exports = async function (RED) {
45
121
  "prm": msg.payload?.prm || node.credentials?.prm || node.config?.prm || '',
46
122
  "start": msg.payload?.start || '',
47
123
  "end": msg.payload?.end || '',
48
- "options_retry_limit": msg.payload?.options_retry_limit || 2,
49
- "options_timeout_lookup": msg.payload?.options_timeout_lookup || 500,
50
- "options_timeout_connect": msg.payload?.options_timeout_connect || 500,
51
- "options_timeout_secureconnect": msg.payload?.options_timeout_secureconnect || 500,
52
- "options_timeout_socket": msg.payload?.options_timeout_socket || 5000,
53
- "options_timeout_send": msg.payload?.options_timeout_send || 10000,
54
- "options_timeout_response": msg.payload?.options_timeout_response || 10000,
55
- "random_delay": msg.payload?.random_delay || 5000,
124
+ "options_api_daily_limit": msg.payload?.options_api_daily_limit || 3,
125
+ "options_retry_limit": msg.payload?.options_retry_limit ?? 2,
126
+ "options_timeout_lookup": msg.payload?.options_timeout_lookup ?? 500,
127
+ "options_timeout_connect": msg.payload?.options_timeout_connect ?? 500,
128
+ "options_timeout_secureconnect": msg.payload?.options_timeout_secureconnect ?? 500,
129
+ "options_timeout_socket": msg.payload?.options_timeout_socket ?? 5000,
130
+ "options_timeout_send": msg.payload?.options_timeout_send ?? 10000,
131
+ "options_timeout_response": msg.payload?.options_timeout_response ?? 10000,
132
+ "random_delay": msg.payload?.random_delay ?? 5000,
56
133
  "endpoint": msg.payload?.endpoint || 'https://conso.boris.sh/api/',
57
- "debug": msg.payload?.debug === true || msg.payload?.debug == "true"
134
+ "debug": String(msg.payload?.debug).toLowerCase() === "true"
58
135
  }
59
136
 
60
137
  const options = {
@@ -78,82 +155,145 @@ module.exports = async function (RED) {
78
155
  }
79
156
  };
80
157
 
81
- !_p.type ? (done ? done('api type not set (ex. daily_consumption) see documentation') : node.error('api type not set (ex. daily_consumption) see documentation')) : null;
82
- !_p.token ? (done ? done('token not set') : node.error('token not set')) : null;
83
- !_p.prm ? (done ? done('PRM not set') : node.error('PRM not set')) : null;
84
- !_p.start ? (done ? done('start not set') : node.error('start not set')) : null;
85
- !_p.end ? (done ? done('end not set') : node.error('end not set')) : null;
86
-
87
- if (_p.type && _p.token && _p.prm && _p.start && _p.end) {
88
- let wait = Math.floor(Math.random() * _p.random_delay);
89
- let msg_wait = nodeStatus.WAIT + Math.ceil(wait / 1000) + 's';
90
- node.status({ fill: 'grey', shape: 'ring', text: msg_wait });
91
- setTimeout(() => {
92
- node.status({ fill: 'grey', shape: 'ring', text: nodeStatus.FETCH });
93
- let request;
94
- try {
95
- let url = new URL(_p.endpoint);
96
- url.pathname += _p.type;
97
- url.search = new URLSearchParams({
98
- prm: _p.prm,
99
- start: _p.start,
100
- end: _p.end
101
- }).toString();
102
- request = url.toString();
103
- } catch (err) {
104
- node.status({ fill: 'red', shape: 'ring', text: 'error' });
105
- if (done) {
106
- done('url ' + err);
107
- } else {
108
- node.error('url ' + err);
109
- }
158
+ if (!_p.type) {
159
+ const err = 'api type not set (ex. daily_consumption) see documentation';
160
+ done ? done(err) : node.error(err, msg);
161
+ return;
162
+ }
163
+ if (!_p.token) {
164
+ const err = 'token not set';
165
+ done ? done(err) : node.error(err, msg);
166
+ return;
167
+ }
168
+ if (!_p.prm) {
169
+ const err = 'PRM not set';
170
+ done ? done(err) : node.error(err, msg);
171
+ return;
172
+ }
173
+ if (!_p.start) {
174
+ const err = 'start not set';
175
+ done ? done(err) : node.error(err, msg);
176
+ return;
177
+ }
178
+ if (!_p.end) {
179
+ const err = 'end not set';
180
+ done ? done(err) : node.error(err, msg);
181
+ return;
182
+ }
183
+
184
+ // Créer une signature unique de la requête
185
+ const requestSignature = createRequestSignature(_p, options);
186
+
187
+ // Vérifier si cette requête spécifique a dépassé la limite
188
+ const counterInfo = getRequestCounterInfo(node, requestSignature, _p.options_api_daily_limit);
189
+ if (counterInfo.isLimited) {
190
+ // Récupérer la réponse en cache
191
+ let responseCache = node.context().get('responseCache') || {};
192
+ const cachedResponse = responseCache[requestSignature];
193
+
194
+ if (cachedResponse) {
195
+ // Retourner la réponse en cache
196
+ const statusMsg = `Limit reached - Returning cached response (${counterInfo.count}/${_p.options_api_daily_limit}) - Sig: ${requestSignature}`;
197
+ node.status({ fill: 'yellow', shape: 'ring', text: statusMsg });
198
+ msg.payload = cachedResponse;
199
+ node.send(msg);
200
+ if (done) done();
201
+ } else {
202
+ // Pas de cache disponible
203
+ const statusMsg = `Error limit reached (${counterInfo.count}/${_p.options_api_daily_limit}) - Sig: ${requestSignature}`;
204
+ node.status({ fill: 'red', shape: 'ring', text: statusMsg });
205
+ const errorMsg = `Daily limit of ${_p.options_api_daily_limit} calls for this request reached. No cache available.`;
206
+ node.error(errorMsg, msg);
207
+ if (done) done(errorMsg);
208
+ }
209
+ return;
210
+ }
211
+
212
+ // Incrémenter le compteur pour cette requête spécifique MAINTENANT (avant l'appel)
213
+ const newCount = incrementAndGetRequestCount(node, requestSignature);
214
+ const remaining = _p.options_api_daily_limit - newCount;
215
+ _p.debug && node.warn(`Request ${requestSignature} - Call ${newCount}/${_p.options_api_daily_limit} - Remaining: ${remaining}`);
216
+
217
+ let wait = Math.floor(Math.random() * _p.random_delay);
218
+ let msg_wait = nodeStatus.WAIT + Math.ceil(wait / 1000) + 's';
219
+ node.status({ fill: 'grey', shape: 'ring', text: msg_wait });
220
+ setTimeout(() => {
221
+ node.status({ fill: 'grey', shape: 'ring', text: nodeStatus.FETCH });
222
+ let request;
223
+ try {
224
+ let url = new URL(_p.endpoint);
225
+ url.pathname = url.pathname.replace(/\/$/, '') + '/' + _p.type;
226
+ url.search = new URLSearchParams({
227
+ prm: _p.prm,
228
+ start: _p.start,
229
+ end: _p.end
230
+ }).toString();
231
+ request = url.toString();
232
+ } catch (err) {
233
+ node.status({ fill: 'red', shape: 'ring', text: 'error' });
234
+ if (done) {
235
+ done('url ' + err);
236
+ } else {
237
+ node.error('url ' + err);
110
238
  }
239
+ }
240
+
241
+ _p.debug && request && node.warn('Request URL:' + request);
242
+ if (_p.debug && options) {
243
+ const debugOptions = { ...options, headers: { ...options.headers } };
244
+ if (debugOptions.headers && debugOptions.headers['Authorization']) {
245
+ debugOptions.headers['Authorization'] = 'Bearer [HIDDEN]';
246
+ }
247
+ node.warn('Options:' + JSON.stringify(debugOptions));
248
+ }
249
+
250
+ request && options && got(request, options)
251
+ .then(res => {
252
+ if (res.statusCode == '200') {
253
+ try {
254
+ msg.payload = JSON.parse(res.body);
255
+
256
+ // Stocker la réponse en cache
257
+ let responseCache = node.context().get('responseCache') || {};
258
+ responseCache[requestSignature] = msg.payload;
259
+ node.context().set('responseCache', responseCache);
260
+
261
+ node.send(msg);
262
+ node.status({ fill: 'blue', shape: 'dot', text: 'OK' });
111
263
 
112
- _p.debug && request && node.warn('Request URL:' + request);
113
- _p.debug && options && node.warn('Options:' + JSON.stringify(options));
114
-
115
- request && options && got(request, options)
116
- .then(res => {
117
- if (res.statusCode == '200') {
118
- try {
119
- msg.payload = JSON.parse(res.body);
120
- node.send(msg);
121
- node.status({ fill: 'blue', shape: 'dot', text: 'OK' });
122
-
123
- setTimeout(() => {
124
- node.status({});
125
- if (done) {
126
- done();
127
- }
128
- }, 1000);
129
- } catch (err) {
130
- node.status({ fill: 'red', shape: 'ring', text: 'error' });
264
+ setTimeout(() => {
265
+ node.status({});
131
266
  if (done) {
132
- done('parsing ' + err);
133
- } else {
134
- node.error('parsing ' + err, msg);
267
+ done();
135
268
  }
136
- }
137
- } else {
138
- node.status({ fill: 'red', shape: 'ring', text: res.statusCode });
269
+ }, 1000);
270
+ } catch (err) {
271
+ node.status({ fill: 'red', shape: 'ring', text: 'error' });
139
272
  if (done) {
140
- done(`statusCode ${res.statusCode}`);
273
+ done('parsing ' + err);
141
274
  } else {
142
- node.error(`statusCode ${res.statusCode}`, msg);
275
+ node.error('parsing ' + err, msg);
143
276
  }
144
277
  }
145
- })
146
- .catch(err => {
147
- node.status({ fill: 'red', shape: 'ring', text: 'error' });
148
-
278
+ } else {
279
+ node.status({ fill: 'red', shape: 'ring', text: res.statusCode });
149
280
  if (done) {
150
- done(err);
281
+ done(`statusCode ${res.statusCode}`);
151
282
  } else {
152
- node.error(err, msg);
283
+ node.error(`statusCode ${res.statusCode}`, msg);
153
284
  }
154
- });
155
- }, wait);
156
- }
285
+ }
286
+ })
287
+ .catch(err => {
288
+ node.status({ fill: 'red', shape: 'ring', text: 'error' });
289
+
290
+ if (done) {
291
+ done(err);
292
+ } else {
293
+ node.error(err, msg);
294
+ }
295
+ });
296
+ }, wait);
157
297
  })
158
298
  }
159
299
  RED.nodes.registerType("linky-api", LinkyMetering, {
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@flecoufle/node-red-linky",
3
- "version": "1.3.0",
4
- "description": "Retrieve Linky power consumption",
3
+ "version": "1.4.2",
4
+ "description": "Récupérer de Linky les données de consommation et production d'énergie via l'API Enedis",
5
5
  "main": "linky/linky.js",
6
6
  "publishConfig": {
7
7
  "access": "public"