CheongKoo / HA_AppDaemonZeverSolarSensor

Get power generation from ZeverSolar Inverter and present it to front end as a sensor for Hass.IO
GNU General Public License v3.0
7 stars 3 forks source link

Negative value in Energy Dashboard HA at the end of the day #7

Open ronbrou opened 3 years ago

ronbrou commented 3 years ago

Hey, I have this app working in HA. And also visible in HA's new Energy Dashboard. Only at the end of the day when there is no more connection with the Zeversolar inverter, the sensor will show 0 (this is also correct). But in the Energy dashboard it goes to a negative value. Can anything be done about this?

ScreenHunter_122 Aug  19 22 15

CheongKoo commented 3 years ago

Hi, If you go to the Appdaemon4 log console. Did you see any negative values there? This shows the values read from the sensor. image

RemcoA1 commented 3 years ago

I solved it as follows:

  1. Change the value to 'none' when the inverter is offline:
    def requestSolarGeneration(self, arg):
        self.dateOfReading = datetime.now() # Get date & time of reading
        req = Request(zeverSolarURL)
        try:
            response = urlopen(req)
            htmlresponse = response.read()
            st = htmlresponse.decode()
            st = st.split() # Convert the string into a list
            #-- Get the string for the Generated Power and Daily Energy
            genPower = st[genPowerIndex]
            dailyEnergy = st[dailyEnergyIndex]
            #-- Convert string into Int and Float
            self.generatedPower = float(genPower)/1000 # Its in W eg. 4978. Convert into kW
            self.totalEnergyDaily = float(dailyEnergy) # It is already in kWh eg. 14.52
            return
        except:
            self.log("Error in connecting to Zever solar server", log="main_log")
            self.generatedPower = 0.00
            self.totalEnergyDaily = None
            return
  2. Set the last_reset attribute to the start of the day (change to UTC offset for your own situation)
    def doGetGenAndSendAsSensor(self, arg):
        self.log("----- ZeverSolar sensor callback -----", log="main_log")
        #-- Get the generated power & energy
        self.requestSolarGeneration(self)
        lastUpdated = self.dateOfReading.strftime(datetimeFormat) # Last updated
        lastReset = self.dateOfReading.strftime("%Y-%m-%d 00:00:00+02:00")
        #-- Output the sensor values
        #-- Instantaneous Generated power
        stateInfo1 = generationFormat.format(self.generatedPower)
        self.set_state("sensor.zeverSolar_generated_power", state=stateInfo1, attributes=\
                       {"unit_of_measurement": "kW", \
                        "last_reset" : lastReset, \
                        "state_class": "measurement", \
                        "device_class": "energy", \
                        "icon": "mdi:white-balance-sunny", \
                        "friendly_name": "Generated Power",
                        "lastUpdated": lastUpdated
                       })
        #-- Daily energy generated
        #- Icons are located at http://materialdesignicons.com/
        stateInfo2 = generationFormat.format(self.totalEnergyDaily)
        self.set_state("sensor.zeverSolar_daily_energy", state=stateInfo2, attributes=\
                       {"unit_of_measurement": "kWh", \
                        "last_reset" : lastReset, \
                        "state_class": "measurement", \
                        "device_class": "energy", \
                        "icon": "mdi:white-balance-sunny", \
                        "friendly_name": "Daily Generated Energy",
                        "lastUpdated": lastUpdated
                       })
        #-- Send out a log to the appdaemon console
        self.log("Updated: " + lastUpdated + " Gen: " + stateInfo1 + "kW, Daily energy: " + stateInfo2 + "kWh", log="main_log")
ronbrou commented 3 years ago

I have entered the adjustment you indicated, and now I no longer have a negative value, but the last value. thanks,

iefke commented 2 years ago

I have the code like this now, but still get wrong info (see screenshots). I now have production in the middle of the night.....

#-------------------------------------------------------------------------------
# Name:        appd_ZeverSolarSensor.py
#
# Purpose:     To get the Zeversolar solar generation from the local website
#              and send it to the front end. It does it by sending it as a
#              sensor to the front end. This module parses the value returned
#              by the URL call and gives us the:
#              - Instantaneous generated power in kW
#              - Total generated energy for the day in kWH
#              You don't have to make any changes to your configuration.yaml file.
#              It automatically generates the sensor for the front end.
#
#              Note: Modified for AppDaemon 4
#
# Author:      Cheong Koo
#
# Created:     09/08/2021
#
# Note that at night, the server is down as there is no power hence need to check
# Below the reading from the URL separated by CR
# 1 1 EAB9618A0399 RSQMMVXNNPJMNWHY M11 17A31-727R+17829-719R 10:58 05/10/2019 0 1 BD500001018A0080 4978 14.52 OK Error
# 0 1      2             3           4           5              6       7      8 9        10         11    12  13   14
#
# In your "apps.yaml" file, put the following lines
# zeversolar_sensor:
#   module: appd_ZeverSolarSensor
#   class: ZeverSolarSensor
#-------------------------------------------------------------------------------

import hassapi as hass
import urllib.request
import datetime

# Check if server is up or down
# https://docs.python.org/3.1/howto/urllib2.html
from urllib.request import Request, urlopen
from urllib.error import  URLError
from datetime import datetime
from datetime import timedelta

#-------------------------------------------------------------------------------
# Global constants
# Get the below from Router
#-------------------------------------------------------------------------------
zeverSolarURL = "http://192.168.50.163/home.cgi"  # Change this to your ZeverSolar Inverter IP address
datetimeFormat = "%d/%m/%Y %H:%M" # Format for strftime()
generationFormat = "{:.2f}"
refreshInterval = 120 # Time interval to read the URL in seconds
genPowerIndex = 11 # Index into the returned string from the URL
dailyEnergyIndex = 12 # Index into returned string from the URL

#-------------------------------------------------------------------------------
# Class to be called by AppDaemon
# Remember to declare this class and the module in apps.yaml
#-------------------------------------------------------------------------------
class ZeverSolarSensor(hass.Hass):
    #---------------------------------------------------------------------
    #-- Initialise the module
    def initialize(self):
        self.log("------------------------------------------------", log="main_log")
        self.log("Initiatilize: ZeverSolar Sensor", log="main_log")
        #-- Intialise some local variables
        self.generatedPower = 0.00 # In kW
        self.totalEnergyDaily = 0.00 # In KwH
        self.dateOfReading = datetime.now()
        #-- Run first time in 5 sec
        self.run_in(self.doGetGenAndSendAsSensor, 5)
        #-- Then run it every refreshInterval
        startTime = datetime.now() + timedelta(seconds=refreshInterval)
        self.run_every(self.doGetGenAndSendAsSensor,  startTime, refreshInterval)

    #---------------------------------------------------------------------
    #-- Get generation and send out as sensor
    def doGetGenAndSendAsSensor(self, arg):
        self.log("----- ZeverSolar sensor callback -----", log="main_log")
        #-- Get the generated power & energy
        self.requestSolarGeneration(self)
        lastUpdated = self.dateOfReading.strftime(datetimeFormat) # Last updated
        lastReset = self.dateOfReading.strftime("%Y-%m-%d 00:00:00+02:00")
        #-- Output the sensor values
        #-- Instantaneous Generated power
        stateInfo1 = generationFormat.format(self.generatedPower)
        self.set_state("sensor.zeverSolar_generated_power", state=stateInfo1, attributes=\
                       {"unit_of_measurement": "kW", \
                        "last_reset" : lastReset, \
                        "state_class": "measurement", \
                        "device_class": "energy", \
                        "icon": "mdi:white-balance-sunny", \
                        "friendly_name": "Generated Power",
                        "lastUpdated": lastUpdated
                       })
        #-- Daily energy generated
        #- Icons are located at http://materialdesignicons.com/
        stateInfo2 = generationFormat.format(self.totalEnergyDaily)
        self.set_state("sensor.zeverSolar_daily_energy", state=stateInfo2, attributes=\
                       {"unit_of_measurement": "kWh", \
                        "last_reset" : lastReset, \
                        "state_class": "measurement", \
                        "device_class": "energy", \
                        "icon": "mdi:white-balance-sunny", \
                        "friendly_name": "Daily Generated Energy",
                        "lastUpdated": lastUpdated
                       })
        #-- Send out a log to the appdaemon console
        self.log("Updated: " + lastUpdated + " Gen: " + stateInfo1 + "kW, Daily energy: " + stateInfo2 + "kWh", log="main_log")

    #---------------------------------------------------------------------
    #-- Gets the reading from the URL. Returns 0 if no generation
    def requestSolarGeneration(self, arg):
        self.dateOfReading = datetime.now() # Get date & time of reading
        req = Request(zeverSolarURL)
        try:
            response = urlopen(req)
            htmlresponse = response.read()
            st = htmlresponse.decode()
            st = st.split() # Convert the string into a list
            #-- Get the string for the Generated Power and Daily Energy
            genPower = st[genPowerIndex]
            dailyEnergy = st[dailyEnergyIndex]
            #-- Convert string into Int and Float
            self.generatedPower = float(genPower)/1000 # Its in W eg. 4978. Convert into kW
            self.totalEnergyDaily = float(dailyEnergy) # It is already in kWh eg. 14.52
            return
        except:
            self.log("Error in connecting to Zever solar server", log="main_log")
            self.generatedPower = 0.00
            self.totalEnergyDaily = None
            return

IMG_1647 2

IMG_1646

QuaySo commented 2 years ago

@iefke, you should make a small change in the code for the zeverSolar_daily_energy sensor.

Change state_class to "total_increasing"

                       {"unit_of_measurement": "kWh", \
                        "last_reset" : lastReset, \
                        "state_class": "total_increasing", \
                        "device_class": "energy", \
                        "icon": "mdi:white-balance-sunny", \
                        "friendly_name": "Daily Generated Energy",
                        "lastUpdated": lastUpdated
                       })
iefke commented 2 years ago

@iefke, you should make a small change in the code for the zeverSolar_daily_energy sensor.

Change state_class to "total_increasing"

                       {"unit_of_measurement": "kWh", \
                        "last_reset" : lastReset, \
                        "state_class": "total_increasing", \
                        "device_class": "energy", \
                        "icon": "mdi:white-balance-sunny", \
                        "friendly_name": "Daily Generated Energy",
                        "lastUpdated": lastUpdated
                       })

Thanks! That's fast. I will let you know tomorrow if it's working.......

QuaySo commented 2 years ago

Maybe you could also check the issue that I just submitted. #8 It might be something that affects your sensor too

iefke commented 2 years ago

It's working now. Also had to change code like you wrote above.

Thanks!!!

RemcoA1 commented 2 years ago

Got a response from Zeversolar support, this bug will not be fixed.

Sorry that Zeversolar ceased trading, we only support aftersales but no resource on the debug now. I don’t recommend you to read the data from /home.cgi, and you can retrieve data from cloud by API.