{"id":5248,"date":"2023-10-21T15:56:49","date_gmt":"2023-10-21T13:56:49","guid":{"rendered":"https:\/\/www.sigalou-domotique.fr\/?p=5248"},"modified":"2026-01-04T21:26:27","modified_gmt":"2026-01-04T19:26:27","slug":"utiliser-un-script-python-pour-enregistrer-des-donnees-dans-ha","status":"publish","type":"post","link":"https:\/\/www.sigalou-domotique.fr\/es\/utiliser-un-script-python-pour-enregistrer-des-donnees-dans-ha","title":{"rendered":"Usar un script de Python para guardar datos en HA"},"content":{"rendered":"<p>Advierto inmediatamente, este Retex es solo para fines pedag\u00f3gicos, lo escribo para compartir una metodolog\u00eda, a saber, que me he dado cuenta de esta caracter\u00edstica de manera diferente desde entonces, por lo que no tengo este proceso en prod.<!--more--><\/p>\r\n<h3>Integraci\u00f3n para ejecutar el script de Python<\/h3>\r\n<p>Por una raz\u00f3n que nunca entend\u00ed, las caracter\u00edsticas de scripting python integradas en HA son archi-mini, no es posible importar, por lo que tengo que usar una integraci\u00f3n llamada <a href=\"https:\/\/github.com\/custom-components\/pyscript\" target=\"_blank\" rel=\"noopener\">pyscript<\/a>.<\/p>\r\n<p><a href=\"https:\/\/hacs-pyscript.readthedocs.io\/en\/latest\/\" target=\"_blank\" rel=\"noopener\">El doctor est\u00e1 aqu\u00ed.<\/a><\/p>\r\n<p>Paso los detalles para instalarlo, es muy sencillo con HACS y todo est\u00e1 en el doc.<\/p>\r\n<p>En configuration.yaml, tengo estas l\u00edneas:<\/p>\r\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"yaml\">pictograma: permitir _all_imports: autenticas molestias _is _global: cierto<\/pre>\r\n<h3>Obtengo un token<\/h3>\r\n<p>Abro la interfaz de usuario de Home Assistant.<\/p>\r\n<p>En la esquina inferior izquierda, hago clic en mi nombre de usuario (o icono de perfil).<\/p>\r\n<p>Hago clic en \"Perfil\".<\/p>\r\n<p>Me desplazo hacia abajo hasta la secci\u00f3n \u00abFichas de acceso a largo plazo\u00bb.\u00a0<\/p>\r\n<p>Hago clic en \"Crear token\" para generar un nuevo token.<\/p>\r\n<p>Le doy un nombre a mi token para que me sea m\u00e1s f\u00e1cil encontrarlo.<\/p>\r\n<p>Una vez que se crea el token, veo su contenido. Copio el valor del token, es decir, la serie de caracteres alfanum\u00e9ricos, y lo pego en mi configuraci\u00f3n (p\u00e1rrafo siguiente)<\/p>\r\n<h3>Declaraci\u00f3n de la plataforma que hace el enlace entre Python y el sensor<\/h3>\r\n<p>En configuration.yaml, a\u00f1ado estas l\u00edneas:<\/p>\r\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"yaml\">_comando de descanso: enviar _data_to_api: url: \u00abhttp:\/\/homeassistant.local:8123\/api\/states\/sensor.wallbox _charges\u00bb m\u00e9todo: 'post' content_type: Encabezados \u00abapplication\/json\u00bb: Autorizaci\u00f3n: \u00abBearer TOKEN\u00bb # Sustit\u00fayase TOKEN por el token cf p\u00e1rrafo anterior payload: \u00ab{{ data | tojson }}\u00bb  <\/pre>\r\n<p>Esta es la puerta principal que ser\u00e1 suministrada por el script Python (abajo) y enviar\u00e1 la informaci\u00f3n al sensor.wallbox _charges.<\/p>\r\n<h3>Mi script de Python<\/h3>\r\n<p>\u00a1Coloco el archivo Wallbox de _charges.py en la carpeta \/config\/pyscript!<\/p>\r\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"yaml\">_charges(lastdata=None, action=None, price=\"123\", km=\"123\", dateStart=\"01\/01\/2000 12:00:00\", value=\"123\"): _time _paris = pytz.timezone(\"Europa\/Par\u00eds\") ahora _timestamp = int(datetime.datetime.now().timestamp()) ahora _time _paris = datetime.datetime.fromtimestamp(ahora _timestamp, _time _paris) ahora _formatee = ahora _time _paris.strftime(\"%d\/%m\/%Y %H:%M:%S\") nowMoins20min = datetime.datetime.now() - datetime.timedelta(minutes=20) nowMoins20min _formatee = nowMoins20min.strftime(\"%H:%M\") dateDebutRecue = datetime.datetime.strptime(dateDebut, '%d\/%m\/%Y %H:%M:%S').replace(tzinfo=fuseau_heure_paris) debut_timestamp_formatee = dateDebutRecue.strftime(\"%d\/%m\/%Y %H:%M:%S\") debut_timestamp_formatee_SansSecondes = dateDebutRecue.strftime(\"%d\/%m\/%Y %H:%M\") # Convierta cadenas en objetos datetime ahora _dt = datetime.datetime.strptime(now _formatee, \"%d\/%m\/%Y %H:%M:%S\") debut_dt = datetime.datetime.strptime(debut_timestamp_formatee, \"%d\/%m\/%Y %H:%M:%S\") # Calcula la diferencia entre objetos de tiempoDeCharge = now_dt - debut_dt - datetime.timedelta(minutes=20) # Formatear la duraci\u00f3n en horas, minutos y segundos horas, descanso = divmod(timeDeCharge.seconds, 3600) minutos, segundos = divmod(rest, 60) # Mostrar la duraci\u00f3n como HH:MM:SS timeDeCharge_formatee = f\"{horas:02}:{minutos:02}\" # Escribe un mensaje informativo en el peri\u00f3dico #log.info(action) if action == \"registrar\": valueaadd = [{ \"salvado _at\": A partir de ahora _timestamp, \"comenzando\": debut_timestamp_formatee_SansSecondes, \"fin\": A partir de ahoraMenos20min _formatee, \"duraci\u00f3n\": timeDeCharge_formatee, \"consumo\": valor, \"km\": km, \"coste\": round(value * price, 2) }] si lastdata no es None: valuea add = lastdata + valuea add data = { \"estado\": debut_timestamp_formatee_SansSecondes, \"atributos\": { \"_at actualizado\": ahora _dt.strftime(\"%d\/%m\/%Y %H:%M:%S\"), \"sesi\u00f3n de carga\": valueadd } } if action == \"init\": datos = { \"estado\": \"on\", \"atributos\": { \"_at actualizado\": \u00ab14\/10\/2023 16:42:39\u00bb, \u00abaprobaci\u00f3n de la gesti\u00f3n\u00bb: [ { \"consumo\": 2.14, \"Costo\": 0,43, \"duraci\u00f3n\": \"00:27\", \"principio\": \u00ab19\/09\/2023 20:36\u00bb, \u00abfin\u00bb: \"21:03\", \"km\": 17, \"Salvado _at\": 1695150359 }, { \"consumo\": 23.29, \"coste\": 4.66, \"duraci\u00f3n\": \"04:19\", \"principio\": \u00ab21\/09\/2023 21:06\u00bb, \u00abfin\u00bb: \"01:25\", \"km\": 192, \"Salvado _at\": 1695323159 }, { \"consumo\": 7.68, \"coste\": 1.54, \"duraci\u00f3n\": \"01:23\", \"principio\": \u00ab22\/09\/2023 08:45\u00bb, \u00abfin\u00bb: \"10:08\", \"km\": 63, \"saved_at\": 1695409559 }, { \"consumo\": 23.80, \"coste\": 4.76, \"duraci\u00f3n\": \"04:26\", \"principio\": \u00ab23\/09\/2023 16:57\u00bb, \u00abfin\u00bb: \"21:23\", \"km\": 196, \"Salvado _at\": 1695495959 } ] } } # Llame al servicio REST para enviar el servicio de datos _data = { \"token\": \"xxxxxx\", # Sustit\u00fayase por el token \"datos\": data } if action == \"registro\" o acci\u00f3n == \"init\": # Llamar al servicio REST as\u00edncronamente esperar hass.services.async_call('rest_command', 'send_data_to_api', _data service) # Escribe un mensaje de error en el registro #log.error(\"Fin del script\")<\/pre>\r\n<h3>A\u00f1adir dos automatizaciones<\/h3>\r\n<p>Uno para inicializar el sensor, el otro para agregar datos<\/p>\r\n<h4>Inicializar los datos<\/h4>\r\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"yaml\">alias: Inicializa la descripci\u00f3n de las sesiones de carga: Activador \"\": [] condici\u00f3n: [] acci\u00f3n: - servicio: pyscript.wallbox _carga datos: acci\u00f3n: modo init: soltero\r\n<\/pre>\r\n<h4>Guardar datos<\/h4>\r\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"yaml\">alias: Guarda una descripci\u00f3n de la sesi\u00f3n de carga: &gt;- Esta automatizaci\u00f3n se activa cuando el sensor no ha cambiado su valor durante 20 minutos y no est\u00e1 en 0 trigger: - plataforma: _id, entidad estatal: - sensor.wallbox _portal _added _range para: horas: 0 minutos: 20 segundos: 0 habilitado: verdadera condici\u00f3n: - condici\u00f3n: _id, entidad _state num\u00e9rica: Sensor.wallbox _portal _added _energy arriba: 0 - condici\u00f3n: no condiciones: - condici\u00f3n: _id, entidad estatal: Sensor.wallbox _charges estado: &gt;- {{ as_timestamp(states('sensor.wallbox _portal _added_range _min'))) | int | timestamp _custom('%d\/%m\/%Y %H:%M:%S', true)}}} activado: acci\u00f3n falsa: - servicio: pyscript.wallbox _carga datos: acci\u00f3n: valor de registro: Premio \"{{states('sensor.wallbox _portal _added_energy')}\": \u00ab{{states('sensor.wallbox _portal _energy_price')}}\u00bb km: \"{{states('sensor.wallbox _portal _added_range')}}\" \u00faltimos datos: \u00ab{{state _attr('sensor.wallbox _charges', 'sessionscharge')}}\u00bb Fecha de inicio: &gt;- {{ as_timestamp(states('sensor.wallbox _portal _added_range _min'))) | int | timestamp _custom('%d\/%m\/%Y %H:%M:%S', true)}} - servicio: variable.update _sensor data: sustit\u00fayase _attributes: valor falso: &gt;- {{ as_timestamp(states('sensor.wallbox _portal _added_range _min'))) | int | timestamp _custom('%d\/%m\/%Y %H:%M:%S', verdadero)}} objetivo: _id (entidad): Sensor.wallbox _charges habilitado: Modo falso: soltero<\/pre>\r\n<p>Para tener el valor m\u00ednimo de la carga, utilizo estad\u00edsticas para tener el valor m\u00ednimo<\/p>\r\n<p>Este sensor se declara en sensors.yaml<\/p>\r\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"yaml\">- plataforma: nombre estad\u00edstico: \"Wallbox Portal Added Range Min\" entidad_id: Sensor.wallbox _portal _added _range state _caracter\u00edstica: fecha _value _min max_age: horas: 10  \r\n   <\/pre>\r\n<h3>Resultado del almacenamiento de informaci\u00f3n<\/h3>\r\n<p><a href=\"https:\/\/www.sigalou-domotique.fr\/wp-content\/uploads\/2023\/10\/charge.png\"><img loading=\"lazy\" decoding=\"async\" class=\"alignnone size-full wp-image-5258\" src=\"https:\/\/www.sigalou-domotique.fr\/wp-content\/uploads\/2023\/10\/charge.png\" alt=\"\" width=\"729\" height=\"546\" srcset=\"https:\/\/www.sigalou-domotique.fr\/wp-content\/uploads\/2023\/10\/charge.png 729w, https:\/\/www.sigalou-domotique.fr\/wp-content\/uploads\/2023\/10\/charge-600x449.png 600w, https:\/\/www.sigalou-domotique.fr\/wp-content\/uploads\/2023\/10\/charge-300x225.png 300w\" sizes=\"auto, (max-width: 729px) 100vw, 729px\" \/><\/a><\/p>\r\n<h3>Tarjeta de visualizaci\u00f3n de todas las sesiones<\/h3>\r\n<p><a href=\"https:\/\/www.sigalou-domotique.fr\/wp-content\/uploads\/2023\/10\/charge2.png\"><img loading=\"lazy\" decoding=\"async\" class=\"alignnone size-full wp-image-5261\" src=\"https:\/\/www.sigalou-domotique.fr\/wp-content\/uploads\/2023\/10\/charge2.png\" alt=\"\" width=\"388\" height=\"375\" srcset=\"https:\/\/www.sigalou-domotique.fr\/wp-content\/uploads\/2023\/10\/charge2.png 388w, https:\/\/www.sigalou-domotique.fr\/wp-content\/uploads\/2023\/10\/charge2-300x290.png 300w\" sizes=\"auto, (max-width: 388px) 100vw, 388px\" \/><\/a><\/p>\r\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"yaml\">&lt;div&gt;Sesiones de carga&lt;\/div&gt; \r\n&Uacute;ltima actualizaci&oacute;n: {{ state_attr(&#039;sensor.wallbox _charges&#039;,&#039;updated _at&#039;)}} &lt;br&gt;&lt;br&gt;\r\n{% set items = state_attr(&#039;sensor.wallbox _charges&#039;, &#039;sessionscharge&#039;) %}\r\n&lt;table width=&#039;100%&#039;&gt;\r\n&lt;tbody&gt;\r\n{% para i en rango(0, items | count, 1) %}\r\n&lt;tr&gt;\r\n&lt;td&gt;{{ items[i].start }} a {{ items[i].fin }} ({ items[i].duration }})&lt;\/td&gt;\r\n&lt;td&gt;{{ items[i].consumo }} kWh&lt;\/td&gt;\r\n&lt;td&gt;+{{ items[i].km }} km&lt;\/td&gt;\r\n&lt;td&gt;{{ items[i].cost }} &euro;&lt;\/td&gt;\r\n&lt;\/tr&gt;\r\n\r\n{% endfor %}\r\n&lt;\/tbody&gt;\r\n&lt;\/table&gt;<\/pre>\r\n<p>Tarjeta de visualizaci\u00f3n con totales por a\u00f1o, mes, semana<\/p>\r\n<p><a href=\"https:\/\/www.sigalou-domotique.fr\/wp-content\/uploads\/2023\/10\/charge3.png\"><img loading=\"lazy\" decoding=\"async\" class=\"alignnone size-full wp-image-5262\" src=\"https:\/\/www.sigalou-domotique.fr\/wp-content\/uploads\/2023\/10\/charge3.png\" alt=\"\" width=\"383\" height=\"415\" srcset=\"https:\/\/www.sigalou-domotique.fr\/wp-content\/uploads\/2023\/10\/charge3.png 383w, https:\/\/www.sigalou-domotique.fr\/wp-content\/uploads\/2023\/10\/charge3-277x300.png 277w\" sizes=\"auto, (max-width: 383px) 100vw, 383px\" \/><\/a><\/p>\r\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"yaml\">Tipo: contenido de la reducci&oacute;n: &amp;gt;-\r\n  &lt;div&gt;&lt;h1&gt;Sesiones de carga&lt;\/div&gt;  {%- establecer por defecto _language = &#039;en&#039; %} &Uacute;ltima actualizaci&oacute;n: {{ state_attr(&#039;sensor.wallbox _charges&#039;,&#039;updated _at&#039;)}} &lt;br&gt;&lt;br&gt;\r\n\r\n\r\n  {% set items = state_attr(&#039;sensor.wallbox _charges&#039;, &#039;sessionscharge&#039;) %} {% set septjours = now() - timedelta(days=7) %} {% set power_semaine = namespace(valor=0) %} {% set cout_semaine = namespace(valor=0) %} {% set unmois = now() - timedelta(days=30) %} {% set power_months = namespace(valor=0) %} {% set cout_mois = namespace(valor=0) %} {% set uneannee = now() - timedelta(days=365) %} {% set power_annee = namespace(valor=0) %} {% set cout_annee = namespace(valor=0) %} {% para i en rango(0, items | count, 1) %} {% if items[i].saved_at | int &amp;gt;= as_timestamp(uneannee) %} {% set power_annee.value = power_annee.value + items[i].consumo %} {% set cout_annee.value = cout_annee.value + items[i].cost %} {% if items[i].saved_at | int &amp;gt;= as_timestamp(unmonth) %} {% set power_mois.value = power_mois.value + items[i].consumo %} {% set cout_mois.value = cout_mois.value + items[i].cost %} {% if items[i].saved_at | int &amp;gt;= as_timestamp(sevendays) %} {% set power_semaine.value = power_semaine.value + items[i].consumo %} {% set cout_semaine.value = cout_semaine.value + items[i].cost %} {% endif %} {% endif %} {% endif %} {% endfor %}\r\n\r\n  &lt;\/ha-alert&gt; &lt;ha-alert alert-type=&quot;info&quot; title=&quot;El mes pasado: {{ potencia _mois.value | int }} kWh - {{ coste _mois.value | float | round(2) }} &euro;&quot;&gt;&lt;\/ha-alert&gt; &lt;ha-alert alert-type=&quot;info&quot; title=&quot;El a&ntilde;o pasado: {{ power_annee.value | int }} kWh - {{ cout _annee.value | float | round(2) }} &euro;&quot;&gt;&lt;\/ha-alert&gt;\r\n\r\n\r\n  &lt;br&gt; \r\n\r\n\r\n  {% set items = state_attr(&#039;sensor.wallbox _charges&#039;, &#039;sessionscharge&#039;) %} {% set septjours = now() - timedelta(days=7) %}\r\n\r\n  &lt;table width=&#039;100%&#039;&gt; &lt;tbody&gt;  {% para i en rango(0, items | count, 1) %} {% if items[i].saved_at | int &amp;gt;= as_timestamp(sevendays) %} {% _obj = strptime(items[i].start, &#039;%d\/%m\/%Y %H:%M&#039;) %}\r\n\r\n  &lt;tr&gt; &lt;td&gt;&lt;h5&gt;{{items[i].start }} a {{ items[i].fin }} ({ items[i].duration }})&lt;\/td&gt; &lt;td&gt;{{ items[i].consumo }} kWh&lt;\/td&gt; &lt;td&gt;+{{ items[i].km }} km&lt;\/td&gt; &lt;td&gt;{{ items[i].cost }} &euro;&lt;\/td&gt; &lt;\/tr&gt; {% endif %} {% endfor %} &lt;\/tbody&gt; &lt;\/table&gt;\r\n    &lt;ha-alert alert-type=&quot;success&quot; title=&quot;La semana pasada: {{ power_semaine.value | int}} kWh - {{ cost_semaine.value | float | round(2) }}&euro;&quot;&gt;    \r\nestilo: .: | ha-card { margen: 0px 0px 0px 0px; Sombra de caja: ninguna; } ha-markdown: $: | h5 { tama&ntilde;o de fuente: 15px!importante; peso de fuente: normal!importante; relleno: 0px 0px 0px 8px!importante; frontera izquierda: 3px s&oacute;lido #429f46; }\r\n<\/pre>\r\n<p>&nbsp;<\/p>","protected":false},"excerpt":{"rendered":"<p>Advierto inmediatamente, este Retex es solo para fines pedag\u00f3gicos, lo escribo para compartir una metodolog\u00eda, a saber, que me he dado cuenta de esta caracter\u00edstica de manera diferente desde entonces, por lo que no tengo este proceso en prod.<\/p>","protected":false},"author":1,"featured_media":5257,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[14,53,166],"tags":[81],"class_list":["post-5248","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-domotique","category-home-assistant","category-retex","tag-developpement"],"acf":[],"_links":{"self":[{"href":"https:\/\/www.sigalou-domotique.fr\/es\/wp-json\/wp\/v2\/posts\/5248","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.sigalou-domotique.fr\/es\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.sigalou-domotique.fr\/es\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.sigalou-domotique.fr\/es\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.sigalou-domotique.fr\/es\/wp-json\/wp\/v2\/comments?post=5248"}],"version-history":[{"count":0,"href":"https:\/\/www.sigalou-domotique.fr\/es\/wp-json\/wp\/v2\/posts\/5248\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/www.sigalou-domotique.fr\/es\/wp-json\/wp\/v2\/media\/5257"}],"wp:attachment":[{"href":"https:\/\/www.sigalou-domotique.fr\/es\/wp-json\/wp\/v2\/media?parent=5248"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.sigalou-domotique.fr\/es\/wp-json\/wp\/v2\/categories?post=5248"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.sigalou-domotique.fr\/es\/wp-json\/wp\/v2\/tags?post=5248"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}