solectrus / solectrus

SOLECTRUS is a smart photovoltaic dashboard that shows your energy production and usage. It also calculates costs and savings, helping you get the most out of your solar system.
https://solectrus.de
GNU Affero General Public License v3.0
109 stars 15 forks source link

Darstellung im Graph fehlerhaft #3389

Closed boecks closed 2 months ago

boecks commented 2 months ago

Beschreibung (auf Deutsch oder Englisch)

Ich versuche gerade eine neue Solectrus Instanz aufzusetzen. Diese Instanz bekommt die Inverter Daten via MQTT. Diese Daten werden sekünglich von einem Shelly Pro 3EM auf folgendes MQTT Topic gepublished: home/inverter/power, ein entsprechendes Sensor Mapping in der ENV und im MQTT Collector existiert.

Nach einer Weile (ca. 10 Sek) beginnt der Graph scheinbar zwischen jedem Messpunkt einmal auf die Nullinie zu hüpfen. Wenn ich den Graph neu lade (zB Timeframe hin und her wechseln oder F5 drücken oder Browser Tabs wechsle) wird er richtig gezeichnet, bis er nach einigen Momenten wieder mit den Null-Sprüngen beginnt.

grafik

Gibt es dafür einen Grund? Die MQTT Messages werden direkt am Shelly mittels Script gesendet - das Intervall kann ich beliebig wählen.

Offtopic: Der Shelly Connector ist ja nur für die Verbruachermessung vorgesehen wie es scheint, nicht für die Inverter Power Messung, daher der Umweg über den MQTT Collector und das Shelly Script...

Welche Version von SOLECTRUS verwendest du?

0.15.1

Auf welchem Gerät läuft bei dir SOLECTRUS?

Synology NAS

Auszug aus dem Logfile, sofern zum Verständnis sinnvoll (z.B. bei einem Fehler 500)

No response

Dein vollständiges docker-compose.yml oder compose.yml

services:
  dashboard:
    image: ghcr.io/solectrus/solectrus:latest
    depends_on:
      postgresql:
        condition: service_healthy
      influxdb:
        condition: service_healthy
      redis:
        condition: service_healthy
    links:
      - postgresql
      - influxdb
      - redis
    ports:
      - 8080:3000
    environment:
      - TZ
      - APP_HOST
      - FORCE_SSL
      - SECRET_KEY_BASE
      - WEB_CONCURRENCY
      - INSTALLATION_DATE
      - ADMIN_PASSWORD
      - FRAME_ANCESTORS
      - CO2_EMISSION_FACTOR
      - DB_HOST=postgresql
      - DB_PASSWORD=${POSTGRES_PASSWORD}
      - DB_USER=postgres
      - REDIS_URL
      - INFLUX_HOST
      - INFLUX_TOKEN=${INFLUX_TOKEN_READ}
      - INFLUX_ORG
      - INFLUX_BUCKET
      - INFLUX_POLL_INTERVAL
      - INFLUX_SENSOR_INVERTER_POWER
      - INFLUX_SENSOR_HOUSE_POWER
      - INFLUX_SENSOR_GRID_IMPORT_POWER
      - INFLUX_SENSOR_GRID_EXPORT_POWER
      - INFLUX_SENSOR_BATTERY_CHARGING_POWER
      - INFLUX_SENSOR_BATTERY_DISCHARGING_POWER
      - INFLUX_SENSOR_BATTERY_SOC
      - INFLUX_SENSOR_WALLBOX_POWER
      - INFLUX_SENSOR_CASE_TEMP
      - INFLUX_SENSOR_INVERTER_POWER_FORECAST
      - INFLUX_SENSOR_SYSTEM_STATUS
      - INFLUX_SENSOR_SYSTEM_STATUS_OK
      - INFLUX_SENSOR_GRID_EXPORT_LIMIT
      - INFLUX_SENSOR_HEATPUMP_POWER
      - INFLUX_EXCLUDE_FROM_HOUSE_POWER
    healthcheck:
      test:
        - CMD-SHELL
        - nc -z 127.0.0.1 3000 || exit 1
      interval: 30s
      timeout: 10s
      retries: 5
      start_period: 10s
    restart: unless-stopped
    logging:
      options:
        max-size: 10m
        max-file: '3'
    labels:
      - com.centurylinklabs.watchtower.scope=solectrus

  influxdb:
    image: influxdb:2.7-alpine
    volumes:
      - ${INFLUX_VOLUME_PATH}:/var/lib/influxdb2
    environment:
      - TZ
      - DOCKER_INFLUXDB_INIT_MODE=setup
      - DOCKER_INFLUXDB_INIT_USERNAME=${INFLUX_USERNAME}
      - DOCKER_INFLUXDB_INIT_PASSWORD=${INFLUX_PASSWORD}
      - DOCKER_INFLUXDB_INIT_ORG=${INFLUX_ORG}
      - DOCKER_INFLUXDB_INIT_BUCKET=${INFLUX_BUCKET}
      - DOCKER_INFLUXDB_INIT_ADMIN_TOKEN=${INFLUX_ADMIN_TOKEN}
    command: influxd run --bolt-path /var/lib/influxdb2/influxd.bolt --engine-path /var/lib/influxdb2/engine --store disk
    ports:
      - 8086:8086
    restart: unless-stopped
    healthcheck:
      test:
        - CMD
        - influx
        - ping
      interval: 30s
      timeout: 10s
      retries: 5
      start_period: 30s
    logging:
      options:
        max-size: 10m
        max-file: '3'
    labels:
      - com.centurylinklabs.watchtower.scope=solectrus

  postgresql:
    image: postgres:16-alpine
    environment:
      - TZ
      - POSTGRES_PASSWORD
    volumes:
      - ${DB_VOLUME_PATH}:/var/lib/postgresql/data
    restart: unless-stopped
    healthcheck:
      test:
        - CMD-SHELL
        - pg_isready -U postgres
      interval: 10s
      timeout: 20s
      retries: 5
      start_period: 60s
    logging:
      options:
        max-size: 10m
        max-file: '3'
    labels:
      - com.centurylinklabs.watchtower.scope=solectrus

  redis:
    image: redis:7-alpine
    environment:
      - TZ
    volumes:
      - ${REDIS_VOLUME_PATH}:/data
    restart: unless-stopped
    healthcheck:
      test:
        - CMD
        - redis-cli
        - ping
      interval: 10s
      timeout: 20s
      retries: 5
      start_period: 60s
    logging:
      options:
        max-size: 10m
        max-file: '3'
    labels:
      - com.centurylinklabs.watchtower.scope=solectrus

  mqtt-collector:
    image: ghcr.io/solectrus/mqtt-collector:latest
    environment:
      - TZ
      - MQTT_HOST
      - MQTT_PORT
      - MQTT_SSL
      - MQTT_USERNAME
      - MQTT_PASSWORD
      - INFLUX_HOST
      - INFLUX_SCHEMA
      - INFLUX_PORT
      - INFLUX_TOKEN=${INFLUX_TOKEN_WRITE}
      - INFLUX_ORG
      - INFLUX_BUCKET
      - MAPPING_0_TOPIC
      - MAPPING_0_MEASUREMENT
      - MAPPING_0_FIELD
      - MAPPING_0_TYPE
      - MAPPING_1_TOPIC
      - MAPPING_1_MEASUREMENT
      - MAPPING_1_FIELD
      - MAPPING_1_TYPE
      - MAPPING_2_TOPIC
      - MAPPING_2_MEASUREMENT_POSITIVE
      - MAPPING_2_MEASUREMENT_NEGATIVE
      - MAPPING_2_FIELD_POSITIVE
      - MAPPING_2_FIELD_NEGATIVE
      - MAPPING_2_TYPE
    restart: unless-stopped
    logging:
      options:
        max-size: 10m
        max-file: '3'
    depends_on:
      influxdb:
        condition: service_healthy
    links:
      - influxdb
    labels:
      - com.centurylinklabs.watchtower.scope=solectrus

  forecast-collector:
    image: ghcr.io/solectrus/forecast-collector:latest
    environment:
      - TZ
      - FORECAST_PROVIDER
      - FORECAST_LATITUDE
      - FORECAST_LONGITUDE
      - FORECAST_DECLINATION
      - FORECAST_AZIMUTH
      - FORECAST_KWP
      - FORECAST_CONFIGURATIONS
      - FORECAST_0_LATITUDE
      - FORECAST_0_LONGITUDE
      - FORECAST_0_DECLINATION
      - FORECAST_0_AZIMUTH
      - FORECAST_0_KWP
      - FORECAST_0_DAMPING_MORNING
      - FORECAST_0_DAMPING_EVENING
      - FORECAST_1_LATITUDE
      - FORECAST_1_LONGITUDE
      - FORECAST_1_DECLINATION
      - FORECAST_1_AZIMUTH
      - FORECAST_1_KWP
      - FORECAST_1_DAMPING_MORNING
      - FORECAST_1_DAMPING_EVENING
      - FORECAST_2_LATITUDE
      - FORECAST_2_LONGITUDE
      - FORECAST_2_DECLINATION
      - FORECAST_2_AZIMUTH
      - FORECAST_2_KWP
      - FORECAST_2_DAMPING_MORNING
      - FORECAST_2_DAMPING_EVENING
      - FORECAST_3_LATITUDE
      - FORECAST_3_LONGITUDE
      - FORECAST_3_DECLINATION
      - FORECAST_3_AZIMUTH
      - FORECAST_3_KWP
      - FORECAST_3_DAMPING_MORNING
      - FORECAST_3_DAMPING_EVENING
      - FORECAST_INTERVAL
      - FORECAST_SOLAR_APIKEY
      - INFLUX_HOST
      - INFLUX_PORT
      - INFLUX_SCHEMA
      - INFLUX_TOKEN=${INFLUX_TOKEN_WRITE}
      - INFLUX_ORG
      - INFLUX_BUCKET
      - INFLUX_MEASUREMENT=${FORECAST_INFLUX_MEASUREMENT}
      - FORECAST_DAMPING_MORNING
      - FORECAST_DAMPING_EVENING
      - FORECAST_HORIZON
      - FORECAST_INVERTER
      - SOLCAST_APIKEY
      - SOLCAST_SITE
      - SOLCAST_0_SITE
      - SOLCAST_1_SITE
    restart: unless-stopped
    logging:
      options:
        max-size: 10m
        max-file: '3'
    depends_on:
      influxdb:
        condition: service_healthy
    links:
      - influxdb
    labels:
      - com.centurylinklabs.watchtower.scope=solectrus

  power-splitter:
    image: ghcr.io/solectrus/power-splitter:latest
    environment:
      - TZ
      - INFLUX_HOST
      - INFLUX_SCHEMA
      - INFLUX_PORT
      - INFLUX_TOKEN=${INFLUX_ADMIN_TOKEN}
      - INFLUX_ORG
      - INFLUX_BUCKET
      - INFLUX_SENSOR_HOUSE_POWER
      - INFLUX_SENSOR_GRID_IMPORT_POWER
      - INFLUX_SENSOR_WALLBOX_POWER
      - INFLUX_SENSOR_HEATPUMP_POWER
      - INFLUX_EXCLUDE_FROM_HOUSE_POWER
      - REDIS_URL
    logging:
      options:
        max-size: 10m
        max-file: '3'
    restart: unless-stopped
    depends_on:
      influxdb:
        condition: service_healthy
    links:
      - influxdb
    labels:
      - com.centurylinklabs.watchtower.scope=solectrus

  watchtower:
    image: containrrr/watchtower
    environment:
      - TZ
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock
    command: --scope solectrus --cleanup
    restart: unless-stopped
    logging:
      options:
        max-size: 10m
        max-file: '3'
    labels:
      - com.centurylinklabs.watchtower.scope=solectrus

