script help (easy?) - sma solar system polling script

Posted on
Fri Jan 14, 2022 12:42 am
dtich offline
Posts: 798
Joined: Sep 24, 2005

script help (easy?) - sma solar system polling script

see below. i am getting this error thrown on run and compile:

Jan 13, 2022 at 10:32:34 PM
Script Error embedded script: global name 'v' is not defined
Script Error Exception Traceback (most recent call shown last):

embedded script, line 105, at top level
NameError: global name 'v' is not defined


the section: # INDIGO UPDATE VARIABLES SECTION was commented out originally, i thought that might be throwing a different error i was getting off this - oddly an AppleScript error --
Schedule weather update every 15min
Action Group weather_update vars
Error failed to execute embedded AppleScript "try..."
Error embedded AppleScripts are no longer supported


i did the tweaks so long ago i can't remember a thing about it. any help is appreciated.



Code: Select all
#! /usr/bin/env python
# -*- coding: utf-8 -*-


#!/usr/bin/env python
# version: 0.3
# date:    2012-11-30
# author:  Joerg Raedler joerg@j-raedler.de
# updates: Dylan Tichenor for Indigo Automation system
# license: BSD
# purpose: make RPC to a Sunny WebBox, a monitoring system for solar plants
#
# http://www.sma.de/en/produkte/monitoring-systems/sunny-webbox.html
#
# Use the classes SunnyWebBoxHTTP or SunnyWebBoxUDPStream in your code
# or run this file as a script to test the classes. First parameter is
# the hostname or IP of the box. A password can be provided with an
# optional second parameter.
#
# Example: python SunnyWebBox.py 123.123.123.123 "foobar"
#
# Documentation on the RPC API can be found here:
#
# http://files.sma.de/dl/2585/SWebBoxRPC-BEN092713.pdf
#

import sys, json, hashlib

# INDIGO GLOBALS
global Pac, Mode, GriEgyTot, GriEgyTdy, GriPwr, v


# handle python 2 and 3
if sys.version_info >= (3, 0):
    py3 = True
    def str2buf(s):
        return bytes(s, 'latin1')
    def buf2str(b):
        return str(b, 'latin1')
else:
    py3 = False
    def str2buf(s):
        return bytes(s)
    def buf2str(b):
        return unicode(b, 'latin1')


class Counter:
    """generate increasing numbers with every call - just for convenience"""
   
    def __init__(self, start=0):
        self.i = start

    def __call__(self):
        i = self.i
        self.i += 1
        return i


class SunnyWebBoxBase(object):
    """Communication with a 'Sunny WebBox', a monitoring system for solar plants.
       This is an abstract base class, please use SunnyWebBoxHTTP or
       SunnyWebBoxUDPStream instead!
    """
   
    def __init__(self, host='192.168.0.98', password='xxxxx'):
        self.host = host
        if password:
            self.pwenc = hashlib.md5(password).hexdigest()
        else:
            self.pwenc = ''
        self.count = Counter(1)
        self.openConnection()
   
    def openConnection(self):
        raise Exception('Abstract base class, use child class instead!')

    def newRequest(self, name='GetPlantOverview', withPW=False, **params):
        """return a new rpc request structure (dictionary)"""
        r = {'version': '1.0', 'proc': name, 'format': 'JSON'}
        r['id'] = str(self.count())
        if withPW:
            r['passwd'] = self.pwenc
        if params:
            r['params'] = params
        return r

    def _rpc(self, *args):
        raise Exception('Abstract base class, use child class instead!')

    def printOverview(self):
        """print a short overview of the plant data"""
        for v in self.getPlantOverview():
            print("%15s (%15s): %s %s" %
                (v['name'], v['meta'], v['value'], v['unit']))

# INDIGO UPDATE VARIABLES SECTION

indigo.variables[1822257245] # "PV_EToday"
indigo.variables[1469286291] # "PV_ETotal"
indigo.variables[789149473] # "PV_Mode"
indigo.variables[1958563501] # "PV_Pac"
indigo.variables[532315452] # "PV_Overview"

if (v['meta'] == 'GriEgyTdy'):
                        indigo.variable.updateValue(1822257245, v['value'])
if (v['name'] == 'GriEgyTot'):
                        indigo.variable.updateValue(1469286291, v['value'])
if (v['name'] == 'GriPwr'):
                        indigo.variable.updateValue(1958563501, v['value']) 
if (v['name'] == 'Mode'):
                        indigo.variable.updateValue(789149473, v['value'])

    # wrapper methods for the RPC functions

def getPlantOverview(self):
        res = self._rpc(self.newRequest('GetPlantOverview'))
        return res['overview']

