PVOUTPUT uploader via nodered

Here is a flow i put together to upload my sensor data from HA to PVOutput.org.
This started off as a shell command posted by “Greenhouse” and flow posted by " Paul_F_Prinsloo".

[
    {
        "id": "24befeed.0cb5f2",
        "type": "tab",
        "label": "pvoutput",
        "disabled": false,
        "info": ""
    },
    {
        "id": "ab922e29.c77f7",
        "type": "api-current-state",
        "z": "24befeed.0cb5f2",
        "name": "Daily Solar (Watt hours)",
        "server": "cbbf6ce3.4f5eb",
        "version": 1,
        "outputs": 1,
        "halt_if": "",
        "halt_if_type": "str",
        "halt_if_compare": "is",
        "override_topic": false,
        "entity_id": "sensor.daily_solar_energy_wh",
        "state_type": "str",
        "state_location": "payload",
        "override_payload": "msg",
        "entity_location": "",
        "override_data": "none",
        "blockInputOverrides": false,
        "x": 230,
        "y": 220,
        "wires": [
            [
                "eca1effa.95a05"
            ]
        ]
    },
    {
        "id": "5948f34d.abb71c",
        "type": "inject",
        "z": "24befeed.0cb5f2",
        "name": "Every 5 Minutes",
        "topic": "",
        "payload": "",
        "payloadType": "date",
        "repeat": "300",
        "crontab": "",
        "once": false,
        "onceDelay": "",
        "x": 210,
        "y": 140,
        "wires": [
            [
                "ab922e29.c77f7",
                "17526967.a07137",
                "a50a6ebe.4e214",
                "f6d9d089.0c42d",
                "7fd1ce58.84e1a",
                "e5e9e6d9.dd8f38",
                "cebb4284.5ca53",
                "82eeed4d.79b77"
            ]
        ]
    },
    {
        "id": "48a83575.9c7a9c",
        "type": "join",
        "z": "24befeed.0cb5f2",
        "name": "",
        "mode": "custom",
        "build": "object",
        "property": "payload",
        "propertyType": "msg",
        "key": "topic",
        "joiner": "\\n",
        "joinerType": "str",
        "accumulate": true,
        "timeout": "",
        "count": "8",
        "reduceRight": false,
        "reduceExp": "",
        "reduceInit": "",
        "reduceInitType": "",
        "reduceFixup": "",
        "x": 630,
        "y": 140,
        "wires": [
            [
                "99438705.c661e8"
            ]
        ]
    },
    {
        "id": "eca1effa.95a05",
        "type": "change",
        "z": "24befeed.0cb5f2",
        "name": "V1",
        "rules": [
            {
                "t": "set",
                "p": "topic",
                "pt": "msg",
                "to": "v1",
                "tot": "str"
            }
        ],
        "action": "",
        "property": "",
        "from": "",
        "to": "",
        "reg": false,
        "x": 450,
        "y": 220,
        "wires": [
            [
                "48a83575.9c7a9c"
            ]
        ]
    },
    {
        "id": "17526967.a07137",
        "type": "api-current-state",
        "z": "24befeed.0cb5f2",
        "name": "Total Solar Power (Watts)",
        "server": "cbbf6ce3.4f5eb",
        "version": 1,
        "outputs": 1,
        "halt_if": "",
        "halt_if_type": "str",
        "halt_if_compare": "is",
        "override_topic": false,
        "entity_id": "sensor.total_solar_power",
        "state_type": "str",
        "state_location": "payload",
        "override_payload": "msg",
        "entity_location": "",
        "override_data": "none",
        "blockInputOverrides": false,
        "x": 230,
        "y": 260,
        "wires": [
            [
                "21c59578.da1c0a"
            ]
        ]
    },
    {
        "id": "a50a6ebe.4e214",
        "type": "api-current-state",
        "z": "24befeed.0cb5f2",
        "name": "Daily Consumed (Watt hours)",
        "server": "cbbf6ce3.4f5eb",
        "version": 1,
        "outputs": 1,
        "halt_if": "",
        "halt_if_type": "str",
        "halt_if_compare": "is",
        "override_topic": false,
        "entity_id": "sensor.daily_consumed_energy_wh",
        "state_type": "str",
        "state_location": "payload",
        "override_payload": "msg",
        "entity_location": "",
        "override_data": "none",
        "blockInputOverrides": false,
        "x": 240,
        "y": 300,
        "wires": [
            [
                "6363034f.3ac2ec"
            ]
        ]
    },
    {
        "id": "f6d9d089.0c42d",
        "type": "api-current-state",
        "z": "24befeed.0cb5f2",
        "name": "Consumed Power (Watts)",
        "server": "cbbf6ce3.4f5eb",
        "version": 1,
        "outputs": 1,
        "halt_if": "",
        "halt_if_type": "str",
        "halt_if_compare": "is",
        "override_topic": false,
        "entity_id": "sensor.total_consumed_power",
        "state_type": "str",
        "state_location": "payload",
        "override_payload": "msg",
        "entity_location": "",
        "override_data": "none",
        "blockInputOverrides": false,
        "x": 230,
        "y": 340,
        "wires": [
            [
                "853e698f.ac7f88"
            ]
        ]
    },
    {
        "id": "7fd1ce58.84e1a",
        "type": "api-current-state",
        "z": "24befeed.0cb5f2",
        "name": "Temperature",
        "server": "cbbf6ce3.4f5eb",
        "version": 1,
        "outputs": 1,
        "halt_if": "",
        "halt_if_type": "str",
        "halt_if_compare": "is",
        "override_topic": false,
        "entity_id": "sensor.dark_sky_temperature_0h",
        "state_type": "str",
        "state_location": "payload",
        "override_payload": "msg",
        "entity_location": "",
        "override_data": "none",
        "blockInputOverrides": false,
        "x": 190,
        "y": 380,
        "wires": [
            [
                "5580eb68.666464"
            ]
        ]
    },
    {
        "id": "e5e9e6d9.dd8f38",
        "type": "api-current-state",
        "z": "24befeed.0cb5f2",
        "name": "Volts",
        "server": "cbbf6ce3.4f5eb",
        "version": 1,
        "outputs": 1,
        "halt_if": "",
        "halt_if_type": "str",
        "halt_if_compare": "is",
        "override_topic": false,
        "entity_id": "sensor.utility_voltage",
        "state_type": "str",
        "state_location": "payload",
        "override_payload": "msg",
        "entity_location": "",
        "override_data": "none",
        "blockInputOverrides": false,
        "x": 170,
        "y": 420,
        "wires": [
            [
                "4e6cb33e.56467c"
            ]
        ]
    },
    {
        "id": "21c59578.da1c0a",
        "type": "change",
        "z": "24befeed.0cb5f2",
        "name": "V2",
        "rules": [
            {
                "t": "set",
                "p": "topic",
                "pt": "msg",
                "to": "v2",
                "tot": "str"
            }
        ],
        "action": "",
        "property": "",
        "from": "",
        "to": "",
        "reg": false,
        "x": 450,
        "y": 260,
        "wires": [
            [
                "48a83575.9c7a9c"
            ]
        ]
    },
    {
        "id": "6363034f.3ac2ec",
        "type": "change",
        "z": "24befeed.0cb5f2",
        "name": "V3",
        "rules": [
            {
                "t": "set",
                "p": "topic",
                "pt": "msg",
                "to": "v3",
                "tot": "str"
            }
        ],
        "action": "",
        "property": "",
        "from": "",
        "to": "",
        "reg": false,
        "x": 450,
        "y": 300,
        "wires": [
            [
                "48a83575.9c7a9c"
            ]
        ]
    },
    {
        "id": "853e698f.ac7f88",
        "type": "change",
        "z": "24befeed.0cb5f2",
        "name": "V4",
        "rules": [
            {
                "t": "set",
                "p": "topic",
                "pt": "msg",
                "to": "v4",
                "tot": "str"
            }
        ],
        "action": "",
        "property": "",
        "from": "",
        "to": "",
        "reg": false,
        "x": 450,
        "y": 340,
        "wires": [
            [
                "48a83575.9c7a9c"
            ]
        ]
    },
    {
        "id": "5580eb68.666464",
        "type": "change",
        "z": "24befeed.0cb5f2",
        "name": "V5",
        "rules": [
            {
                "t": "set",
                "p": "topic",
                "pt": "msg",
                "to": "v5",
                "tot": "str"
            }
        ],
        "action": "",
        "property": "",
        "from": "",
        "to": "",
        "reg": false,
        "x": 450,
        "y": 380,
        "wires": [
            [
                "48a83575.9c7a9c"
            ]
        ]
    },
    {
        "id": "4e6cb33e.56467c",
        "type": "change",
        "z": "24befeed.0cb5f2",
        "name": "V6",
        "rules": [
            {
                "t": "set",
                "p": "topic",
                "pt": "msg",
                "to": "v6",
                "tot": "str"
            }
        ],
        "action": "",
        "property": "",
        "from": "",
        "to": "",
        "reg": false,
        "x": 450,
        "y": 420,
        "wires": [
            [
                "48a83575.9c7a9c"
            ]
        ]
    },
    {
        "id": "99438705.c661e8",
        "type": "function",
        "z": "24befeed.0cb5f2",
        "name": "Set API key here 1",
        "func": "msg.action = msg.payload;\nmsg.headers = { \n 'X-Pvoutput-Apikey': 'xxxxxxxxxxxxxxxxxxx',\n 'X-Pvoutput-SystemId': 'xxxxxx',\n 'Content-Type': 'application/x-www-form-urlencoded'\n};\n\nreturn msg;",
        "outputs": 1,
        "noerr": 0,
        "x": 710,
        "y": 200,
        "wires": [
            [
                "d19b860d.981778"
            ]
        ]
    },
    {
        "id": "d19b860d.981778",
        "type": "function",
        "z": "24befeed.0cb5f2",
        "name": "Set up data",
        "func": "msg.action = msg.payload;\n\nmsg.url = \"http://pvoutput.org/service/r2/addstatus.jsp\";\n\n\n\nreturn msg;\n\n",
        "outputs": 1,
        "noerr": 0,
        "x": 710,
        "y": 260,
        "wires": [
            [
                "172a0ac0.cd35c5"
            ]
        ]
    },
    {
        "id": "172a0ac0.cd35c5",
        "type": "http request",
        "z": "24befeed.0cb5f2",
        "name": "Post",
        "method": "POST",
        "ret": "txt",
        "paytoqs": false,
        "url": "",
        "tls": "",
        "persist": false,
        "proxy": "",
        "authType": "",
        "x": 710,
        "y": 320,
        "wires": [
            [
                "ea2bee87.e3fec"
            ]
        ]
    },
    {
        "id": "ea2bee87.e3fec",
        "type": "debug",
        "z": "24befeed.0cb5f2",
        "name": "headercheck",
        "active": true,
        "tosidebar": true,
        "console": false,
        "tostatus": false,
        "complete": "true",
        "targetType": "full",
        "x": 710,
        "y": 380,
        "wires": []
    },
    {
        "id": "cebb4284.5ca53",
        "type": "moment",
        "z": "24befeed.0cb5f2",
        "name": "Date",
        "topic": "d",
        "input": "",
        "inputType": "date",
        "inTz": "America/Edmonton",
        "adjAmount": 0,
        "adjType": "days",
        "adjDir": "add",
        "format": "YYYYMMDD",
        "locale": "C",
        "output": "payload",
        "outputType": "msg",
        "outTz": "America/Edmonton",
        "x": 450,
        "y": 140,
        "wires": [
            [
                "48a83575.9c7a9c"
            ]
        ]
    },
    {
        "id": "82eeed4d.79b77",
        "type": "moment",
        "z": "24befeed.0cb5f2",
        "name": "Time",
        "topic": "t",
        "input": "",
        "inputType": "date",
        "inTz": "America/Edmonton",
        "adjAmount": 0,
        "adjType": "days",
        "adjDir": "add",
        "format": "HH:mm",
        "locale": "C",
        "output": "payload",
        "outputType": "msg",
        "outTz": "America/Edmonton",
        "x": 450,
        "y": 180,
        "wires": [
            [
                "48a83575.9c7a9c"
            ]
        ]
    },
    {
        "id": "cbbf6ce3.4f5eb",
        "type": "server",
        "z": "",
        "name": "Home Assistant",
        "legacy": false,
        "hassio": true,
        "rejectUnauthorizedCerts": true,
        "ha_boolean": "y|yes|true|on|home|open",
        "connectionDelay": false
    }
]
6 Likes

Thanks a lot for this. I adapted it to my use case but it’s been really useful having 99% ot the work already done.

Thanks for the feedback!
I also have the config files for all my shelly power sensors if that interests anyone.

Sorry for the late response, I got no notification for your reply, strange.

Lots of shellies here, and always looking for best practices, would be glad to see if I can reuse it. Thanks. :slight_smile:

sensors.yaml

- platform: mqtt
  name: "Utility Power 1"
  state_topic: "shellies/Utility/emeter/0/power"
  qos: 1
  unit_of_measurement: "W"
  icon: mdi:gauge
- platform: mqtt
  name: "Utility Power 2"
  state_topic: "shellies/Utility/emeter/1/power"
  qos: 1
  unit_of_measurement: "W"
  icon: mdi:gauge        
- platform: mqtt
  name: "Utility Voltage"
  state_topic: "shellies/Utility/emeter/1/voltage"
  qos: 1
  unit_of_measurement: "V"
  icon: mdi:gauge   
- platform: mqtt
  name: "Utility Energy 1"
  state_topic: "shellies/Utility/emeter/0/energy"
  qos: 1
  unit_of_measurement: "Wm"
- platform: mqtt
  name: "Utility Energy 2"
  state_topic: "shellies/Utility/emeter/1/energy"
  qos: 1
  unit_of_measurement: "Wm"

- platform: mqtt
  name: "Solar Power 1 Raw"
  state_topic: "shellies/Solar/emeter/0/power"
  qos: 1
  unit_of_measurement: "W"
  icon: mdi:gauge
- platform: mqtt
  name: "Solar Power 2 Raw"
  state_topic: "shellies/Solar/emeter/1/power"
  qos: 1
  unit_of_measurement: "W"
  icon: mdi:gauge        
- platform: mqtt
  name: "Solar Voltage"
  state_topic: "shellies/Solar/emeter/1/voltage"
  qos: 1
  unit_of_measurement: "V"
  icon: mdi:gauge   
- platform: mqtt
  name: "Solar Energy 1"
  state_topic: "shellies/Solar/emeter/0/energy"
  qos: 1
  unit_of_measurement: "Wm"
- platform: mqtt
  name: "Solar Energy 2"
  state_topic: "shellies/Solar/emeter/1/energy"
  qos: 1
  unit_of_measurement: "Wm"


- platform: integration
  source: sensor.total_solar_power
  name: total_solar_energy
  unit_prefix: k
  round: 2   
- platform: integration
  source: sensor.total_utility_power
  name: total_utility_energy
  unit_prefix: k
  round: 2  
- platform: integration
  source: sensor.total_consumed_power
  name: total_consumed_energy
  unit_prefix: k
  round: 2
  
- platform: template
  sensors:
    solar_power_1:
      value_template: >
        {{ (states('sensor.solar_power_1_raw')|float - 3) |round(2) }}
      friendly_name: 'Solar Power 1'
      unit_of_measurement: 'W'   
    solar_power_2:
      value_template: >
        {{ (states('sensor.solar_power_2_raw')|float - 3) |round(2) }}
      friendly_name: 'Solar Power 2'
      unit_of_measurement: 'W'       
    consumed_power_1:
      value_template: >
        {{ (states('sensor.solar_power_1')|float 
        + states('sensor.utility_power_1')|float) |round(2) }}
      friendly_name: 'Consumed Power 1'
      unit_of_measurement: 'W'       
    consumed_power_2:
      value_template: >
        {{ (states('sensor.solar_power_2')|float 
        + states('sensor.utility_power_2')|float) |round(2) }}
      friendly_name: 'Consumed Power 2'
      unit_of_measurement: 'W'          
    total_consumed_power:
      value_template: >
        {{ (states('sensor.consumed_power_1')|float 
        + states('sensor.consumed_power_2')|float)| round(2) }}
      friendly_name: 'Total Consumed Power'
      unit_of_measurement: 'W'   
    total_solar_power:
      value_template: >
        {{ (states('sensor.solar_power_1')|float 
        + states('sensor.solar_power_2')|float)| round(2) }}
      friendly_name: 'Total Solar Power'
      unit_of_measurement: 'W'       
    total_utility_power:
      value_template: >
        {{ (states('sensor.utility_power_1')|float 
        + states('sensor.utility_power_2')|float)| round(2) }}
      friendly_name: 'Total Utility Power'
      unit_of_measurement: 'W'
    total_solar_power_kw:
      value_template: >
        {{ (states('sensor.total_solar_power')|float /1000) | round(2) }}
      friendly_name: 'Total Solar Power kW'
      unit_of_measurement: 'kW'       
    total_utility_power_kw:
      value_template: >
        {{ (states('sensor.total_utility_power')|float /1000) | round(2) }}
      friendly_name: 'Total Utility Power kW'
      unit_of_measurement: 'kW'
    daily_solar_energy_wh:
      value_template: >
        {{ (states('sensor.solar_daily_energy_peak')|float *1000) | round(2) }}
      friendly_name: 'Daily Solar Energy Wh'
      unit_of_measurement: 'Wh'         
    daily_consumed_energy_wh:
      value_template: >
        {{ (states('sensor.consumed_daily_energy_peak')|float *1000) | round(2) }}
      friendly_name: 'Daily Solar Energy Wh'
      unit_of_measurement: 'Wh'