Dein vollständiges .env

TZ=Europe/Vienna
APP_HOST=xxxxxxxxxxxxxxxxx
INSTALLATION_DATE=2023-09-11
ADMIN_PASSWORD=xxxxxxxxxxxxxxxxx
INFLUX_POLL_INTERVAL=5
FORCE_SSL=false
SECRET_KEY_BASE=xxxxxxxxxxxxxxxxx
WEB_CONCURRENCY=0
CO2_EMISSION_FACTOR=312
POSTGRES_PASSWORD=xxxxxxxxxxxxxxxxx
INFLUX_SENSOR_INVERTER_POWER=pv:inverter_power
INFLUX_SENSOR_HOUSE_POWER=pv:house_power
INFLUX_SENSOR_GRID_IMPORT_POWER=pv:grid_import_power
INFLUX_SENSOR_GRID_EXPORT_POWER=pv:grid_export_power
INFLUX_SENSOR_INVERTER_POWER_FORECAST=forecast:watt
MQTT_HOST=xxxxxxxxxxxxxxxxx
MQTT_PORT=1883
MQTT_SSL=false
MQTT_USERNAME=xxxxxxxxxxxxxxxxx
MQTT_PASSWORD=xxxxxxxxxxxxxxxxx
MAPPING_0_TOPIC=home/inverter/power
MAPPING_0_MEASUREMENT=pv
MAPPING_0_FIELD=inverter_power
MAPPING_0_TYPE=integer
MAPPING_1_TOPIC=home/energy/house_power
MAPPING_1_MEASUREMENT=pv
MAPPING_1_FIELD=house_power
MAPPING_1_TYPE=integer
MAPPING_2_TOPIC=home/grid/power
MAPPING_2_MEASUREMENT_POSITIVE=pv
MAPPING_2_MEASUREMENT_NEGATIVE=pv
MAPPING_2_FIELD_POSITIVE=grid_import_power
MAPPING_2_FIELD_NEGATIVE=grid_export_power
MAPPING_2_TYPE=integer
FORECAST_PROVIDER=solcast
FORECAST_CONFIGURATIONS=1
FORECAST_INTERVAL=8640
FORECAST_INFLUX_MEASUREMENT=forecast
SOLCAST_APIKEY=xxxxxxxxxxxxxxxxx
SOLCAST_SITE=xxxxxxxxxxxxxxxxx
SOLCAST_0_SITE=xxxxxxxxxxxxxxxxx
INFLUX_HOST=influxdb
INFLUX_SCHEMA=http
INFLUX_PORT=8086
INFLUX_ORG=solectrus
INFLUX_USERNAME=xxxxxxxxxxxxxxxxx
INFLUX_PASSWORD=xxxxxxxxxxxxxxxxx
INFLUX_ADMIN_TOKEN=xxxxxxxxxxxxxxxxx
INFLUX_BUCKET=solectrus
INFLUX_TOKEN_WRITE=xxxxxxxxxxxxxxxxx
INFLUX_TOKEN_READ=xxxxxxxxxxxxxxxxx
INFLUX_VOLUME_PATH=/volume1/docker/solectrus/influxdb
DB_VOLUME_PATH=/volume1/docker/solectrus/postgresql
REDIS_VOLUME_PATH=/volume1/docker/solectrus/redis
REDIS_URL=redis://redis:6379/1

