Externaliser son historique - Stockage long terme

J’ai pas le contenu de ton fonction, donc possible

EDIT:
Vu. C’est rigolo que tu mettre une instance ‹ prod ›… Personnellement j’aurai fait un bucket séparé (prod/hors prod), directement dans le recoder d’Ha

Je viens d’éditer mon message et rajouter la fonction, même si elle est simple.

Salut à tous !

Suite à la demande de @Galadan, je vous partage mes tasks sur InfluxDB.
Je tiens à dire que je ne suis absolument pas informaticien donc il est fort probable que le code ne soit pas optimisé. Je suis bien évidemment preneur de toutes vos remarques constructives :slight_smile:

Voici donc la liste de mes tasks avec le code ci-dessous :

# Task « daily_kwh » :

import "date"
import "experimental"
import "math"

option task = {
    name: "daily_kwh",
    cron: "0 1 * * *",
}

TODAY = date.truncate(t: now(), unit: 1d)
YESTERDAY = experimental.addDuration(d: -1d, to: TODAY)

DATA1 = from(bucket: "home1_raw_data")
    |> range(start: YESTERDAY, stop: TODAY)
    |> filter(fn: (r) => r["_measurement"] == "kWh")
    |> filter(fn: (r) => r["_field"] == "value")

DATA1
    |> set(key: "source", value: "calculated")
    |> reduce(
        fn: (r, accumulator) => ({
            min: if r._value < accumulator.min then r._value else accumulator.min,
            max: if r._value > accumulator.max then r._value else accumulator.max,
            _time: r._time,
        }),
        identity: {min: 10000000000.0, max: -10000000000.0, _time: YESTERDAY},
    )
    |> map(fn: (r) => ({r with _time: YESTERDAY}))
    |> map(fn: (r) => ({r with _value: math.round(x: (r.max - r.min) * 100.0) / 100.0}))
    |> map(fn: (r) => ({r with min: r.min}))
    |> map(fn: (r) => ({r with min: r.max}))
    |> to(bucket: "home1_daily_data")

# Task « daily_kwh_current_day » :

import "date"
import "experimental"
import "math"

option task = {name: "daily_kwh_current_day", cron: "* * * * *"}

TODAY = date.truncate(t: now(), unit: 1d)

DATA1 = from(bucket: "home1_raw_data")
    |> range(start: TODAY, stop: now())
    |> filter(fn: (r) => r["_measurement"] == "kWh")
    |> filter(fn: (r) => r["_field"] == "value")

DATA1
    |> set(key: "source", value: "calculated")
    |> reduce(
        fn: (r, accumulator) => ({
            min: if r._value < accumulator.min then r._value else accumulator.min,
            max: if r._value > accumulator.max then r._value else accumulator.max,
            _time: r._time,
        }),
        identity: {min: 10000000000.0, max: -10000000000.0, _time: now()},
    )
    |> map(fn: (r) => ({r with _time: TODAY}))
    |> map(fn: (r) => ({r with _value: math.round(x: (r.max - r.min) * 100.0) / 100.0}))
    |> to(bucket: "home1_daily_data")

# Task « daily_m3 » :

import "date"
import "experimental"
import "math"

option task = {
    name: "daily_m3",
    cron: "0 1 * * *",
}

TODAY = date.truncate(t: now(), unit: 1d)
YESTERDAY = experimental.addDuration(d: -1d, to: TODAY)

DATA = from(bucket: "home1_raw_data")
    |> range(start: YESTERDAY, stop: TODAY)
    |> filter(fn: (r) => r["_measurement"] == "m3")
    |> filter(fn: (r) => r["_field"] == "value")

DATA
    |> set(key: "source", value: "calculated")
    |> reduce(
        fn: (r, accumulator) => ({
            min: if r._value < accumulator.min then r._value else accumulator.min,
            max: if r._value > accumulator.max then r._value else accumulator.max,
            _time: YESTERDAY,
        }),
        identity: {min: 10000000000.0, max: -10000000000.0, _time: YESTERDAY},
    )
    |> map(fn: (r) => ({r with _time: YESTERDAY}))
    |> map(fn: (r) => ({r with _value: math.round(x: (r.max - r.min) * 1000.0) / 1000.0}))
    |> to(bucket: "home1_daily_data")

DATA
    |> set(key: "source", value: "calculated")
    |> reduce(
        fn: (r, accumulator) => ({
            min: if r._value < accumulator.min then r._value else accumulator.min,
            max: if r._value > accumulator.max then r._value else accumulator.max,
            _time: YESTERDAY,
        }),
        identity: {min: 10000000000.0, max: -10000000000.0, _time: YESTERDAY},
    )
    |> map(fn: (r) => ({r with _time: YESTERDAY}))
    |> map(fn: (r) => ({r with _value: math.round(x: (r.max - r.min) * 1000.0) / 1000.0}))
    |> pivot(rowKey: ["_time"], columnKey: ["entity_id"], valueColumn: "_value")
    |> map(fn: (r) => ({r with _value: math.round(x: (r.eau_adoucie - r.eau_chaude) * 1000.0) / 1000.0}))
    |> set(key: "entity_id", value: "eau_froide")
    |> to(bucket: "home1_daily_data")

