forked from smarthomeNG/plugins
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathSunnyWebBox.py
201 lines (170 loc) · 6.93 KB
/
SunnyWebBox.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
#!/usr/bin/env python
# version: 0.4
# date: 2013-07-27
# author: Joerg Raedler joerg@j-raedler.de
# 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
#
# This python module provides a classes that can be used to communicate with a
# Sunny WebBox. You can use the classes SunnyWebBoxHTTP and SunnyWebBoxUDPStream
# in your own code.
#
# A small sample program is used if you run the module as a script. The hostname
# or address must be provided as a parameter.
#
# Example: python SunnyWebBox.py 123.123.123.123
#
# Use the --help switch to learn about options for the udp mode and password.
import sys, json, hashlib
import argparse
# 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='1.2.3.4', password=''):
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__':
parser = argparse.ArgumentParser(description='SunnyWebBox API - CLI query')
parser.add_argument('host', help='host name or adress')
parser.add_argument('-u', '--udp', action='store_true', help='use UDP mode instead of default HTTP mode')
parser.add_argument('-p', '--password', action='store', help='password', default='')
args = parser.parse_args()
if args.udp:
swb = SunnyWebBoxUDPStream(args.host, password=args.password)
else:
swb = SunnyWebBoxHTTP(args.host, password=args.password)
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']))