Gibt es sonst noch etwas?

Shelly Script für die MQTT Nachrichten:

// Configuration: Set the MQTT topic and the publishing interval
let mqttTopic = "home/inverter/power";
let publishInterval = 1000;  // Publish every 1000 milliseconds (1 second)

// Function to retrieve the total active power data
function getTotalActivePower() {
    let powerData = Shelly.getComponentStatus("em", 0).total_act_power;

    // Process the power data: Multiply by -1 and ensure non-negative result
    powerData = processPowerData(powerData);

    return powerData;
}

// Function to process the power data
function processPowerData(powerData) {
    // Multiply by -1
    powerData *= -1;

    // Ensure the value is non-negative
    if (powerData < 0) {
        powerData = 0;
    }

    return powerData;
}

// Function to publish the processed power data to the MQTT topic
function publishPowerData() {
    let powerValue = getTotalActivePower();

    // Convert the power value to a JSON string and publish to the MQTT topic
    MQTT.publish(mqttTopic, JSON.stringify(powerValue), 0, false);

    // Optional: Log the published value for debugging purposes
    // print("Published power value: ", powerValue);
}

// Timer to periodically publish the power data
let notifyTimer = Timer.set(publishInterval, true, function() {
    publishPowerData();
});
ledermann commented 2 months ago