def getDevices(self):
        res = self._rpc(self.newRequest('GetDevices'))
        return res['devices']

def getProcessDataChannels(self, deviceKey):
        res = self._rpc(self.newRequest('GetProcessDataChannels', device=deviceKey))
        return res[deviceKey]

def getProcessData(self, channels):
        res = self._rpc(self.newRequest('GetProcessData', devices=channels))
        # reorder data structure: {devKey: {dict of channels}, ...}
        # return {l['key']: l['channels'] for l in res['devices']}
        r = {}
        for l in res['devices']:
            r[l['key']] = l['channels']
        return r

def getParameterChannels(self, deviceKey):
        res = self._rpc(self.newRequest('GetParameterChannels', withPW=True, device=deviceKey))
        return res[deviceKey]

def getParameter(self, channels):
        res = self._rpc(self.newRequest('GetParameter', withPW=True, devices=channels))
        # reorder data structure: {devKey: {dict of channels}, ...}
        # return {l['key']: l['channels'] for l in res['devices']}
        r = {}
        for l in res['devices']:
            r[l['key']] = l['channels']
        return r

def setParameter(self, *args):
        raise Exception('Not yet implemented!')


class SunnyWebBoxHTTP(SunnyWebBoxBase):
    """Communication with a 'Sunny WebBox' via HTTP."""
   
    def openConnection(self):
        if py3:
            from http.client import HTTPConnection
        else:
            from httplib import HTTPConnection
        self.conn  = HTTPConnection(self.host)

    def _rpc(self, request):
        """send an rpc request as JSON object via http and read the result"""
        js = json.dumps(request)
        self.conn.request('POST', '/rpc', "RPC=%s" % js)
        tmp = buf2str(self.conn.getresponse().read())
        response = json.loads(tmp)
        if response['id'] != request['id']:
            raise Exception('RPC answer has wrong id!')
        return response['result']


#class SunnyWebBoxUDPStream(SunnyWebBoxBase):
    """Communication with a 'Sunny WebBox' via UDP Stream."""
   
    #def openConnection(self):
        #from socket import socket, AF_INET, SOCK_DGRAM
        #self.udpPort = 34268
        #self.ssock = socket(AF_INET, SOCK_DGRAM)
        #self.rsock = socket(AF_INET, SOCK_DGRAM)
        #self.rsock.bind(("", self.udpPort))
        #self.rsock.settimeout(100.0)

    #def _rpc(self, request):
        #"""send an rpc request as JSON object via UDP Stream and read the result"""
        #js = ''.join(i+'\0' for i in json.dumps(request, separators=(',',':')))
        #self.ssock.sendto(str2buf(js), (self.host, self.udpPort))
        #while True:
            #data, addr = self.rsock.recvfrom(10*1024)
            #if addr[0] == self.host:
                #break
        #tmp = buf2str(data).replace('\0', '')
        #response = json.loads(tmp)
        #if response['id'] != request['id']:
            #raise Exception('RPC answer has wrong id!')
        #return response['result']


#if __name__ == '__main__':
   
     #pw = ''
     #ip = sys.argv[1]
     #if len(sys.argv) > 2:
        #pw = sys.argv[2]

ip = "192.168.0.98"
pw = "xxxx"
   
swb = SunnyWebBoxHTTP(ip, password=pw)
# swb = SunnyWebBoxUDPStream(ip, password=pw)

print('###### Overview:')
swb.printOverview()
     
for d in swb.getDevices():
         devKey = d['key']
         print("\n  #### Device %s (%s):" % (devKey, d['name']))
         
         print("\n    ## Process data:")
         channels = swb.getProcessDataChannels(devKey)
         data = swb.getProcessData([{'key':devKey, 'channels':channels}])
         for c in data[devKey]:
            print("      %15s (%15s): %s %s" %
                (c['name'], c['meta'], c['value'], c['unit']))
 
               
         print("\n    ## Parameters:")
         channels = swb.getParameterChannels(devKey)
         data = swb.getParameter([{'key':devKey, 'channels':channels}])
         for c in data[devKey]:
             print("      %15s (%15s): %s %s" %
                 (c['name'], c['meta'], c['value'], c['unit']))

#indigo.server.log(unicode(swb))

Posted on
Fri Jan 14, 2022 10:35 am
jay (support) offline
Site Admin
User avatar
Posts: 18220
Joined: Mar 19, 2008
Location: Austin, Texas

Re: script help (easy?) - sma solar system polling script

There were a variety of issues with the script. I've updated it so that I *think* it will work (can't test it myself of course). Save this script as a file (that ends with .py) and run as an external script whenever you want to update the values (be sure to update the IP address and password). All of the comments I added start with ### so you can distinguish the changes I made.

