Tutorial - Actualización de la tarifa TEMPO (Actualización automática vía PDF)

Hola a todos,

Como prometido en el tutorial de @mutmut : (Gracias a él por este tutorial)

Y siguiendo mi tutorial que permite recuperar las tarifas fuera de tempo :

Quise ver cómo automatizar la recuperación de los diferentes costos.

Así que me encontré con este tutorial de Jeedom (Gracias a su autor) del cual me inspiré fuertemente, con la copia del código de node-red :

El objetivo aquí es recuperar el archivo pdf de la tabla tarifaria tempo directamente del sitio web de EDF para actualizar los valores directamente en Gladys y tener su costo real por kWh listo para poder usarlo en otras escenas :slight_smile:

El archivo se puede encontrar aquí :
https://particulier.edf.fr/content/dam/2-Actifs/Documents/Offres/Grille_prix_Tarif_Bleu.pdf

Solo hay que esperar que la estructura/composición del archivo no sea cambiada por EDF :grin:

El resultado final :

image

Aquí está el flujo de Node-Red :
( Un catch all está presente para recuperar los errores y enviarlos por correo electrónico )

[
    {
        "id": "670838a9edef0758",
        "type": "catch",
        "z": "d6333988d9cfb817",
        "name": "Errores",
        "scope": null,
        "uncaught": false,
        "x": 110,
        "y": 60,
        "wires": [
            [
                "c097dad994334b5e"
            ]
        ]
    },
    {
        "id": "c097dad994334b5e",
        "type": "e-mail",
        "z": "d6333988d9cfb817",
        "server": "monserveuremail",
        "port": "465",
        "authtype": "BASIC",
        "saslformat": false,
        "token": "oauth2Response.access_token",
        "secure": true,
        "tls": true,
        "name": "monadresse@email.fr",
        "dname": "Correo",
        "x": 250,
        "y": 60,
        "wires": []
    },
    {
        "id": "bf708d78a507d9c3",
        "type": "http request",
        "z": "d6333988d9cfb817",
        "name": "",
        "method": "GET",
        "ret": "bin",
        "paytoqs": "ignore",
        "url": "https://particulier.edf.fr/content/dam/2-Actifs/Documents/Offres/Grille_prix_Tarif_Bleu.pdf",
        "tls": "",
        "persist": false,
        "proxy": "",
        "insecureHTTPParser": false,
        "authType": "",
        "senderr": false,
        "headers": [],
        "x": 430,
        "y": 140,
        "wires": [
            [
                "c6602cec6bea28ca"
            ]
        ]
    },
    {
        "id": "c6602cec6bea28ca",
        "type": "file",
        "z": "d6333988d9cfb817",
        "name": "",
        "filename": "/tmp/Grille_prix_Tarif_Bleu.pdf",
        "filenameType": "str",
        "appendNewline": true,
        "createDir": false,
        "overwriteFile": "true",
        "encoding": "none",
        "x": 290,
        "y": 200,
        "wires": [
            [
                "866bd16b214c1d1d"
            ]
        ]
    },
    {
        "id": "a0349e7017779b4a",
        "type": "change",
        "z": "d6333988d9cfb817",
        "name": "Texto",
        "rules": [
            {
                "t": "set",
                "p": "payload",
                "pt": "msg",
                "to": "payload.text",
                "tot": "msg"
            }
        ],
        "action": "",
        "property": "",
        "from": "",
        "to": "",
        "reg": false,
        "x": 670,
        "y": 200,
        "wires": [
            [
                "d23eb75a79c8996d"
            ]
        ]
    },
    {
        "id": "d23eb75a79c8996d",
        "type": "function",
        "z": "d6333988d9cfb817",
        "name": "Después de \"Opción Tempo\"",
        "func": "const regex = /Option Tempo((.|\\n)*)/gm;\nconst found = msg.payload.match(regex);\nmsg.payload = found[0];\nreturn msg;",
        "outputs": 1,
        "timeout": 0,
        "noerr": 0,
        "initialize": "",
        "finalize": "",
        "libs": [],
        "x": 480,
        "y": 280,
        "wires": [
            [
                "09faecd167fed230"
            ]
        ]
    },
    {
        "id": "09faecd167fed230",
        "type": "function",
        "z": "d6333988d9cfb817",
        "name": "6 kVA",
        "func": "const regex = /^12\b.*$/gm; // potencia suscrita\nconst found = msg.payload.match(regex);\nconst searchRegExp = /,/g; // reemplaza las comas por puntos\nconst replaceWith = '.';\nmsg.payload = found[0];\nmsg.payload = msg.payload.replace(searchRegExp, replaceWith);\nreturn msg;",
        "outputs": 1,
        "timeout": 0,
        "noerr": 0,
        "initialize": "",
        "finalize": "",
        "libs": [],
        "x": 670,
        "y": 280,
        "wires": [
            [
                "e7ca8ee4d891c581"
            ]
        ]
    },
    {
        "id": "e7ca8ee4d891c581",
        "type": "split",
        "z": "d6333988d9cfb817",
        "name": "",
        "splt": " ",
        "spltType": "str",
        "arraySplt": 1,
        "arraySpltType": "len",
        "stream": false,
        "addname": "",
        "x": 510,
        "y": 360,
        "wires": [
            [
                "9f3df8ae0bd5d4bf"
            ]
        ]
    },
    {
        "id": "9f3df8ae0bd5d4bf",
        "type": "join",
        "z": "d6333988d9cfb817",
        "name": "",
        "mode": "custom",
        "build": "array",
        "property": "payload",
        "propertyType": "msg",
        "key": "topic",
        "joiner": "\\n",
        "joinerType": "str",
        "accumulate": false,
        "timeout": "",
        "count": "",
        "reduceRight": false,
        "reduceExp": "",
        "reduceInit": "",
        "reduceInitType": "num",
        "reduceFixup": "",
        "x": 650,
        "y": 360,
        "wires": [
            [
                "88eaf52f02be48c8"
            ]
        ]
    },
    {
        "id": "88eaf52f02be48c8",
        "type": "function",
        "z": "d6333988d9cfb817",
        "name": "JSON",
        "func": "msg.payload = {\n    \"potencia\": msg.payload[0],\n    \"precio_suscripcion\": Number((Number(msg.payload[1]) * 12).toFixed(2)),\n    \"precio_HCJB\": Number(Number(msg.payload[2] / 100).toFixed(4)),\n    \"precio_HPJB\": Number(Number(msg.payload[3] / 100).toFixed(4)),\n    \"precio_HCJW\": Number(Number(msg.payload[4] / 100).toFixed(4)),\n    \"precio_HPJW\": Number(Number(msg.payload[5] / 100).toFixed(4)),\n    \"precio_HCJR\": Number(Number(msg.payload[6] / 100).toFixed(4)),\n    \"precio_HPJR\": Number(Number(msg.payload[7] / 100).toFixed(4)),\n}\nreturn msg;",
        "outputs": 1,
        "timeout": "",
        "noerr": 0,
        "initialize": "",
        "finalize": "",
        "libs": [],
        "x": 790,
        "y": 360,
        "wires": [
            [
                "6e2b78e27c34c026",
                "4d19e36f157bd2c0",
                "50608e9e9bdecdc6",
                "d9fe4a5ac7c52d90",
                "b4a2b0d2177a9058",
                "c5f9959555f3e7b9"
            ]
        ]
    },
    {
        "id": "1316dbce795332d5",
        "type": "inject",
        "z": "d6333988d9cfb817",
        "name": "Todos los días a las 9:30",
        "props": [
            {
                "p": "payload"
            }
        ],
        "repeat": "",
        "crontab": "30 09 * * *",
        "once": false,
        "onceDelay": 0.1,
        "topic": "",
        "payload": "",
        "payloadType": "date",
        "x": 220,
        "y": 140,
        "wires": [
            [
                "bf708d78a507d9c3"
            ]
        ]
    },
    {
        "id": "866bd16b214c1d1d",
        "type": "pdfparse",
        "z": "d6333988d9cfb817",
        "name": "",
        "path": "/tmp/Grille_prix_Tarif_Bleu.pdf",
        "x": 520,
        "y": 200,
        "wires": [
            [
                "a0349e7017779b4a"
            ]
        ]
    },
    {
        "id": "6e2b78e27c34c026",
        "type": "function",
        "z": "d6333988d9cfb817",
        "name": "Día azul HC",
        "func": "msg.payload = msg.payload.precio_HCJB;\nmsg.payload = parseFloat(msg.payload);\nreturn msg;",
        "outputs": 1,
        "timeout": 0,
        "noerr": 0,
        "initialize": "",
        "finalize": "",
        "libs": [],
        "x": 960,
        "y": 300,
        "wires": [
            [
                "0a6e473635b8b423"
            ]
        ]
    },
    {
        "id": "4d19e36f157bd2c0",
        "type": "function",
        "z": "d6333988d9cfb817",
        "name": "Día azul HP",
        "func": "msg.payload = msg.payload.precio_HPJB;\nmsg.payload = parseFloat(msg.payload);\nreturn msg;",
        "outputs": 1,
        "timeout": 0,
        "noerr": 0,
        "initialize": "",
        "finalize": "",
        "libs": [],
        "x": 960,
        "y": 340,
        "wires": [
            [
                "69c1fb09c3ede0e0"
            ]
        ]
    },
    {
        "id": "50608e9e9bdecdc6",
        "type": "function",
        "z": "d6333988d9cfb817",
        "name": "Día blanco HC",
        "func": "msg.payload = msg.payload.precio_HCJW;\nmsg.payload = parseFloat(msg.payload);\nreturn msg;",
        "outputs": 1,
        "timeout": 0,
        "noerr": 0,
        "initialize": "",
        "finalize": "",
        "libs": [],
        "x": 970,
        "y": 380,
        "wires": [
            [
                "467f6ce03f7ed6c0"
            ]
        ]
    },
    {
        "id": "d9fe4a5ac7c52d90",
        "type": "function",
        "z": "d6333988d9cfb817",
        "name": "Día blanco HP",
        "func": "msg.payload = msg.payload.prix_HPJW;\nmsg.payload = parseFloat(msg.payload);\nreturn msg;",
        "outputs": 1,
        "timeout": 0,
        "noerr": 0,
        "initialize": "",
        "finalize": "",
        "libs": [],
        "x": 970,
        "y": 420,
        "wires": [
            [
                "ed628e4d71dbbc38"
            ]
        ]
    },
    {
        "id": "b4a2b0d2177a9058",
        "type": "function",
        "z": "d6333988d9cfb817",
        "name": "Día rojo HC",
        "func": "msg.payload = msg.payload.prix_HCJR;\nmsg.payload = parseFloat(msg.payload);\nreturn msg;",
        "outputs": 1,
        "timeout": 0,
        "noerr": 0,
        "initialize": "",
        "finalize": "",
        "libs": [],
        "x": 970,
        "y": 460,
        "wires": [
            [
                "f2b3869570196614"
            ]
        ]
    },
    {
        "id": "c5f9959555f3e7b9",
        "type": "function",
        "z": "d6333988d9cfb817",
        "name": "Día rojo HP",
        "func": "msg.payload = msg.payload.prix_HPJR;\nmsg.payload = parseFloat(msg.payload);\nreturn msg;",
        "outputs": 1,
        "timeout": 0,
        "noerr": 0,
        "initialize": "",
        "finalize": "",
        "libs": [],
        "x": 970,
        "y": 500,
        "wires": [
            [
                "ae1d548f6cb679aa"
            ]
        ]
    },
    {
        "id": "0a6e473635b8b423",
        "type": "mqtt out",
        "z": "d6333988d9cfb817",
        "name": "",
        "topic": "gladys/master/device/mqtt:edf/feature/mqtt:cout-abonnement-jour-bleu-hc/state",
        "qos": "2",
        "retain": "true",
        "respTopic": "",
        "contentType": "",
        "userProps": "",
        "correl": "",
        "expiry": "",
        "broker": "0ee77e0f90aa9681",
        "x": 1370,
        "y": 300,
        "wires": []
    },
    {
        "id": "69c1fb09c3ede0e0",
        "type": "mqtt out",
        "z": "d6333988d9cfb817",
        "name": "",
        "topic": "gladys/master/device/mqtt:edf/feature/mqtt:cout-abonnement-jour-bleu-hp/state",
        "qos": "2",
        "retain": "true",
        "respTopic": "",
        "contentType": "",
        "userProps": "",
        "correl": "",
        "expiry": "",
        "broker": "0ee77e0f90aa9681",
        "x": 1370,
        "y": 340,
        "wires": []
    },
    {
        "id": "467f6ce03f7ed6c0",
        "type": "mqtt out",
        "z": "d6333988d9cfb817",
        "name": "",
        "topic": "gladys/master/device/mqtt:edf/feature/mqtt:cout-abonnement-jour-blanc-hc/state",
        "qos": "2",
        "retain": "true",
        "respTopic": "",
        "contentType": "",
        "userProps": "",
        "correl": "",
        "expiry": "",
        "broker": "0ee77e0f90aa9681",
        "x": 1370,
        "y": 380,
        "wires": []
    },
    {
        "id": "ed628e4d71dbbc38",
        "type": "mqtt out",
        "z": "d6333988d9cfb817",
        "name": "",
        "topic": "gladys/master/device/mqtt:edf/feature/mqtt:cout-abonnement-jour-blanc-hp/state",
        "qos": "2",
        "retain": "true",
        "respTopic": "",
        "contentType": "",
        "userProps": "",
        "correl": "",
        "expiry": "",
        "broker": "0ee77e0f90aa9681",
        "x": 1370,
        "y": 420,
        "wires": []
    },
    {
        "id": "f2b3869570196614",
        "type": "mqtt out",
        "z": "d6333988d9cfb817",
        "name": "",
        "topic": "gladys/master/device/mqtt:edf/feature/mqtt:cout-abonnement-jour-rouge-hc/state",
        "qos": "2",
        "retain": "true",
        "respTopic": "",
        "contentType": "",
        "userProps": "",
        "correl": "",
        "expiry": "",
        "broker": "0ee77e0f90aa9681",
        "x": 1370,
        "y": 460,
        "wires": []
    },
    {
        "id": "ae1d548f6cb679aa",
        "type": "mqtt out",
        "z": "d6333988d9cfb817",
        "name": "",
        "topic": "gladys/master/device/mqtt:edf/feature/mqtt:cout-abonnement-jour-rouge-hp/state",
        "qos": "2",
        "retain": "true",
        "respTopic": "",
        "contentType": "",
        "userProps": "",
        "correl": "",
        "expiry": "",
        "broker": "0ee77e0f90aa9681",
        "x": 1370,
        "y": 500,
        "wires": []
    },
    {
        "id": "0ee77e0f90aa9681",
        "type": "mqtt-broker",
        "name": "GLADYS-MQTT",
        "broker": "mqtt://192.168.0.248",
        "port": "1883",
        "clientid": "",
        "autoConnect": true,
        "usetls": false,
        "protocolVersion": "4",
        "keepalive": "60",
        "cleansession": true,
        "autoUnsubscribe": true,
        "birthTopic": "",
        "birthQos": "0",
        "birthRetain": "false",
        "birthPayload": "",
        "birthMsg": {},
        "closeTopic": "",
        "closeQos": "0",
        "closeRetain": "false",
        "closePayload": "",
        "closeMsg": {},
        "willTopic": "",
        "willQos": "0",
        "willRetain": "false",
        "willPayload": "",
        "willMsg": {},
        "userProps": "",
        "sessionExpiry": ""
    }
]

