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>
168 lines
5.8 KiB
Python
168 lines
5.8 KiB
Python
# -*- coding: utf-8 -*-
|
|
import warnings
|
|
import json
|
|
|
|
from tarfile import TarFile
|
|
from pkgutil import get_data
|
|
from io import BytesIO
|
|
|
|
from dateutil.tz import tzfile as _tzfile
|
|
|
|
__all__ = ["get_zonefile_instance", "gettz", "gettz_db_metadata"]
|
|
|
|
ZONEFILENAME = "dateutil-zoneinfo.tar.gz"
|
|
METADATA_FN = 'METADATA'
|
|
|
|
|
|
class tzfile(_tzfile):
|
|
def __reduce__(self):
|
|
return (gettz, (self._filename,))
|
|
|
|
|
|
def getzoneinfofile_stream():
|
|
try:
|
|
return BytesIO(get_data(__name__, ZONEFILENAME))
|
|
except IOError as e: # TODO switch to FileNotFoundError?
|
|
warnings.warn("I/O error({0}): {1}".format(e.errno, e.strerror))
|
|
return None
|
|
|
|
|
|
class ZoneInfoFile(object):
|
|
def __init__(self, zonefile_stream=None):
|
|
if zonefile_stream is not None:
|
|
with TarFile.open(fileobj=zonefile_stream) as tf:
|
|
self.zones = {zf.name: tzfile(tf.extractfile(zf), filename=zf.name)
|
|
for zf in tf.getmembers()
|
|
if zf.isfile() and zf.name != METADATA_FN}
|
|
# deal with links: They'll point to their parent object. Less
|
|
# waste of memory
|
|
links = {zl.name: self.zones[zl.linkname]
|
|
for zl in tf.getmembers() if
|
|
zl.islnk() or zl.issym()}
|
|
self.zones.update(links)
|
|
try:
|
|
metadata_json = tf.extractfile(tf.getmember(METADATA_FN))
|
|
metadata_str = metadata_json.read().decode('UTF-8')
|
|
self.metadata = json.loads(metadata_str)
|
|
except KeyError:
|
|
# no metadata in tar file
|
|
self.metadata = None
|
|
else:
|
|
self.zones = {}
|
|
self.metadata = None
|
|
|
|
def get(self, name, default=None):
|
|
"""
|
|
Wrapper for :func:`ZoneInfoFile.zones.get`. This is a convenience method
|
|
for retrieving zones from the zone dictionary.
|
|
|
|
:param name:
|
|
The name of the zone to retrieve. (Generally IANA zone names)
|
|
|
|
:param default:
|
|
The value to return in the event of a missing key.
|
|
|
|
.. versionadded:: 2.6.0
|
|
|
|
"""
|
|
return self.zones.get(name, default)
|
|
|
|
|
|
# The current API has gettz as a module function, although in fact it taps into
|
|
# a stateful class. So as a workaround for now, without changing the API, we
|
|
# will create a new "global" class instance the first time a user requests a
|
|
# timezone. Ugly, but adheres to the api.
|
|
#
|
|
# TODO: Remove after deprecation period.
|
|
_CLASS_ZONE_INSTANCE = []
|
|
|
|
|
|
def get_zonefile_instance(new_instance=False):
|
|
"""
|
|
This is a convenience function which provides a :class:`ZoneInfoFile`
|
|
instance using the data provided by the ``dateutil`` package. By default, it
|
|
caches a single instance of the ZoneInfoFile object and returns that.
|
|
|
|
:param new_instance:
|
|
If ``True``, a new instance of :class:`ZoneInfoFile` is instantiated and
|
|
used as the cached instance for the next call. Otherwise, new instances
|
|
are created only as necessary.
|
|
|
|
:return:
|
|
Returns a :class:`ZoneInfoFile` object.
|
|
|
|
.. versionadded:: 2.6
|
|
"""
|
|
if new_instance:
|
|
zif = None
|
|
else:
|
|
zif = getattr(get_zonefile_instance, '_cached_instance', None)
|
|
|
|
if zif is None:
|
|
zif = ZoneInfoFile(getzoneinfofile_stream())
|
|
|
|
get_zonefile_instance._cached_instance = zif
|
|
|
|
return zif
|
|
|
|
|
|
def gettz(name):
|
|
"""
|
|
This retrieves a time zone from the local zoneinfo tarball that is packaged
|
|
with dateutil.
|
|
|
|
:param name:
|
|
An IANA-style time zone name, as found in the zoneinfo file.
|
|
|
|
:return:
|
|
Returns a :class:`dateutil.tz.tzfile` time zone object.
|
|
|
|
.. warning::
|
|
It is generally inadvisable to use this function, and it is only
|
|
provided for API compatibility with earlier versions. This is *not*
|
|
equivalent to ``dateutil.tz.gettz()``, which selects an appropriate
|
|
time zone based on the inputs, favoring system zoneinfo. This is ONLY
|
|
for accessing the dateutil-specific zoneinfo (which may be out of
|
|
date compared to the system zoneinfo).
|
|
|
|
.. deprecated:: 2.6
|
|
If you need to use a specific zoneinfofile over the system zoneinfo,
|
|
instantiate a :class:`dateutil.zoneinfo.ZoneInfoFile` object and call
|
|
:func:`dateutil.zoneinfo.ZoneInfoFile.get(name)` instead.
|
|
|
|
Use :func:`get_zonefile_instance` to retrieve an instance of the
|
|
dateutil-provided zoneinfo.
|
|
"""
|
|
warnings.warn("zoneinfo.gettz() will be removed in future versions, "
|
|
"to use the dateutil-provided zoneinfo files, instantiate a "
|
|
"ZoneInfoFile object and use ZoneInfoFile.zones.get() "
|
|
"instead. See the documentation for details.",
|
|
DeprecationWarning)
|
|
|
|
if len(_CLASS_ZONE_INSTANCE) == 0:
|
|
_CLASS_ZONE_INSTANCE.append(ZoneInfoFile(getzoneinfofile_stream()))
|
|
return _CLASS_ZONE_INSTANCE[0].zones.get(name)
|
|
|
|
|
|
def gettz_db_metadata():
|
|
""" Get the zonefile metadata
|
|
|
|
See `zonefile_metadata`_
|
|
|
|
:returns:
|
|
A dictionary with the database metadata
|
|
|
|
.. deprecated:: 2.6
|
|
See deprecation warning in :func:`zoneinfo.gettz`. To get metadata,
|
|
query the attribute ``zoneinfo.ZoneInfoFile.metadata``.
|
|
"""
|
|
warnings.warn("zoneinfo.gettz_db_metadata() will be removed in future "
|
|
"versions, to use the dateutil-provided zoneinfo files, "
|
|
"ZoneInfoFile object and query the 'metadata' attribute "
|
|
"instead. See the documentation for details.",
|
|
DeprecationWarning)
|
|
|
|
if len(_CLASS_ZONE_INSTANCE) == 0:
|
|
_CLASS_ZONE_INSTANCE.append(ZoneInfoFile(getzoneinfofile_stream()))
|
|
return _CLASS_ZONE_INSTANCE[0].metadata
|