Der Grund für das "Hüpfen" ist vermutlich, dass immer wieder tatsächlich der Wert 0 geschrieben wird, verursacht vermutlich durch diese Zeile in deinem Script:

// Ensure the value is non-negative
if (powerData < 0) {
  powerData = 0;
}

Dass dies erst nach einigen Sekunden sichtbar wird, liegt daran, dass bei JETZT zunächst eine gemittelte Darstellung der letzten Stunde erfolgt, dann aber im 5-Sekunden-Takt fortgeschrieben wird.

Insofern denke ich, dass eine Anpassung deines Scripts helfen sollte. Es darf keine 0 geschrieben werden, wenn nicht die Messung wirklich 0 ist, also in deinem Fall in der Nacht.

Zum Shelly-Collector noch: Ja, der ist eigentlich dafür gedacht, den Verbrauch zu messen. Aber wenn der Shelly bei dir so angeschlossen ist, dass er die Erzeugung misst, dann spricht eigentlich nichts dagegen, den Collector dennoch zu verwenden. Er schreibt in ein Measurement deiner Wahl (z.B. inverter) und dort ins Field power. Du musst dann das Dashboard nur so konfigurieren, dass es die Erzeugung von dort gelesen wird. Beispiel:

Shelly-Collector:

INFLUX_MEASUREMENT=inverter

Dashboard:

INFLUX_SENSOR_INVERTER_POWER=inverter:power
boecks commented 2 months ago

Habe es korrigiert und du hattest Recht. Danke für den Hinweis! Empfiehlst du eher den Shelly Connector für meinen Use Case? Das Problem mit dem Shelly wäre doch, dass ich den Verbrauch des Wechselrichters (bzw. sind es mehrere Per-Panel Wechselrichter) dann in meinem PV Inverter Power Measurement bekomme oder täusche ich mich? Ich müsste jedenfalls garantieren, dass negative werte als 0 interpretiert werden.

ledermann commented 2 months ago

Unabhängig von deiner elektrischen Installation arbeitet der Shelly-Collector so:

Im 5-Sekunden-Takt (konfigurierbar) holt der sich über die HTTP-Schnittstelle des Shelly (http://[ip-adresse]/rpc/Shelly.GetStatus) die aktuellen Messwerte (insbesondere total_act_power) und legt diese in InfluxDB ab - im selbst festzulegenden Measurement.

Wie dieser Messwert interpretiert wird (Verbrauch? Oder doch Erzeugung?) ist Sache des Dashboards. Mit dem Eintrag INFLUX_SENSOR_INVERTER_POWER=inverter:power erfolgt eine Behandlung als Erzeugung.

Eine Spezialbehandlung von negativen Werten erfolgt aber nicht. Wie können die denn überhaupt auftreten? Ich könnte im Shelly-Collector eine Werte-Korrektur ergänzen, sodass immer positiv (oder 0) geschrieben wird.

Oder du bleibst beim MQTT-Collector.

boecks commented 2 months ago

Ich habe einen Pro 3 EM an der Wurzel der PV Anlage (3 Phasen) und einen Pro 3 EM an der Wurzel beim Netzanschluss. Wenn die PV erzeugt kommen positive Werte, wenn nicht, messe ich den Verbrauch der Wechselrichter und bekomme negative Werte. Zumindest theoretisch...

ledermann commented 2 months ago

Ich vermute, dieses Issue ist hier erledigt, ich schließe es somit. Diskussion über den Umgang mit zwei Shellys und die Errechnung des Hausverbrauchs in #3392.