No olvides modificar la potencia suscrita :slight_smile:

Prerrequisitos:

Instalar el módulo node-red-contrib-pdfparse:

Aquí está el dispositivo MQTT con las funcionalidades:





Aquí están las escenas:

Día blanco - HC:





Día blanco - HP:





Día azul - HC:





Día azul - HP:





Día rojo - HC:





Día rojo - HP:





Si tienes preguntas/comentarios, no dudes en decírmelo :slight_smile:

Próximos cambios:

  • Ninguno (¡Tus comentarios :slight_smile: )

Super tuto ! Merci @prohand !

Bravo @prohand , je n’avais pas eu le courage de mettre tous mes screenshots pour tous les cas HCHP&JourQuiVaBien :wink:
Petite question sur les déclencheurs, pourquoi en as-tu mis un à 00:01 ? Gladys ne gère pas le suivi d’un jour à l’autre ?

EDIT : il y a un paragraphe « Download CSV » qui traîne à la fin de ton flux (je l’ai viré car pas compris) et pour ceux qui ont un node-red un peu vierge comme le mien, j’ajouterai qu’il faut installer le pdfparse :


Bien sûr, ne pas oublier de paramétrer votre serveur MQTT (avec login/pwd).

EDIT 2 : @prohand tu as pris quoi comme module pour le mail ?
j’ai des pbs d’install :frowning:

2024-12-13T18:18:42.602Z Installer : node-red-node-email 3.0.2

2024-12-13T18:18:42.699Z npm install --no-audit --no-update-notifier --no-fund --save --save-prefix=~ --production --engine-strict node-red-node-email@3.0.2
2024-12-13T18:18:43.732Z [err] npm
2024-12-13T18:18:43.733Z [err]  WARN
2024-12-13T18:18:43.734Z [err]  config production Use `--omit=dev` instead.
2024-12-13T18:18:45.070Z [err] npm
2024-12-13T18:18:45.071Z [err]  ERR! code EBADENGINE
2024-12-13T18:18:45.075Z [err] npm
2024-12-13T18:18:45.076Z [err]  ERR!
2024-12-13T18:18:45.076Z [err]  engine Unsupported engine
2024-12-13T18:18:45.076Z [err] npm ERR!
2024-12-13T18:18:45.076Z [err]  
2024-12-13T18:18:45.076Z [err] engine Not compatible with your version of node/npm: node-red-node-email@3.0.2
2024-12-13T18:18:45.077Z [err] npm 
2024-12-13T18:18:45.077Z [err] ERR! notsup Not compatible with your version of node/npm: node-red-node-email@3.0.2
2024-12-13T18:18:45.077Z [err] npm ERR! notsup
2024-12-13T18:18:45.077Z [err]  Required: {"node":">=18.0.0"}
2024-12-13T18:18:45.077Z [err] npm 
2024-12-13T18:18:45.077Z [err] ERR! notsup Actual:   {"npm":"8.19.4","node":"v16.20.2"}
2024-12-13T18:18:45.082Z [err] 
2024-12-13T18:18:45.082Z [err] npm ERR!
2024-12-13T18:18:45.083Z [err]  A complete log of this run can be found in:
2024-12-13T18:18:45.083Z [err] npm ERR!     /data/.npm/_logs/2024-12-13T18_18_43_653Z-debug-0.log
2024-12-13T18:18:45.094Z rc=1

