jasonacox / pypowerwall

Python API for Tesla Powerwall and Solar Power Data
MIT License
123 stars 21 forks source link

Add grid_status function #7

Closed wcwong closed 1 year ago

wcwong commented 2 years ago

Provide a call to /api/system_status/grid_status

from https://github.com/vloschiavo/powerwall2 the return values would be:

{"grid_status":"SystemGridConnected","grid_services_active":false}
{"grid_status":"SystemGridConnected"} = grid is up
{"grid_status":"SystemIslandedActive"} = grid is down
{"grid_status":"SystemTransitionToGrid"} = grid is restored but not yet in sync.

if verbose=True then return the full json which would include grid_services_active

jasonacox commented 2 years ago

I like this! Thanks @wcwong .

def grid_status(self, verbose=False)

Response (not verbose) - something like this:

Thoughts? I would welcome a PR or I'll get to it in a few days.

Thanks for the suggestion!

wcwong commented 2 years ago

Yep, I think that makes sense. I should be able to get this done pretty quickly, assuming I didn't mess anything up with the other PR.

I considered whether or not it would be better to return (-1, 0, 1) to be an easier check (e.g. if grid_status() > 0 then it's up but that's likely over optimizing and sacrificing readability.

jasonacox commented 2 years ago

Great idea. We could do both...

def grid_status(self, verbose=False, numerical=False):

If numerical is True, respond with -1 for down, 0 for syncing, and 1 for up?

wcwong commented 2 years ago

verbose and numerical have the potential to conflict (i.e. the case where both values are True).

What do you think about the following?

   def grid_status(self, type="string"):
        """
        Get the status of the grid  

        Args:
            type == "string" (default) returns: "UP", "DOWN", "SYNCING"
            type == "JSON" return raw JSON
            type == "numeric" return -1 (Syncing), 0 (DOWN), 1 (UP)
        """
wcwong commented 2 years ago

Take a look at the PR above implemented with the type argument. Let me know if you'd prefer the implementation to be the boolean parameters.

jasonacox commented 2 years ago

Thanks @wcwong ! I love the approach. Make sure you add your cmu.edu email address to your github profile so you get contributor credit. 👍

jasonacox commented 2 years ago

@wcwong I made some slight modifications to grid_status() - make sure you sync these and please provide feedback if I missed anything. My changes were based on:

    def grid_status(self, type="string"):
        """
        Get the status of the grid  

        Args:
            type == "string" (default) returns: "UP", "DOWN", "SYNCING"
            type == "json" return raw JSON
            type == "numeric" return -1 (Syncing), 0 (DOWN), 1 (UP)
        """
        if type not in ['json', 'string', 'numeric']:
            raise ValueError("Invalid value for parameter 'type': " + str(type))

        payload = self.poll('/api/system_status/grid_status', jsonformat=True)

        if type == "json":
            return json.dumps(payload, indent=4, sort_keys=True)

        map = {'SystemGridConnected': {'string': 'UP', 'numeric': 1}, 
               'SystemIslandedActive': {'string': 'DOWN', 'numeric': 0}, 
               'SystemTransitionToGrid': {'string': 'SYNCING', 'numeric': -1}}
        try:
            grid_status = payload['grid_status']
            return map[grid_status][type]
        except:
            # The payload from powerwall was not valid
            log.debug("ERROR Invalid return value received from gateway: " + str(payload.grid_status))
            return None
wcwong commented 2 years ago

Looks good to me. The use of map is definitely more elegant.

mcbirse commented 2 years ago

Is it possible to have some additional response values added to the map? I'd be happy submit a PR with these added if that is okay.

I have had a shell script running for about year, monitoring the grid_status every 10 seconds and sending alerts when it changes. Over this time, I have observed the below additional "valid" responses from my Powerwall:

As these are not documented anywhere that I can find, the comment is my interpretation of the status and what I decided to send in an e-mail alert when this occurs.

At the moment, if these occur the pypowerwall grid_status function will raise an exception and return None, however they are valid responses.

I have had these happen only a couple of times in the past year. I believe you may receive these responses based on "timing" of the request, i.e. you just happened to request the grid_status from the Powerwall in the same millisecond that a power outage or off-grid request occurred.

I think I had these responses once when manually switching to off-grid from the Tesla app, and another time when the transition to off-grid was not quite seamless (power in my house went out for about 1 second when a blackout occurred).

@jasonacox - If it still makes sense to consider these responses as "SYNCING" (i.e. in this case, we are syncing to off-grid rather than on-grid), then the map could be updated as below?

map = {'SystemGridConnected': {'string': 'UP', 'numeric': 1}, 
       'SystemIslandedActive': {'string': 'DOWN', 'numeric': 0}, 
       'SystemTransitionToGrid': {'string': 'SYNCING', 'numeric': -1},
       'SystemTransitionToIsland': {'string': 'SYNCING', 'numeric': -1},
       'SystemIslandedReady': {'string': 'SYNCING', 'numeric': -1}}

What are your thoughts - I can submit a PR if you agree it still makes sense for these?

jasonacox commented 2 years ago

Awesome find @mcbirse ! Yes, please submit a PR for this.

I agree with your SYNCING categorization. That meets the intent of grid_status() describing the status of the grid vs the specific transition state. A user wanting the transition (SYNC) details would just sent grid_status(type="json").