# Task « daily_m3_current_day » :

import "date"
import "experimental"
import "math"

option task = {name: "daily_m3_current_day", cron: "* * * * *"}

TODAY = date.truncate(t: now(), unit: 1d)

DATA = from(bucket: "home1_raw_data")
    |> range(start: TODAY, stop: now())
    |> filter(fn: (r) => r["_measurement"] == "m3")
    |> filter(fn: (r) => r["_field"] == "value")

DATA
    |> set(key: "source", value: "calculated")
    |> reduce(
        fn: (r, accumulator) => ({min: if r._value < accumulator.min then r._value else accumulator.min, max: if r._value > accumulator.max then r._value else accumulator.max, _time: TODAY}),
        identity: {min: 10000000000.0, max: -10000000000.0, _time: TODAY},
    )
    |> map(fn: (r) => ({r with _time: TODAY}))
    |> map(fn: (r) => ({r with _value: math.round(x: (r.max - r.min) * 1000.0) / 1000.0}))
    |> to(bucket: "home1_daily_data")
DATA
    |> set(key: "source", value: "calculated")
    |> reduce(
        fn: (r, accumulator) => ({min: if r._value < accumulator.min then r._value else accumulator.min, max: if r._value > accumulator.max then r._value else accumulator.max, _time: TODAY}),
        identity: {min: 10000000000.0, max: -10000000000.0, _time: TODAY},
    )
    |> map(fn: (r) => ({r with _time: TODAY}))
    |> map(fn: (r) => ({r with _value: math.round(x: (r.max - r.min) * 1000.0) / 1000.0}))
    |> pivot(rowKey: ["_time"], columnKey: ["entity_id"], valueColumn: "_value")
    |> map(fn: (r) => ({r with _value: math.round(x: (r.eau_adoucie - r.eau_chaude) * 1000.0) / 1000.0}))
    |> set(key: "entity_id", value: "eau_froide")
    |> to(bucket: "home1_daily_data")

# Task « daily_temperature » :

import "date"
import "experimental"
import "math"

option task = {name: "daily_temperature", cron: "0 1 * * *"}

TODAY = date.truncate(t: now(), unit: 1d)
YESTERDAY = experimental.addDuration(d: -1d, to: TODAY)
DATA = from(bucket: "home1_raw_data")
    |> range(start: YESTERDAY, stop: TODAY)
    |> filter(fn: (r) => r["_measurement"] == "°C")
    |> filter(fn: (r) => r["_field"] == "value")

DATA
    |> mean()
    |> map(fn: (r) => ({r with _time: YESTERDAY}))
    |> set(key: "statistics", value: "mean")
    |> set(key: "source", value: "calculated")
    |> map(fn: (r) => ({r with _value: math.round(x: r._value * 10.0) / 10.0}))
    |> to(bucket: "home1_daily_data")
DATA
    |> min()
    |> map(fn: (r) => ({r with _time: YESTERDAY}))
    |> set(key: "statistics", value: "min")
    |> set(key: "source", value: "calculated")
    |> to(bucket: "home1_daily_data")
DATA
    |> max()
    |> map(fn: (r) => ({r with _time: YESTERDAY}))
    |> set(key: "statistics", value: "max")
    |> set(key: "source", value: "calculated")
    |> to(bucket: "home1_daily_data")

# Task « daily_temperature_current_day » :

import "date"
import "experimental"
import "math"

option task = {name: "daily_temperature_current_day", cron: "* * * * *"}

TODAY = date.truncate(t: now(), unit: 1d)

DATA = from(bucket: "home1_raw_data")
    |> range(start: TODAY, stop: now())
    |> filter(fn: (r) => r["_measurement"] == "°C")
    |> filter(fn: (r) => r["_field"] == "value")

DATA
    |> mean()
    |> map(fn: (r) => ({r with _time: TODAY}))
    |> set(key: "statistics", value: "mean")
    |> set(key: "source", value: "calculated")
    |> map(fn: (r) => ({r with _value: math.round(x: r._value * 10.0) / 10.0}))
    |> to(bucket: "home1_daily_data")
DATA
    |> min()
    |> map(fn: (r) => ({r with _time: TODAY}))
    |> set(key: "statistics", value: "min")
    |> set(key: "source", value: "calculated")
    |> to(bucket: "home1_daily_data")
DATA
    |> max()
    |> map(fn: (r) => ({r with _time: TODAY}))
    |> set(key: "statistics", value: "max")
    |> set(key: "source", value: "calculated")
    |> to(bucket: "home1_daily_data")
2 « J'aime »

Merci beaucoup @Neuvidor et @AlexHass pour vos retours. Ca marche désormais parfaitement. Il ne me reste plus qu’à mettre tout cela en forme !

@Pulpy-Luke : je m’embête peut-être un peu mais dans le cas présent j’aime bien choisir ce que je vais envoyer mais surtout le format dans lequel je l’envoie. J’ai fait un test en envoyant directement de HA et je n’ai alors pas la main sur les colonnes. Si un jour je veux envoyer les infos d’un autre système alors cela ne correspondra pas forcément.
Là je peux envoyer les donner en provenance de MQTT, de HA ou de n’importe quel autre source. Et je trouve cela confortable.

1 « J'aime »

Salut.

Oui, par contre justement c’est assez normé influxdb, pour conserver un modèle de données homogènes et léger:

  • field key
  • field set
  • field value
  • measurement
  • point

Pour le reste, c’est les tags qui servent.
Quant à utiliser NR pour traiter l’import des données « autres », pourquoi pas mais il y a des connecteurs pour ça aussi (au même titre qu’il te faut un node NR)
Une simple question de gouts et de complexité

2 « J'aime »

Hello!

La documentation d’InfluxDB m’avait pas mal aidé à comprendre comment les données doivent être organisés, même si c’est souvent une histoire de compromis.

https://docs.influxdata.com/influxdb/v2.1/write-data/best-practices/schema-design/

1 « J'aime »

Merci pour le script.
Pour la consommation électrique journalière, j’ai fait une petite modification dans le reduce. J’exclus les valeurs à zéro pour éviter d’avoir un range énorme.

    |> reduce(
        fn: (r, accumulator) =>
            ({
                min:
                    if r._value < accumulator.min and r._value != 0.0 then
                        r._value
                    else
                        accumulator.min,
                max: if r._value > accumulator.max then r._value else accumulator.max,
                _time: r._time,
            }),
        identity: {min: 10000000000.0, max: -10000000000.0, _time: yday},
    )
    |> map(fn: (r) => ({r with _time: yday}))
    

et dans le map qui renvoi la valeur max il y a une coquille.

  |> map(fn: (r) => ({r with min: r.min}))
  |> map(fn: (r) => ({r with max: r.max}))

Superbe post!!
Je veux installer, comme vous l’avez fait, influxDB et Node-red dans des contener Proxmox, pouvez vous m’indiquer un tutoriel pour cela???
Ca m’aidera à gagner du temps sans passer par des tutos pas toujours adaptés

Il doit même y avoir moyen de se passer de tuto

Merci, pour le lien. Je n’aurais pas trouver tout seul.
Par contre je n’ai jamais créer de container dans Proxmox, uniquement des VMs. Il me manque vraiment les bases pour les containers.

Salut,

Avec le lien donné par @Pulpy-Luke tu as juste à copier coller la ligne de commande dans le shell de ton proxmox. Et ça créé et configure le container tout seul…

Dans les grandes lignes un container c’est comme une VM… ça se démarre et s’arrête pareil et tu as une shell à l’intérieur du container. La grosse différence c’est que ce n’est ps une machine virtuelle à part entière, c’est juste la couche logicielle.

1 « J'aime »

Merci, je viens de trouver comment faire. je n’était pas dans le shell de Proxmox;
Je continue de découvrir.
Je rame encore un peu pour choisir les bons espaces de stockage

Node-red s’est bien installé, j’y ai accès avec :1880
mais quel est login et le mdp:
Est-ce les mêmes que pour Proxmox???

Node-Red est fonctionnel
J’ai un peu ramé surement parce que j’avais mal copié le token dans HA

Complément:

Avec bien du mal, j’ai enfin réussi à me connecter avec Maria DB dans un container avec adminer

ça reste mon avis personnel mais :

  • autant influxdb ça peut avoir du sens de l’installer : faire une base 'filtrée" et générer des graphiques
  • autant Mariadb n’a aucun avantage par rapport à la mécanique de base : c’est plus lourd, pas forcement plus performant. De plus jouer avec adminer, c’est prendre le risque de tout casser…
1 « J'aime »

Je suis toujours en phase de test, donc j’installe et je compare
J’ai utlisé adminer pour tester le bon fonctionement de MariaDN dans LXC
Mauvaise idée???
Il me reste à connecter HA et MariaDN dans LXC
Toutes les idées sont bien venues.

Quelle différence entre MariaDB et InfluDB??

MariaDB : base de données relationnelle
InfluxDB : base de données séries temporelles

Les 2 ne sont pas destinées aux mêmes besoins.

A noter que MariaDB est en fait l’équivalent du Mysql classique

Et Grafana fonctionne avec InfluxDB uniquement?

J’'ai installé également influxDB et Grafana en LXC, mais pour l’instant je n’ai pas trouvé comment les relier à HA. Bien documenté en plugins, mais très peu de choses en LXC.

Je vais continuer à chercher/.
Cordialement

Grafana est un outils de visualisation, il peut être connecté à tout un tas de chose, il est compatible avec les 2 bases.

Pour mois avant de passer sous grafana il faut comprendre comment les données sont organisé dans ta base source.

1 « J'aime »

Je ne peux qu’être d’accord avec ça… Avant d’ajouter des extras sur HA, il faut déjà comprendre ce que ça fait de base.
De base, 90% des usages sont couverts à mon avis.
Partir sur infra plus complète ou on met tout rends plus compliqué/longue la prise en main.

1 « J'aime »