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)
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)
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
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?
I haven’t touched HA for some months but I can show you what I have if that helps…probably bad coding practice throughout
(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
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:
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:
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’
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
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
As an update to this, I made a couple of modifications to my automation as to only upload when my internet is ‘online’ (heaps of internet drop outs in our 3rd world internet here in Australia).
In addition to that, sometimes I get an error “homeassistant.exceptions.TemplateError: UndefinedError: ‘None’ has no attribute ‘state’”, even when there is a state. My solution is to wait 10 seconds and then try again, with an almost 100% success rate (before the double-up I would get 1-3 missed uploads a day).
- alias: UploadPvoutput
initial_state: 'on'
hide_entity: False
trigger:
platform: time_pattern
minutes: '/5'
seconds: 00
action:
- wait_template: "{{ is_state('binary_sensor.online', 'on') }}"
timeout: '00:00:30'
continue_on_timeout: 'false'
- service: shell_command.pvshellgh
- delay: 0:00:10
- service: shell_command.pvshellgh
Thank you Adam
the simple curl solution saved me so much time and frustration to try to upload to pvoutput.org
I was looking for this too and I am happy I found this solution. I don;t like my API key in the YAML files though as I use Github so I did this to template the API key:
sensor:
- platform: template
sensors:
pvoutput_api_key:
value_template: !secret pvoutput_api_key
pvoutput_system_id:
value_template: !secret pvoutput_system_id
and in the curl command:
shell_command:
pvoutput_generation: 'curl -d "d={{now().strftime("%Y%m%d")}}" -d "t={{now().strftime("%H:%M")}}" -d "v2={{states.sensor.cs31011195_output_power.state|round(0)}}" -H "X-Pvoutput-Apikey: {{states.sensor.pvoutput_api_key.state}}" -H "X-Pvoutput-SystemId: {{states.sensor.pvoutput_system_id.state}}" https://pvoutput.org/service/r2/addstatus.jsp'
pvoutput_consumption: 'curl -d "d={{now().strftime("%Y%m%d")}}" -d "t={{now().strftime("%H:%M")}}" -d "c1=1" -d "v3={{(states.sensor.energy_consumption_tariff_1.state|int)+(states.sensor.energy_consumption_tariff_2.state|int)}}" -H "X-Pvoutput-Apikey: {{states.sensor.pvoutput_api_key.state}}" -H "X-Pvoutput-SystemId: {{states.sensor.pvoutput_system_id.state}}" https://pvoutput.org/service/r2/addstatus.jsp'
I am using two commands as I have a cumulative energy usage value from my smart meter and a power generation value from the solar power invertor. (note: the c1=1 means cumulative)
Hi Didier,
I am testing Home assistant for the first time and i am struggling trying to use the description you posted.
I input the portion sensor and shell_command into configuration.yaml . The API and system id into the file secret.yalm
Checking the file configuration.yaml system says it is OK …no errors… but how to run every 5 minutes the command to upload data to PVOUTPUT?
Would be very appreaciated a support from you.
many thanks in advance
Cheers
Finally I did it !!
automation file was wrongly formatted.
Hi @ciottomate glad you figured it out already I was on holiday and could not respond any sooner.
For those who come looking for the answer, create an automation rule that runs every 5 mins and runs the two shell scripts:
alias: PVOutput Uploader
description: Uploads values to PVOutput.
trigger:
- platform: time_pattern
minutes: /5
condition: []
action:
- service: shell_command.pvoutput_consumption
data: {}
- service: shell_command.pvoutput_generation
data: {}
mode: single
Hi,
So I got the API working fine thanks to your posts above, however I’m a bit confused how I create a 5min average sensor to send to PVoutput.
I have a template sensor that calculates my instantaneous house consumption (removing solar generation):
# Template sensor for values of power consumption (active_power < 0)
energy_consumption:
friendly_name: "Power Consumption"
unit_of_measurement: 'W'
value_template: >-
{% if (states('sensor.house_consumption')|float - 685 - states('sensor.pv_power')|float - states('sensor.pv_power_2')|float) < 0 %}
{{ (states('sensor.house_consumption')|float - 685 - states('sensor.pv_power')|float - states('sensor.pv_power_2')|float) * -1 }}
{% else %}
{{ (states('sensor.house_consumption')|float - 685 + states('sensor.pv_power')|float + states('sensor.pv_power_2')|float) }}
{% endif %}
How do I turn that into a 5 min average to send up to Pvoutput.
You mentioned you had something with a timestamp or similar?
There doesn’t seem to be a average, only min-max median, confused.
Thanks for any bone you can throw me.
For anyone looking in the future, I think I figured it out using statistics:
sensor:
# Sensor for 5 min average consumption for Pvoutput
- platform: statistics
name: "Energy Consumption 5min"
entity_id: sensor.energy_consumption
state_characteristic: mean
max_age:
minutes: 5
sampling_size: 300