PVOuput Uploader


Has anyone already created python code to upload Electricity Consumption and Solar Generation data from HA up into PVOutput ( http://pvoutput.org/ )

(I know there’s a plugin to download data, but I’m talking about uploading here)


found this: https://github.com/MartijnBraam/pvoutput


Can you please provide a guide on how you got it to work? I’m looking to do the same thing but don’t really know how to get it working. Any help/config examples would be very much appreciated!


I pretty much just used the cost from MartiynBraam I linked to above…he gives an example on his front page which then calls the code he has in the init.py file…


OK, so I have just got around to playing with this. I have put the script in my python_scripts folder and am using an automation to call it but not getting anywhere with it. My automation is:

- alias: UploadPvoutput
hide_entity: False
  platform: time
  minutes: '/5'
  - service: python_script.pvoutput

Right now I’m just using static numbers to test it but understand I need a template to feed actual numbers.

I’ve tried a few variations of what the ‘data’ looks like to no avail - all I keep getting is:

“Invalid config for [automation]: expected dict for dictionary value @ data[‘action’][0][‘data’]. Got None.”

Can you please help?


I haven’t touched HA for some months but I can show you what I have if that helps…probably bad coding practice throughout :slight_smile:

(1) picking up current cost values and publishing to MQTT queue so it’s available:

# -*- coding: utf-8 -*-
# CurrentCostReader.py

import serial
import xml.etree.ElementTree as ET
import paho.mqtt.client as mqtt
import json

MQTT_Host = "localhost"
MQTT_Port = "1883"
MQTT_User = "homeassistant"
MQTT_Password = "XXXXX"
SERIAL_PORT = '/dev/ttyUSB0'

client = mqtt.Client("current-cost-publisher") 
client.username_pw_set(MQTT_User, MQTT_Password)

def get_data(port=SERIAL_PORT):  
        # Read In Data from Serial
        ser = serial.Serial(port, 57600)
        xmldata = ser.readline().strip().decode("utf-8")

        # Parse Values from XML
        root = ET.fromstring(xmldata)
        sensor = root.find('sensor').text
        power = float(root.find('ch1')[0].text)

        return (sensor, power)

                # Get Data
                (sensor, power) = get_data()
                # state_topic
                stateTopic = "current-cost/%s" % (sensor)
                # payload
                payload = {}
                payload["power"] = power
                payloadJson = json.dumps(payload)
                # publish
                print("PUBLISH: %s : %s" % (stateTopic, payloadJson))
                client.publish(stateTopic, payloadJson)


Then using App Daemon as the means to run a ongoing app to monitor the updates the following:

import appdaemon.plugins.hass.hassapi as hass
from pvoutput import PvOutputApi

class PvOutputApi_AppDaemon(hass.Hass):

  def initialize(self):
     self.log("Test AppDaemon App - Initializing")
     self.run_in(self.log_pvoutput, 5)
     self.power = None
     self.solar = 0
     self.api = PvOutputApi(api_key="xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", system_id="xxxxx")

  def motion(self, entity, attribute, old, new, kwargs):
    if entity == "sensor.power_consumption":
        self.power = float(new)
    if entity == "sensor.solar_generation":
        self.solar = float(new)

  def log_pvoutput(self, kwargs):
    if (self.power is not None): 
        # Schedule next run early in case of failure calling PVOuput
        self.run_in(self.log_pvoutput, 60*5)

        # Now call PVOuput
        self.log("PVOUTPUT: Power:%s, Solar:%s" % (self.power, self.solar) )
        responseStr = self.api.update_status(self.log, power_generation=self.solar, power_consumption=self.power)

        self.log("AWAITING BOTH TO BE NOT NULL. Power=%s, Solar=%s" % (self.power, self.solar) )
        self.run_in(self.log_pvoutput, 5)

And the library the above calls:

import requests
import datetime

class PvOutputApi(object):
    def __init__(self, api_key, system_id, cumulative=False):
        self.api_key = api_key
        self.system_id = system_id
        self.cumulative = cumulative
        self.api_url = 'http://pvoutput.org/service/r2/addstatus.jsp'

    def ADLog(self, text):
        with open("/home/homeassistant/Devel/appdaemon/appdaemon.log", 'a') as log:
            log.write(text + "\n")

    def update_status(self, logFn, energy_generation=None, power_generation=None, energy_consumption=None,
                      power_consumption=None, temperature=None,
                      voltage=None, time=None):

        if not energy_generation and not power_generation and not energy_consumption and not power_consumption:
            raise ValueError("You need at least one of: enegergy_generation, power_generation,"
                             "energy_consumption, power_consumption")

        # if not time:
        time = datetime.datetime.now()

        parameters = {
            'c': "1" if self.cumulative else "0",
            'd': '{:%Y%m%d}'.format(time),
            't': '{:%H:%M}'.format(time)

        if energy_generation:
            parameters['v1'] = str(energy_generation)

        if power_generation:
            parameters['v2'] = str(power_generation)

        if energy_consumption:
            parameters['v3'] = str(energy_consumption)

        if power_consumption:
            parameters['v4'] = str(power_consumption)

        if temperature:
            parameters['v5'] = str(temperature)

        if voltage:
            parameters['v6'] = str(voltage)

        headers = {
            'X-Pvoutput-Apikey': self.api_key,
            'X-Pvoutput-SystemId': self.system_id

        res = requests.post(self.api_url, parameters, headers=headers)

        summaryStr = "PVOUTPUT POST\nURL:%s\nParameters:%s\nHeaders:%s\nResponse Code:%s\nResponse:%s\n" % (self.api_url, parameters, headers, res.status_code, res)
        return summaryStr


Thanks - I appreciate the reply but that is way beyond me.

I got a little further and am able to call that script but get a:

Traceback (most recent call last):
  File "/usr/lib/python3.6/site-packages/homeassistant/components/python_script.py", line 166, in execute
    exec(compiled.code, restricted_globals, local)
  File "pvoutput.py", line 5, in <module>
NameError: name 'object' is not defined

I think I will just go the CURL method and see where that gets me.


OK, for anybody interested, I have the solution!! It has taken a while since I’ve been busy, but it’s quite simple:

   pvoutputcurl: 'curl -d "d={{now().strftime("%Y%m%d")}}" -d "t={{now().strftime("%H:%M")}}" -d "v4={{states.sensor.studioforpvoutput_mean.state|round(0)}}" -H "X-Pvoutput-Apikey: apikeygoeshere" -H "X-Pvoutput-SystemId: idgoeshere" https://pvoutput.org/service/r2/addstatus.jsp'   

(the sensor mean has a max age of 5 minutes of realtime readings)

And then run an automation:

  - alias: UploadPvoutput
    hide_entity: False
      platform: time
      minutes: '/5'
      seconds: 00
      service: shell_command.pvoutputcurl

In the above example I am uploading “v4”. but you can do whatever you want as per the documentation:




I have copied your shell command and automation, but when it runs i get the error below. Any suggestions?

2019-01-11 11:40:05 INFO (MainThread) [homeassistant.components.automation] Executing pvoupload
2019-01-11 11:40:05 INFO (MainThread) [homeassistant.helpers.script] Script pvoupload: Running script
2019-01-11 11:40:05 INFO (MainThread) [homeassistant.helpers.script] Script pvoupload: Executing step call service

2019-01-11 11:40:05 ERROR (MainThread) [homeassistant.components.shell_command] Error rendering command template: UndefinedError: ‘None’ has no attribute ‘state’
Traceback (most recent call last):
File “/usr/src/app/homeassistant/helpers/template.py”, line 138, in async_render
return self._compiled.render(kwargs).strip()
File “/usr/local/lib/python3.6/site-packages/jinja2/asyncsupport.py”, line 76, in render
return original_render(self, *args, **kwargs)
File “/usr/local/lib/python3.6/site-packages/jinja2/environment.py”, line 1008, in render
return self.environment.handle_exception(exc_info, True)
File “/usr/local/lib/python3.6/site-packages/jinja2/environment.py”, line 780, in handle_exception
reraise(exc_type, exc_value, tb)
File “/usr/local/lib/python3.6/site-packages/jinja2/_compat.py”, line 37, in reraise
raise value.with_traceback(tb)
File “<template>”, line 1, in top-level template code
File “/usr/src/app/homeassistant/helpers/template.py”, line 446, in forgiving_round
value = round(float(value), precision)
jinja2.exceptions.UndefinedError: ‘None’ has no attribute ‘state’

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
File “/usr/src/app/homeassistant/components/shell_command.py”, line 54, in async_service_handler
rendered_args = args_compiled.async_render(service.data)
File “/usr/src/app/homeassistant/helpers/template.py”, line 140, in async_render
raise TemplateError(err)
homeassistant.exceptions.TemplateError: UndefinedError: ‘None’ has no attribute ‘state’


I’m no HA/python expert, but that sounds like you don’t have a value associated with what you’re trying to put in the curl command. Have you made sure the value works in the templating area in HA?


Hi Greenhouse

thankyou for the quick response. No, i have not setup a value nor tested anything in the templating area. This is my first foray into curl/automation. Back to the doco i go :slight_smile:


In the pvoutput curl statement I changed




and now the error is gone and it is uploading power usage data :slight_smile: