APSystem Solar Energy Integration (local)

Hi.

I have APSystems ECU-C and I have been using the integration found at

The thing is that this integration does not provide export/import information that can be very useful to automate optional energy hungry devices like the AC.

So I have created a simple script that scrapes ECU-C local webpage to obtain that info. This script is running in crontab each 5 minutes (ECU-C does not updates the info more often that that) and creates a file in a private network web server. Then, I have sensors in Home Assistant that retrieves that file to populate my dashboard.

It is my first Python script and very beginner with HA so everything is very rudimentary. I am sure you can provide me ideas to improve it. :grin:

Scrapper (/home/apsystems_ecu_scrapper.py):

#coding=utf-8

import urllib2
import json
from argparse import Namespace

#Set ECU-C IP

ECU_IP = "192.168.1.25"

#Get the Power Graph webpage from ECU-C

resp = urllib2.urlopen("http://" + ECU_IP + "/index.php/meter/meter_power_graph")
page = resp.read()

#Original ECU-C page
#print (page)

#Extracts the javascript that contains the graph data
initialPosition = page.find("$('#myChart').highcharts(")
initialPosition = initialPosition + 25
endPosition = page.find(");", initialPosition)

#Extracted javascript data

data = page[initialPosition:endPosition]
#print (data)

#Cleans the data removing all invalid stuff for a JSON object

data = data.replace("//副标题设置", "")
data = data.replace("右侧说明", "")
data = data.replace("subtitle", "'subtitle'")
data = data.replace("text: '',", "'text': ''")
data = data.replace("legend", "'legend'")
data = data.replace("layout", "'layout'")
data = data.replace("align", "'align'")
data = data.replace("verticalAlign", "'verticalAlign'")
data = data.replace("borderWidth", "'borderWidth'")
data = data.replace("series", "'series'")
data = data.replace("name", "'name'")
data = data.replace("// Production A", "")
data = data.replace("//Production CT 1A", "")
data = data.replace("// Production B", "")
data = data.replace("//Production CT 1B", "")
data = data.replace("//Production CT 1C", "")
data = data.replace("// Consumption A", "")
data = data.replace("+ Consumption CT 2A", "")
data = data.replace("// Consumption B", "")
data = data.replace("//Production CT 1B + Consumption CT 2B", "")
data = data.replace("+ Consumption CT 2B", "")
data = data.replace("// Consumption C", "")
data = data.replace("+ Consumption CT 2C", "")
data = data.replace("// Imported/Exported  A", "")
data = data.replace("//Consumption CT 2A", "")
data = data.replace("// Imported/Exported  B", "")
data = data.replace("//Consumption CT 2B", "")
data = data.replace("// Imported/Exported  C", "")
data = data.replace("//Consumption CT 2C", "")

#Converts data arrays to strings
#data = data.replace("data: ", "'data': '")
#data = data.replace(",]", "]'")

#OR fix data arrays format as pure numbers arrays
data = data.replace("data: ", "'data': ")
data = data.replace(",]", "]")

#Changes all ' for "
data = data.replace("'", "\"")

#Fixes latest , because the json is malformed
lastCommaPosition = data.rfind(",")
data = data[:lastCommaPosition] + data[lastCommaPosition + 1:]

#Final data json that has valid format
#print (data)

jsonObject = json.loads(data, object_hook=lambda d: Namespace(**d))

#Get Produced values
producedDataValues = jsonObject.series[0].data

#Get Consumed values
consumedDataValues = jsonObject.series[3].data

#Get Exported values
exportedDataValues = jsonObject.series[6].data

#Get latest position for most recent value
latestProducedDataValue = producedDataValues[len(producedDataValues)-1]
latestConsumedDataValue = consumedDataValues[len(consumedDataValues)-1]
latestExportedDataValue = exportedDataValues[len(exportedDataValues)-1]

#Generate JSON
print "{"
print ' "produced": "' + str(latestProducedDataValue[1]) + '",'
print ' "consumed": "' + str(latestConsumedDataValue[1]) + '",'
print ' "exported": "' + str(latestExportedDataValue[1]) + '"'
print "}"

