Hi!
Anbei ein Plgin für smarthome.py, welches über ein am PC angeschlossenes Bluetooth-Dongle einen SMA-Wechselrichter auslesen kann. Unterstützt werden erstmal nur
- Tagesertrag
- Gesamtertrag
- momentane Einspeiseleistung
- weitere (Neben-)Sachen wie Seriennummer, BT-Adresse, Signalstärke der Bluetooth-Verbindung ...
plugin.conf
bt_addr => Bluetooth-Adresse des Wechselrichters (per hcitool scan)
password => Wechselrichter-Passwort - Standard ist 0000
update_cycle => Wie häufig werden (alle!) Werte neu gelesen (60s ist ganz ok)
Beispielitems:
__init__.py (im Ordner /plugins/sma/)
Das Plugin sollte halbwegs stabil laufen, allerdings wären ein paar try-except-Blöcke ganz sinnvoll. Zudem gibt es noch mehr Datenfelder zu lesen - da hab ich bisher keine Muße für gehabt. Interessant wären wohl noch (zwei) Strings auf der DC-Seite.
Grüße
Robert
Anbei ein Plgin für smarthome.py, welches über ein am PC angeschlossenes Bluetooth-Dongle einen SMA-Wechselrichter auslesen kann. Unterstützt werden erstmal nur
- Tagesertrag
- Gesamtertrag
- momentane Einspeiseleistung
- weitere (Neben-)Sachen wie Seriennummer, BT-Adresse, Signalstärke der Bluetooth-Verbindung ...
plugin.conf
Code:
[sma]
class_name = SMA
class_path = plugins.sma
bt_addr = 00:80:25:xx:xx:xx
password = 0000
update_cycle = 60
password => Wechselrichter-Passwort - Standard ist 0000
update_cycle => Wie häufig werden (alle!) Werte neu gelesen (60s ist ganz ok)
Beispielitems:
Code:
[Wechselrichter]
[[Einspeiseleistung]]
type = num
visu = yes
history = true
sma = "AC_POWER"
knx_dpt = 12
knx_send = 5/4/50
knx_reply = 5/4/50
[[Tagesertrag]]
type = num
visu = yes
history = true
sma = "DAY_YIELD"
knx_dpt = 12
knx_send = 5/4/51
knx_reply = 5/4/51
[[Gesamtertrag]]
type = num
visu = yes
history = true
sma = "TOTAL_YIELD"
knx_dpt = 12
knx_send = 5/4/52
knx_reply = 5/4/52
[[Seriennummer]]
type = num
visu = yes
sma = "INV_SERIAL"
knx_dpt = 12
knx_send = 5/4/53
knx_reply = 5/4/53
[[MAC_Adresse]]
type = str
visu = yes
sma = "INV_ADDRESS"
knx_dpt = 16000
knx_send = 5/4/54
knx_reply = 5/4/54
[[Letzte_Aktualisierung]]
type = str
visu = yes
sma = "LAST_UPDATE"
knx_dpt = 16000
knx_send = 5/4/55
knx_reply = 5/4/55
Code:
#!/usr/bin/env python
# vim: set encoding=utf-8 tabstop=4 softtabstop=4 shiftwidth=4 expandtab
#########################################################################
# Copyright 2013 Robert Budde robert@projekt131.de
#########################################################################
# This software is based on Stuart Pittaway's "NANODE SMA PV MONITOR"
# https://github.com/stuartpittaway/nanodesmapvmonitor
#
# SMA-Plugin for SmartHome.py. http://smarthome.sourceforge.net/
#
# This plugin is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This plugin is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this plugin. If not, see <http://www.gnu.org/licenses/>.
#########################################################################
from bluetooth import *
import struct
import logging
import time
from datetime import datetime
from dateutil import tz
BCAST_ADDR = [0xFF,0xFF,0xFF,0xFF,0xFF,0xFF]
ZERO_ADDR = [0x00,0x00,0x00,0x00,0x00,0x00]
SMANET2_HDR = [0x7E,0xFF,0x03,0x60,0x65]
FCSTAB = [ 0x0000, 0x1189, 0x2312, 0x329b, 0x4624, 0x57ad, 0x6536, 0x74bf, \
0x8c48, 0x9dc1, 0xaf5a, 0xbed3, 0xca6c, 0xdbe5, 0xe97e, 0xf8f7, \
0x1081, 0x0108, 0x3393, 0x221a, 0x56a5, 0x472c, 0x75b7, 0x643e, \
0x9cc9, 0x8d40, 0xbfdb, 0xae52, 0xdaed, 0xcb64, 0xf9ff, 0xe876, \
0x2102, 0x308b, 0x0210, 0x1399, 0x6726, 0x76af, 0x4434, 0x55bd, \
0xad4a, 0xbcc3, 0x8e58, 0x9fd1, 0xeb6e, 0xfae7, 0xc87c, 0xd9f5, \
0x3183, 0x200a, 0x1291, 0x0318, 0x77a7, 0x662e, 0x54b5, 0x453c, \
0xbdcb, 0xac42, 0x9ed9, 0x8f50, 0xfbef, 0xea66, 0xd8fd, 0xc974, \
0x4204, 0x538d, 0x6116, 0x709f, 0x0420, 0x15a9, 0x2732, 0x36bb, \
0xce4c, 0xdfc5, 0xed5e, 0xfcd7, 0x8868, 0x99e1, 0xab7a, 0xbaf3, \
0x5285, 0x430c, 0x7197, 0x601e, 0x14a1, 0x0528, 0x37b3, 0x263a, \
0xdecd, 0xcf44, 0xfddf, 0xec56, 0x98e9, 0x8960, 0xbbfb, 0xaa72, \
0x6306, 0x728f, 0x4014, 0x519d, 0x2522, 0x34ab, 0x0630, 0x17b9, \
0xef4e, 0xfec7, 0xcc5c, 0xddd5, 0xa96a, 0xb8e3, 0x8a78, 0x9bf1, \
0x7387, 0x620e, 0x5095, 0x411c, 0x35a3, 0x242a, 0x16b1, 0x0738, \
0xffcf, 0xee46, 0xdcdd, 0xcd54, 0xb9eb, 0xa862, 0x9af9, 0x8b70, \
0x8408, 0x9581, 0xa71a, 0xb693, 0xc22c, 0xd3a5, 0xe13e, 0xf0b7, \
0x0840, 0x19c9, 0x2b52, 0x3adb, 0x4e64, 0x5fed, 0x6d76, 0x7cff, \
0x9489, 0x8500, 0xb79b, 0xa612, 0xd2ad, 0xc324, 0xf1bf, 0xe036, \
0x18c1, 0x0948, 0x3bd3, 0x2a5a, 0x5ee5, 0x4f6c, 0x7df7, 0x6c7e, \
0xa50a, 0xb483, 0x8618, 0x9791, 0xe32e, 0xf2a7, 0xc03c, 0xd1b5, \
0x2942, 0x38cb, 0x0a50, 0x1bd9, 0x6f66, 0x7eef, 0x4c74, 0x5dfd, \
0xb58b, 0xa402, 0x9699, 0x8710, 0xf3af, 0xe226, 0xd0bd, 0xc134, \
0x39c3, 0x284a, 0x1ad1, 0x0b58, 0x7fe7, 0x6e6e, 0x5cf5, 0x4d7c, \
0xc60c, 0xd785, 0xe51e, 0xf497, 0x8028, 0x91a1, 0xa33a, 0xb2b3, \
0x4a44, 0x5bcd, 0x6956, 0x78df, 0x0c60, 0x1de9, 0x2f72, 0x3efb, \
0xd68d, 0xc704, 0xf59f, 0xe416, 0x90a9, 0x8120, 0xb3bb, 0xa232, \
0x5ac5, 0x4b4c, 0x79d7, 0x685e, 0x1ce1, 0x0d68, 0x3ff3, 0x2e7a, \
0xe70e, 0xf687, 0xc41c, 0xd595, 0xa12a, 0xb0a3, 0x8238, 0x93b1, \
0x6b46, 0x7acf, 0x4854, 0x59dd, 0x2d62, 0x3ceb, 0x0e70, 0x1ff9, \
0xf78f, 0xe606, 0xd49d, 0xc514, 0xb1ab, 0xa022, 0x92b9, 0x8330, \
0x7bc7, 0x6a4e, 0x58d5, 0x495c, 0x3de3, 0x2c6a, 0x1ef1, 0x0f78 ]
TOTAL_YIELD = (0x2601, 0xA009, 0x54)
DAY_YIELD = (0x2622, 0xA009, 0x54)
AC_POWER = (0x263F, 0xA109, 0x51)
logger = logging.getLogger('')
class SMA():
def __init__(self, smarthome, bt_addr, password="0000", update_cycle="60"):
self._sh = smarthome
self._val = {}
self._inv_bt_addr = bt_addr
self._inv_password = password
self._inv_last_read_datetime = datetime.fromtimestamp(0, tz.tzlocal())
self._inv_last_read = self._inv_last_read_datetime.strftime("%d.%m.%Y %H:%M:%S")
self._own_bt_addr_le = BCAST_ADDR
self._btsocket = BluetoothSocket( RFCOMM )
if not (is_valid_address(self._inv_bt_addr)):
logger.warning("sma: inverter bluetooth address is invalid: %s" % self._inv_bt_addr)
return
self._inv_bt_name = lookup_name(self._inv_bt_addr, timeout=5)
if (self._inv_bt_name == None):
logger.warning("sma: inverter bluetooth name could not be looked up")
self._inv_bt_name = "unknown"
self._btsocket.connect((self._inv_bt_addr, 1))
logger.debug("sma: via bluetooth connected to %s (%s)" % (self._inv_bt_name, self._inv_bt_addr))
self._inv_connect()
self._inv_login()
self._sh.scheduler.add('sma.update', self._update_values, prio=5, cycle=int(update_cycle))
def _update_values(self):
#logger.warning("sma: signal strength = %d%%" % self._inv_get_bt_signal_strength())
value = self._inv_get_3byte_value(AC_POWER)
if (value >= 0):
logger.debug("sma: current AC power = %dW" % value)
if 'AC_POWER' in self._val:
for item in self._val['AC_POWER']['items']:
item(value, 'SMA', self._inv_bt_addr)
else:
logger.warning("sma: could not read current AC power!")
value = self._inv_get_3byte_value(DAY_YIELD)
if (value >= 0):
logger.debug("sma: day yield = %dWh" % value)
if 'DAY_YIELD' in self._val:
for item in self._val['DAY_YIELD']['items']:
item(value, 'SMA', self._inv_bt_addr)
else:
logger.warning("sma: could not read day yield!")
value = self._inv_get_3byte_value(TOTAL_YIELD)
if (value >= 0):
logger.debug("sma: total yield = %dWh" % value)
if 'TOTAL_YIELD' in self._val:
for item in self._val['TOTAL_YIELD']['items']:
item(value, 'SMA', self._inv_bt_addr)
else:
logger.warning("sma: could not read total yield!")
logger.debug("sma: last read = %s" % self._inv_last_read)
if 'LAST_UPDATE' in self._val:
for item in self._val['LAST_UPDATE']['items']:
item(self._inv_last_read, 'SMA', self._inv_bt_addr)
def run(self):
self.alive = True
def stop(self):
self.alive = False
try:
self._btsocket.close()
self._btsocket = False
except:
pass
def parse_item(self, item):
if 'sma' in item.conf:
sma_value = item.conf['sma']
logger.debug("sma: {0} connected to value of {1}".format(item, sma_value))
if not sma_value in self._val:
self._val[sma_value] = {'items': [item], 'logics': []}
else:
if not item in self._val[sma_value]['items']:
self._val[sma_value]['items'].append(item)
if (sma_value == 'OWN_ADDRESS'):
item(self._own_bt_addr, 'SMA', self._inv_bt_addr)
if (sma_value == 'INV_ADDRESS'):
item(self._inv_bt_addr, 'SMA', self._inv_bt_addr)
if (sma_value == 'INV_SERIAL'):
item(self._inv_serial, 'SMA', self._inv_bt_addr)
# return None to indicate "read-only"
return None
def parse_logic(self, logic):
if 'sma' in item.conf:
sma_value = item.conf['sma']
logger.debug("sma: {0} connected to value of {1}".format(item, sma_value))
if not sma_value in self._val:
self._val[sma_value] = {'items': [], 'logics': [item]}
else:
if not item in self._val[sma_value]['logics']:
self._val[sma_value]['logics'].append(item)
# receive function both for single and multi-part messages
def _recv_msg(self, timeout=1.0):
# wait for sfd
recv_char = 0
self._btsocket.settimeout(timeout)
while (recv_char != chr(0x7E)):
recv_char = self._btsocket.recv(1)
if (recv_char == None):
logger.warning("sma: rx: could not receive SFD within %ds" % timeout)
return []
msg = [ord(recv_char)]
# get level 1 length and validate
while (len(msg) < 4):
recv_char = self._btsocket.recv(1)
if (recv_char == None):
logger.warning("sma: rx: could not receive complete length field within %ds" % timeout)
return []
msg += [ord(recv_char)]
if ((msg[1] ^ msg[2] ^ msg[3]) != 0x7E):
logger.warning("sma: rx: length fields invalid")
return []
length = (msg[2] << 8) + msg[1]
if (length < 18):
logger.warning("sma: rx: length to small: %d" % length)
return []
# get remaining characters
while (len(msg) < length):
# by reading as much as possible we are also receiving follow-up msgs (to clear stucked msgs - no better solution yet)!
recv_char = self._btsocket.recv(1000)
if (recv_char == None):
logger.warning("sma: rx: could not receive msg body within %ds" % timeout)
return []
msg += [ord(i) for i in recv_char];
#logger.debug("sma: rx: len=%d/%d" % (len(msg), length))
if (len(msg) > length):
logger.warning("sma: rx: dismissing follow-up data: %s" % ' '.join(['0x%02x' % b for b in msg[length::]]))
msg = msg[0:length]
# check src and dst addr and check
if (msg[4:10] != self._inv_bt_addr_le):
logger.warning("sma: rx: unknown src addr")
return []
if (msg[10:16] != self._own_bt_addr_le) and (msg[10:16] != ZERO_ADDR) and (msg[10:16] != BCAST_ADDR):
logger.warning("sma: rx: wrong dst addr")
return []
# 18 (SMANET1) + 4 (SMANET2+ HDR) + 2 + 6 (SRC) + 2 + 6 (DST) + 6 + 1 (CNT) + 2 (CRC) + 1 (TAIL)
if (length >= 48) and (msg[18:23] == SMANET2_HDR):
#logger.debug("sma: smanet2+ msg")
# remove escape characters after SMANET2+ HDR
i = 22
while (i < length):
if (msg[i] == 0x7d):
del msg[i]
msg[i] ^= 0x20
length -= 1
i += 1
#logger.debug("sma: escape chars removed")
crc = self._calc_crc16(msg[19:-3])
if (((crc>>8) != msg[-2]) or ((crc&0xFF) != msg[-3])):
logger.warning("sma: crc: crc16 error - %04x" % crc)
logger.warning("sma: crc: len=%d %s\n" % (len(msg), ' '.join(['0x%02x' % b for b in msg])))
return []
#print "rx: len=%d %s\n" % (len(msg), ' '.join(['0x%02x' % b for b in msg]))
return msg
def _recv_msg_with_cmdcode(self, cmdcodes_expected):
retries = 10
while (retries > 0):
retries -= 1
msg = self._recv_msg()
# get cmdcode
if (msg != []) and (((msg[17] << 8) + msg[16]) in cmdcodes_expected):
break
if (retries == 0):
logger.warning("sma: recv msg with cmdcode - retries used up!")
return []
return msg
def _send_msg(self, msg):
if (len(msg) >= 0x3a):
# calculate crc starting with byte 19 and append with LE byte-oder
crc = self._calc_crc16(msg[19::])
msg += [crc & 0xff, (crc >> 8) & 0xff]
# add escape sequences starting with byte 19
msg = msg[0:19] + self._add_escapes(msg[19::])
# add msg delimiter
msg += [0x7e]
# set length fields - msg[1] is exact overall length, msg[3] = 0x73-msg[1]
msg[1] = len(msg) & 0xff
msg[2] = (len(msg) >> 8) & 0xff
msg[3] = msg[1] ^ msg[2] ^ 0x7e
#print "tx: len=%d %s\n" % (len(msg), ' '.join(['0x%02x' % b for b in msg]))
send = ''
for i in msg:
send += chr(i)
self._btsocket.send(send)
def _calc_crc16(self, msg):
crc = 0xFFFF
for i in msg:
crc = (crc>>8) ^ FCSTAB[(crc ^ i) & 0xFF]
crc ^= 0xFFFF
#print("crc16 = %x") % crc
return crc
def _check_crc(self, msg):
crc = self._calc_crc16(msg[19:-3])
if (((crc>>8) != msg[-2]) or ((crc&0xFF) != msg[-3])):
logger.debug("sma: crc: crc16 error - %04x" % crc)
logger.debug("sma: crc: len=%d %s\n" % (len(msg), ' '.join(['0x%02x' % b for b in msg])))
return False
return True
def _add_escapes(self, msg):
escaped = []
for i in msg:
if (i == 0x7d) or (i == 0x7e) or (i == 0x11) or (i == 0x12) or (i == 0x13):
escaped += [0x7d, i^0x20]
else:
escaped += [i];
return escaped
def _inv_connect(self):
self._send_count = 0
self._inv_bt_addr_le = [int(x, 16) for x in self._inv_bt_addr.split(':')]
self._inv_bt_addr_le = self._inv_bt_addr_le[::-1]
# receive broadcast-msg from inverter
msg = self._recv_msg_with_cmdcode([0x0002]);
# extract net-id from the 0x0002 msg
self._net_id = msg[22]
# reply with wildcard src addr
msg[4:10] = ZERO_ADDR
msg[10:16] = self._inv_bt_addr_le
self._send_msg(msg)
# receive msg from inverter
msg = self._recv_msg_with_cmdcode([0x000a]);
# receive msg from inverter
msg = self._recv_msg_with_cmdcode([0x0005,0x000c]);
# receive msg from inverter
msg = self._recv_msg_with_cmdcode([0x0005]);
# extract own bluetooth addr
self._own_bt_addr_le = msg[26:32]
logger.debug("sma: own bluetooth address: %s" % ':'.join(['%02x' % b for b in self._own_bt_addr_le[::-1]]))
# first SMA net2 msg
retries = 10
while (retries > 0):
retries -= 1
# level1
cmdcode = 0x0001
msg = [0x7E,0,0,0] + self._own_bt_addr_le + self._inv_bt_addr_le + [cmdcode&0xFF,(cmdcode>>8)&0xFF]
# sma-net2 level
ctrl = 0xA009
self._send_count += 1
if (self._send_count > 75):
self._send_count = 1
msg += SMANET2_HDR + [ctrl&0xFF,(ctrl>>8)&0xFF] + BCAST_ADDR + [0x00,0x00] + self._inv_bt_addr_le + [0x00] + [0x00] + [0,0,0,0] + [self._send_count]
msg += [0x80,0x00,0x02,0x00] + [0x00] + [0x00,0x00,0x00,0x00] + [0x00,0x00,0x00,0x00]
# send msg to inverter
self._send_msg(msg)
# receive msg from inverter
msg = self._recv_msg_with_cmdcode([0x0001]);
if (msg != []):
break;
if (retries == 0):
logger.warning("sma: connect - retries used up!")
return
# second SMA net2 msg
cmdcode = 0x0001
msg = [0x7E, 0x00, 0x00, 0x00] + self._own_bt_addr_le + self._inv_bt_addr_le + [cmdcode&0xFF,(cmdcode>>8)&0xFF]
# sma-net2 level
ctrl = 0xA008
self._send_count += 1
if (self._send_count > 75):
self._send_count = 1
msg += SMANET2_HDR + [ctrl&0xFF,(ctrl>>8)&0xFF] + BCAST_ADDR + [0x00,0x03] + self._inv_bt_addr_le + [0x00] + [0x03] + [0,0,0,0] + [self._send_count]
msg += [0x80,0x0E,0x01,0xFD,0xFF,0xFF,0xFF,0xFF,0xFF]
# send msg
self._send_msg(msg)
def _inv_login(self):
timestamp_utc = int(time.time())
password_pattern = [0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88]
password_pattern[0:len(self._inv_password)] = [((0x88 + ord(char)) & 0xff) for char in self._inv_password]
retries = 10
while (retries > 0):
retries -= 1
# level1
cmdcode = 0x0001
msg = [0x7E,0,0,0] + self._own_bt_addr_le + self._inv_bt_addr_le + [cmdcode&0xFF,(cmdcode>>8)&0xFF]
# sma-net2 level
ctrl = 0xA00E
self._send_count += 1
if (self._send_count > 75):
self._send_count = 1
msg += SMANET2_HDR + [ctrl&0xFF,(ctrl>>8)&0xFF] + BCAST_ADDR + [0x00,0x01] + self._inv_bt_addr_le + [0x00] + [0x01] + [0,0,0,0] + [self._send_count]
msg += [0x80, 0x0C, 0x04, 0xFD, 0xFF, 0x07, 0x00, 0x00, 0x00, 0x84, 0x03, 0x00, 0x00]
msg += [timestamp_utc & 0xff, (timestamp_utc >> 8) & 0xff, (timestamp_utc >> 16) & 0xff, (timestamp_utc >> 24) & 0xff]
msg += [0x00, 0x00, 0x00, 0x00] + password_pattern
# send msg to inverter
self._send_msg(msg)
# send msg to inverter
self._send_msg(msg)
# receive msg from inverter
msg = self._recv_msg_with_cmdcode([0x0001]);
if (msg != []):
break;
if (retries == 0):
logger.warning("sma: login - retries used up!")
return
# extract serial
self._inv_serial = (msg[38] << 24) + (msg[37] << 16) + (msg[36] << 8) + msg[35]
logger.debug("sma: inverter serial = %d" % self._inv_serial)
def _inv_get_bt_signal_strength(self):
cmdcode = 0x0003
msg = [0x7E,0,0,0] + self._own_bt_addr_le + self._inv_bt_addr_le + [cmdcode&0xFF,(cmdcode>>8)&0xFF]
msg += [0x05, 0x00]
self._send_msg(msg)
msg = self._recv_msg_with_cmdcode([0x0004])
# extract signal strength
return ((msg[22] * 100.0)/0xff)
def _inv_get_value(self, value_set):
retries = 10
while (retries > 0):
retries -= 1
# level1
cmdcode = 0x0001
msg = [0x7E,0,0,0] + self._own_bt_addr_le + self._inv_bt_addr_le + [cmdcode&0xFF,(cmdcode>>8)&0xFF]
# sma-net2 level
self._send_count += 1
if (self._send_count > 75):
self._send_count = 1
msg += SMANET2_HDR + [value_set[1]&0xFF,(value_set[1]>>8)&0xFF] + BCAST_ADDR + [0x00,0x00] + self._inv_bt_addr_le + [0x00] + [0x00] + [0,0,0,0] + [self._send_count]
msg += [0x80,0x00,0x02,0x00] + [value_set[2]] + [0x00] + [value_set[0]&0xFF,(value_set[0]>>8)&0xFF] + [0x00, 0xFF] + [value_set[0]&0xFF,(value_set[0]>>8)&0xFF] + [0x00]
# send msg to inverter
self._send_msg(msg)
#print "tx: len=%d %s\n" % (len(msg), ' '.join(['0x%02x' % b for b in msg]))
# receive msg from inverter
msg = self._recv_msg_with_cmdcode([cmdcode]);
if (len(msg) >= 63):
# extract valuetype
recv_value_code = (msg[61]<<8)+msg[60]
if (len(msg) >= 63) and (recv_value_code == value_set[0]):
break
else:
logger.warning("sma: recv unrequested msg!")
logger.warning("sma: len=%d %s\n" % (len(msg), ' '.join(['0x%02x' % b for b in msg])))
if (retries == 0):
logger.warning("sma: get value - retries used up!")
return []
# extract time(utc)
self._inv_last_read_timestamp_utc = (msg[66]<<24)+(msg[65]<<16)+(msg[64]<<8)+msg[63]
self._inv_last_read_datetime = datetime.fromtimestamp(self._inv_last_read_timestamp_utc, tz.tzlocal())
self._inv_last_read = self._inv_last_read_datetime.strftime("%d.%m.%Y %H:%M:%S")
return msg
def _inv_get_3byte_value(self, value_set):
msg = self._inv_get_value(value_set)
if (msg):
return (msg[69]<<16)+(msg[68]<<8)+msg[67]
else:
return -1
if __name__ == '__main__':
logging.basicConfig(level=logging.DEBUG)
myplugin = Plugin('SMA')
myplugin.run()
Grüße
Robert


.
!
Kommentar