Community
OpenEnergyMonitor

OpenEnergyMonitor Community

PowerWall Interfacer

@TrystanLea

The EmonHubTeslaPowerWallInterfacer works well publishing the Battery % Charge to emoncms.

However there is another Tesla API (unofficially available) with a wealth of additional battery data – most importantly – instant power and total energy imported & exported.

Per below, I’ve managed to adapt my EmonHubTeslaPowerWallInterfacer.py file to capture this additional data and publish it to emoncms …

import time, json, Cargo, requests
from emonhub_interfacer import EmonHubInterfacer

"""class EmonHubTeslaPowerWallInterfacer

Fetch Tesla Power Wall power and energy 

url needs a trailing /

"""

class EmonHubTeslaPowerWallInterfacer(EmonHubInterfacer):

    def __init__(self, name):
        super().__init__(name)

        self._settings.update(self._defaults)

        # Interfacer specific settings
        self._template_settings = {'name': 'powerwall',
                                   'url': False,
                                   'readinterval': 10.0}

        # FIXME is there a good reason to reduce this from the default of 1000? If so, document it here.
        # set an absolute upper limit for number of items to process per post
        self._item_limit = 250

        # Fetch first reading at one interval lengths time
        self._last_time = 0

    def read(self):
        # Request Power Wall data at user specified interval
        if time.time() - self._last_time >= self._settings['readinterval']:
            self._last_time = time.time()

            # If URL is set, fetch the POWER and ENERGY
            if self._settings['url']:
                # HTTP Request
                try:
                    reply = requests.get(self._settings['url'] + 'api/meters/aggregates', timeout=int(self._settings['readinterval']), verify=False)
                    reply.raise_for_status()  # Raise an exception if status code isn't 200
                except requests.exceptions.RequestException as ex:
                    self._log.warning("%s couldn't send to server: %s", self.name, ex)

                jsonstr = reply.text.rstrip()
                self._log.debug("%s Request response: %s", self.name, jsonstr)

                # Decode JSON
                try:
                    data = json.loads(jsonstr)
                except Exception:  # FIXME Too general exception
                    self._log.warning("%s Invalid JSON", self.name)
                    return


                # Create cargo object
                c = Cargo.new_cargo()
                c.nodeid = self._settings['name']
                c.names = ["power and energy"]
                c.realdata = [data['battery']['instant_power'], data['battery']['energy_exported'], data['battery']['energy_imported']]  # All in watts and +ve power=discharging  -ve power=charging
                return c

        # return empty if not time
        return

    def set(self, **kwargs):
        for key, setting in self._template_settings.items():
            # Decide which setting value to use
            if key in kwargs.keys():
                setting = kwargs[key]
            else:
                setting = self._template_settings[key]
            if key in self._settings and self._settings[key] == setting:
                continue
            elif key == 'readinterval':
                self._log.info("Setting %s %s: %s", self.name, key, setting)
                self._settings[key] = float(setting)
                continue
            elif key == 'name':
                self._log.info("Setting %s %s: %s", self.name, key, setting)
                self._settings[key] = setting
                continue
            elif key == 'url':
                self._log.info("Setting %s %s: %s", self.name, key, setting)
                self._settings[key] = setting
                continue
            else:
                self._log.warning("'%s' is not valid for %s: %s", setting, self.name, key)

        # include kwargs from parent
        super().set(**kwargs)

This approach means I can only run one or the other version – not both at the same time.

I am confused about whether a new Child Class should be created or whether this should be just another Object in the same Child Class.

Also what changes need to be made to the Interfacer stanza in emonhub.conf?

@TrystanLea – I would really appreciate yr guidance - thx

PS - There is a small annoyance. Per this screenshot of the Inputs page, the 2nd and 3rd data items have meaningless titles.

1 Like

2 posts were merged into an existing topic: PowerWall Data Integration