Merge branch 'main' into feat/german-translation

This commit is contained in:
Markus Lorenz
2023-10-09 09:20:49 +02:00
38 changed files with 1685 additions and 793 deletions

View File

@@ -718,6 +718,10 @@ input[readonly] {
}
/* Devices */
#txtIconFA {
min-width: 18px;
}
.drp-edit
{
cursor: pointer;
@@ -795,7 +799,8 @@ input[readonly] {
#networkTree .netPort
{
float:left;
display:inline;
display:inline;
text-align: center;
}
#networkTree .portBckgIcon
@@ -816,7 +821,8 @@ input[readonly] {
{
width: 25px;;
float:left;
display:inline;
display:inline;
text-align: center;
}
#networkTree .netCollapse
{

View File

@@ -149,7 +149,7 @@
<div class="input-group">
<input class="form-control" id="txtName" type="text" value="--">
<span class="input-group-addon"><i class="fa fa-pencil pointer" onclick="editDrp('txtName');"></i></span>
</div>
</div>
</div>
</div>
@@ -196,9 +196,9 @@
</label>
<div class="col-sm-9">
<div class="input-group">
<input class="form-control" id="txtIcon" type="text" value="--">
<span class="input-group-addon"><i class="fa" id="txtIconFA" onclick="editDrp('txtIcon');"></i></span>
<input class="form-control" id="txtIcon" type="text" value="--">
<span class="input-group-addon" title='<?= lang('DevDetail_button_OverwriteIcons_Tooltip');?>'><i class="fa fa-copy pointer" onclick="askOverwriteIconType();"></i></span>
<span class="input-group-addon"><i class="fa fa-pencil pointer" onclick="editDrp('txtIcon');"></i></span>
<div class="input-group-btn">
<button type="button" class="btn btn-info dropdown-toggle" data-toggle="dropdown" aria-expanded="false">
<span class="fa fa-caret-down"></span>
@@ -749,6 +749,11 @@ function main () {
}
});
// Show device icon as it changes
$('#txtIcon').on('change input', function() {
$('#txtIconFA').removeClass().addClass(`fa fa-${$(this).val()} pointer`)
});
});
});
@@ -1285,7 +1290,8 @@ function getDeviceData (readAllData=false) {
$('#txtOwner').val (deviceData['dev_Owner']);
$('#txtDeviceType').val (deviceData['dev_DeviceType']);
$('#txtVendor').val (deviceData['dev_Vendor']);
$('#txtIcon').val (initDefault(deviceData['dev_Icon'], 'laptop'));
$('#txtIcon').val (initDefault(deviceData['dev_Icon'], 'laptop'));
$('#txtIcon').trigger('change')
if (deviceData['dev_Favorite'] == 1) {$('#chkFavorite').iCheck('check');} else {$('#chkFavorite').iCheck('uncheck');}
$('#txtGroup').val (deviceData['dev_Group']);
@@ -1700,6 +1706,7 @@ function setTextValue (textElement, textValue) {
$('#'+textElement).attr ('data-myvalue', textValue);
$('#'+textElement).val (textValue);
}
$('#'+textElement).trigger('change')
}
// -----------------------------------------------------------------------------

View File

@@ -617,23 +617,20 @@
var sizeCoefficient = 1
function initTree(myHierarchy)
{
{
// calculate the font size of the leaf nodes to fit everything into the tree area
leafNodesCount == 0 ? 1 : leafNodesCount;
emSize = ((treeAreaHeight/(25*leafNodesCount)).toFixed(2));
emSize = emSize > 1 ? 1 : emSize;
emSize = ((treeAreaHeight/(25*leafNodesCount)).toFixed(2));
emSize = emSize > 1 ? 1 : emSize;
// nodeHeight = ((emSize*100*0.30).toFixed(0))
nodeHeight = ((emSize*100*0.30).toFixed(0))
$("#networkTree").attr('style', `height:${treeAreaHeight}px; width:${$('.content-header').width()}px`)
console.log('here')
myTree = Treeviz.create({
htmlId: "networkTree",
renderNode: nodeData => {
renderNode: nodeData => {
var fontSize = "font-size:"+emSize+"em;";
(!emptyArr.includes(nodeData.data.port )) ? port = nodeData.data.port : port = "";
@@ -641,52 +638,51 @@
(port == "" || port == 0 ) ? portBckgIcon = `<i class="fa fa-wifi"></i>` : portBckgIcon = `<i class="fa fa-ethernet"></i>`;
// Build HTML for individual nodes in the network diagram
deviceIcon = (!emptyArr.includes(nodeData.data.icon )) ? "<div class='netIcon ' ><i class='fa fa-"+nodeData.data.icon +"'></i></div>" : "";
devicePort = `<div class='netPort ' style="width:${emSize*sizeCoefficient}em;height:${emSize*sizeCoefficient}em" >${port}</div> <div class="portBckgIcon" style="margin-left:-${emSize*sizeCoefficient}em;">${portBckgIcon}</div>`;
collapseExpandIcon = nodeData.data.hiddenChildren ? "square-plus" :"square-minus";
collapseExpandHtml = (nodeData.data.hasChildren) ? "<div class='netCollapse' style='font-size:"+emSize*sizeCoefficient+"em;' data-mytreepath='"+nodeData.data.path+"' data-mytreemac='"+nodeData.data.mac+"'><i class='fa fa-"+ collapseExpandIcon +" pointer'></i></div>" : "";
statusCss = " netStatus-" + nodeData.data.status;
deviceIcon = (!emptyArr.includes(nodeData.data.icon )) ? `<div class="netIcon"><i class="fa fa-${nodeData.data.icon}"></i></div>` : "";
devicePort = `<div class="netPort" style="width:${emSize*sizeCoefficient}em;height:${emSize*sizeCoefficient}em">${port}</div> <div class="portBckgIcon" style="margin-left:-${emSize*sizeCoefficient}em;">${portBckgIcon}</div>`;
collapseExpandIcon = nodeData.data.hiddenChildren ? "square-plus" : "square-minus";
collapseExpandHtml = nodeData.data.hasChildren ? `<div class="netCollapse" style="font-size:${emSize*sizeCoefficient}em;" data-mytreepath='${nodeData.data.path}" data-mytreemac="${nodeData.data.mac}"><i class='fa fa-${collapseExpandIcon} pointer"></i></div>` : "";
statusCss = ` netStatus-${nodeData.data.status}`;
selectedNodeMac = $(".nav-tabs-custom .active a").attr('data-mytabmac')
highlightedCss = nodeData.data.mac == selectedNodeMac ? " highlightedNode" : "";
highlightedCss = nodeData.data.mac == selectedNodeMac ? " highlightedNode" : "";
return result = `<div class='box ${(nodeData.data.hasChildren)? "pointer":""} ${statusCss} ${highlightedCss}'
data-mytreemacmain='${nodeData.data.mac}'
style='height:${nodeData.settings.nodeHeight}px;${fontSize}
return result = `<div class="box ${nodeData.data.hasChildren ? "pointer":""} ${statusCss} ${highlightedCss}"
data-mytreemacmain="${nodeData.data.mac}"
style="height:${nodeData.settings.nodeHeight}px;${fontSize}"
>
<div class='netNodeText '>\
<strong>${devicePort} ${deviceIcon}
<span class='spanNetworkTree anonymizeDev'>${nodeData.data.name}</span>\
</strong>
${collapseExpandHtml}
</div></div>`;
},
<div class="netNodeText">
<strong>${devicePort} ${deviceIcon}
<span class="spanNetworkTree anonymizeDev">${nodeData.data.name}</span>
</strong>
${collapseExpandHtml}
</div>
</div>`;
},
onNodeClick: nodeData => {
console.log(this)
},
onNodeClick: nodeData => {
console.log(this)
},
mainAxisNodeSpacing: 'auto',
// mainAxisNodeSpacing: 3,
secondaryAxisNodeSpacing: 0.3,
nodeHeight: nodeHeight.toString(),
nodeHeight: nodeHeight.toString(),
marginTop: '5',
hasZoom: false,
hasPan: false,
// marginLeft: '15',
idKey: "id",
hasFlatData: false,
hasFlatData: false,
linkWidth: (nodeData) => 3,
linkColor: (nodeData) => "#ffcc80",
onNodeClick: (nodeData) => handleNodeClick(nodeData),
relationnalField: "children",
relationnalField: "children",
});
console.log(myHierarchy)
myTree.refresh(myHierarchy);
myTree.refresh(myHierarchy);
}
// ---------------------------------------------------------------------------

View File

@@ -504,25 +504,13 @@
"Email_icon" : "<i class=\"fa fa-at\"></i>",
"REPORT_MAIL_name" : "Enable email",
"REPORT_MAIL_description" : "If enabled an email is sent out with a list of changes you nove subscribed to. Please also fill out all remaining settings related to the SMTP setup below. If facing issues, set <code>LOG_LEVEL</code> to <code>debug</code> and check the <a href=\"/maintenance.php#tab_Logging\">error log</a>.",
"SMTP_SERVER_name" : "SMTP server URL",
"SMTP_SERVER_description" : "The SMTP server host URL. For example <code>smtp-relay.sendinblue.com</code>. To use Gmail as an SMTP server <a target=\"_blank\" href=\"https://github.com/jokob-sk/Pi.Alert/blob/main/docs/SMTP.md\">follow this guide</a>",
"SMTP_PORT_name" : "SMTP server PORT",
"SMTP_PORT_description" : "Port number used for the SMTP connection. Set to <code>0</code> if you do not want to use a port when connecting to the SMTP server.",
"SMTP_SKIP_LOGIN_name" : "Skip authentication",
"SMTP_SKIP_LOGIN_description" : "Do not use authentication when connecting to the SMTP server.",
"SMTP_USER_name" : "SMTP user",
"SMTP_USER_description" : "The user name used to login into the SMTP server (sometimes a full email address).",
"SMTP_PASS_name" : "SMTP password",
"SMTP_PASS_description" : "The SMTP server password. ",
"SMTP_SKIP_TLS_name" : "Do not use TLS",
"SMTP_SKIP_TLS_description" : "Disable TLS when connecting to your SMTP server.",
"SMTP_FORCE_SSL_name" : "Force SSL",
"SMTP_FORCE_SSL_description" : "Force SSL when connecting to your SMTP server.",
"SYSTEM_TITLE" : "System Information",
"REPORT_TO_name" : "Send email to",
"REPORT_TO_description" : "Email address to which the notification will be send to.",
"REPORT_FROM_name" : "Email subject",
"REPORT_FROM_description" : "Notification email subject line. Some SMTP servers need this to be an email.",
"SYSTEM_TITLE" : "System Information",
"REPORT_TO_name" : "deprecated",
"REPORT_TO_description" : "deprecated",
"REPORT_FROM_name" : "deprecated",
"REPORT_FROM_description" : "deprecated",
"Webhooks_display_name" : "Webhooks",
"Webhooks_icon" : "<i class=\"fa fa-circle-nodes\"></i>",
"REPORT_WEBHOOK_name" : "Enable Webhooks",
@@ -536,17 +524,7 @@
"WEBHOOK_SIZE_name" : "Max payload size",
"WEBHOOK_SIZE_description" : "The maximum size of the webhook payload as number of characters in the passed string. If above limit, it will be truncated and a <code>(text was truncated)</code> message is appended.",
"WEBHOOK_SECRET_name": "HMAC Secret",
"WEBHOOK_SECRET_description": "When set, use this secret to generate the SHA256-HMAC hex digest value of the request body, which will be passed as the <code>X-Webhook-Signature</code> header to the request. You can find more informations <a target=\"_blank\" href=\"https://github.com/jokob-sk/Pi.Alert/blob/main/docs/WEBHOOK_SECRET.md\">here</a>.",
"Apprise_display_name" : "Apprise",
"Apprise_icon" : "<i class=\"fa fa-bullhorn\"></i>",
"REPORT_APPRISE_name" : "Enable Apprise",
"REPORT_APPRISE_description" : "Enable sending notifications via <a target=\"_blank\" href=\"https://hub.docker.com/r/caronc/apprise\">Apprise</a>.",
"APPRISE_HOST_name" : "Apprise host URL",
"APPRISE_HOST_description" : "Apprise host URL starting with <code>http://</code> or <code>https://</code>. (do not forget to include <code>/notify</code> at the end)",
"APPRISE_URL_name" : "Apprise notification URL",
"APPRISE_URL_description" : "Apprise notification target URL. For example for Telegram it would be <code>tgram://{bot_token}/{chat_id}</code>.",
"APPRISE_SIZE_name" : "Max payload size",
"APPRISE_SIZE_description" : "The maximum size of the apprise payload as number of characters in the passed string. If above limit, it will be truncated and a <code>(text was truncated)</code> message is appended.",
"WEBHOOK_SECRET_description": "When set, use this secret to generate the SHA256-HMAC hex digest value of the request body, which will be passed as the <code>X-Webhook-Signature</code> header to the request. You can find more informations <a target=\"_blank\" href=\"https://github.com/jokob-sk/Pi.Alert/blob/main/docs/WEBHOOK_SECRET.md\">here</a>.",
"NTFY_display_name" : "NTFY",
"NTFY_icon" : "<i class=\"fa fa-terminal\"></i>",
"REPORT_NTFY_name" : "Enable NTFY",
@@ -564,9 +542,7 @@
"REPORT_PUSHSAFER_name" : "Enable Pushsafer",
"REPORT_PUSHSAFER_description" : "Enable sending notifications via <a target=\"_blank\" href=\"https://www.pushsafer.com/\">Pushsafer</a>.",
"PUSHSAFER_TOKEN_name" : "Pushsafer token",
"PUSHSAFER_TOKEN_description" : "Your secret Pushsafer API key (token).",
"APPRISE_PAYLOAD_name" : "Payload type",
"APPRISE_PAYLOAD_description" : "Select the payoad type sent to Apprise. For example <code>html</code> works well with emails, <code>text</code> with chat apps, such as Telegram.",
"PUSHSAFER_TOKEN_description" : "Your secret Pushsafer API key (token).",
"MQTT_display_name" : "MQTT",
"MQTT_icon" : "<i class=\"fa fa-square-rss\"></i>",
"REPORT_TITLE" : "Report",

View File

@@ -542,8 +542,8 @@ Required attributes are:
| `"name"` | Displayed on the Settings page. An array of localized strings. See Localized strings below. |
| `"description"` | Displayed on the Settings page. An array of localized strings. See Localized strings below. |
| (optional) `"events"` | Specifies whether to generate an execution button next to the input field of the setting. Supported values: |
| | - `test` |
| | - `run` |
| | - `"test"` - For notification plugins testing |
| | - `"run"` - Regular plugins testing |
| (optional) `"override_value"` | Used to determine a user-defined override for the setting. Useful for template-based plugins, where you can choose to leave the current value or override it with the value defined in the setting. (Work in progress) |
| (optional) `"events"` | Used to trigger the plugin. Usually used on the `RUN` setting. Not fully tested in all scenarios. Will show a play button next to the setting. After clicking, an event is generated for the backend in the `Parameters` database table to process the front-end event on the next run. |
@@ -619,7 +619,8 @@ The UI will adjust how columns are displayed in the UI based on the resolvers de
| Supported Types | Description |
| -------------- | ----------- |
| `label` | Displays a column only. |
| `text` | Makes a column editable, and a save icon is displayed next to it. See below for information on `threshold`, `replace`. |
| `textarea_readonly` | Generates a read only text area and cleans up the text to display it somewhat formatted with new lines preserved. |
| See below for information on `threshold`, `replace`. | |
| | |
| `options` Property | Used in conjunction with types like `threshold`, `replace`, `regex`. |
| `threshold` | The `options` array contains objects ordered from the lowest `maximum` to the highest. The corresponding `hexColor` is used for the value background color if it's less than the specified `maximum` but more than the previous one in the `options` array. |

View File

@@ -0,0 +1,8 @@
## Overview
[Apprise](front/plugins/arp_scan/README.md) is a notification gateway/publisher that allows you to push notifications to 80+ different services.
### Usage
- Go to settings and fill in relevant details.

View File

@@ -1,5 +1,4 @@
#!/usr/bin/env python
# Based on the work of https://github.com/leiweibau/Pi.Alert
import json
import subprocess
@@ -15,87 +14,115 @@ sys.path.extend(["/home/pi/pialert/front/plugins", "/home/pi/pialert/pialert"])
import conf
from plugin_helper import Plugin_Objects
from logger import mylog, append_line_to_file
from helper import timeNowTZ, noti_struc
from helper import timeNowTZ, noti_obj, get_setting_value
from notification import Notification_obj
from database import DB
CUR_PATH = str(pathlib.Path(__file__).parent.resolve())
RESULT_FILE = os.path.join(CUR_PATH, 'last_result.log')
pluginName = 'APPRISE'
def main():
mylog('verbose', ['[APPRISE](publisher) In script'])
mylog('verbose', [f'[{pluginName}](publisher) In script'])
# Check if basic config settings supplied
if check_config() == False:
mylog('none', [f'[{pluginName}] Error: Publisher notification gateway not set up correctly. Check your pialert.conf {pluginName}_* variables.'])
return
parser = argparse.ArgumentParser(description='APPRISE publisher Plugin')
values = parser.parse_args()
# Create a database connection
db = DB() # instance of class DB
db.open()
# Initialize the Plugin obj output file
plugin_objects = Plugin_Objects(RESULT_FILE)
speedtest_result = send()
# Create a Notification_obj instance
notifications = Notification_obj(db)
plugin_objects.add_object(
primaryId = 'APPRISE',
secondaryId = timeNowTZ(),
watched1 = speedtest_result['download_speed'],
watched2 = speedtest_result['upload_speed'],
watched3 = 'null',
watched4 = 'null',
extra = 'null',
foreignKey = 'null'
)
# Retrieve new notifications
new_notifications = notifications.getNew()
# Process the new notifications
for notification in new_notifications:
# Send notification
result = send(notification["HTML"], notification["Text"])
# Log result
plugin_objects.add_object(
primaryId = pluginName,
secondaryId = timeNowTZ(),
watched1 = notification["GUID"],
watched2 = result,
watched3 = 'null',
watched4 = 'null',
extra = 'null',
foreignKey = 'null'
)
plugin_objects.write_result_file()
#-------------------------------------------------------------------------------
def check_config():
if conf.APPRISE_URL == '' or conf.APPRISE_HOST == '':
mylog('none', ['[Check Config] Error: Apprise service not set up correctly. Check your pialert.conf APPRISE_* variables.'])
if get_setting_value('APPRISE_URL') == '' or get_setting_value('APPRISE_HOST') == '':
return False
else:
return True
#-------------------------------------------------------------------------------
def send(msg: noti_struc):
html = msg.html
text = msg.text
def send(html, text):
payloadData = ''
result = ''
# limit = 1024 * 1024 # 1MB limit (1024 bytes * 1024 bytes = 1MB)
limit = conf.APPRISE_SIZE
limit = get_setting_value('APPRISE_SIZE')
# truncate size
if conf.APPRISE_PAYLOAD == 'html':
if len(msg.html) > limit:
payloadData = msg.html[:limit] + " <h1> (text was truncated)</h1>"
if get_setting_value('APPRISE_PAYLOAD') == 'html':
if len(html) > limit:
payloadData = html[:limit] + "<h1>(text was truncated)</h1>"
else:
payloadData = msg.html
if conf.APPRISE_PAYLOAD == 'text':
if len(msg.text) > limit:
payloadData = msg.text[:limit] + " (text was truncated)"
payloadData = html
if get_setting_value('APPRISE_PAYLOAD') == 'text':
if len(text) > limit:
payloadData = text[:limit] + " (text was truncated)"
else:
payloadData = msg.text
payloadData = text
# Define Apprise compatible payload (https://github.com/caronc/apprise-api#stateless-solution)
_json_payload = {
"urls": conf.APPRISE_URL,
"urls": get_setting_value('APPRISE_URL'),
"title": "Pi.Alert Notifications",
"format": conf.APPRISE_PAYLOAD,
"format": get_setting_value('APPRISE_PAYLOAD'),
"body": payloadData
}
try:
# try runnning a subprocess
p = subprocess.Popen(["curl","-i","-X", "POST" ,"-H", "Content-Type:application/json" ,"-d", json.dumps(_json_payload), conf.APPRISE_HOST], stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
p = subprocess.Popen(["curl","-i","-X", "POST" ,"-H", "Content-Type:application/json" ,"-d", json.dumps(_json_payload), get_setting_value('APPRISE_HOST')], stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
stdout, stderr = p.communicate()
# write stdout and stderr into .log files for debugging if needed
# write stdout and stderr into .log files for debugging if needed
# Log the stdout and stderr
mylog('debug', [stdout, stderr]) # TO-DO should be changed to mylog
mylog('debug', [stdout, stderr])
# log result
result = stdout
except subprocess.CalledProcessError as e:
# An error occurred, handle it
mylog('none', [e.output])
# log result
result = e.output
return result
if __name__ == '__main__':
sys.exit(main())

View File

@@ -1,22 +1,30 @@
{
"code_name": "internet_speedtest",
"unique_prefix": "INTRSPD",
"code_name": "_publisher_apprise",
"unique_prefix": "APPRISE",
"enabled": true,
"data_source": "script",
"show_ui": true,
"localized": ["display_name", "description", "icon"],
"display_name" : [{
"display_name" : [
{
"language_code": "en_us",
"string" : "Internet speedtest"
}],
"string" : "Apprise publisher"
},
{
"language_code": "es_es",
"string" : "Habilitar Apprise"
}
],
"icon":[{
"language_code": "en_us",
"string" : "<i class=\"fa-solid fa-gauge-high\"></i>"
"string" : "<i class=\"fa-solid fa-bullhorn\"></i>"
}],
"description": [{
"description": [
{
"language_code": "en_us",
"string" : "A plugin to perform a scheduled internet speedtest."
}],
"string" : "A plugin to publish a notification via the Apprise gateway."
}
],
"params" : [],
"database_column_definitions":
[
@@ -94,7 +102,7 @@
"localized": ["name"],
"name":[{
"language_code": "en_us",
"string" : "Test run on"
"string" : "Sent when"
}]
},
{
@@ -118,60 +126,26 @@
"column": "Watched_Value1",
"css_classes": "col-sm-2",
"show": true,
"type": "threshold",
"type": "label",
"default_value":"",
"options": [
{
"maximum": 1,
"hexColor": "#D33115"
},
{
"maximum": 5,
"hexColor": "#792D86"
},
{
"maximum": 10,
"hexColor": "#7D862D"
},
{
"maximum": 100,
"hexColor": "#05483C"
}
],
"options": [],
"localized": ["name"],
"name":[{
"language_code": "en_us",
"string" : "Download"
"string" : "Notification GUID"
}]
},
{
"column": "Watched_Value2",
"css_classes": "col-sm-2",
"css_classes": "col-sm-8",
"show": true,
"type": "threshold",
"type": "textarea_readonly",
"default_value":"",
"options": [
{
"maximum": 1,
"hexColor": "#D33115"
},
{
"maximum": 5,
"hexColor": "#792D86"
},
{
"maximum": 10,
"hexColor": "#7D862D"
},
{
"maximum": 100,
"hexColor": "#05483C"
}
],
"options": [],
"localized": ["name"],
"name":[{
"language_code": "en_us",
"string" : "Upload"
"string" : "Result"
}]
},
{
@@ -280,10 +254,10 @@
"settings":[
{
"function": "RUN",
"events": ["run"],
"events": ["test"],
"type": "text.select",
"default_value":"disabled",
"options": ["disabled", "once", "schedule", "always_after_scan" ],
"options": ["disabled", "on_notification" ],
"localized": ["name", "description"],
"name" :[{
"language_code": "en_us",
@@ -293,15 +267,21 @@
"language_code": "es_es",
"string" : "Cuando ejecuta"
}],
"description": [{
"description": [
{
"language_code": "en_us",
"string" : "Enable a regular internet speedtest. If you select <code>schedule</code> the scheduling settings from below are applied. If you select <code>once</code> the scan is run only once on start of the application (container) for the time specified in <a href=\"#INTRSPD_RUN_TIMEOUT\"><code>INTRSPD_RUN_TIMEOUT</code> setting</a>."
}]
"string" : "Enable sending notifications via <a target=\"_blank\" href=\"https://hub.docker.com/r/caronc/apprise\">Apprise</a>."
},
{
"language_code": "es_es",
"string" : "Habilitar el envío de notificaciones a través de <a target=\"_blank\" href=\"https://hub.docker.com/r/caronc/apprise\">Apprise</a>."
}
]
},
{
"function": "CMD",
"type": "readonly",
"default_value":"python3 /home/pi/pialert/front/plugins/internet_speedtest/script.py",
"default_value":"python3 /home/pi/pialert/front/plugins/_publisher_apprise/apprise.py",
"options": [],
"localized": ["name", "description"],
"name" : [{
@@ -321,33 +301,10 @@
"string" : "Comando a ejecutar"
}]
},
{
"function": "RUN_SCHD",
"type": "text",
"default_value":"*/30 * * * *",
"options": [],
"localized": ["name", "description"],
"name" : [{
"language_code": "en_us",
"string" : "Schedule"
},
{
"language_code": "es_es",
"string" : "Schedule"
}],
"description": [{
"language_code": "en_us",
"string" : "Only enabled if you select <code>schedule</code> in the <a href=\"#INTRSPD_RUN\"><code>INTRSPD_RUN</code> setting</a>. Make sure you enter the schedule in the correct cron-like format (e.g. validate at <a href=\"https://crontab.guru/\" target=\"_blank\">crontab.guru</a>). For example entering <code>0 4 * * *</code> will run the scan after 4 am in the <a onclick=\"toggleAllSettings()\" href=\"#TIMEZONE\"><code>TIMEZONE</code> you set above</a>. Will be run NEXT time the time passes."
},
{
"language_code": "es_es",
"string": "Solo habilitado si selecciona <code>schedule</code> en la configuración <a href=\"#INTRSPD_RUN\"><code>INTRSPD_RUN</code></a>. Asegúrese de ingresar el schedule en el formato similar a cron correcto (por ejemplo, valide en <a href=\"https://crontab.guru/\" target=\"_blank\">crontab.guru</a>). Por ejemplo, ingrese <code >0 4 * * *</code> ejecutará el escaneo después de las 4 am en el <a onclick=\"toggleAllSettings()\" href=\"#TIMEZONE\"><code>TIMEZONE</code> que configuró arriba </a>. Se ejecutará la PRÓXIMA vez que pase el tiempo."
}]
},
{
"function": "RUN_TIMEOUT",
"type": "integer",
"default_value":60,
"default_value": 10,
"options": [],
"localized": ["name", "description"],
"name" : [{
@@ -372,46 +329,96 @@
}]
},
{
"function": "WATCH",
"type": "text.multiselect",
"default_value":[],
"options": ["Watched_Value1","Watched_Value2","Watched_Value3","Watched_Value4"],
"function": "HOST",
"type": "text",
"default_value": "",
"options": [],
"localized": ["name", "description"],
"name" :[{
"name" : [{
"language_code": "en_us",
"string" : "Watched"
"string" : "Apprise host URL"
},
{
"language_code": "es_es",
"string" : "Visto"
}],
"description":[{
"string" : "URL del host de Apprise"
}],
"description": [{
"language_code": "en_us",
"string" : "Send a notification if selected values change. Use <code>CTRL + Click</code> to select/deselect. <ul> <li><code>Watched_Value1</code> is Download speed (not recommended)</li><li><code>Watched_Value2</code> is Upload speed (not recommended)</li><li><code>Watched_Value3</code> unused </li><li><code>Watched_Value4</code> unused </li></ul>"
}]
"string" : "Apprise host URL starting with <code>http://</code> or <code>https://</code>. (do not forget to include <code>/notify</code> at the end)"
},
{
"language_code": "es_es",
"string" : "URL del host de Apprise que comienza con <code>http://</code> o <code>https://</code>. (no olvide incluir <code>/notify</code> al final)"
}]
},
{
"function": "REPORT_ON",
"type": "text.multiselect",
"default_value":[],
"options": ["new","watched-changed","watched-not-changed", "missing-in-last-scan"],
"function": "URL",
"type": "text",
"default_value": "",
"options": [],
"localized": ["name", "description"],
"name" :[{
"name" : [{
"language_code": "en_us",
"string" : "Report on"
"string" : "Apprise notification URL"
},
{
"language_code": "es_es",
"string" : "Informar sobre"
}] ,
"description":[{
"string" : "URL de notificación de Apprise"
}],
"description": [{
"language_code": "en_us",
"string" : "Send a notification only on these statuses. <code>new</code> means a new unique (unique combination of PrimaryId and SecondaryId) object was discovered. <code>watched-changed</code> means that selected <code>Watched_ValueN</code> columns changed."
"string" : "Apprise notification target URL. For example for Telegram it would be <code>tgram://{bot_token}/{chat_id}</code>."
},
{
"language_code": "es_es",
"string" : "Envíe una notificación solo en estos estados. <code>new</code> significa que se descubrió un nuevo objeto único (combinación única de PrimaryId y SecondaryId). <code>watched-changed</code> significa que seleccionó <code>Watched_ValueN Las columnas </code> cambiaron."
}]
}
"string" : "Informar de la URL de destino de la notificación. Por ejemplo, para Telegram sería <code>tgram://{bot_token}/{chat_id}</code>."
}]
},
{
"function": "PAYLOAD",
"type": "text.select",
"default_value": "html",
"options": ["html", "text"],
"localized": ["name", "description"],
"name" : [{
"language_code": "en_us",
"string" : "Payload type"
},
{
"language_code": "es_es",
"string" : "Tipo de carga"
}],
"description": [{
"language_code": "en_us",
"string" : "Select the payoad type sent to Apprise. For example <code>html</code> works well with emails, <code>text</code> with chat apps, such as Telegram."
},
{
"language_code": "es_es",
"string" : "Seleccione el tipo de carga útil enviada a Apprise. Por ejemplo, <code>html</code> funciona bien con correos electrónicos, <code>text</code> con aplicaciones de chat, como Telegram."
}]
},
{
"function": "SIZE",
"type": "integer",
"default_value": 1024,
"options": [],
"localized": ["name", "description"],
"name" : [{
"language_code": "en_us",
"string" : "Max payload size"
},
{
"language_code": "es_es",
"string" : "Tamaño máximo de carga útil"
}],
"description": [{
"language_code": "en_us",
"string" : "The maximum size of the apprise payload as number of characters in the passed string. If above limit, it will be truncated and a <code>(text was truncated)</code> message is appended."
},
{
"language_code": "es_es",
"string" : "El tamaño máximo de la carga útil de información como número de caracteres en la cadena pasada. Si supera el límite, se truncará y se agregará un mensaje <code>(text was truncated)</code>."
}]
}
]
}

View File

@@ -1 +0,0 @@
This plugin will not be loaded

View File

@@ -0,0 +1,8 @@
## Overview
[Apprise](front/plugins/arp_scan/README.md) is a notification gateway/publisher that allows you to push notifications to 80+ different services.
### Usage
- Go to settings and fill in relevant details.

View File

@@ -0,0 +1,683 @@
{
"code_name": "_publisher_email",
"unique_prefix": "SMTP",
"enabled": true,
"data_source": "script",
"show_ui": true,
"localized": [
"display_name",
"description",
"icon"
],
"display_name": [
{
"language_code": "en_us",
"string": "Email publisher (SMTP)"
},
{
"language_code": "es_es",
"string": "Habilitar email (SMTP)"
}
],
"icon": [
{
"language_code": "en_us",
"string": "<i class=\"fa-solid fa-at\"></i>"
}
],
"description": [
{
"language_code": "en_us",
"string": "A plugin to publish a notification via Email (SMTP) gateway."
}
],
"params": [],
"database_column_definitions": [
{
"column": "Index",
"css_classes": "col-sm-2",
"show": false,
"type": "label",
"default_value": "",
"options": [],
"localized": [
"name"
],
"name": [
{
"language_code": "en_us",
"string": "N/A"
},
{
"language_code": "es_es",
"string": "N/A"
}
]
},
{
"column": "Plugin",
"css_classes": "col-sm-2",
"show": false,
"type": "label",
"default_value": "",
"options": [],
"localized": [
"name"
],
"name": [
{
"language_code": "en_us",
"string": "N/A"
},
{
"language_code": "es_es",
"string": "N/A"
}
]
},
{
"column": "Object_PrimaryID",
"css_classes": "col-sm-2",
"show": false,
"type": "url",
"default_value": "",
"options": [],
"localized": [
"name"
],
"name": [
{
"language_code": "en_us",
"string": "N/A"
}
]
},
{
"column": "Object_SecondaryID",
"css_classes": "col-sm-2",
"show": false,
"type": "label",
"default_value": "",
"options": [],
"localized": [
"name"
],
"name": [
{
"language_code": "en_us",
"string": "N/A"
},
{
"language_code": "es_es",
"string": "N/A"
}
]
},
{
"column": "DateTimeCreated",
"css_classes": "col-sm-2",
"show": true,
"type": "label",
"default_value": "",
"options": [],
"localized": [
"name"
],
"name": [
{
"language_code": "en_us",
"string": "Sent when"
}
]
},
{
"column": "DateTimeChanged",
"css_classes": "col-sm-2",
"show": false,
"type": "label",
"default_value": "",
"options": [],
"localized": [
"name"
],
"name": [
{
"language_code": "en_us",
"string": "Changed"
},
{
"language_code": "es_es",
"string": "Cambiado"
}
]
},
{
"column": "Watched_Value1",
"css_classes": "col-sm-2",
"show": true,
"type": "label",
"default_value": "",
"options": [],
"localized": [
"name"
],
"name": [
{
"language_code": "en_us",
"string": "Notification GUID"
}
]
},
{
"column": "Watched_Value2",
"css_classes": "col-sm-8",
"show": true,
"type": "textarea_readonly",
"default_value": "",
"options": [],
"localized": [
"name"
],
"name": [
{
"language_code": "en_us",
"string": "Result"
}
]
},
{
"column": "Watched_Value3",
"css_classes": "col-sm-2",
"show": false,
"type": "label",
"default_value": "",
"options": [],
"localized": [
"name"
],
"name": [
{
"language_code": "en_us",
"string": "N/A"
},
{
"language_code": "es_es",
"string": "N/A"
}
]
},
{
"column": "Watched_Value4",
"css_classes": "col-sm-2",
"show": false,
"type": "label",
"default_value": "",
"options": [],
"localized": [
"name"
],
"name": [
{
"language_code": "en_us",
"string": "N/A"
},
{
"language_code": "es_es",
"string": "N/A"
}
]
},
{
"column": "UserData",
"css_classes": "col-sm-2",
"show": false,
"type": "textbox_save",
"default_value": "",
"options": [],
"localized": [
"name"
],
"name": [
{
"language_code": "en_us",
"string": "Comments"
},
{
"language_code": "es_es",
"string": "Comentarios"
}
]
},
{
"column": "Status",
"css_classes": "col-sm-1",
"show": false,
"type": "replace",
"default_value": "",
"options": [
{
"equals": "watched-not-changed",
"replacement": "<div style='text-align:center'><i class='fa-solid fa-square-check'></i><div></div>"
},
{
"equals": "watched-changed",
"replacement": "<div style='text-align:center'><i class='fa-solid fa-triangle-exclamation'></i></div>"
},
{
"equals": "new",
"replacement": "<div style='text-align:center'><i class='fa-solid fa-circle-plus'></i></div>"
},
{
"equals": "missing-in-last-scan",
"replacement": "<div style='text-align:center'><i class='fa-solid fa-question'></i></div>"
}
],
"localized": [
"name"
],
"name": [
{
"language_code": "en_us",
"string": "Status"
},
{
"language_code": "es_es",
"string": "Estado"
}
]
},
{
"column": "Extra",
"css_classes": "col-sm-3",
"show": false,
"type": "label",
"default_value": "",
"options": [],
"localized": [
"name"
],
"name": [
{
"language_code": "en_us",
"string": "Extra"
},
{
"language_code": "es_es",
"string": "Extra"
}
]
}
],
"settings": [
{
"function": "RUN",
"events": [
"test"
],
"type": "text.select",
"default_value": "disabled",
"options": [
"disabled",
"on_notification"
],
"localized": [
"name",
"description"
],
"name": [
{
"language_code": "en_us",
"string": "When to run"
},
{
"language_code": "es_es",
"string": "Cuando ejecuta"
}
],
"description": [
{
"language_code": "en_us",
"string": "Enable sending notifications via the Email (SMTP) gateway."
},
{
"language_code": "es_es",
"string": "Si está habilitado, se envía un correo electrónico con una lista de cambios a los que se ha suscrito. Complete también todas las configuraciones restantes relacionadas con la configuración de SMTP a continuación"
}
]
},
{
"function": "CMD",
"type": "readonly",
"default_value": "python3 /home/pi/pialert/front/plugins/_publisher_email/email_smtp.py",
"options": [],
"localized": [
"name",
"description"
],
"name": [
{
"language_code": "en_us",
"string": "Command"
},
{
"language_code": "es_es",
"string": "Comando"
}
],
"description": [
{
"language_code": "en_us",
"string": "Command to run"
},
{
"language_code": "es_es",
"string": "Comando a ejecutar"
}
]
},
{
"function": "RUN_TIMEOUT",
"type": "integer",
"default_value": 20,
"options": [],
"localized": [
"name",
"description"
],
"name": [
{
"language_code": "en_us",
"string": "Run timeout"
},
{
"language_code": "es_es",
"string": "Tiempo de espera de ejecución"
},
{
"language_code": "de_de",
"string": "Wartezeit"
}
],
"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."
}
]
},
{
"function": "SERVER",
"type": "text",
"default_value": "",
"options": [],
"localized": [
"name",
"description"
],
"name": [
{
"language_code": "en_us",
"string": "SMTP server URL"
},
{
"language_code": "es_es",
"string": "URL del servidor SMTP"
}
],
"description": [
{
"language_code": "en_us",
"string": "The SMTP server host URL. For example <code>smtp-relay.sendinblue.com</code>. To use Gmail as an SMTP server <a target=\"_blank\" href=\"https://github.com/jokob-sk/Pi.Alert/blob/main/docs/SMTP.md\">follow this guide</a>"
},
{
"language_code": "es_es",
"string": "La URL del host del servidor SMTP. Por ejemplo, <code>smtp-relay.sendinblue.com</code>. Para utilizar Gmail como servidor SMTP <a target=\"_blank\" href=\"https://github.com/jokob-sk/Pi.Alert/blob/main/docs/SMTP.md\">siga esta guía</a >"
}
]
},
{
"function": "PORT",
"type": "integer",
"default_value": 587,
"options": [],
"localized": [
"name",
"description"
],
"name": [
{
"language_code": "en_us",
"string": "SMTP server PORT"
},
{
"language_code": "es_es",
"string": "Puerto del servidor SMTP"
}
],
"description": [
{
"language_code": "en_us",
"string": "Port number used for the SMTP connection. Set to <code>0</code> if you do not want to use a port when connecting to the SMTP server."
},
{
"language_code": "es_es",
"string": "Número de puerto utilizado para la conexión SMTP. Establézcalo en <code>0</code> si no desea utilizar un puerto al conectarse al servidor SMTP."
}
]
},
{
"function": "SKIP_LOGIN",
"type": "boolean",
"default_value": false,
"options": [],
"localized": [
"name",
"description"
],
"name": [
{
"language_code": "en_us",
"string": "Skip authentication"
},
{
"language_code": "es_es",
"string": "Omitir autenticación"
}
],
"description": [
{
"language_code": "en_us",
"string": "Do not use authentication when connecting to the SMTP server."
},
{
"language_code": "es_es",
"string": "No utilice la autenticación cuando se conecte al servidor SMTP."
}
]
},
{
"function": "USER",
"type": "text",
"default_value": "",
"options": [],
"localized": [
"name",
"description"
],
"name": [
{
"language_code": "en_us",
"string": "SMTP user"
},
{
"language_code": "es_es",
"string": "Nombre de usuario SMTP"
}
],
"description": [
{
"language_code": "en_us",
"string": "The user name used to login into the SMTP server (sometimes a full email address)."
},
{
"language_code": "es_es",
"string": "El nombre de usuario utilizado para iniciar sesión en el servidor SMTP (a veces, una dirección de correo electrónico completa)."
}
]
},
{
"function": "PASS",
"type": "password",
"default_value": "",
"options": [],
"localized": [
"name",
"description"
],
"name": [
{
"language_code": "en_us",
"string": "SMTP password"
},
{
"language_code": "es_es",
"string": "Contraseña de SMTP"
}
],
"description": [
{
"language_code": "en_us",
"string": "The SMTP server password."
},
{
"language_code": "es_es",
"string": "La contraseña del servidor SMTP."
}
]
},
{
"function": "SKIP_TLS",
"type": "boolean",
"default_value": false,
"options": [],
"localized": [
"name",
"description"
],
"name": [
{
"language_code": "en_us",
"string": "Do not use TLS"
},
{
"language_code": "es_es",
"string": "No usar TLS"
}
],
"description": [
{
"language_code": "en_us",
"string": "Disable TLS when connecting to your SMTP server."
},
{
"language_code": "es_es",
"string": "Deshabilite TLS cuando se conecte a su servidor SMTP."
}
]
},
{
"function": "FORCE_SSL",
"type": "boolean",
"default_value": false,
"options": [],
"localized": [
"name",
"description"
],
"name": [
{
"language_code": "en_us",
"string": "Force SSL"
},
{
"language_code": "es_es",
"string": "Forzar SSL"
}
],
"description": [
{
"language_code": "en_us",
"string": "Force SSL when connecting to your SMTP server."
},
{
"language_code": "es_es",
"string": "Forzar SSL al conectarse a su servidor SMTP"
}
]
},
{
"function": "REPORT_TO",
"type": "text",
"default_value": "user@gmail.com",
"options": [],
"localized": [
"name",
"description"
],
"name": [
{
"language_code": "en_us",
"string": "Send email to"
},
{
"language_code": "es_es",
"string": "Enviar el email a"
}
],
"description": [
{
"language_code": "en_us",
"string": "Email address to which the notification will be send to."
},
{
"language_code": "es_es",
"string": "Dirección de correo electrónico a la que se enviará la notificación."
}
]
},
{
"function": "REPORT_FROM",
"type": "text",
"default_value": "Pi.Alert <user@gmail.com>",
"options": [],
"localized": [
"name",
"description"
],
"name": [
{
"language_code": "en_us",
"string": "Email subject"
},
{
"language_code": "es_es",
"string": "Asunto del email"
}
],
"description": [
{
"language_code": "en_us",
"string": "Notification email subject line. Some SMTP servers need this to be an email."
},
{
"language_code": "es_es",
"string": "Asunto del correo electrónico de notificación."
}
]
}
]
}

View File

@@ -0,0 +1,160 @@
#!/usr/bin/env python
import json
import subprocess
import argparse
import os
import pathlib
import sys
from datetime import datetime
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText
import smtplib
import socket
# 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
import conf
from plugin_helper import Plugin_Objects
from logger import mylog, append_line_to_file, print_log
from helper import timeNowTZ, noti_obj, get_setting_value, hide_email
from notification import Notification_obj
from database import DB
CUR_PATH = str(pathlib.Path(__file__).parent.resolve())
RESULT_FILE = os.path.join(CUR_PATH, 'last_result.log')
pluginName = 'SMTP'
def main():
mylog('verbose', [f'[{pluginName}](publisher) In script'])
# Check if basic config settings supplied
if check_config() == False:
mylog('none', [f'[{pluginName}] Error: Publisher notification gateway not set up correctly. Check your pialert.conf {pluginName}_* variables.'])
return
# Create a database connection
db = DB() # instance of class DB
db.open()
# Initialize the Plugin obj output file
plugin_objects = Plugin_Objects(RESULT_FILE)
# Create a Notification_obj instance
notifications = Notification_obj(db)
# Retrieve new notifications
new_notifications = notifications.getNew()
# Process the new notifications
for notification in new_notifications:
# Send notification
result = send(notification["HTML"], notification["Text"])
# Log result
plugin_objects.add_object(
primaryId = pluginName,
secondaryId = timeNowTZ(),
watched1 = notification["GUID"],
watched2 = result,
watched3 = 'null',
watched4 = 'null',
extra = 'null',
foreignKey = 'null'
)
plugin_objects.write_result_file()
#-------------------------------------------------------------------------------
def check_config ():
if get_setting_value('SMTP_SERVER') == '' or get_setting_value('SMTP_REPORT_FROM') == '' or get_setting_value('SMTP_REPORT_TO') == '':
mylog('none', ['[Email Check Config] Error: Email service not set up correctly. Check your pialert.conf SMTP_*, SMTP_REPORT_FROM and SMTP_REPORT_TO variables.'])
return False
else:
return True
#-------------------------------------------------------------------------------
def send(pHTML, pText):
mylog('debug', [f'[{pluginName}] SMTP_REPORT_TO: {hide_email(str(get_setting_value("SMTP_REPORT_TO")))} SMTP_USER: {hide_email(str(get_setting_value("SMTP_USER")))}'])
# Compose email
msg = MIMEMultipart('alternative')
msg['Subject'] = 'Pi.Alert Report'
msg['From'] = get_setting_value('SMTP_REPORT_FROM')
msg['To'] = get_setting_value('SMTP_REPORT_TO')
msg.attach (MIMEText (pText, 'plain'))
msg.attach (MIMEText (pHTML, 'html'))
failedAt = ''
failedAt = print_log ('SMTP try')
try:
# Send mail
failedAt = print_log('Trying to open connection to ' + str(get_setting_value('SMTP_SERVER')) + ':' + str(get_setting_value('SMTP_PORT')))
# Set a timeout for the SMTP connection (in seconds)
smtp_timeout = 30
if get_setting_value('SMTP_FORCE_SSL'):
failedAt = print_log('SMTP_FORCE_SSL == True so using .SMTP_SSL()')
if get_setting_value('SMTP_PORT') == 0:
failedAt = print_log('SMTP_PORT == 0 so sending .SMTP_SSL(SMTP_SERVER)')
smtp_connection = smtplib.SMTP_SSL(get_setting_value('SMTP_SERVER'))
else:
failedAt = print_log('SMTP_PORT == 0 so sending .SMTP_SSL(SMTP_SERVER, SMTP_PORT)')
smtp_connection = smtplib.SMTP_SSL(get_setting_value('SMTP_SERVER'), get_setting_value('SMTP_PORT'), timeout=smtp_timeout)
else:
failedAt = print_log('SMTP_FORCE_SSL == False so using .SMTP()')
if get_setting_value('SMTP_PORT') == 0:
failedAt = print_log('SMTP_PORT == 0 so sending .SMTP(SMTP_SERVER)')
smtp_connection = smtplib.SMTP (get_setting_value('SMTP_SERVER'))
else:
failedAt = print_log('SMTP_PORT == 0 so sending .SMTP(SMTP_SERVER, SMTP_PORT)')
smtp_connection = smtplib.SMTP (get_setting_value('SMTP_SERVER'), get_setting_value('SMTP_PORT'))
failedAt = print_log('Setting SMTP debug level')
# Log level set to debug of the communication between SMTP server and client
if get_setting_value('LOG_LEVEL') == 'debug':
smtp_connection.set_debuglevel(1)
failedAt = print_log( 'Sending .ehlo()')
smtp_connection.ehlo()
if not get_setting_value('SMTP_SKIP_TLS'):
failedAt = print_log('SMTP_SKIP_TLS == False so sending .starttls()')
smtp_connection.starttls()
failedAt = print_log('SMTP_SKIP_TLS == False so sending .ehlo()')
smtp_connection.ehlo()
if not get_setting_value('SMTP_SKIP_LOGIN'):
failedAt = print_log('SMTP_SKIP_LOGIN == False so sending .login()')
smtp_connection.login (get_setting_value('SMTP_USER'), get_setting_value('SMTP_PASS'))
failedAt = print_log('Sending .sendmail()')
smtp_connection.sendmail (get_setting_value('SMTP_REPORT_FROM'), get_setting_value('SMTP_REPORT_TO'), msg.as_string())
smtp_connection.quit()
except smtplib.SMTPAuthenticationError as e:
mylog('none', [' ERROR: Failed at - ', failedAt])
mylog('none', [' ERROR: Couldn\'t connect to the SMTP server (SMTPAuthenticationError), skipping Email (enable LOG_LEVEL=debug for more logging)'])
mylog('none', [' ERROR: ', str(e)])
except smtplib.SMTPServerDisconnected as e:
mylog('none', [' ERROR: Failed at - ', failedAt])
mylog('none', [' ERROR: Couldn\'t connect to the SMTP server (SMTPServerDisconnected), skipping Email (enable LOG_LEVEL=debug for more logging)'])
mylog('none', [' ERROR: ', str(e)])
except socket.gaierror as e:
mylog('none', [' ERROR: Failed at - ', failedAt])
mylog('none', [' ERROR: Could not resolve hostname (socket.gaierror), skipping Email (enable LOG_LEVEL=debug for more logging)'])
mylog('none', [' ERROR: ', str(e)])
mylog('debug', [f'[{pluginName}] Last executed - {str(failedAt)}'])
if __name__ == '__main__':
sys.exit(main())

View File

@@ -1 +0,0 @@
This plugin will not be loaded

View File

@@ -1,5 +1,4 @@
#!/usr/bin/env python
# Based on the work of https://github.com/leiweibau/Pi.Alert
import argparse
import os

View File

@@ -89,6 +89,11 @@ function processColumnValue(dbColumnDef, value, index, type) {
case 'label':
value = `<span>${value}<span>`;
break;
case 'textarea_readonly':
value = `<textarea cols="70" rows="3" wrap="off" readonly style="white-space: pre-wrap;">
${value.replace(/^b'(.*)'$/gm, '$1').replace(/\\n/g, '\n').replace(/\\r/g, '\r')}
</textarea>`;
break;
case 'textbox_save':
value = value == 'null' ? '' : value; // hide 'null' values

View File

@@ -112,7 +112,7 @@ while ($row = $result -> fetchArray (SQLITE3_ASSOC)) {
const settingGroups = [];
const settingKeyOfLists = [];
// core groups are the ones not generated by plugins
const settingCoreGroups = ['General', 'Email', 'Webhooks', 'Apprise', 'NTFY', 'PUSHSAFER', 'MQTT', 'DynDNS', 'API'];
const settingCoreGroups = ['General'];
// Loop through the settingsArray and collect unique settingGroups
@@ -749,8 +749,7 @@ while ($row = $result -> fetchArray (SQLITE3_ASSOC)) {
// -----------------------------------------------------------------------------
// ---------------------------------------------------------
// Show last time settings have been imported
// getParam("lastImportedTime", "Back_Settings_Imported", skipCache = true);
// Show last time settings have been imported
handleLoadingDialog()