pysma 0.2.0__zip

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,128 @@
1
+ """SMA WebConnect library for Python.
2
+
3
+ See: http://www.sma.de/en/products/monitoring-control/webconnect.html
4
+
5
+ Source: http://www.github.com/kellerza/pysma
6
+ """
7
+ import asyncio
8
+ import json
9
+ import logging
10
+
11
+ import async_timeout
12
+
13
+ _LOGGER = logging.getLogger(__name__)
14
+
15
+ GROUP_USER = 'usr'
16
+ GROUP_INSTALLER = 'istl'
17
+
18
+ KEY_CURRENT_POWER_W = '6100_40263F00'
19
+ KEY_CURRENT_CONSUMPTION_W = '6100_00543100'
20
+ KEY_TOTAL_YIELD_KWH = '6400_00260100'
21
+ KEY_TOTAL_CONSUMPTION_KWH = '6400_00543A00'
22
+
23
+
24
+ URL_LOGIN = "{}/dyn/login.json"
25
+ URL_LOGOUT = "{}/dyn/logout.json"
26
+ URL_VALUES = "{}/dyn/getValues.json"
27
+
28
+
29
+ class SMA:
30
+ """Class to connect to the SMA webconnect module and read parameters."""
31
+
32
+ def __init__(self, session, url, password, group=GROUP_USER):
33
+ """Init SMA connection."""
34
+ self._new_session_data = {
35
+ 'right': group, 'pass': password}
36
+ self._url = url.rstrip('/')
37
+ if not url.startswith('http'):
38
+ self._url = "http://" + self._url
39
+ self._aio_session = session
40
+ self._sma_sid = None
41
+ self._401_error_count = 0
42
+
43
+ @asyncio.coroutine
44
+ def _fetch_json(self, url, payload, params=None):
45
+ """Fetch json data for requests."""
46
+ headers = {'content-type': 'application/json'}
47
+ with async_timeout.timeout(3):
48
+ res = yield from self._aio_session.post(
49
+ url, data=json.dumps(payload), headers=headers, params=params)
50
+ return (yield from res.json())
51
+
52
+ @asyncio.coroutine
53
+ def new_session(self):
54
+ """Establish a new session."""
55
+ try:
56
+ res = yield from self._fetch_json(
57
+ URL_LOGIN.format(self._url), self._new_session_data)
58
+ self._sma_sid = res['result']['sid']
59
+ return True
60
+ except asyncio.TimeoutError:
61
+ _LOGGER.error("Could not connect to SMA at %s", self._url)
62
+ except KeyError:
63
+ self._sma_sid = None
64
+ if str(res.get('err', '')) == '503':
65
+ _LOGGER.error("Max amount of sesions reached")
66
+ else:
67
+ _LOGGER.error("Session ID expected ['result']['sid'], got %s",
68
+ res)
69
+ return False
70
+
71
+ @asyncio.coroutine
72
+ def close_session(self):
73
+ """Close the session login."""
74
+ if self._sma_sid is None:
75
+ return
76
+ yield from self._fetch_json(
77
+ URL_LOGOUT.format(self._url), {}, params={'sid': self._sma_sid})
78
+ self._sma_sid = None
79
+
80
+ @asyncio.coroutine
81
+ def read(self, keys):
82
+ """Read a set of keys."""
83
+ payload = {'destDev': [], 'keys': keys}
84
+ if self._sma_sid is None:
85
+ yield from self.new_session()
86
+ if self._sma_sid is None:
87
+ return None
88
+ try:
89
+ res = yield from self._fetch_json(
90
+ URL_VALUES.format(self._url), payload=payload,
91
+ params={'sid': self._sma_sid})
92
+ except asyncio.TimeoutError:
93
+ return None
94
+
95
+ # On the first 401 error we close the session which will re-login
96
+ if res.get('err') == 401 and self._401_error_count == 0:
97
+ _LOGGER.warning("401 error detected, closing session to force " +
98
+ "another login attempt")
99
+ self.close_session()
100
+ self._401_error_count += 1
101
+ return None
102
+
103
+ try:
104
+ _, res = res['result'].popitem() # Only use first value
105
+ result = []
106
+ for key in keys:
107
+ result.append(res[key])
108
+ except (KeyError, TypeError) as err:
109
+ _LOGGER.error("Unexpected return: %s - %s", res, err)
110
+ return None
111
+
112
+ for idx, val in enumerate(result):
113
+ try:
114
+ # First list item indicates result length,
115
+ # Results are the second item in list.
116
+ result[idx] = val.popitem()[1]
117
+ # Extract all ['val'] values
118
+ result[idx] = [value['val'] for value in result[idx]]
119
+ # If only 1 item in list, remove list
120
+ if len(result[idx]) == 1:
121
+ result[idx] = result[idx][0]
122
+ except (KeyError, TypeError) as err:
123
+ _LOGGER.error(err)
124
+
125
+ # reset 401 error count
126
+ self._401_error_count = 0
127
+
128
+ return result
@@ -0,0 +1,11 @@
1
+ Metadata-Version: 1.1
2
+ Name: pysma
3
+ Version: 0.2.0
4
+ Summary: Library to interface an SMA Solar WebConnect module
5
+ Home-page: https://github.com/kellerza/pysma
6
+ Author: Johann Kellerman
7
+ Author-email: kellerza@gmail.com
8
+ License: MIT
9
+ Download-URL: https://github.com/kellerza/pysma/tarball/0.2
10
+ Description: UNKNOWN
11
+ Platform: UNKNOWN
@@ -0,0 +1,9 @@
1
+ README.rst
2
+ setup.py
3
+ pysma/__init__.py
4
+ pysma.egg-info/PKG-INFO
5
+ pysma.egg-info/SOURCES.txt
6
+ pysma.egg-info/dependency_links.txt
7
+ pysma.egg-info/requires.txt
8
+ pysma.egg-info/top_level.txt
9
+ pysma.egg-info/zip-safe
@@ -0,0 +1,2 @@
1
+ aiohttp>2,<3
2
+ async_timeout>2,<3