SolarManager unter Versionsverwaltung

Erster Stand der Hintergrundprozesse, die auf der Synology unter
/volume1/homes/wagner/SolarManager laufen: der Manager selbst, die Sammler
je Geraet, die MQTT-Bruecke, der Wecker und - neu hinzugezogen - der
AutoAction-Runner, der als Hintergrundprozess hierher gehoert und nicht ins
Web-Verzeichnis.

Zugangsdaten stehen nicht mehr im Quelltext, sondern in config.ini, die
nicht mit eingecheckt wird. Vorlage ist config.ini.example, gelesen wird sie
von konfig.py. Betroffen waren solarManager.py (Datenbank und Wattpilot),
zeit.py, gatherWaterData.py, wecker.py und skoda_testdaten.py, das sich das
Passwort bisher aus dem Quelltext eines anderen Moduls herausgesucht hat.

Die Kia-Anbindung ist mit dem Fahrzeug entfallen: kiaTest.py,
gatherCarData.py und hyundai_kia_connect_api sind nicht mehr dabei, ebenso
gatherInverterData.py, auf das nur noch eine auskommentierte Zeile zeigte.

Die mitgelieferten Bibliotheken bleiben im Repository - die NAS hat kein
pip, sie muessen neben den Skripten liegen.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
2026-09-02 20:46:59 +02:00
co-authored by Claude Opus 5
commit 79843aa2ae
968 changed files with 261182 additions and 0 deletions
+183
View File
@@ -0,0 +1,183 @@
from __future__ import print_function
import os
import hashlib
import sys
import xml.etree.ElementTree as ET
PY2 = (2,) <= sys.version_info < (3,)
MANIFEST_ROOT = 'manifest'
MANIFEST_FILE = 'file'
MANIFEST_NAME = 'name'
MANIFEST_MD5 = 'md5'
MANIFEST_FILE_NAME = 'manifest.xml'
MANIFEST_MD5_EXT = '.md5'
manifest_files = ['CHANGELOG', 'smdx.xsd']
def xml_indent(elem, level=0):
"""
Simple XML pretty print support function
"""
i = "\n" + level*" "
if len(elem):
if not elem.text or not elem.text.strip():
elem.text = i + " "
if not elem.tail or not elem.tail.strip():
elem.tail = i
for elem in elem:
xml_indent(elem, level+1)
if not elem.tail or not elem.tail.strip():
elem.tail = i
else:
if level and (not elem.tail or not elem.tail.strip()):
elem.tail = i
class ManifestError(Exception):
pass
class File(object):
def __init__(self, name=None, md5=None):
self.name = name
self.md5 = md5
class Manifest(object):
def __init__(self, path='.', filename=None):
self.path = path
self.files = {}
self.filename = filename
if filename:
self.from_xml(filename=filename)
def create(self):
self.scan()
filename = os.path.join(self.path, MANIFEST_FILE_NAME)
self.to_xml_file(filename=filename)
md5 = hashlib.md5(open(filename, 'rb').read()).hexdigest()
f = open(filename + MANIFEST_MD5_EXT, 'w')
f.write(md5)
f.close()
def md5(self):
return hashlib.md5(self.to_xml_bytes()).hexdigest()
def diff(self, m):
diff_str = ''
for f, md5 in self.files.items():
m_md5 = m.files.get(f)
if md5 != m_md5:
diff_str += '%s: %s %s\n' % (f, md5, m_md5)
if len(self.files) != len(m.files):
for f, md5 in m.files.items():
if self.files.get(f) is None:
diff_str += '%s: "None" %s\n' % (f, md5)
return diff_str
def scan(self):
try:
files = os.listdir(self.path)
for f in files:
name, ext = os.path.splitext(f)
if f in manifest_files or (ext == '.xml' and f.startswith('smdx_')):
filename = os.path.join(self.path, f)
content = open(filename, 'rb').read()
# if content.find(b'\r\n') >= 0:
# print('windows file ', self.path)
content = content.replace(b'\r\n', b'\n')
md5 = hashlib.md5(content).hexdigest()
self.files[f] = md5
except Exception as e:
raise ManifestError('Error scanning directory %s: %s' % (self.path, str(e)))
def scan_strip(self):
try:
files = os.listdir(self.path)
for f in files:
name, ext = os.path.splitext(f)
if f in manifest_files or (ext == '.xml' and f.startswith('smdx_')):
filename = os.path.join(self.path, f)
content = open(filename, 'rb').read()
if content.find(b'\r\n') >= 0:
content = content.replace(b'\r\n', b'\n')
fc = open(filename, 'w')
fc.write(content)
fc.close()
md5 = hashlib.md5(content).hexdigest()
self.files[f] = md5
except Exception as e:
raise ManifestError('Error scanning directory %s: %s' % (self.path, str(e)))
def to_xml(self, parent=None, filename=None):
if parent is not None:
e = ET.SubElement(parent, MANIFEST_ROOT)
else:
e = ET.Element(MANIFEST_ROOT)
for f in sorted(self.files.keys()):
attr = {MANIFEST_NAME: f, MANIFEST_MD5: self.files.get(f)}
ET.SubElement(e, MANIFEST_FILE, attrib=attr)
return e
def to_xml_bytes(self, pretty_print=True):
e = self.to_xml()
if pretty_print:
xml_indent(e)
return ET.tostring(e)
def to_xml_file(self, filename=None, pretty_print=True, replace_existing=True):
xml = self.to_xml_bytes(pretty_print)
if filename is not None:
if replace_existing is False and os.path.exists(filename):
raise ManifestError('File %s already exists' % (filename))
f = open(filename, 'wb')
f.write(xml)
f.close()
else:
if PY2:
print(xml)
else:
print(xml.decode())
def from_xml(self, element=None, filename=None):
if element is None and filename is not None:
element = ET.ElementTree(file=filename).getroot()
if element is None:
raise ManifestError('No xml document element')
if element.tag != MANIFEST_ROOT:
raise ManifestError('Unexpected test suite root element %s' % (element.tag))
for e in element.findall('*'):
if e.tag == MANIFEST_FILE:
name = e.attrib.get(MANIFEST_NAME)
md5 = e.attrib.get(MANIFEST_MD5)
if name and md5:
self.files[name] = md5
if __name__ == "__main__":
path = os.path.dirname(os.path.realpath(__file__))
a = Manifest(path)
a.scan()
a.create()
# b = Manifest(filename=os.path.join(path, 'manifest.xml'))
# print a.diff(b)
# print '----------------------------------------------------'
# print a.to_xml_str()
# print a.md5()