Shell script to generate the file (/usr/bin/generatePower.sh)

#!/bin/sh
python /home/apsystems_ecu_scrapper.py > /var/www/html/power.txt

Crontab entry:

0,5,10,15,20,25,30,35,40,45,50,55  * * * * generatePower.sh

Home Assistant Sensors (configuration.yaml) (192.168.1.200 is my private web server IP):

rest:    
  - resource: http://192.168.1.200/power.txt
    verify_ssl: false
    sensor:
        - name: Electricidad exportada
          value_template: "{{ value_json.exported }}"
        - name: Electricidad generada
          value_template: "{{ value_json.produced }}"
        - name: Electricidad consumida
          value_template: "{{ value_json.consumed }}"

Example in Dashboard:

Captura de pantalla 2023-06-28 a las 9.15.02

Maybe you find it useful.

Regards.

2 Likes

Hola

Conseguiste que te funcione bien la ECU c en home assistant?

Cualquier ayuda que me pudieras dar te lo agradecería muchísimo

Un saludo

Hola.

La integración que tengo es la que comento y funciona correctamente. ¿Qué necesitas?

Saludos.

1 Like

Soy novato total en home assistant y no se por donde empezar mi objetivo es poder ver lo que está produciendo cada uno de mis 8 paneles solares

I have reformated first post to allow easy copy&paste.

For the people that want to use this device with the CT clamps and use them in the Energy Dashboard, I created this.

1 Like

Hello
You can find here a yaml file with all sensors to get all energy data from ECU-C.
It works fine without ECU-C interruption

sensor:
    - platform: rest
      name: "PV Power" # Solar production power
      device_class: "power"
      state_class: "measurement"
      unit_of_measurement: "W"
      resource: http://192.168.0.xx/index.php/meter/old_meter_power_graph
      scan_interval: 300
      value_template: >
        {% if value_json is defined %}
            {{ value_json.power1[-1].powerA }}
        {% else %}
            {{ "None" }}
        {% endif %}
      
    - platform: integration
      name: "PV Energy"  # solar production energy
      source: sensor.pv_power
      unit_prefix: "k"
      unit_time: "h"
      method: "left"
      
    - platform: rest
      name: "Import Export Power" # grid import if  + ou export if  -
      device_class: "power"
      state_class: "measurement"
      unit_of_measurement: "W"
      resource: http://192.168.0.xx/index.php/meter/old_meter_power_graph
      scan_interval: 300
      value_template: >
        {% if value_json is defined %}
            {{ value_json.power2[-1].powerA }}
        {% else %}
            {{ "None" }}
        {% endif %}
     
    - platform: template
      sensors:
          export_power: # power grid export
              friendly_name: "Export Power"
              unit_of_measurement: "W"
              device_class: "power"
              value_template: >
                {% set p = states('sensor.import_export_power') | float(0) %}
                {{ ((p | abs) - p) / 2 }}
          import_power:  # power grid import             
              friendly_name: "Import Power"
              unit_of_measurement: "W"
              device_class: "power"
              value_template: >
                {% set p = states('sensor.import_export_power') | float(0) %}
                {{ ((p | abs) + p) / 2 }}

    - platform: integration
      name: "Export Energy" # total grid export energy
      source: sensor.export_power
      unit_prefix: "k"
      unit_time: "h"
      method: "left"

    - platform: integration
      name: "Import Energy" # total grid import energy
      source: sensor.import_power
      unit_prefix: "k"
      unit_time: "h"
      method: "left"

utility_meter: 
    pv_energy_daily: # solar panel daily energy balance
        name: "PV Energy Daily"
        unique_id: pv_energy_daily
        source: sensor.pv_energy
        cycle: daily
    export_energy_daily: # solar production daily export energy
        name: "Export Energy Daily"
        unique_id: export_energy_daily
        source: sensor.export_energy
        cycle: daily
        
    import_energy_daily: # solar production daily import energy
        name: "Import Energy Daily"
        unique_id: import_energy_daily
        source: sensor.export_energy
        cycle: daily

Don’t forget to update your ECU-C IP address in the RES commands.