{{ _('Hier kommt der Inhalt des Webinterfaces hin.') }} (optional)
@@ -226,7 +225,7 @@
Etwaige Informationen unterhalb der Tabelle (optional)
-
+
{% endblock bodytab1 %}
diff --git a/doc/build_plugin_config_files.py b/doc/build_plugin_config_files.py
index 12245aaaa3..5de2276ef9 100644
--- a/doc/build_plugin_config_files.py
+++ b/doc/build_plugin_config_files.py
@@ -233,7 +233,10 @@ def build_pluginlist( plugin_type='all' ):
else:
plugin_yaml = ''
if plugin_yaml != '':
- section_dict = plugin_yaml.get('plugin')
+ try:
+ section_dict = plugin_yaml.get('plugin')
+ except Exception as e:
+ raise AttributeError(f"'{metafile}: Exception {e}")
if section_dict != None:
if section_dict.get('type') != None:
if section_dict.get('type').lower() in plugin_types:
diff --git a/doc/user/source/entwicklung/plugins/webinterface_automatic_update.rst b/doc/user/source/entwicklung/plugins/webinterface_automatic_update.rst
index 1380d16ef5..0177929fac 100755
--- a/doc/user/source/entwicklung/plugins/webinterface_automatic_update.rst
+++ b/doc/user/source/entwicklung/plugins/webinterface_automatic_update.rst
@@ -107,8 +107,7 @@ Bei Tabellen im bodytab werden die einzelnen Datenzeilen beim Rendern durch die
.. code-block:: html+jinja
{% block **bodytab1** %}
-
-
+
@@ -130,7 +129,6 @@ Bei Tabellen im bodytab werden die einzelnen Datenzeilen beim Rendern durch die
{% endfor %}
-
{% endblock **bodytab1** %}
@@ -157,8 +155,7 @@ pro Zeile eine leere Zelle einzufügen! Bei headtables sollten leere Spalten ver
...
{% block **bodytab1** %}
-
-
+
@@ -176,7 +173,6 @@ pro Zeile eine leere Zelle einzufügen! Bei headtables sollten leere Spalten ver
{% endfor %}
-
{% endblock **bodytab1** %}
Jetzt können die DOM-Elemente über die IDs ``fromip`` und ``_value`` angesprochen werden.
@@ -184,10 +180,9 @@ Jetzt können die DOM-Elemente über die IDs ``fromip`` und ``_value`` ang
.. warning::
Damit die Anzeige und Adaption der Datatables einwandfrei funktioniert, ist es elementar, den
- Aufbau sauber und exakt aus dem Sampleplugin zu übernehmen. So muss beispielsweise die Tabelle
- selbst in ein div gepackt werden, dem die Klasse ``table-resize`` zugewiesen ist. Außerdem
- müssen leere Zellen am Anfang jeder Zeile eingefügt werden. Ein Angabe von Klassen ist nicht nötig,
- da dies automatisch passiert.
+ Aufbau sauber und exakt aus dem Sampleplugin zu übernehmen. So müssen leere Zellen am Anfang jeder Zeile eingefügt werden. Eine Angabe von Klassen ist nicht nötig,
+ da dies automatisch passiert, wobei die Klasse ``dataTableAdditional`` gegebenenfalls
+ zu einem schnelleren Laden führen kann.
Erweitern der JavaScript-Funktion handleUpdatedData()
diff --git a/doc/user/source/entwicklung/plugins/webinterface_filling_webinterface.rst b/doc/user/source/entwicklung/plugins/webinterface_filling_webinterface.rst
index 6defcc62a9..7685293c63 100755
--- a/doc/user/source/entwicklung/plugins/webinterface_filling_webinterface.rst
+++ b/doc/user/source/entwicklung/plugins/webinterface_filling_webinterface.rst
@@ -88,18 +88,15 @@ Die folgenden Schritte dienen dazu, das Webinterface mit Leben zu füllen:
sowie der jeweiligen End-Tags. Außerdem muss jeder Tabelle eine einzigartige ID vergeben werden.
Sowohl im Tablehead als auch Tablebody ist eine leere erste Spalte einzufügen, die für das responsive
Feature der Datatables genutzt wird.
- Die Klasse``table-resize`` ist zwingend dem ``
`` Tag, in dem sich die Tabelle befindet, hinzuzufügen,
- um die automatische Anpassung der Datentabelle an die Fensterhöhe zu ermöglichen
- (siehe auch index.html im Example-Plugin). Sollen ober- oder unterhalb der Tabelle zusätzliche Informationen
+ Sollen ober- oder unterhalb der Tabelle zusätzliche Informationen
angezeigt werden, müssen diese in einem ``
`` Tag stehen.
.. code-block:: html+jinja
-
Informationen oberhalb der Tabelle
-
+
@@ -122,7 +119,6 @@ Die folgenden Schritte dienen dazu, das Webinterface mit Leben zu füllen:
Informationen unterhalb der Tabelle
-
diff --git a/doc/user/source/entwicklung/plugins/webinterface_plugin_interaction.rst b/doc/user/source/entwicklung/plugins/webinterface_plugin_interaction.rst
index 099b446538..4c182c2778 100755
--- a/doc/user/source/entwicklung/plugins/webinterface_plugin_interaction.rst
+++ b/doc/user/source/entwicklung/plugins/webinterface_plugin_interaction.rst
@@ -132,8 +132,7 @@ Tabellen in einem ``bodytab?`` können mit einer Schleife befüllt werden, das i
{% block **bodytab1** %}
-
-
+
@@ -155,7 +154,6 @@ Tabellen in einem ``bodytab?`` können mit einer Schleife befüllt werden, das i
{% endfor %}
-
{% endblock **bodytab1** %}
@@ -183,8 +181,7 @@ Damit die IDs in den Wertetabellen eindeutig sind, verwenden wir die Variable au
...
{% block **bodytab1** %}
-
-
+
@@ -206,7 +203,6 @@ Damit die IDs in den Wertetabellen eindeutig sind, verwenden wir die Variable au
{% endfor %}
-
{% endblock **bodytab1** %}
Jetzt können die DOM-Elemente über die IDs ``fromip`` und ``_value`` angesprochen werden.
@@ -233,49 +229,45 @@ Wie weiter unten beschrieben, ist für jeden Button, der auf diese Weise impleme
eine eigene Handler-Routine erforderlich.
Wenn mehrere Buttons dieser Art vorgesehen sind, oder z.B. in einer Wertetabelle ein Button
-in jeder Zeile stehen soll, dann bietet es sich an, statt einzelnen Button-Elementen eine Formularkonstruktion zu nutzen:
+in jeder Zeile stehen soll, dann bietet es sich an, statt einzelnen Button-Elementen eine Formularkonstruktion zu nutzen. Um das automatische Skalieren von Tabellen zu gewährleisten,
+sollte das ``form`` Element NACH der Tabelle deklariert werden.
.. code-block:: html+jinja
{% block bodytab1 %}
-
-
-
-
{% endblock bodytab1 %}
diff --git a/doc/user/source/konfiguration/logging.rst b/doc/user/source/konfiguration/logging.rst
index 186567aa44..be5a6c5801 100644
--- a/doc/user/source/konfiguration/logging.rst
+++ b/doc/user/source/konfiguration/logging.rst
@@ -10,22 +10,22 @@ Ein `Log `_ zeichnet Ergebnisse von Vorgänge
und dient der Dokumentation. Anhand eines Logs kann man Programmfehlern auf die Spur kommen oder bestimmte
Situationen können im Nachhinein untersucht werden. Je detaillierter ein Log geführt wird, desto einfacher
ist die Untersuchung bestimmter Sachverhalte.
-Je nachdem, was man untersuchen möchte, kann man mit einem **Logging Level** im Programm vorgeben wie ernst
+Je nachdem, was man untersuchen möchte, kann man mit einem **Logging Level** im Programm vorgeben, wie ernst
oder wie wichtig ein bestimmter Logeintrag ist.
-Innerhalb des Kerns von SmartHomeNG finden sich zum Beispiel Einträge im Programm mit dem Log Level **NOTICE**
+Innerhalb des Kerns von SmartHomeNG finden sich zum Beispiel Einträge im Programm mit dem Log Level **NOTICE**,
die in einer Logdatei dann im Ergebnis so aufgezeichnet werden:
``2021-04-16 21:56:31 NOTICE lib.smarthome -------------------- Init SmartHomeNG 1.8.2c.4e0938c2.develop --------------------``
-Das ist als Information zu sehen um bei Problemen Hilfe zu erhalten. Es deutet hier nichts auf Fehler oder Probleme hin.
+Das ist als Information zu sehen, um bei Problemen Hilfe zu erhalten. Es deutet hier nichts auf Fehler oder Probleme hin.
Ein anderer Logging Befehl im Core mit dem Log Level **WARNING** erzeugt hingehen folgendes:
``2021-04-16 21:56:32 WARNING lib.module Not loading module Mqtt from section 'mqtt': Module is disabled``
-Das ist als Warnung gedacht um darauf hinzuweisen, das ein Module nicht geladen wird und in dieser Folge eventuell
+Das ist als Warnung gedacht, um darauf hinzuweisen, dass ein Modul nicht geladen wird und in dieser Folge eventuell
weitere Fehler oder Probleme auftauchen könnten. Steigerungen von Warnungen sind Log Level **ERROR** oder **CRITICAL**.
-Während ein **ERROR** also ein Fehler durchaus bedeuten kann das SmartHomeNG weiterarbeiten kann, bedeutet ein **CRITICAL**
-also ein kritischer Fehler das das Programm beendet werden muss.
+Während ein **ERROR** also ein Fehler durchaus bedeuten kann, dass SmartHomeNG weiterarbeiten kann, bedeutet ein **CRITICAL**,
+also ein kritischer Fehler, dass das Programm beendet werden muss.
Fehlt ein für den Kern von SmartHomeNG benötigtes Modul, so stell das einen kritischen Fehler dar.
Die Log Level in der Übersicht, absteigend in der Bedeutung für den Programmablauf:
@@ -36,33 +36,44 @@ Die Log Level in der Übersicht, absteigend in der Bedeutung für den Programmab
* - Level
- Numerischer Wert
- Anmerkung
- * - 50
- - CRITICAL
+ * - CRITICAL
+ - 50
- kritisch, führt zumeist zum Programmabbruch
- * - 40
- - ERROR
+ * - ERROR
+ - 40
- Fehler im Programmablauf, Programm kann zumeist weiterlaufen, Funktionalität möglicherweise eingeschränkt
- * - 31
- - NOTICE
+ * - WARNING
+ - 30
+ - Warnung, dass etwas Unerwartetes passiert ist aber trotzdem weitergearbeitet werden kann.
+ * - NOTICE
+ - 29
+
+ (31 falls der Level des FileHandler des root Loggers nicht auf NOTICE sondern WARNING konfiguriert ist.)
- Ein Hinweis der zur grundlegenden Information dient und nicht als Warnung verstanden werden soll.
Dieser Log Level ist spezifisch für SmartHomeNG und ist im Standard Logging von Python nicht vordefiniert.
- * - 30
- - WARNING
- - Warnung das etwas unerwartetes passiert ist aber trotzdem weitergearbeitet werden kann
- * - 20
- - INFO
- - Eine Ablaufinformation die nicht unbedingt wichtig ist
+ * - INFO
+ - 20
+ - Eine Ablaufinformation, die nicht unbedingt wichtig ist
+ * - DBGHIGH
+ - 13
+ - Informationen für die Fehlersuche, die normalerweise nicht benötigt werden
+ * - DBGMED
+ - 12
+ - Informationen für die Fehlersuche, die normalerweise nicht benötigt werden
+ * - DBGLOW
+ - 11
+ - Informationen für die Fehlersuche, die normalerweise nicht benötigt werden
* - DEBUG
- 10
- - Informationen für die Fehlersuche die normalerweise nicht benötigt werden
+ - Informationen für die Fehlersuche, die normalerweise nicht benötigt werden
* - NOTSET
- 0
- Es wird kein Logeintrag erzeugt
-Es können prinzipiell auch weitere eigene Log Level definiert werden die dann für besondere Situationen benutzt werden können.
-Ein Beispiel wäre ein Log Level **VERBOSE** mit dem Wert **8** der für die Fehlersuche in einem bestimmten Bereich eines Plugins
+Es können prinzipiell auch weitere eigene Log Level definiert werden, die dann für besondere Situationen benutzt werden können.
+Ein Beispiel wäre ein Log Level **VERBOSE** mit dem Wert **8**, der für die Fehlersuche in einem bestimmten Bereich eines Plugins
Verwendung finden könnte.
-Für SmartHomeNG ist derzeit nur **NOTICE** vordefiniert um informelle Logging Einträge zu erzeugen, die nicht als Warnung
+Für SmartHomeNG ist derzeit nur **NOTICE** vordefiniert, um informelle Logging Einträge zu erzeugen, die nicht als Warnung
verstanden werden sollen.
Konfiguration des Loggings
@@ -72,7 +83,7 @@ Auf der Seite `Python Logging `_ |
| | beschrieben sind. Als eigentliche Handler sind in der Konfigurationsdatei **etc/logging.yaml** |
| | die Handler **`console`** und **`file`** vordefiniert. Wenn Log-Einträge z.B. in eine andere |
| | Datei geschrieben werden sollen, muss ein weiterer Handler definiert werden. |
| | Sollen Filter angewendet werden, so sind diese im entsprechenden Handler durch |
-| | filters: [`filtername1`, `filtername2`] anzugeben (siehe filters) |
+| | filters: [`filtername1`, `filtername2`] anzugeben (siehe filters). |
+-----------------+----------------------------------------------------------------------------------------------------+
| **filters:** | Filter bestimmen durch Angabe des Loggernamen, -moduls und -eintrags, welche Zeilen aus dem Log |
| | angezeigt bzw. versteckt werden sollen. Der Eintrag (z.B. loggerfilter) kann bei den Handlers |
@@ -123,15 +134,15 @@ Die einzelnen Konfigurationseinträge haben die folgende Bedeutung:
| | Durch die Angabe von invert: True werden NUR die passenden Messages geloggt und sonst nichts. |
| | Ein Beispiel ist unter :doc:`Logging - Best Practices ` zu finden. |
+-----------------+----------------------------------------------------------------------------------------------------+
-| **loggers:** | Hier werden die einzelnen Logger definiert und was mit diesen Einträgen passiert, |
-| | welche Handler und formatter verwendet werden. Das Level konfiguriert dabei die |
-| | Logtiefe für die einzelne Komponente. Bei den Loggern ist es nun möglich einzelne |
+| **loggers:** | Hier werden die einzelnen Logger definiert, was mit diesen Einträgen passiert, |
+| | und welche Handler und formatter verwendet werden. Das Level konfiguriert dabei die |
+| | Logtiefe für die einzelne Komponente. Bei den Loggern ist es nun möglich, einzelne |
| | Plugins oder Libs im Debug protokollieren zu lassen. Dazu sind in der Konfiguration |
| | bereits einige Beispiele. |
+-----------------+----------------------------------------------------------------------------------------------------+
-| **root:** | Hier ist die Konfiguration des root Loggers der für die ganze Anwendung gilt. Dieser |
-| | root Logger wird für alle Komponenten verwendet, auch die die nicht unter loggers: konfiguriert |
-| | sind. Da der root Logger ALLE Logeinträge empfängt sollte der level: unbedingt auf WARNING stehen. |
+| **root:** | Hier ist die Konfiguration des root Loggers, der für die ganze Anwendung gilt. Dieser |
+| | root Logger wird für alle Komponenten verwendet auch die, die nicht unter loggers: konfiguriert |
+| | sind. Da der root Logger ALLE Logeinträge empfängt, sollte der level: unbedingt auf WARNING stehen.|
+-----------------+----------------------------------------------------------------------------------------------------+
Wenn man **Logger** definiert, welche die Log-Einträge über zusätzliche **Handler** ausgeben, ist
@@ -203,7 +214,7 @@ muss in der config auch dieser Name verwendet werden. Ohne `plugin.` oder `logic
level: DEBUG
Standardmäßig haben Logiken bereits einen Logger, der in der ``etc/logging.yaml`` konfiguriert werden kann/muss.
-Der Name des Loggers ist ``logics.``, wobei der Name der Logik, der in der Konfiguration festgelegte
+Der Name des Loggers ist ``logics.``, wobei der Name der Logik der in der Konfiguration festgelegte
Name ist und nicht der Name des Python Skriptes.
@@ -218,7 +229,7 @@ beschrieben ist.
Best Practices
==============
-Wer eine brauchbare leicht konfigurierbare Logging Konfiguration oder Beispiele zum Nutzen
+Wer eine brauchbare und leicht konfigurierbare Logging Konfiguration oder Beispiele zum Nutzen
von RegEx Ausdrücken sucht, der wird hier :doc:`Logging - Best Practices ` fündig.
diff --git a/doc/user/source/konfiguration/plugins.rst b/doc/user/source/konfiguration/plugins.rst
index 23312328b9..a98a4b83ca 100644
--- a/doc/user/source/konfiguration/plugins.rst
+++ b/doc/user/source/konfiguration/plugins.rst
@@ -51,6 +51,9 @@ Es gibt folgende allgemeine Parameter im Abschnitt eines Plugins:
| plugin_name | Der Kurzname des Plugins, das eingebunden werden soll (Name des Verzeichnisses |
| | im **../plugins** Verzeichnis). Statt des Parameters **plugin_name** konnten bisher |
| | auch die Parameter **class_name** und **class_path** angegeben werden. |
+| | |
+| | **WICHTIG**: Bitte darauf achten, dass der Plugin Name Case-sensitive ist und der |
+| | Schreibweise des Verzeichnisnamens des Plugin entsprechen muss. |
+------------------+-------------------------------------------------------------------------------------+
| plugin_enabled | Optional: Wenn dieser Parameter auf **False** gesetzt wird, wird das Plugin nicht |
| | geladen, die Konfiguration bleibt jedoch erhalten und die Verwendung von Item- |
@@ -64,6 +67,11 @@ Es gibt folgende allgemeine Parameter im Abschnitt eines Plugins:
| | Dokumentation zum jeweiligen Plugin. Stattdessen ist die Konfiguration über den |
| | Parameter **plugin_name** vorzunehmen. |
+------------------+-------------------------------------------------------------------------------------+
+| plugin_version | Optional: Wenn im Plugin Repository mehrere Versionen eines Plugins zur Verfügung |
+| | stehen, kann über diesen Parameter eine andere als die neueste Version des Plugins |
+| | geladen werden. Dazu muss die Versionsnummer des Plugins angegeben werden. |
+| | (z.B. **plugin_version: 1.4.9**) |
++------------------+-------------------------------------------------------------------------------------+
| instance | Optional: Dieser Parameter muss nur verwendet werden, wenn mehrere Instanzen des |
| | selben Plugins geladen werden sollen. Das Plugin selbst muss dazu **Multiinstance** |
| | fähig sein. Damit die Items der richtigen Plugin-Instanz zugeordnet werden, muss |
@@ -71,16 +79,11 @@ Es gibt folgende allgemeine Parameter im Abschnitt eines Plugins:
| | die Angabe der Instanz ergänzt werden. Also z.B.: Statt **avm_data_type: uptime** |
| | muss **avm_data_type@: uptime** geschrieben werden. |
+------------------+-------------------------------------------------------------------------------------+
-| plugin_version | Optional: Wenn im Plugin Repository mehrere Versionen eines Plugins zur Verfügung |
-| | stehen, kann über diesen Parameter eine andere als die neueste Version des Plugins |
-| | geladen werden. Dazu muss die Versionsnummer des Plugins angegeben werden. |
-| | (z.B. **plugin_version: 1.4.9**) |
-+------------------+-------------------------------------------------------------------------------------+
| webif_pagelength | Optional: Anzahl an Tabellenreihen, die im Plugin Webinterface standardmäßig |
| | pro Seite angezeigt werden sollen. Bei **-1** werden alle Einträge auf einer Seite |
| | gezeigt, bei **0** so viele, dass sie genau auf die Seite ohne Scrolling passen. |
| | Weitere mögliche Werte sind 25, 50, 100. Der hier angegebene Wert überschreibt den |
-| | für das **http Modul** im /etc/module.yaml File über den gleichnamigen Parameter | |
+| | für das **http Modul** im /etc/module.yaml File über den gleichnamigen Parameter |
| | definierten globalen Wert. |
+------------------+-------------------------------------------------------------------------------------+
diff --git a/doc/user/source/referenz/referenz.rst b/doc/user/source/referenz/referenz.rst
index 0ea70a9fa1..4afa2c0877 100644
--- a/doc/user/source/referenz/referenz.rst
+++ b/doc/user/source/referenz/referenz.rst
@@ -28,5 +28,6 @@ können.
netzwerk
plugins/plugins
userfunctions/userfunctions
+ virtual_environments/virtual_environments
libraries_plugins_logics
APIs
diff --git a/doc/user/source/referenz/virtual_environments/virtual_environments.rst b/doc/user/source/referenz/virtual_environments/virtual_environments.rst
new file mode 100644
index 0000000000..0c2325b9dd
--- /dev/null
+++ b/doc/user/source/referenz/virtual_environments/virtual_environments.rst
@@ -0,0 +1,176 @@
+
+.. index:: Virtual Environments
+.. index:: Virtuelle Umgebungen
+
+.. role:: bluesup
+.. role:: greensup
+.. role:: redsup
+
+====================================
+Virtuelle Environments :redsup:`Neu`
+====================================
+
+Virtual Environments sind isolierte und unabhängige Umgebungen, die den Code und die Abhängigkeiten eines Projekts
+enthalten. Mit virtuellen Environments kann man parallel Umgebungen schaffen, in denen zum Beispiel Python Packages in
+unterschiedlichen Versionen installiert sind.
+
+Falls auf dem Computer mehrere Python Versionen installiert sind, ist es auch möglich parallel Umgebungen zu
+schaffen, die unter unterschiedlichen Python Versionen laufen. Dieses ist besonders hilfreich, wenn man bei der
+Entwicklung von SmartHomeNG oder von Plugins testen möchte, wie sich SmartHomeNG (bzw. das Plugin) unter verschiedenen
+Python Versionen verhält.
+
+Es gibt eine Reihe von Tools, um virtuelle Environments zu erstellen. Die verbreitetesten Tools sind **virtualenv**
+und **venv**.
+
+**venv** gibt es seit Python 3.3. Es ist Bestandteil der Python Installation. Allerdings ist es in
+einigen Umgebungen (z.B. Debian) nicht in der Standard Python Installation enthalten und muss dann separat installiert
+werden.
+
+**virtualenv** bietet eine größere Funktionalität als **venv**. Die Funktionalität von **venv** ist eine Untermenge
+der Funktionalität von **virtualenv**. **virtualenv** ist Vergleich zu **venv** schneller und es ist erweiterbar.
+
+|
+
+Installation von virtualenv
+===========================
+
+Im folgenden wird davon ausgegangen, dass die Environments im Unterverzeichnis **environments** des Users **smarthome**
+erstellt/gespeichert werden.
+
+.. code-block:: bash
+
+ smarthome:~$ mkdir environments
+
+ smarthome:~$ cd environments
+
+ smarthome:~/environments$
+
+
+**virtualenv** wird am besten unter der Python Version installiert, für die später virtuelle Environments erstellt
+werden sollen. Um zum Beispiel ein virtual Environment für Python 3.9 zu erstellen, ist der folgende Befehl auszuführen:
+
+.. code-block:: bash
+
+ smarthome:~/environments$ pip3.9 install virtualenv
+
+|
+
+Erstellung von virtuellen Environments
+======================================
+
+Jetzt kann mit dem folgenden Befehl ein virtual Environment erstellt werden, welches den Namen **py_39** trägt:
+
+.. code-block:: bash
+
+ smarthome:~/environments$ python3.9 -m virtualenv py_39
+
+Jetzt sollte noch ein Shell Skript angelegt werden, mit dem das Environment aktiviert werden kann. Für das Environment
+**py_39** sollte es **act_39** heissen und kann mit:
+
+.. code-block:: bash
+
+ smarthome:~/environments$ nano act_39
+
+angelegt werden und der folgende Text kann dort eingefügt werden:
+
+.. code-block:: bash
+ :caption: act_39
+
+ #!/bin/bash
+ #
+ # Environment erzeugt mit:
+ # $ pip3.9 install virtualenv
+ # $ python3.9 -m virtualenv py_39
+ #
+ # Aktivieren mit:
+ # $ source act_39
+ #
+ # Deaktivieren mit:
+ # $ deactivate
+ #
+
+
+Anschließend nicht vergessen, das Skript ausführbar zu machen.
+
+.. code-block:: bash
+
+ smarthome:~/environments$ chmod +x act_39
+
+Nun kann mit dem Aufruf von
+
+.. code-block:: bash
+
+ smarthome:~/environments$ source act_39
+ (py_39) smarthome:~/environments$
+
+das Environment aktiviert werden. Das **source** ist wichtig, weil das Environment sonst bei Beendigung der
+Skriptes **act_39** wieder beendet wird. Der geänderte Prompt zeigt an, dass das Virtual Environment **py_39**
+aktiv ist. Bitte daran denken, den Pfad mit anzugeben, falls das aktuelle Verzeichnis nicht **environments ist (oder
+alternativ das Verzeichnis mit in den Pfad aufzunehmen):
+
+.. code-block:: bash
+
+ smarthome:/usr/local/smarthome$ source /home/smarthome/environments/act_39
+
+|
+
+In dem Environment sind nun nur die Packages **pip**, **setuptools** und **wheel** installiert. Weitere benötigte
+Packages können ganz normal mit **pip3** nachinstalliert werden. (SmartHomeNG installiert wie bei einer kompletten
+Neuinstallation die benötigten Packages).
+
+Der Aufruf von
+
+.. code-block:: bash
+
+ (py_39) smarthome:~$ python3
+
+führt nun dazu, dass Python 3.9 gestartet wird.
+
+Um das virtuelle Environment zu deaktivieren, muss nur
+
+.. code-block:: bash
+
+ (py_39) smarthome:~$ deactivate
+ smarthome:~$
+
+eingegeben werden.
+
+|
+
+.. index:: Virtuelle Environements als Dienst
+
+Virtuelle Environements als Dienst
+==================================
+
+Wenn SmartHomeNG als Dienst eingerichtet werden und in einem virtuellen Environment laufen soll, muss die xxx-Datei
+im Vergleich zur Beschreibung in der Komplettanleitung abgeändert werden. Es muss der Pfad zu Python in dem entsprechenden
+Environment angegeben werden.
+
+Zum Einrichten den Texteditor starten mit
+
+.. code-block:: bash
+
+ sudo nano /etc/systemd/system/smarthome.service
+
+und folgenden Text hineinkopieren:
+
+.. code-block:: bash
+
+ [Unit]
+ Description=SmartHomeNG daemon
+ After=network.target
+ After=knxd.service
+ After=knxd.socket
+
+ [Service]
+ Type=forking
+ ExecStart=/home/smarthome/environments/py_39/bin/python3 /usr/local/shng_dev/bin/smarthome.py
+ WorkingDirectory=/usr/local/smarthome
+ User=smarthome
+ PIDFile=/usr/local/smarthome/var/run/smarthome.pid
+ Restart=on-failure
+ TimeoutStartSec=900
+ RestartForceExitStatus=5
+
+ [Install]
+ WantedBy=default.target
diff --git a/doc/user/source/release/1_9_5.rst b/doc/user/source/release/1_9_5.rst
new file mode 100644
index 0000000000..194e19522d
--- /dev/null
+++ b/doc/user/source/release/1_9_5.rst
@@ -0,0 +1,349 @@
+=============================
+Release 1.9.5 - 31. März 2023
+=============================
+
+Dieses Release ist ein Wartungs-Release. Außer Bugfixes im Core gibt es einige neue Features und Bugfixes in Plugins
+von SmartHomeNG, sowie ein neues Plugins.
+
+.. only: comment
+
+ Dieses Release ist ein Feature-Release. Es gibt eine Menge neuer Features im Core von SmartHomeNG und den Plugins.
+
+|
+
+.. only: comment
+
+ .. note::
+
+ Diese Release Notes sind ein Arbeitsstand.
+
+ - Berücksichtigt sind Commits im smarthome Repository bis incl. 31. März 2023
+ (logics/check_items.py: fix naming)
+ - Berücksichtigt sind Commits im plugins Repository bis incl. 31. März 2023
+ (Merge pull request #716 from sisamiwe/z2m)
+
+
+Überblick
+=========
+
+Dieses ist neues Release für SmartHomeNG. Die Änderungen gegenüber dem Release v1.9.4 sind im
+folgenden in diesen Release Notes beschrieben.
+
+
+Minimum Python Version
+----------------------
+
+Die absolute Minimum Python Version in der SmartHomeNG startet, ist **Python 3.7**.
+
+Für das SmartHomeNG Release 1.10 wird die absolute Minimum Python Version auf **Python 3.8** angehoben, da der
+Community Support für Python 3.7 am 27. Juni 2023 endete.
+
+Bei einer Neuinstallation wird jedoch empfohlen auf einer der neueren Python Versionen (3.9 oder 3.10) aufzusetzen.
+
+
+Unterstützte Python Versionen
+-----------------------------
+
+Die älteste offiziell unterstützte Python Version für SmartHomeNG Release 1.9.x ist **Python 3.7**.
+Automatisierte Tests von SmartHomeNG werden nur in den unterstützten Python Versionen durchgeführt.
+(Siehe auch :ref:`Hard- u. Software Anforderungen ` im Abschnitt **Installation**
+zu unterstützten Python Versionen)
+
+|
+
+Änderungen am Core
+==================
+
+Allgmein
+--------
+
+* Workflows:
+
+ * Replaced deprecated 'set-output'
+ * (re)joined pr_unittests into unittests.yml
+ * Disabled seperate pr_unittests workflow
+ * Updates to unittests.yml
+
+
+Updates in the CORE
+-------------------
+
+* Libs:
+
+ * lib.connection:
+
+ * Added extended deprecation warning (lib will be removed in SmartHomeNG v1.10.0)
+
+ * lib.items:
+
+ * Fix name clash parameter "sorted" conflicting with function name sorted
+
+ * lib.metadata:
+
+ * Fix for empty plugin parameter sectionand section not declared NONE
+
+ * lib.network:
+
+ * Log some more for insights on wrong parameter passing from plugins on callbacks
+ * Fix logging
+
+ * lib.plugin:
+
+ * Fix for wrong cwd on Plugin load
+
+ * lib.shpypi:
+
+ * Adressing requirements file with absolute path to fix bug, when current working directory is not
+ the SmartHomeNG base directory
+ * Added warning, if plugin_name: is not in lowercase
+
+ * lib.smarthome:
+
+ * Fix in stop(), if SmartHomeNG is killed during initialization
+
+ * lib.systeminfo:
+
+ * Changed read_macosinfo() to be compatible with more MacOS versions
+ * Added text, that testing cpu speed could take several minutes on slow machines
+ * Ensure initialization of cpu_speed_class for display in shngadmin
+
+ * lib.triggertimes:
+
+ * Catch a possible error with event related calculations, resulting now in invalid time
+
+* Modules:
+
+ * admin:
+
+ * Removed old shngadmin version
+ * Update shngadmin to v0.6.6:
+
+ * Added new loglevels to list of loggers
+ * Fixed translation glitch in SmartHomeNG status display; Displays info, if running in virtual environment
+
+ * Added handling, if 'documentation' in plugin metadata is explicitly se to None
+
+ * http:
+
+ * Webinterfaces: fix docstring and issue when text is None/Null
+
+ * websocket:
+
+ * Fixed bug for wss protocol handling
+ * Added setting for protocol_over_reverseproxy to smartvisu payload protocol handling
+ * Changed loglevel for error 1011 on ping timeout in sv payload protocol
+ * Changed loglevel in sv payload protocol when sending (url-)command to visu
+ * Update datatables, colreorder, fixedheader and responsive
+ * Introduce option to re-choose automatic resize of tables, minor fixes
+ * Corrected method in smartvisu payload protocol which sends updates for monitored items
+ * Bumped version to 1.1.2
+
+* logics:
+
+ * check_items.py: fix naming of parameter in return_items()
+
+|
+
+Änderungen bei Plugins
+======================
+
+Allgmein
+--------
+
+* Workflows:
+
+ * Replaced deprecated 'set-output'
+ * (re)joined pr_unittests into unittests.yml
+ * Disabled seperate pr_unittests workflow
+ * Update to unittests.yml
+
+
+Neue Plugins
+------------
+
+Für Details zu den neuen Plugins, bitte die Dokumentation des jeweiligen Plugins unter
+http://www.smarthomeng.de/user/plugins_all.html konsultieren.
+
+* mieleathome: Connect Miele@Home (state 'develop')
+
+
+
+Plugin Updates
+--------------
+
+Für Details zu den Änderungen an den einzelnen Plugins, bitte die Dokumentation des jeweiligen Plugins unter
+http://www.smarthomeng.de/user/plugins_all.html konsultieren.
+
+* alexarc4shng:
+
+ * Updates; Bumped version to 1.0.3
+
+* avm:
+
+ * Larger rework of plugin
+ * Bumed version to 2.0.1
+ * Added version 1.6.8 from SmartHomeNG v1.9.3 master as 'previous version'
+ * Fix for RGB lightbulb (fritzdect 500): set on/off state to off and dim level to 0 if device is not
+ connected (same behavior as in plugin version 1.6.8)
+ * Multiple fixes
+ * Plugin Code cleanup
+ * WebIF Update
+ * Bumed version to 2.0.2
+ * Changed requirement from lxml==4.9.2 to lxml>=4.9.2
+
+* database:
+
+ * Added info to 'Could not connect to the database' log entry
+
+* db_addon:
+
+ * Various fixes and enhancements
+ * Bumped version to 1.1.0
+
+* enocean:
+
+ * Catch exception that occurs, if Tx Enocean item is defined without tx_id_offset attribute
+ Output error log in this case
+ * small improvements in comments and status info
+
+* husky2:
+
+ * Catch exception on plugin shutdown
+ * Moved to actual aioautomower version
+ * Cleanup
+ * Imroved/fixed webif tables
+
+* ical:
+
+ * It is logged as level INFO, if no dtend is found
+ * Bumped version to 1.6.2
+
+* indego4shng:
+
+ * Updates
+ * Bumped version to 4.0.0
+
+* lirc:
+
+ * Introduce web interface
+ * Bump version to 1.5.1
+
+* modbus_tcp:
+
+ * Corrected version number in __init__.py
+
+* neato:
+
+ * Added additional debug log in case that a command cannot be executed
+
+* onewire:
+
+ * Fixed debug messages
+ * Added a "sleep-time" for testing to improve sensor reading for parasite powered sensors
+ * Added _parasitic_power_wait to webinterface and added unit to values in headtable
+ * Added a "sleep-time" for testing to improve io_cycle reading for parasite powered devices
+ * Bumped version to 1.9.4
+
+* piratewthr:
+
+ * Bugfix
+
+* smartvisu:
+
+ * New parameter 'protocol_over_reverseproxy'
+ * Bumped version to 1.8.10
+ * Removed parameter 'protocol_over_reverseproxy'
+ * Removed old documentation link from metadata
+ * Fix some webinterface issues with datatables init, clearing and ordering options
+
+* snmp:
+
+ * Changed requirement from puresnmp >=1.7.2 to puresnmp >=1.7.2,<2.0.0 because v2 implements breaking changes
+
+* sonos:
+
+ * Fix major bug introduced with PR #699, causing the av_transport to stop working after some time
+ * Upgraded to SoCo version 0.29.1
+
+* uzsu:
+
+ * Changed requirements for numpy
+
+* webpush:
+
+ * Added shng varpath to parameters
+ * Changed to correct doc link
+ * Re-removed superfluous doc link
+
+* zigbee2mqtt:
+
+ * Updated/corrected metadata
+
+* Diverse Plugins:
+
+ * Changed readme.md class_path/class_name -> plugin_name
+
+
+Outdated Plugins
+----------------
+
+Die folgenden Plugins wurden bereits in v1.6 als *deprecated* (veraltet) markiert. Das bedeutet, dass diese
+Plugins zwar noch funktionsfähig sind, aber nicht mehr weiter entwickelt werden. Sie werden in einem kommenden
+Release von SmartHomeNG entfernt werden. Anwender dieser Plugins sollten auf geeignete Nachfolge-Plugins
+wechseln.
+
+* System Plugins
+
+ * sqlite_visu2_8 - switch to the **database** plugin
+
+* Web Plugins
+
+ * wunderground - the free API is not provided anymore by Wunderground
+
+
+Die folgenden Plugins wurden in v1.7 als *deprecated* (veraltet) markiert, weil kein Nutzer oder Tester
+dieser Plugins gefunden werden konnte:
+
+* Gateway Plugins
+
+ * ecmd
+ * elro
+ * iaqstick
+ * snom
+ * tellstick
+
+* Interface Plugins
+
+ * easymeter
+ * smawb
+ * vr100
+
+* Web Plugins
+
+ * nma
+
+Weiterhin wurde das bisherige mqtt Plugin zu mqtt1 umbenannt und als *deprecated* markiert, da das neue mqtt
+Plugin diese Funktionalität übernimmt. Das neue mqtt Plugin nutzt dazu das mqtt Modul des aktuellen Cores
+von SmartHomeNG.
+
+|
+
+Weitere Änderungen
+==================
+
+Tools
+-----
+
+* plugin_metadata_checker:
+
+ * Added handling, if 'documentation' in plugin metadata is explicitly se to None
+
+
+
+Dokumentation
+-------------
+
+* Added description for using virtual environemnts
+* Added new log levels to user documentation
+* build documentation: Added Error output to build_plugin_config_files.py
+
diff --git a/lib/connection.py b/lib/connection.py
index d737679b02..83d967f245 100644
--- a/lib/connection.py
+++ b/lib/connection.py
@@ -42,6 +42,71 @@
logger = logging.getLogger(__name__)
+# =====================================================================================
+
+import lib.utils
+import sys
+
+dep_id_list = []
+
+#####################################################################
+# Diplay DEPRECATED warning
+#####################################################################
+def _deprecated_warning(n_func='', o_func=''):
+ """
+ Display function deprecated warning
+ """
+ # if hasattr(self, '_deprecated_warnings'):
+ # if lib.utils.Utils.to_bool(self._deprecated_warnings) == False:
+ # return
+ # else:
+ # return # if parameter is not defined
+
+ if o_func != '':
+ d_func = 'lib.connection'+' '+o_func
+ else:
+ d_func = 'lib.connection'+'.'+str(sys._getframe(1).f_code.co_name)+'()'
+ if n_func != '':
+ n_func = '- use the '+n_func+' instead'
+ try:
+ d_test = ' (' + str(sys._getframe(2).f_locals['self'].__module__) + ')'
+ except:
+ d_test = ''
+
+ called_by = str(sys._getframe(2).f_code.co_name)
+ in_class = ''
+ try:
+ in_class = 'class ' + str(sys._getframe(2).f_locals['self'].__class__.__name__) + d_test
+ except:
+ in_class = 'a logic?' + d_test
+ if called_by == '':
+ called_by = str(sys._getframe(3).f_code.co_name)
+ level = 3
+ while True:
+ level += 1
+ try:
+ c_b = str(sys._getframe(level).f_code.co_name)
+ except ValueError:
+ c_b = ''
+ if c_b == '':
+ break
+ called_by += ' -> ' + c_b
+
+# called_by = str(sys._getframe(3).f_code.co_name)
+
+# if not hasattr(self, 'dep_id_list'):
+# self.dep_id_list = []
+ id_str = d_func + '|' + in_class + '|' + called_by
+ if not id_str in dep_id_list:
+ if in_class.find('lib.smarthome') == -1 :
+ logger.warning(f"DEPRECATED: lib.connection is deprecated and will be removed in SmartHommeNG v1.10")
+ logger.warning(f"DEPRECATED: Used '{d_func}', called in '{in_class}' by '{called_by}' {n_func}")
+ dep_id_list.append(id_str)
+ return
+
+# =====================================================================================
+
+
class Base():
"""
@@ -62,6 +127,7 @@ def __init__(self, monitor=False):
if monitor:
self._monitor.append(self)
+
def _create_socket(self, flags=None):
family, type, proto, canonname, sockaddr = socket.getaddrinfo(self._host, self._port, family=self._family[self._proto], type=self._type[self._proto])[0]
self.socket = socket.socket(family, type, proto)
@@ -135,6 +201,7 @@ class Connections(Base):
def __init__(self):
Base.__init__(self)
Base._poller = self
+ _deprecated_warning('', 'class Connections')
if hasattr(select, 'epoll'):
self._epoll = select.epoll()
elif hasattr(select, 'kqueue'):
@@ -403,7 +470,8 @@ def __init__(self, host, port, proto='TCP'):
self._proto = proto
self.address = "{}:{}".format(host, port)
self.connected = False
- self._deprecated_warning('lib.network.Tcp_server() class')
+ #self._deprecated_warning('lib.network.Tcp_server() class')
+ _deprecated_warning('lib.network.Tcp_server() class', 'class Server')
def connect(self):
try:
@@ -631,7 +699,8 @@ def __init__(self, host, port, proto='TCP', monitor=False):
self._connection_attempts = 0
self._connection_errorlog = 60
self._connection_lock = threading.Lock()
- self._deprecated_warning('lib.network.Tcp_client() class')
+ #self._deprecated_warning('lib.network.Tcp_client() class')
+ _deprecated_warning('lib.network.Tcp_client() class', 'class Client')
def connect(self):
self._connection_lock.acquire()
@@ -655,3 +724,5 @@ def connect(self):
self._connected()
finally:
self._connection_lock.release()
+
+
diff --git a/lib/item/items.py b/lib/item/items.py
index 89574f15ed..a609b0ead6 100755
--- a/lib/item/items.py
+++ b/lib/item/items.py
@@ -297,18 +297,18 @@ def return_item(self, string):
return self.__item_dict[string]
- def return_items(self, sorted=False):
+ def return_items(self, ordered=False):
"""
Function to return a list with all defined items
- :param sorted: return list sorted alphabetically, defaults to False
- :type sorted: bool
+ :param ordered: return list sorted alphabetically, defaults to False
+ :type ordered: bool
:return: List of all items
:rtype: list
"""
- if sorted:
+ if ordered:
for item in sorted(self.__items):
yield self.__item_dict[item]
else:
diff --git a/lib/metadata.py b/lib/metadata.py
index 5fc9956324..d4ec59861e 100644
--- a/lib/metadata.py
+++ b/lib/metadata.py
@@ -128,8 +128,8 @@ def __init__(self, sh, addon_name, addon_type, classpath=''):
else:
self.global_parameters = self.get_global_plugin_parameters()
self.parameters = self.meta.get(META_PLUGIN_PARAMETER_SECTION, {})
- if isinstance(self.parameters, str):
- # if plugin parameter section is declared as NONE
+ if self.parameters is None or isinstance(self.parameters, str):
+ # if plugin parameter section is empty or is declared as NONE
self.parameters = self.global_parameters
else:
self.parameters.update(self.global_parameters)
diff --git a/lib/network.py b/lib/network.py
index 765df29bdd..896bc2f808 100644
--- a/lib/network.py
+++ b/lib/network.py
@@ -37,11 +37,12 @@
from lib.utils import Utils
import sys
import traceback
+from inspect import signature
import re
import asyncio
import logging
import requests
-from iowait import IOWait # BMX
+from iowait import IOWait
import socket
import struct
import subprocess
@@ -621,10 +622,22 @@ def set_callbacks(self, connected=None, receiving=None, data_received=None, disc
:type data_received: function
:type disconnected: function
"""
- self._connected_callback = connected
- self._receiving_callback = receiving
- self._disconnected_callback = disconnected
- self._data_received_callback = data_received
+ if connected:
+ params = len(signature(connected).parameters)
+ self.logger.debug(f"connected_callback for {self._id} is {connected.__qualname__} and it expects {params} arguments")
+ self._connected_callback = connected
+ if receiving:
+ params = len(signature(receiving).parameters)
+ self.logger.debug(f"connected_callback for {self._id} is {receiving.__qualname__} and it expects {params} arguments")
+ self._receiving_callback = receiving
+ if disconnected:
+ params = len(signature(disconnected).parameters)
+ self.logger.debug(f"connected_callback for {self._id} is {disconnected.__qualname__} and it expects {params} arguments")
+ self._disconnected_callback = disconnected
+ if data_received:
+ params = len(signature(data_received).parameters)
+ self.logger.debug(f"connected_callback for {self._id} is {data_received.__qualname__} and it expects {params} arguments")
+ self._data_received_callback = data_received
def connect(self):
"""
@@ -646,8 +659,11 @@ def connect(self):
if not self.__connect_thread or not self.__connect_thread.is_alive():
self.__connect_thread = threading.Thread(target=self._connect_thread_worker, name=f'TCP_Connect {self._id}')
self.__connect_thread.daemon = True
+ self.logger.debug(f'connect() to {self._host}:{self._port}: self.__running={self.__running}, self.__connect_thread.is_alive()={self.__connect_thread.is_alive()}')
if not self.__running or not self.__connect_thread.is_alive():
+ self.logger.debug(f'connect() to {self._host}:{self._port}: calling __connect_thread.start()')
self.__connect_thread.start()
+ self.logger.debug(f'leaving connect() to {self._host}:{self._port}')
return True
def connected(self):
@@ -741,8 +757,11 @@ def _connect_thread_worker(self):
self.__receive_thread.daemon = True
self.__receive_thread.start()
except Exception:
+ self.logger.error(f"could not start __receive_thread_worker for {name}")
raise
return True
+ else:
+ self.logger.warning(f"self._connect() for {name} did not work")
if self.__running:
self._sleep(self._connect_cycle)
@@ -754,6 +773,7 @@ def _connect_thread_worker(self):
try:
self.__connect_threadlock.release()
except Exception:
+ self.logger.debug(f'{self._id} exception while trying self.__connect_threadlock.release()')
pass
def _connect(self):
@@ -873,7 +893,7 @@ def __receive_thread_worker(self):
return
except Exception as ex:
if not self.__running:
- self.logger.debug('{self._id} receive thread shutting down')
+ self.logger.debug(f'{self._id} receive thread shutting down')
self._is_receiving = False
return
else:
diff --git a/lib/plugin.py b/lib/plugin.py
index 34d1a05208..06c1c12176 100644
--- a/lib/plugin.py
+++ b/lib/plugin.py
@@ -166,6 +166,7 @@ def __init__(self, smarthome, configfile):
except Exception as e:
plugin_version = 'version unknown'
dummy = self._test_duplicate_pluginconfiguration(plugin, classname, instance)
+ os.chdir((self._sh._base_dir))
try:
plugin_thread = PluginWrapper(smarthome, plugin, classname, classpath, args, instance, self.meta, self._configfile)
if plugin_thread._init_complete == True:
@@ -195,7 +196,7 @@ def __init__(self, smarthome, configfile):
self._threads = threads_early + self._threads + threads_late
logger.info('Load of plugins finished')
del(_conf) # clean up
-
+ os.chdir((self._sh._base_dir))
# Tests für logic-Parameter Metadaten
self.logic_parameters = {}
diff --git a/lib/shpypi.py b/lib/shpypi.py
index b862043e3c..d15cb36bd5 100644
--- a/lib/shpypi.py
+++ b/lib/shpypi.py
@@ -308,6 +308,14 @@ def test_conf_plugins_requirements(self, plugin_conf_basename, plugins_dir):
plugin = plugin_name
filename = os.path.join(plugins_dir, plugin, 'requirements.txt')
+
+ if not os.path.isdir(os.path.join(plugins_dir, plugin)):
+ if plugin != plugin.lower():
+ self.logger.warning(f"There is no plugin '{plugin}' - Change the configuration parameter 'plugin_name: {plugin}' to lowercase 'plugin_name: {plugin.lower()}'")
+ filename = os.path.join(plugins_dir, plugin.lower(), 'requirements.txt')
+ else:
+ self.logger.error(f"There is no plugin {plugin}")
+
if not os.path.isfile(filename):
filename = ''
else:
@@ -392,13 +400,16 @@ def install_requirements(self, req_type, logging=True, pip3_command=None):
self.logger.notice("> using PIP command: '{}'".format(pip_command))
except:
self.logger.warning("> using PIP command: '{}'".format(pip_command))
+
+ req_filepath = os.path.join(self._sh_dir, 'requirements', req_type+'.txt')
+ command_line = pip_command +' install -r ' + req_filepath + ' --user --no-warn-script-location'
if logging:
- self.logger.info('> '+pip_command+' install -r requirements/'+req_type+'.txt --user --no-warn-script-location')
+ self.logger.info('> '+command_line)
else:
- #print('> ' + pip_command + ' install -r requirements/' + req_type + '.txt --user --no-warn-script-location')
+ #print('> ' + command_line)
pass
+ stdout, stderr = Utils.execute_subprocess(command_line)
- stdout, stderr = Utils.execute_subprocess(pip_command+' install -r requirements/'+req_type+'.txt --user --no-warn-script-location')
# ToDo
# create_directories is available in lib.smarthome.py but shpypi.py might be started prior to SH object creation
# thus it is needed to create the var/log directory here
@@ -422,7 +433,7 @@ def install_requirements(self, req_type, logging=True, pip3_command=None):
print()
print("Running in a virtualenv environment,")
print("installing "+req_type_display+" requirements only to actual virtualenv, please wait...")
- stdout, stderr = Utils.execute_subprocess('pip3 install -r requirements/'+req_type+'.txt')
+ stdout, stderr = Utils.execute_subprocess('pip3 install -r '+req_filepath)
if logging:
self.logger.debug("stdout = 'Output from PIP command:\n{}'".format(stdout))
if not logging:
@@ -1396,8 +1407,9 @@ def _consolidate_requirements(self, packagelist):
1])
packagelist_consolidated.append(p)
elif p['req'][0][0] == '==':
- print("p Gleichheit p['key']=" + p['key'] + ': >' + package_consolidated['req'][0][1] + '< / >' + p['req'][0][1] + '<')
- print('p=' + p)
+ # p Gleichheit p['key']=py-vapid+: >< / >1.8.2<
+ print(f"p Gleichheit p['key']='{p['key']}': package_consolidated >{package_consolidated['req'][0][1]}< / p['req'] >{p['req'][0][1]}<")
+ print(f"p = {p}")
elif package_consolidated['req'][0][0] == '':
# if consolidated version has no special requirements
self.logger.debug("_consolidate_requirements: package_consolidated requirement w/o version - pkg={}".format(package_consolidated['pkg']))
diff --git a/lib/smarthome.py b/lib/smarthome.py
index 53ffbbac20..370f0f069d 100644
--- a/lib/smarthome.py
+++ b/lib/smarthome.py
@@ -147,7 +147,7 @@ def initialize_vars(self):
self._smarthome_conf_basename = None
self.__event_listeners = {}
self.__all_listeners = []
- self.modules = []
+ self.modules = None
self.__children = []
@@ -227,7 +227,7 @@ def __init__(self, MODE, extern_conf_dir=''):
# VERSION = shngversion.get_shng_version()
# self.branch = shngversion.get_shng_branch()
# self.version = shngversion.get_shng_version()
- self.connections = []
+ self.connections = None
self._etc_dir = os.path.join(self._extern_conf_dir, 'etc')
self._items_dir = os.path.join(self._extern_conf_dir, 'items'+os.path.sep)
@@ -644,6 +644,7 @@ def start(self):
# Init Plugins
#############################################################
self.shng_status = {'code': 12, 'text': 'Starting: Initializing plugins'}
+ os.chdir(self._base_dir)
self._logger.info("Init Plugins")
self.plugins = lib.plugin.Plugins(self, configfile=self._plugin_conf_basename)
@@ -723,13 +724,16 @@ def stop(self, signum=None, frame=None):
self.alive = False
self._logger.info(f"stop: Number of Threads: {threading.activeCount()}")
- self.items.stop()
- self.scheduler.stop()
+ if self.items is not None:
+ self.items.stop()
+ if self.scheduler is not None:
+ self.scheduler.stop()
if self.plugins is not None:
self.plugins.stop()
if self.modules is not None:
self.modules.stop()
- self.connections.close()
+ if self.connections is not None:
+ self.connections.close()
self.shng_status = {'code': 32, 'text': 'Stopping: Stopping threads'}
diff --git a/lib/systeminfo.py b/lib/systeminfo.py
index 151ba43eb7..3ce2bae1f1 100644
--- a/lib/systeminfo.py
+++ b/lib/systeminfo.py
@@ -148,11 +148,11 @@ def read_linuxinfo(cls):
def read_macosinfo(cls):
output = subprocess.Popen(["sw_vers", ], stdout=subprocess.PIPE).communicate()
- os, vers, build, extra = output[0].decode().split('\n')
- os = os.split('\t')[2]
- vers = vers.split('\t')[2]
- build = build.split('\t')[2]
- os_release = os + ' ' + vers + ' (build ' + build + ')'
+ ostype, vers, build, extra = output[0].decode().split('\n')
+ ostype = ostype.split('\t')[-1]
+ vers = vers.split('\t')[-1]
+ build = build.split('\t')[-1]
+ os_release = ostype + ' ' + vers + ' (build ' + build + ')'
return os_release
# ---------
@@ -262,12 +262,12 @@ def get_cpu_speed(cls, var_dir):
if yaml_support:
# read previous results from yaml file
cls._systeminfo_dict = shyaml.yaml_load(os.path.join(var_dir, 'systeminfo.yaml'), ignore_notfound=True)
- pass
try:
- cpu_speed_class = cls._systeminfo_dict['systeminfo']['cpu_speed_class']
+ cls.cpu_speed_class = cls._systeminfo_dict['systeminfo']['cpu_speed_class']
+ cls.cpu_measured_time = cls._systeminfo_dict['systeminfo']['cpu_measured_time']
if cls.get_cpubrand() == cls._systeminfo_dict['systeminfo']['cpu_brand']:
# return time, if cpu brand has not changed since stored measurement
- return cpu_speed_class
+ return cls.cpu_speed_class
except:
return None
return None # None = No previous measurement stored
@@ -287,28 +287,30 @@ def measure_cpu_speed(cls):
measured data for 50000 calculations: slow > 120sec > medium > 50sec > fast
- computer / cpu seconds measured by Python version
- ------------------------------------------ ----------- ----------- --------------
- Raspi 2, ARMv7 rev 5 (v7l) 181.47 morg42 3.7.3
-
- Raspberry Pi 3 119.83 sisamiwe 3.8.6
- Raspberry Pi 3 108.04 onkelandy 3.9.2
- Raspberry Pi 3 ARMv7 Processor rev 4 (v7l) 99.65 msinn 3.7.3
- Raspi 3B+, ARMv7 rev4 (v7l) 87.8 morg42 3.7.3
-
- Raspberry Pi 4 41.09 onkelandy 3.9.2
- Raspberry Pi 4 36.61 sisamiwe 3.9.2
- NUC mit Celeron(R) CPU N2820 @ 2.13GHz 36.05 bmxp 3.9.2
- NUC mit Celeron(R) CPU N2830 @ 2.16GHz 34.88 bmxp 3.9.2
- Intel(R) Celeron(R) CPU J3455 @ 1.50GHz 23.49-26.35 msinn 3.8.3
- NUC mit Celeron(R) J4005 CPU @ 2.00GHz 17.96 bmxp 3.9.2
- E31265L 10.39 morg42 3.9.2
- i5-8600K 9.78 morg42 3.9.7
+ computer / cpu seconds measured by Python version
+ ----------------------------------------------- ----------- ----------- --------------
+ Raspi 1, ARMv6-compatible processor rev 7 (v6l) 607,28 wvhn
+ Raspi 2, ARMv7 Processor rev 5 (v7l) 262.46 wvhn
+ Raspi 2, ARMv7 rev 5 (v7l) 181.47 morg42 3.7.3
+
+ Raspberry Pi 3 119.83 sisamiwe 3.8.6
+ Raspberry Pi 3 108.04 onkelandy 3.9.2
+ Raspberry Pi 3 ARMv7 Processor rev 4 (v7l) 99.65 msinn 3.7.3
+ Raspi 3B+, ARMv7 rev4 (v7l) 87.8 morg42 3.7.3
+
+ Raspberry Pi 4 41.09 onkelandy 3.9.2
+ Raspberry Pi 4 36.61 sisamiwe 3.9.2
+ NUC mit Celeron(R) CPU N2820 @ 2.13GHz 36.05 bmxp 3.9.2
+ NUC mit Celeron(R) CPU N2830 @ 2.16GHz 34.88 bmxp 3.9.2
+ Intel(R) Celeron(R) CPU J3455 @ 1.50GHz 23.49-26.35 msinn 3.8.3
+ NUC mit Celeron(R) J4005 CPU @ 2.00GHz 17.96 bmxp 3.9.2
+ E31265L 10.39 morg42 3.9.2
+ i5-8600K 9.78 morg42 3.9.7
"""
import timeit
- _logger.notice(f"Testing cpu speed...")
+ _logger.notice(f"Testing cpu speed... (could take several minutes on slow computers)")
#cpu_speed = round(timeit.timeit('"|".join(str(i) for i in range(99999))', number=1000), 2)
cpu_duration = round(timeit.timeit('"|".join(str(i) for i in range(50000))', number=1000), 2)
diff --git a/lib/triggertimes.py b/lib/triggertimes.py
index dc5b8b48c9..d857a7eb61 100644
--- a/lib/triggertimes.py
+++ b/lib/triggertimes.py
@@ -917,7 +917,13 @@ def get_next(self, starttime: datetime):
# the day, month and weekday is correct with searchtime
# now get the skyevent time and see if it fits for this day.
if self.event in mappings:
- eventtime = mappings[self.event](self.doff, self.moff, dt=searchtime)
+ try:
+ eventtime = mappings[self.event](self.doff, self.moff, dt=searchtime)
+ except:
+ eventtime = None
+ if eventtime is None:
+ eventtime = get_invalid_time()
+ logger.error(f"skyevent {self.event} could not be calculated, setting to invalid eventtime '{eventtime}' and try to continue")
# time in next_time will be in utctime. So we need to adjust it
if eventtime.tzinfo == tzutc():
eventtime = eventtime.astimezone(Skytime.sh.shtime.tzinfo())
diff --git a/logics/check_items.py b/logics/check_items.py
index d009545f1f..d11db568d3 100644
--- a/logics/check_items.py
+++ b/logics/check_items.py
@@ -78,7 +78,7 @@ def check_item(sh, path):
problems_found = 0
problems_fixed = 0
-for one in items.return_items(sorted=True):
+for one in items.return_items(ordered=True):
# get the items full path
path = one.id()
try:
@@ -94,4 +94,4 @@ def check_item(sh, path):
if problems_found:
logger.error(f"{problems_found} problematic item assignment{'' if problems_found == 1 else 's'} found, {problems_fixed} item assignment{'' if problems_fixed == 1 else 's'} fixed")
else:
- logger.notice("no problems found")
\ No newline at end of file
+ logger.notice("no problems found")
diff --git a/modules/admin/api_plugins.py b/modules/admin/api_plugins.py
index 61593bf483..56ea1d1765 100644
--- a/modules/admin/api_plugins.py
+++ b/modules/admin/api_plugins.py
@@ -466,6 +466,8 @@ def read(self, id=None):
plugin['metadata']['keywords'] = x._metadata.get_string('keywords')
# documentation link from metadata
plugin['metadata']['documentation'] = x._metadata.get_string('documentation')
+ if plugin['metadata']['documentation'] is None:
+ plugin['metadata']['documentation'] = ''
if plugin['metadata']['documentation'].endswith(f"plugins/{plugin['pluginname']}/user_doc.html"):
plugin['metadata']['documentation'] = ''
elif plugin['metadata']['documentation'].endswith(f"plugins_doc/config/{plugin['pluginname']}.html"):
diff --git a/modules/admin/systemdata.py b/modules/admin/systemdata.py
index a2c7927474..f2a2d5f5f3 100644
--- a/modules/admin/systemdata.py
+++ b/modules/admin/systemdata.py
@@ -126,6 +126,7 @@ def systeminfo_json(self):
response['hardware'] = self._sh.systeminfo.get_cpubrand()
response['rasppi'] = self._sh.systeminfo.running_on_rasppi()
#response['hwspeed'] = ''
+
if self._sh.systeminfo.cpu_speed_class is not None:
response['hwspeed'] = self._sh.systeminfo.cpu_speed_class
diff --git a/modules/admin/webif/static/assets/i18n/de.json b/modules/admin/webif/static/assets/i18n/de.json
index 0e88ddc210..6a426d80fc 100644
--- a/modules/admin/webif/static/assets/i18n/de.json
+++ b/modules/admin/webif/static/assets/i18n/de.json
@@ -88,6 +88,11 @@
"SHOW_PASSWORDS": "Passworte anzeigen",
"REMEMBER TO SAVE CHANGES": "Daran denken die Änderungen zu speichern, nachdem ein neues Passwort gesetzt wurde"
},
+ "SPEEDCLASS": {
+ "slow": "Langsam",
+ "medium": "Mittel",
+ "fast": "Schnell"
+ },
"SERVICES": {
"SERVICES": "Dienste",
"LANGUAGE_UI": "Sprache der graphischen Oberfläche",
@@ -135,9 +140,10 @@
"Initalizing": "Initialisierung",
"Initalizing: Logging initalized": "Initialisierung: Logging initialisiert",
"Initalizing: Requirements checked": "Initialisierung: Voraussetzungen überprüft",
+ "Checking processor speed": "Test der Prozessor Geschwindigkeit...",
"Starting": "Startet",
- "Starting: Initalizing and starting loadable modules": "Start: Initialisiert und startet ladbare Module",
- "Starting: Initalizing plugins": "Start: Initialisiert Plugins",
+ "Starting: Initializing and starting loadable modules": "Start: Initialisiert und startet ladbare Module",
+ "Starting: Initializing plugins": "Start: Initialisiert Plugins",
"Starting: Loading item definitions": "Start: Lädt Item Definitionen",
"Starting: Preparing loaded items": "Start: Geladene Items vorbereiten",
"Starting: Initializing logics": "Start: Initialisiert Logiken",
@@ -159,9 +165,10 @@
"Initalizing": "0",
"Initalizing: Logging initalized": "1",
"Initalizing: Requirements checked": "2",
+ "Checking processor speed": 3,
"Starting": "10",
- "Starting: Initalizing and starting loadable modules": "11",
- "Starting: Initalizing plugins": "12",
+ "Starting: Initializing and starting loadable modules": "11",
+ "Starting: Initializing plugins": "12",
"Starting: Loading item definitions": "13",
"Starting: Preparing loaded items": "14",
"Starting: Initializing logics": "15",
diff --git a/modules/admin/webif/static/assets/i18n/en.json b/modules/admin/webif/static/assets/i18n/en.json
index a5161b46ae..9546ad4e1c 100644
--- a/modules/admin/webif/static/assets/i18n/en.json
+++ b/modules/admin/webif/static/assets/i18n/en.json
@@ -88,6 +88,11 @@
"SHOW_PASSWORDS": "Show passwords",
"REMEMBER TO SAVE CHANGES": "Remember to save changes after setting a new password"
},
+ "SPEEDCLASS": {
+ "slow": "Slow",
+ "medium": "Medium",
+ "fast": "Fast"
+ },
"SERVICES": {
"SERVICES": "Services",
"LANGUAGE_UI": "Language of the user interface",
@@ -135,9 +140,10 @@
"Initalizing": "Initalizing",
"Initalizing: Logging initalized": "Initalizing: Logging initalized",
"Initalizing: Requirements checked": "Initalizing: Requirements checked",
+ "Checking processor speed": "Checking processor speed...",
"Starting": "Starting",
- "Starting: Initalizing and starting loadable modules": "Starting: Initalizing and starting loadable modules",
- "Starting: Initalizing plugins": "Starting: Initalizing plugins",
+ "Starting: Initializing and starting loadable modules": "Starting: Initalizing and starting loadable modules",
+ "Starting: Initializing plugins": "Starting: Initalizing plugins",
"Starting: Loading item definitions": "Starting: Loading item definitions",
"Starting: Preparing loaded items": "Starting: Preparing loaded items",
"Starting: Initializing logics": "Starting: Initializing logics",
@@ -157,9 +163,10 @@
"Initalizing": "0",
"Initalizing: Logging initalized": "1",
"Initalizing: Requirements checked": "2",
+ "Checking processor speed": 3,
"Starting": "10",
- "Starting: Initalizing and starting loadable modules": "11",
- "Starting: Initalizing plugins": "12",
+ "Starting: Initializing and starting loadable modules": "11",
+ "Starting: Initializing plugins": "12",
"Starting: Loading item definitions": "13",
"Starting: Preparing loaded items": "14",
"Starting: Initializing logics": "15",
diff --git a/modules/admin/webif/static/assets/i18n/fr.json b/modules/admin/webif/static/assets/i18n/fr.json
index 63a59488e7..a2e0a0c1ce 100644
--- a/modules/admin/webif/static/assets/i18n/fr.json
+++ b/modules/admin/webif/static/assets/i18n/fr.json
@@ -87,6 +87,11 @@
"SHOW_PASSWORDS": "Afficher mots de passe",
"REMEMBER TO SAVE CHANGES": "Ne pas oublier de sauvegarder les modification après avoir défini un nouveau mot de passe"
},
+ "SPEEDCLASS": {
+ "slow": "Lent",
+ "medium": "Moyen",
+ "fast": "Rapide"
+ },
"SERVICES": {
"SERVICES": "Services",
"LANGUAGE_UI": "Langue de l'interface graphique",
diff --git a/modules/admin/webif/static/assets/testdata/systeminfo.json b/modules/admin/webif/static/assets/testdata/systeminfo.json
index b78418754e..126492dee1 100644
--- a/modules/admin/webif/static/assets/testdata/systeminfo.json
+++ b/modules/admin/webif/static/assets/testdata/systeminfo.json
@@ -12,7 +12,7 @@
"arch": "x86_64",
"user": "smarthome",
"hardware": "\nIntel(R) Celeron(R) CPU J3455 @ 1.50GHz",
- "hwspeed": "Hier fehlt die Geschwindigkeitsangabe",
+ "hwspeed": "medium",
"rasppi": "",
"freespace": 90249.54296875,
"uptime": 4680364.345,
diff --git a/modules/admin/webif/static/index.html b/modules/admin/webif/static/index.html
index 38f0a6c70a..1caa871583 100644
--- a/modules/admin/webif/static/index.html
+++ b/modules/admin/webif/static/index.html
@@ -17,5 +17,5 @@
-
+