utility_meter.yaml

  utility_yearly_energy:
    source: sensor.total_utility_energy
    cycle: yearly
    tariffs: 
      - peak
    net_consumption: false


  utility_monthly_energy:
    source: sensor.total_utility_energy
    cycle: monthly
    net_consumption: false
    tariffs: 
      - peak
    
  utility_daily_energy:
    source: sensor.total_utility_energy
    cycle: daily  
    net_consumption: false
    tariffs: 
      - peak
    
  solar_daily_energy:
    source: sensor.total_solar_energy
    cycle: daily  
    net_consumption: true
    tariffs: 
      - peak
    
  solar_monthly_energy:
    source: sensor.total_solar_energy
    cycle: monthly  
    net_consumption: true
    tariffs: 
      - peak    

  solar_yearly_energy:
    source: sensor.total_solar_energy
    cycle: yearly  
    net_consumption: true
    tariffs: 
      - peak
      
  consumed_daily_energy:
    source: sensor.total_consumed_energy
    cycle: daily  
    net_consumption: true
    tariffs: 
      - peak    
      
  consumed_monthly_energy:
    source: sensor.total_consumed_energy
    cycle: monthly  
    net_consumption: true
    tariffs: 
      - peak    
      
  consumed_yearly_energy:
    source: sensor.total_consumed_energy
    cycle: yearly  
    net_consumption: true
    tariffs: 
      - peak

I just wanted to say thank you very much for sharing this - very helpful!

Hopefully someone can help, I’ve copied the Node-Red flow into my HA setup and then inputted my API Key & System ID but not getting anything uploaded to my PVOutput page, all suggestions would be greatly appreciated

Hi Graham, sorry it’s been a while since I last visited the forum. Were you able to sort out your problem?

I did, but not by using NodeRed. I’ve ended up doing it using curl commands in my configuration.yaml file along with some automations in HA which is working perfectly. Thanks for asking

Hello @Picnic
Trying to get this PVOutput upload working from HA and your node-red solution sounded like a winner for me. Realizing this is an old thread, wondering if you could clarify something for me.
I have my data coming from solar with a Tesla powerwall configuration. So that is the raw source for me instead of shellys. However I am looking at your sensors and templates trying to track what values are needed for upload. I am starting with just solar for testing while I wrap my head around this.
So, I see your solar is two raw points combined into one as sensor.total_solar_power expressed in watts. Looks like that becomes your V2. All good and understood.
Then you have an integration to get sensor.total_solar_energy expressed in Wh’s? Is that correct?
Is this your V1 value?
I get a little lost with this . daily_solar_energy_wh
Where does sensor.solar_daily_energy_peak come from? Where is it used? Is that your V1?

daily_solar_energy_wh:
      value_template: >
        {{ (states('sensor.solar_daily_energy_peak')|float *1000) | round(2) }}
      friendly_name: 'Daily Solar Energy Wh'
      unit_of_measurement: 'Wh'

Thanks for your efforts and time here.
Cheers!

So I ended up moving away from PVOUTPUT when the Energy dashboard came around. I no longer have that sensor setup in my config.

If you are interested in that I can share my config.

@picnic Thanks, I got it sorted and PVoutput uploads are working fine for me now. Also using the the energy dashboard here but PVoutput has all my history from Day 1 of solar so want to keep that going.
Cheers

I just found this solution and it is really great. I have one issue. The last upload of each day goes to the next day with this flow. I measn that each day the first upload is the last day’s last status, so e.g. 9th September starts in PVOutput with 9th September 23:55 and contains the data of 8th September 23:55 Then continues to upload with the first data of 9th September. So right after midnight I can see the current day’s 23:55 data even if it is only 00:10
I tried to add a “condition” not to upload data between 23:53 and 00:03 but the olny change that the first uplod of the day changed to 23:50 :frowning:
What could I do? (I changed the Input and Output Timezone of Date formatter node to Europe /Budapest because I live here.

Sorry for the late kick, but did you find a solution? Experiencing the same problem here.

I’m experiencing the same issue… Any solution?

Hmm interesting problem. Are you all using the Date/Time Formatter node to make your date and time?
I’m guessing this issues lies with that and the timing of the join node.

Sorry for late reply. Yes, I’m using NodeRed date/time formatter nodes, both on the correct timezone.

Anyone found a solution yet?

Even if my last upload of the day is at 22:55, it still shows as the next day…

Well, I’ve made a change to the join. Instead of waiting for 5 message parts and the next, I’ve cleared the 5, and I let the join send the message after 2 seconds. That seems to work :slight_smile: