question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

[Ready to use] OpenWeatherMap - a new EMS Module that works without quering your Inverter

See original GitHub issue

I have coded a EMS module that works without the need to access your inverter. It works by weather data from OpenWeatherMap and some fixed details about your solar installation.

This is my first step into python coding so Im sorry for mistakes on it. Also Im sure it can be improved a lot more and leave it open for others to jump in.

# OpenWeatherMap,py module for TWCManager by GMerg
import logging

logger = logging.getLogger(__name__.rsplit(".")[-1])

class OpenWeatherMap:
    import requests
    import time

    cacheTime = 60
    config = None
    configConfig = None
    configOpenWeatherMap = None
    fetchFailed = False
    generatedW = 0
    consumedW = 0
    lastFetch = 0
    master = None
    APIKey = None
    Latitude = 0
    Longitude = 0
    status = False
    timeout = 10
    PeakKW = None
    LastJson = None

    def __init__(self, master):
        self.master = master
        self.config = master.config
        try:
            self.configConfig = master.config["config"]
        except KeyError:
            self.configConfig = {}
        try:
            self.configOpenWeatherMap = master.config["sources"]["OpenWeatherMap"]
        except KeyError:
            self.configOpenWeatherMap = {}

        self.status = self.configOpenWeatherMap.get("enabled", False)
        self.Latitude = self.configOpenWeatherMap.get("Latitude", 0)
        self.Longitude = self.configOpenWeatherMap.get("Longitude", 0)
        self.APIKey = self.configOpenWeatherMap.get("APIKey", None)
        self.PeakKW = self.configOpenWeatherMap.get("PeakKW", None)

        # Unload if this module is disabled or misconfigured
        if (not self.status) or (not self.APIKey):
            self.master.releaseModule("lib.TWCManager.EMS", "OpenWeatherMap")
            return None

    def getConsumption(self):
        # since this is not knowing our consumption!
        return 0;

    def getGeneration(self):

        if not self.status:
            logger.debug("OpenWeatherMap EMS Module Disabled. Skipping getGeneration")
            return 0

        # Perform updates if necessary
        self.update()

        # Return generation value
        if not self.generatedW:
            self.generatedW = 0
        return float(self.generatedW)

    def getOpenWeatherMapData(self):
        url = "https://api.openweathermap.org/data/2.5/onecall?lat=" + str(self.Latitude) + "&lon=" + str(self.Longitude) + "&appid=" + self.APIKey + "&exclude=minutely&units=metric"

        return self.getOpenWeatherMapValue(url)

    def getOpenWeatherMapValue(self, url):

        # Fetch the specified URL from the OpenWeatherMap and return the data
        self.fetchFailed = False

        try:
            r = self.requests.get(url, timeout=self.timeout)
        except self.requests.exceptions.ConnectionError as e:
            logger.log(
                logging.INFO4,
                "Error connecting to OpenWeatherMap to fetch sensor value",
            )
            logger.debug(str(e))
            self.fetchFailed = True
            return False

        r.raise_for_status()
        jsondata = r.json()
        return jsondata

    def getBestJsonSet(self, jsondata, dt):
        #get the json data that matches best based on dt

        bestjson=jsondata['current']
        dif=abs(bestjson['dt']-dt)

        for section in ['hourly', 'daily' ]:
            for subset in jsondata[section]:
                cmp = abs(subset['dt']-dt)
                if (dif==0) or (cmp<dif):
                    dif=cmp
                    bestjson=subset

        return bestjson


    def update(self):

        month = int(self.time.strftime("%m"))
        dt = int(self.time.time())
        if (int(self.time.time()) - self.lastFetch) > self.cacheTime:
            # Cache has expired. Fetch values from OpenWeatherMap inverter.
            tmp = self.getOpenWeatherMapData()
            if tmp:
                self.LastJson = tmp
                # Update last fetch time
                if self.fetchFailed is not True:
                    self.lastFetch = int(self.time.time())

        if self.LastJson:
            try:
                subset = self.getBestJsonSet(self.LastJson,dt)
                sunrise = self.LastJson['current']['sunrise']
                sunset  = self.LastJson['current']['sunset']
                clouds  = subset['clouds']
                temp    = subset['temp']

                logger.info( "OpenWeatherMap response/subset: " + str(subset))

                #it's night time!
                if (dt < sunrise) or (dt > sunset):
                   self.generatedW=0
                   logger.info("OpenWeatherMap said it's Nighttime -> 0 kw")
                else:
                   #modifier based on date time
                   midday = int( sunrise + ((sunset-sunrise)/2) )
                   if (dt < midday):
                        mod_day = (1/(midday-sunrise))*(dt-sunrise)
                   else:
                        mod_day = (1/(midday-sunrise))*(midday-(dt-midday)-sunrise)
                   if   mod_day > 0.9:
                        mod_day = 1
                   elif mod_day < 0.2:
                        mod_day = 0.2


                   #modifier based on clouds, the /3 was done to improve results...cuz clouds with e.g. 70% almost always gave back 100% kw anyway
                   mod_cloud = 1-(clouds/100/3)

                   #a temperature above 87° fahrenheit (or 31° celsius) will reduce effeciency by 1% each degree
                   #at least thats what a paper suggested
                   if (temp > 31):
                        mod_temp = 1-((temp-31)/100)
                        if(mod_temp>0.5):
                                # limit this modification by 0.5 maximum
                                mod_temp=0.5
                   else:
                        mod_temp = 1

                   #other ideas...
                   #- reduce on snow
                   #- use uvi value to calculate another modifier...though needs monitoring a longer range
                   #- reduce on fog
                   #- use none linear sunrise/sunset -> midday but gausian algo here

                   self.generatedW = self.PeakKW[ month - 1 ] * mod_day * mod_cloud * mod_temp * 1000
                   logger.info("peak: "+str(self.PeakKW[ month - 1 ])+", mod_cloud: "+str(mod_cloud)+", mod_day:" +str(mod_day)+", mod_temp:"+str(mod_temp))

            except (KeyError, TypeError) as e:
                logger.log(
                    logging.INFO4, "Exception during parsing OpenWeatherMapData"
                )
                logger.debug(e)

            return True
        else:
            # Cache time has not elapsed since last fetch, serve from cache.
            return False

What you need to configure in /etc/twcmanager/config.json under sources…

        "OpenWeatherMap": {
            "enabled": true,
            #your position on earth, use maps.google and copy from the URL parameters after the @ sign
            "Latitude": 41.909986
            "Longitude": 12.3959158,
            #APIKey from https://openweathermap.org/
            "APIKey": "xyz-EDIT-ME-xyz",
            #a 12 items array with the best kW reached on a day in that month
            "PeakKW": [2, 4, 6, 6, 6.5, 6.9, 6.5, 6.0, 5.5, 4.5, 3, 2]
        }

Issue Analytics

  • State:closed
  • Created 2 years ago
  • Comments:5 (2 by maintainers)

github_iconTop GitHub Comments

1reaction
GMergcommented, Apr 19, 2021

Let me show it with a sample… Graph: https://www.tesla.com/sites/default/files/support-images/solarpanels-production-app-homescreen.png You see it’s almost a perfect curve. And on noon it had almost 13kW peak. Thats the value you would use here, not the 91.2kWh. As that was for March, you would set the 3rd element to 13.

1reaction
GMergcommented, Apr 19, 2021

@ngardiner yes correct. This should be the value where no clouds are present and at midday…so basically the best value you can get on that day in that month. Thats just a suggestion…maybe there is a better way to do it but it worked pretty good for me.

Read more comments on GitHub >

github_iconTop Results From Across the Web

How to start to work with Openweather API - OpenWeatherMap
It is quite easy to work with Openweather API. Just sign up to get your API key and then call any weather API....
Read more >
Frequently Asked Questions - OpenWeatherMap
Get started · How to get an API key · What products and types of data can I request? · Do I need...
Read more >
OpenWeatherMap API guide
The "OpenWeather API Guide" helps you find the list of weather products, useful information, links, and documentation to start using our weather API ......
Read more >
Partners and solutions - OpenWeatherMap
It allows quick and easy consumption of OWM weather data (either observations and forecast) from Python applications via a simple object model. No...
Read more >
Weather API - OpenWeatherMap
Please, sign up to use our fast and easy-to-work weather APIs. ... One Call API 3.0 NEW ... Read more about this API...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found