Effectivement le 00h01 c’est une erreur car je pensais qu’a 00h00 on changeait de jour…
Mais la preuve ici que non :grin: :

Donc il n’y en a effectivement pas besoin :slight_smile:
Je corrige les captures d’écran directement après ce message

Je corrige également le flux node red, c’est une erreur de copier coller

Je rajoute le faite d’installer le module pdfparse

Pour l’email j’ai pris celui-ci :

image

Merci pour toutes ces remontées :wink:

Bonjour,
je viens de voir que j’avais pris le même module node-red-node-email que toi mais je n’ai que la 3.0.2 et tu as la 2.2.1, et le problème est qu’il faut nodeJS >18 pour ce module en version 3.x.x :frowning:
Comment as-tu fait pour avoir cette version spécifique ?

Je plussoie la mise à jour de nodeJS si c’est possible bien entendu :wink:

Hello,

J’ai surement du le télécharger lorsque c’était la dernière version tout simplement ^^
Si tu trouves comment installer une version plus ancienne, je suis preneur :slight_smile:

je galère : j’ai essayé dans une console docker de faire npm i node-red-node-email@2.2.1
Ça dit qu’un nouveau module est installé, d’autres à mettre à jour, mais rien n’arrive dans node-red, même avec un refresh de la page web.

Alors j’ai voulu désactiver l’intégration node-red depuis GLadys et l’activer de nouveau.
Résultat : j’ai tout perdu (flux et modules installés) :cry:
La désactivation supprime le conteneur mais aussi toutes les data sauvegardées sur le volume monté (/volume1/docker/gladysassistant/node-red dans mon cas sur un Syno). La loose :grimacing:

Je ne sais pas si c’est un comportement normal mais c’est très c.n surtout quand tu n’as pas sauvegardé ce rep, @pierre-gilles tu saurais nous dire ?

Aie, oui la sa sort de mes compétences ^^
C’est pour cela que je sauvegarde la VM entière tout les soirs de mon côté :slight_smile:
J’ai aussi un export de mes flux vers mon adresse email au cas ou.
Bon courage à toi :neutral_face:

Je ne suis pas le développeur de cette intégration mais oui c’est le comportement attendu :slight_smile:

Un petit message de warning pourrait être le bienvenue par contre !

J’ai créé une issue :

@prohand demain (1er fev) on devrait normalement savoir si le fichier garde le même nom et au pire il faudra changer le lien dans le node-red :wink:

Hello,

Tu me dira car je ne suis plus en tempo à l’heure actuelle :wink:

EDIT : Tout ce qui suit est une belle grosse erreur de ma part mais je le laisse pour le suivi du post.