Code: Select all
#! /usr/bin/env python
# -*- coding: utf-8 -*-


#!/usr/bin/env python
# version: 0.3
# date:    2012-11-30
# author:  Joerg Raedler joerg@j-raedler.de
# updates: Dylan Tichenor for Indigo Automation system
# license: BSD
# purpose: make RPC to a Sunny WebBox, a monitoring system for solar plants
#
# http://www.sma.de/en/produkte/monitoring-systems/sunny-webbox.html
#
# Use the classes SunnyWebBoxHTTP or SunnyWebBoxUDPStream in your code
# or run this file as a script to test the classes. First parameter is
# the hostname or IP of the box. A password can be provided with an
# optional second parameter.
#
# Example: python SunnyWebBox.py 123.123.123.123 "foobar"
#
# Documentation on the RPC API can be found here:
#
# http://files.sma.de/dl/2585/SWebBoxRPC-BEN092713.pdf
#

import sys, json, hashlib

### I stick this at the top of all Indigo python scripts
try:
    import indigo
except:
    print("This script must be run from Indigo to work properly")
    raise

### No need for globals. They are generally bad and most often unnecessary as was the case here.

# handle python 2 and 3
if sys.version_info >= (3, 0):
    py3 = True
    def str2buf(s):
        return bytes(s, 'latin1')
    def buf2str(b):
        return str(b, 'latin1')
else:
    py3 = False
    def str2buf(s):
        return bytes(s)
    def buf2str(b):
        return unicode(b, 'latin1')


class Counter:
    """generate increasing numbers with every call - just for convenience"""
   
    def __init__(self, start=0):
        self.i = start

    def __call__(self):
        i = self.i
        self.i += 1
        return i


class SunnyWebBoxBase(object):
    """Communication with a 'Sunny WebBox', a monitoring system for solar plants.
       This is an abstract base class, please use SunnyWebBoxHTTP or
       SunnyWebBoxUDPStream instead!
    """
   
    def __init__(self, host='192.168.0.98', password='xxxxx'):
        self.host = host
        if password:
            self.pwenc = hashlib.md5(password).hexdigest()
        else:
            self.pwenc = ''
        self.count = Counter(1)
        self.openConnection()
   
    def openConnection(self):
        raise Exception('Abstract base class, use child class instead!')

    def newRequest(self, name='GetPlantOverview', withPW=False, **params):
        """return a new rpc request structure (dictionary)"""
        r = {'version': '1.0', 'proc': name, 'format': 'JSON'}
        r['id'] = str(self.count())
        if withPW:
            r['passwd'] = self.pwenc
        if params:
            r['params'] = params
        return r

    def _rpc(self, *args):
        raise Exception('Abstract base class, use child class instead!')

    def printOverview(self):
        """print a short overview of the plant data"""
        for v in self.getPlantOverview():
            print("%15s (%15s): %s %s" % (v['name'], v['meta'], v['value'], v['unit']))
           
    # wrapper methods for the RPC functions
    def getPlantOverview(self):
            res = self._rpc(self.newRequest('GetPlantOverview'))
            return res['overview']

    def getDevices(self):
            res = self._rpc(self.newRequest('GetDevices'))
            return res['devices']

    def getProcessDataChannels(self, deviceKey):
            res = self._rpc(self.newRequest('GetProcessDataChannels', device=deviceKey))
            return res[deviceKey]

    def getProcessData(self, channels):
            res = self._rpc(self.newRequest('GetProcessData', devices=channels))
            # reorder data structure: {devKey: {dict of channels}, ...}
            # return {l['key']: l['channels'] for l in res['devices']}
            r = {}
            for l in res['devices']:
                r[l['key']] = l['channels']
            return r

    def getParameterChannels(self, deviceKey):
            res = self._rpc(self.newRequest('GetParameterChannels', withPW=True, device=deviceKey))
            return res[deviceKey]

    def getParameter(self, channels):
            res = self._rpc(self.newRequest('GetParameter', withPW=True, devices=channels))
            # reorder data structure: {devKey: {dict of channels}, ...}
            # return {l['key']: l['channels'] for l in res['devices']}
            r = {}
            for l in res['devices']:
                r[l['key']] = l['channels']
            return r

    def setParameter(self, *args):
            raise Exception('Not yet implemented!')


class SunnyWebBoxHTTP(SunnyWebBoxBase):
    """Communication with a 'Sunny WebBox' via HTTP."""
   
    def openConnection(self):
        if py3:
            from http.client import HTTPConnection
        else:
            from httplib import HTTPConnection
        self.conn  = HTTPConnection(self.host)

    def _rpc(self, request):
        """send an rpc request as JSON object via http and read the result"""
        js = json.dumps(request)
        self.conn.request('POST', '/rpc', "RPC=%s" % js)
        tmp = buf2str(self.conn.getresponse().read())
        response = json.loads(tmp)
        if response['id'] != request['id']:
            raise Exception('RPC answer has wrong id!')
        return response['result']


#class SunnyWebBoxUDPStream(SunnyWebBoxBase):
    """Communication with a 'Sunny WebBox' via UDP Stream."""
   
    #def openConnection(self):
        #from socket import socket, AF_INET, SOCK_DGRAM
        #self.udpPort = 34268
        #self.ssock = socket(AF_INET, SOCK_DGRAM)
        #self.rsock = socket(AF_INET, SOCK_DGRAM)
        #self.rsock.bind(("", self.udpPort))
        #self.rsock.settimeout(100.0)

    #def _rpc(self, request):
        #"""send an rpc request as JSON object via UDP Stream and read the result"""
        #js = ''.join(i+'\0' for i in json.dumps(request, separators=(',',':')))
        #self.ssock.sendto(str2buf(js), (self.host, self.udpPort))
        #while True:
            #data, addr = self.rsock.recvfrom(10*1024)
            #if addr[0] == self.host:
                #break
        #tmp = buf2str(data).replace('\0', '')
        #response = json.loads(tmp)
        #if response['id'] != request['id']:
            #raise Exception('RPC answer has wrong id!')
        #return response['result']


#if __name__ == '__main__':
   
     #pw = ''
     #ip = sys.argv[1]
     #if len(sys.argv) > 2:
        #pw = sys.argv[2]

ip = "192.168.0.98"
pw = "xxxx"
   
swb = SunnyWebBoxHTTP(ip, password=pw)
# swb = SunnyWebBoxUDPStream(ip, password=pw)

### Update Indigo variables with values from the overview
indigo.server.log("Starting updates to Indigo variables", type="SunnyWebBox Script")
for overview_dict in swb.getPlantOverview():
   ### The indigo.variables lines above did nothing, so I just removed them.
   ### Everything else needed to be moved. The variable v used below wasn't defined as
   ### you had it before because it wasn't within the scope of the for loop.
   ### Because Indigo variables are always stored as strings, I converted each
   ### value because I wanted to make sure that they were strings.
   if overview_dict['meta'] == 'GriEgyTdy':
      indigo.variable.updateValue(1822257245, str(overview_dict['value']))
   if overview_dict['name'] == 'GriEgyTot':
      indigo.variable.updateValue(1469286291, str(overview_dict['value']))
   if overview_dict['name'] == 'GriPwr':
      indigo.variable.updateValue(1958563501, str(overview_dict['value']))
   if overview_dict['name'] == 'Mode):
      indigo.variable.updateValue(789149473, str(overview_dict['value']))


     
# for d in swb.getDevices():
#          devKey = d['key']
#          print("\n  #### Device %s (%s):" % (devKey, d['name']))
#         
#          print("\n    ## Process data:")
#          channels = swb.getProcessDataChannels(devKey)
#          data = swb.getProcessData([{'key':devKey, 'channels':channels}])
#          for c in data[devKey]:
#             print("      %15s (%15s): %s %s" %
#                 (c['name'], c['meta'], c['value'], c['unit']))

#                 
#          print("\n    ## Parameters:")
#          channels = swb.getParameterChannels(devKey)
#          data = swb.getParameter([{'key':devKey, 'channels':channels}])
#          for c in data[devKey]:
#              print("      %15s (%15s): %s %s" %
#                  (c['name'], c['meta'], c['value'], c['unit']))
#
#indigo.server.log(unicode(swb))

Jay (Indigo Support)
Twitter | Facebook | LinkedIn

Posted on
Fri Jan 14, 2022 11:10 am
dtich offline
Posts: 798
Joined: Sep 24, 2005

Re: script help (easy?) - sma solar system polling script

Jay! Thanks so much. So appreciate the help.

So I get this running as external or embedded script:

Code: Select all
Action Group                    sma webbox poll
   Script Error                    smapoll.py: EOL while scanning string literal
   Script Error                    around line 214 - "if overview_dict['name'] == 'Mode):"


Made it to the last line!

Posted on
Fri Jan 14, 2022 11:16 am
dtich offline
Posts: 798
Joined: Sep 24, 2005

Re: script help (easy?) - sma solar system polling script

was small typo, paren instead of '

fixed!!!

works!!!

jay. thanks so much.

Page 1 of 1

Who is online

Users browsing this forum: No registered users and 3 guests