diff --git a/config/.gitignore b/config/.gitignore
old mode 100644
new mode 100755
diff --git a/db/.gitignore b/db/.gitignore
old mode 100644
new mode 100755
diff --git a/dockerfiles/README.md b/dockerfiles/README.md
index dec8eaaf..ba669f13 100755
--- a/dockerfiles/README.md
+++ b/dockerfiles/README.md
@@ -35,7 +35,7 @@ docker run -d --rm --network=host \
| Variable | Description | Default |
| :------------- |:-------------| -----:|
| `PORT` |Port of the web interface | `20211` |
-| `LISTEN_ADDR` |Set the specific IP Address for the web interface | `0.0.0.0` |
+| `LISTEN_ADDR` |Set the specific IP Address for the listener address for the nginx webserver (web interface). This could be useful when using multiple subnets to hide the web interface from all untrusted networks. | `0.0.0.0` |
|`TZ` |Time zone to display stats correctly. Find your time zone [here](https://en.wikipedia.org/wiki/List_of_tz_database_time_zones) | `Europe/Berlin` |
|`HOST_USER_GID` |User ID (UID) to map the user in the container to a server user with sufficient read&write permissions on the mapped files | `1000` |
|`HOST_USER_ID` |User Group ID (GID) to map the user group in the container to a server user group with sufficient read&write permissions on the mapped files | `1000` |
diff --git a/docs/HW_INSTALL.md b/docs/HW_INSTALL.md
old mode 100644
new mode 100755
diff --git a/front/plugins/README.md b/front/plugins/README.md
index 868b45bf..1ec61ce0 100755
--- a/front/plugins/README.md
+++ b/front/plugins/README.md
@@ -25,11 +25,12 @@
| | | DHCPSRVS | Script | ♻ other | 📚[dhcp_servers](/front/plugins/dhcp_servers/) |
| | Yes | INTRNT | Script | 🔍dev scanner | 📚[internet_ip](/front/plugins/internet_ip/) |
| | | INTRSPD | Script | ♻ other | 📚[internet_speedtest](/front/plugins/internet_speedtest/) |
-| Yes | | NEWDEV | Template | ⚙ system | 📚[newdev_template](/front/plugins/newdev_template/) |
-| | | PHOLUS | Script | ♻ other | 📚[pholus_scan](/front/plugins/pholus_scan/) |
+| | | MAINT | Script | ⚙ system | 📚[maintenance](/front/plugins/maintenance/) |
| | | MQTT | Script | 💬 publisher | 📚[_publisher_mqtt](/front/plugins/_publisher_mqtt/) |
+| Yes | | NEWDEV | Template | ⚙ system | 📚[newdev_template](/front/plugins/newdev_template/) |
| | | NMAP | Script | ♻ other | 📚[nmap_scan](/front/plugins/nmap_scan/) |
| | | NTFY | Script | 💬 publisher | 📚[_publisher_ntfy](/front/plugins/_publisher_ntfy/) |
+| | | PHOLUS | Script | ♻ other | 📚[pholus_scan](/front/plugins/pholus_scan/) |
| | Yes | PIHOLE | External SQLite DB | 🔍dev scanner | 📚[pihole_scan](/front/plugins/pihole_scan/) |
| | | PUSHSAFER | Script | 💬 publisher | 📚[_publisher_pushsafer](/front/plugins/_publisher_pushsafer/) |
| | | SETPWD | Script | ⚙ system | 📚[set_password](/front/plugins/set_password/) |
diff --git a/front/plugins/_publisher_email/email_smtp.py b/front/plugins/_publisher_email/email_smtp.py
index 40be1642..b74ecfe8 100755
--- a/front/plugins/_publisher_email/email_smtp.py
+++ b/front/plugins/_publisher_email/email_smtp.py
@@ -15,7 +15,6 @@ import smtplib
import socket
import ssl
-# Replace these paths with the actual paths to your Pi.Alert directories
sys.path.extend(["/home/pi/pialert/front/plugins", "/home/pi/pialert/pialert"])
# PiAlert modules
diff --git a/front/plugins/csv_backup/config.json b/front/plugins/csv_backup/config.json
index 9cb9907a..14aa391d 100755
--- a/front/plugins/csv_backup/config.json
+++ b/front/plugins/csv_backup/config.json
@@ -25,15 +25,7 @@
{
"language_code": "en_us",
"string": ""
- },
- {
- "language_code": "es_es",
- "string": ""
- },
- {
- "language_code": "de_de",
- "string": ""
- }
+ }
],
"description": [
{
diff --git a/front/plugins/maintenance/README.md b/front/plugins/maintenance/README.md
new file mode 100755
index 00000000..e19cafc8
--- /dev/null
+++ b/front/plugins/maintenance/README.md
@@ -0,0 +1,9 @@
+## Overview
+
+A plugin responsible for general maintenance tasks. These currently include:
+
+- pialert.log cleanup
+
+### Usage
+
+- N/A
diff --git a/front/plugins/maintenance/config.json b/front/plugins/maintenance/config.json
new file mode 100755
index 00000000..fbe301de
--- /dev/null
+++ b/front/plugins/maintenance/config.json
@@ -0,0 +1,176 @@
+{
+ "code_name": "maintenance",
+ "unique_prefix": "MAINT",
+ "plugin_type": "system",
+ "enabled": true,
+ "data_source": "script",
+ "show_ui": false,
+ "localized": ["display_name", "description", "icon"],
+ "display_name": [
+ {
+ "language_code": "en_us",
+ "string": "Maintenance"
+ }
+ ],
+ "icon": [
+ {
+ "language_code": "en_us",
+ "string": ""
+ }
+ ],
+ "description": [
+ {
+ "language_code": "en_us",
+ "string": "A plugin for maintenance tasks."
+ }
+ ],
+ "params" : [
+ ],
+ "settings": [
+ {
+ "function": "RUN",
+ "events": ["run"],
+ "type": "text.select",
+ "default_value":"schedule",
+ "options": ["disabled", "once", "schedule", "always_after_scan", "on_new_device"],
+ "localized": ["name", "description"],
+ "name" :[{
+ "language_code":"en_us",
+ "string" : "When to run"
+ },
+ {
+ "language_code":"es_es",
+ "string" : "Cuándo ejecutar"
+ },
+ {
+ "language_code":"de_de",
+ "string" : "Wann laufen"
+ }],
+ "description": [{
+ "language_code":"en_us",
+ "string" : "When the maintenance tasks should run. A daily or weekly SCHEDULE is a good option."
+ }]
+ },
+ {
+ "function": "CMD",
+ "type": "readonly",
+ "default_value": "python3 /home/pi/pialert/front/plugins/maintenance/maintenance.py",
+ "options": [],
+ "localized": ["name", "description"],
+ "name": [
+ {
+ "language_code": "en_us",
+ "string": "Command"
+ },
+ {
+ "language_code": "es_es",
+ "string": "Comando"
+ },
+ {
+ "language_code": "de_de",
+ "string": "Befehl"
+ }
+ ],
+ "description": [
+ {
+ "language_code": "en_us",
+ "string": "Command to run. This can not be changed"
+ },
+ {
+ "language_code": "es_es",
+ "string": "Comando a ejecutar. Esto no se puede cambiar"
+ },
+ {
+ "language_code": "de_de",
+ "string": "Befehl zum Ausführen. Dies kann nicht geändert werden"
+ }
+ ]
+ },
+ {
+ "function": "RUN_SCHD",
+ "type": "text",
+ "default_value":"0 2 * * 3",
+ "options": [],
+ "localized": ["name", "description"],
+ "name" : [{
+ "language_code":"en_us",
+ "string" : "Schedule"
+ },
+ {
+ "language_code":"es_es",
+ "string" : "Schedule"
+ },
+ {
+ "language_code":"de_de",
+ "string" : "Schedule"
+ }],
+ "description": [{
+ "language_code":"en_us",
+ "string" : "Only enabled if you select schedule in the MAINT_RUN setting. Make sure you enter the schedule in the correct cron-like format (e.g. validate at crontab.guru). For example entering 0 4 * * * will run the scan after 4 am in the TIMEZONE you set above. Will be run NEXT time the time passes."
+ },
+ {
+ "language_code":"es_es",
+ "string" : "Solo está habilitado si selecciona schedule en la configuración MAINT_RUN. Asegúrese de ingresar la programación en el formato similar a cron correcto (por ejemplo, valide en crontab.guru). Por ejemplo, ingresar 0 4 * * * ejecutará el escaneo después de las 4 a.m. en el TIMEZONE código> que configuró arriba. Se ejecutará la PRÓXIMA vez que pase el tiempo."
+ },
+ {
+ "language_code":"de_de",
+ "string" : "Nur aktiviert, wenn Sie schedule in der CSVBCKP_RUN-Einstellung auswählen. Stellen Sie sicher, dass Sie den Zeitplan im richtigen Cron-ähnlichen Format eingeben (z. B. validieren unter crontab.guru). Wenn Sie beispielsweise 0 4 * * * eingeben, wird der Scan nach 4 Uhr morgens in der TIMEZONE ausgeführt. Code> den Sie oben festgelegt haben. Wird das NÄCHSTE Mal ausgeführt, wenn die Zeit vergeht."
+ }]
+ },
+ {
+ "function": "RUN_TIMEOUT",
+ "type": "integer",
+ "default_value": 30,
+ "options": [],
+ "localized": ["name", "description"],
+ "name": [
+ {
+ "language_code": "en_us",
+ "string": "Run timeout"
+ },
+ {
+ "language_code": "es_es",
+ "string": "Tiempo límite de ejecución"
+ },
+ {
+ "language_code": "de_de",
+ "string": "Zeitüberschreitung"
+ }
+ ],
+ "description": [
+ {
+ "language_code": "en_us",
+ "string": "Maximum time in seconds to wait for the script to finish. If this time is exceeded the script is aborted."
+ },
+ {
+ "language_code": "es_es",
+ "string": "Tiempo máximo en segundos para esperar a que finalice el script. Si se supera este tiempo, el script se cancela."
+ },
+ {
+ "language_code": "de_de",
+ "string": "Maximale Zeit in Sekunden, die auf den Abschluss des Skripts gewartet werden soll. Bei Überschreitung dieser Zeit wird das Skript abgebrochen."
+ }
+ ]
+ },
+ {
+ "function": "LOG_LENGTH",
+ "type": "integer",
+ "default_value": 250000,
+ "options": [],
+ "localized": ["name", "description"],
+ "name" : [{
+ "language_code":"en_us",
+ "string" : "Log length"
+ }],
+ "description": [{
+ "language_code":"en_us",
+ "string" : "How many last pialert.log lines to keep. If LOG_LEVEL is set to debug the app generates about 10000 lines per hour, so when debugging an issue the recommended setting should cover the bug occurence timeframe. For example for a bug with a 3 day periodical appearence the value 1000000 should be sufficient. Setting this value to 1000000 generates approximatelly a 50MB pialert.log file. Set to 0 disables log cleaning."
+ }]
+ }
+ ],
+
+ "database_column_definitions":
+ [
+
+ ]
+}
diff --git a/front/plugins/maintenance/maintenance.py b/front/plugins/maintenance/maintenance.py
new file mode 100755
index 00000000..8065915b
--- /dev/null
+++ b/front/plugins/maintenance/maintenance.py
@@ -0,0 +1,67 @@
+#!/usr/bin/env python
+# test script by running:
+# /home/pi/pialert/front/plugins/maintenance/maintenance.py
+
+import os
+import pathlib
+import argparse
+import sys
+import hashlib
+import csv
+import sqlite3
+from io import StringIO
+from datetime import datetime
+from collections import deque
+
+sys.path.extend(["/home/pi/pialert/front/plugins", "/home/pi/pialert/pialert"])
+
+# pialert modules
+from plugin_helper import Plugin_Object, Plugin_Objects, decodeBase64
+from logger import mylog, append_line_to_file
+from helper import timeNowTZ, get_setting_value
+from const import logPath, pialertPath
+
+
+CUR_PATH = str(pathlib.Path(__file__).parent.resolve())
+LOG_FILE = os.path.join(CUR_PATH, 'script.log')
+RESULT_FILE = os.path.join(CUR_PATH, 'last_result.log')
+
+pluginName = 'MAINT'
+
+def main():
+
+ mylog('verbose', [f'[{pluginName}] In script'])
+
+ MAINT_LOG_LENGTH = int(get_setting_value('MAINT_LOG_LENGTH'))
+
+ # Check if set
+ if MAINT_LOG_LENGTH != 0:
+
+ mylog('verbose', [f'[{pluginName}] Cleaning file'])
+
+ logFile = logPath + "/pialert.log"
+
+ # Using a deque to efficiently keep the last N lines
+ lines_to_keep = deque(maxlen=MAINT_LOG_LENGTH)
+
+ with open(logFile, 'r') as file:
+ # Read lines from the file and store the last N lines
+ for line in file:
+ lines_to_keep.append(line)
+
+ with open(logFile, 'w') as file:
+ # Write the last N lines back to the file
+ file.writelines(lines_to_keep)
+
+ mylog('verbose', [f'[{pluginName}] Cleanup finished'])
+
+
+
+ return 0
+
+
+#===============================================================================
+# BEGIN
+#===============================================================================
+if __name__ == '__main__':
+ main()
\ No newline at end of file