Résumé

L’URL a malheureusement changé, voici la nouvelle : https://particuliers.es.fr/sites/default/files/documents/2025.02.01_PART_TRV_Bleu_(INF456).pdf
Et le format du tableau aussi :frowning:
AVANT :


MAINTENANT :

Et comme je suis une quiche en node-red, le flow est out …
@prohand tu aurais un petit moment pour modifier le flow ? Merci d’avance

Ah mince :frowning:

Je vais pas avoir le temps durant les prochains jours malheureusement.

Après il n’y a rien de très compliqué tu peux donner le code actuel à ChatGPT et lui demander de l’adapter au nouveau.

Pour le flow node red actuel c’est ce que j’avais fait :wink:

je me suis trompé car je suis chez ES et non EDF, j’ai pris le fichier ES … je suis en train d’analyser et de chercher le fichier EDF.

Oyé Oyé, je suis un boulet !

Mise à part ça, le fichier de chez EDF a toujours le même nom et même URL depuis ce 1er février 2025 avec ses nouveaux tarifs et tout fonctionne parfaitement bien sans rien changer !

Merci @prohand pour le flow d’origine :wink: et je ne tapoterai plus plusieurs fois sur mon clavier avant de vérifier 5 fois :sweat_smile:

EDIT : et pour info il y a maintenant les tarifs TEMPO en opendata depuis le 30/01/25 : Historique des tarifs réglementés de vente d'électricité pour les consommateurs résidentiels - data.gouv.fr

Hola,

Estoy haciendo una pequeña modificación en el regex de la potencia suscrita.
De hecho, este año, para mí que tengo una suscripción de 12 kVA, el regex busca el 12 en el PDF y cae en un precio de consumo, a saber 16,12 y recupera solo lo que sigue en la línea:


Para estar seguro de tomar solo la línea que comienza con 12, hay que poner:

const regex = /^12\b.*$/gm; // potencia suscrita

^12\b : la línea comienza con 12 como valor entero (no 112 o 120).
@prohand si puedes cambiar en tu primer mensaje el regex = /6 .*/gm; en la línea del flujo de node-red:

"func": "const regex = /6 .*/gm; // potencia suscrita\nconst found = msg.payload.match(regex);\nconst searchRegExp = /,/g; // reemplaza las comas por puntos\nconst replaceWith = '.';\nmsg.payload = found[0];\nmsg.payload = msg.payload.replace(searchRegExp, replaceWith);\nreturn msg;",

sería genial :slight_smile:

Gracias @mutmut
He modificado en el primer mensaje :slight_smile: