PVOuput Uploader


#1

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)


#2

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


#3

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!


#5

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…


#6

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
trigger:
  platform: time
  minutes: '/5'
action:
  - service: python_script.pvoutput
    data:
      api_key:"<apikey>"
      system_id:"<id>"
      power_generation:200
      temperature:39.2
      voltage:800

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?


#7

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:

#!/usr/bin/python
# -*- 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)
client.connect(MQTT_Host)


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

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

        return (sensor, power)

while(True):
        try:
                # 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)

        except:
                pass

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.listen_state(self.motion)
     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)

    else:
        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

#8

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.


#9

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

shell_command:
   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
    trigger:
      platform: time
      minutes: '/5'
      seconds: 00
    action:
      service: shell_command.pvoutputcurl

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

https://pvoutput.org/help.html#api-addstatus


#10

Hello

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’


#11

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?


#12

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:


#13

In the pvoutput curl statement I changed

states.sensor.studioforpvoutput_mean.state

to

states.sensor.currentcost_power.state

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