WOL plugin #887
Some checks are pending
docker / docker_dev (push) Waiting to run

This commit is contained in:
jokob-sk
2024-12-23 13:10:27 +11:00
parent a4c34140bf
commit 8f8264c6fa
9 changed files with 698 additions and 4 deletions

View File

@@ -0,0 +1,37 @@
# Wake-on-LAN Plugin User Guide
## Overview
The Wake-on-LAN (WOL) plugin allows you to remotely wake devices on your network that support Wake-on-LAN functionality. This plugin sends a "magic packet" to the specified devices, which powers them on, provided they are configured to accept WOL requests.
## Configuration
All settings for the plugin can be configured via the user interface. The key settings include:
- **Broadcast IPs (`WOL_broadcast_ips`)**:
A list of IP addresses to use for broadcasting the WOL packet. Ensure these are valid network broadcast addresses for your environment.
- **Devices to Wake (`WOL_devices_to_wake`)**:
Defines the group of devices to be woken. You can choose from:
- `offline`: Wake devices that are currently offline.
- `down`: Wake devices that are in a "down" state.
- **Ports (`WOL_ports`)**:
A list of ports to use when sending the WOL packet. The default is usually port 9.
## Usage
1. Configure the settings through the UI.
2. The plugin will automatically detect devices based on the selected criteria (offline or down) and attempt to wake them by sending WOL magic packets.
3. The plugin logs the outcome of each attempt and processes results for monitoring and notifications.
## Logs
Logs for each run of the plugin are stored in the specified log path, where you can track:
- WOL packet sending attempts.
- Success or failure of waking devices.
## Notes
- Ensure the devices are configured to allow Wake-on-LAN in BIOS and the network adapter supports WOL when powered off.
- Make sure your network is configured to allow broadcast packets.
### Usage
- Head to **Settings** > **Plugin name** to adjust the default values.

View File

@@ -0,0 +1,518 @@
{
"code_name": "wake_on_lan",
"unique_prefix": "WOL",
"plugin_type": "other",
"execution_order" : "Layer_0",
"enabled": true,
"data_source": "script",
"data_filters": [
{
"compare_column": "Object_PrimaryID",
"compare_operator": "==",
"compare_field_id": "txtMacFilter",
"compare_js_template": "'{value}'.toString()",
"compare_use_quotes": true
}
],
"show_ui": true,
"localized": ["display_name", "description", "icon"],
"display_name": [
{
"language_code": "en_us",
"string": "Wake on Lan (WOL)"
}
],
"description": [
{
"language_code": "en_us",
"string": "Automatically wake your devices when they are down"
}
],
"icon": [
{
"language_code": "en_us",
"string": "<i class=\"fa fa-satellite-dish\"></i>"
}
],
"params": [],
"settings": [
{
"function": "RUN",
"events": ["run"],
"type": {
"dataType": "string",
"elements": [
{ "elementType": "select", "elementOptions": [], "transformers": [] }
]
},
"default_value": "disabled",
"options": [
"disabled",
"once",
"schedule",
"always_after_scan",
"on_new_device",
"on_notification"
],
"localized": ["name", "description"],
"name": [
{
"language_code": "en_us",
"string": "When to run"
}
],
"description": [
{
"language_code": "en_us",
"string": "When the plugin should run. Good options are <code>always_after_scan</code>, <code>on_new_device</code>, <code>on_notification</code>"
}
]
},
{
"function": "RUN_SCHD",
"type": {
"dataType": "string",
"elements": [
{ "elementType": "input", "elementOptions": [], "transformers": [] }
]
},
"default_value": "*/5 * * * *",
"options": [],
"localized": ["name", "description"],
"name": [
{
"language_code": "en_us",
"string": "Schedule"
}
],
"description": [
{
"language_code": "en_us",
"string": "Only enabled if you select <code>schedule</code> in the <a href=\"#WOL_RUN\"><code>WOL_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."
}
]
},
{
"function": "devices_to_wake",
"events": ["run"],
"type": {
"dataType": "string",
"elements": [
{ "elementType": "select", "elementOptions": [], "transformers": [] }
]
},
"default_value": "down",
"options": [
"offline",
"down"
],
"localized": ["name", "description"],
"name": [
{
"language_code": "en_us",
"string": "Devices to wake"
}
],
"description": [
{
"language_code": "en_us",
"string": "Select devices in what state should be woken up."
}
]
},
{
"function": "broadcast_ips",
"type": {
"dataType": "array",
"elements": [
{
"elementType": "input",
"elementOptions": [
{ "placeholder": "Enter value" },
{ "suffix": "_in" },
{ "cssClasses": "col-sm-10" },
{ "prefillValue": "null" }
],
"transformers": []
},
{
"elementType": "button",
"elementOptions": [
{ "sourceSuffixes": ["_in"] },
{ "separator": "" },
{ "cssClasses": "col-xs-12" },
{ "onClick": "addList(this, false)" },
{ "getStringKey": "Gen_Add" }
],
"transformers": []
},
{
"elementType": "select",
"elementHasInputValue": 1,
"elementOptions": [
{ "multiple": "true" },
{ "readonly": "true" },
{ "editable": "true" }
],
"transformers": []
},
{
"elementType": "button",
"elementOptions": [
{ "sourceSuffixes": [] },
{ "separator": "" },
{ "cssClasses": "col-xs-6" },
{ "onClick": "removeAllOptions(this)" },
{ "getStringKey": "Gen_Remove_All" }
],
"transformers": []
},
{
"elementType": "button",
"elementOptions": [
{ "sourceSuffixes": [] },
{ "separator": "" },
{ "cssClasses": "col-xs-6" },
{ "onClick": "removeFromList(this)" },
{ "getStringKey": "Gen_Remove_Last" }
],
"transformers": []
}
]
},
"default_value": ["255.255.255.255"],
"options": [],
"localized": ["name", "description"],
"name": [
{
"language_code": "en_us",
"string": "Broadcast IPs"
}
],
"description": [
{
"language_code": "en_us",
"string": "Which IPs are used to broadcast the wake on lan message."
}
]
},
{
"function": "ports",
"type": {
"dataType": "array",
"elements": [
{
"elementType": "input",
"elementOptions": [
{ "placeholder": "Enter value" },
{ "suffix": "_in" },
{ "cssClasses": "col-sm-10" },
{ "prefillValue": "null" }
],
"transformers": []
},
{
"elementType": "button",
"elementOptions": [
{ "sourceSuffixes": ["_in"] },
{ "separator": "" },
{ "cssClasses": "col-xs-12" },
{ "onClick": "addList(this, false)" },
{ "getStringKey": "Gen_Add" }
],
"transformers": []
},
{
"elementType": "select",
"elementHasInputValue": 1,
"elementOptions": [
{ "multiple": "true" },
{ "readonly": "true" },
{ "editable": "true" }
],
"transformers": []
},
{
"elementType": "button",
"elementOptions": [
{ "sourceSuffixes": [] },
{ "separator": "" },
{ "cssClasses": "col-xs-6" },
{ "onClick": "removeAllOptions(this)" },
{ "getStringKey": "Gen_Remove_All" }
],
"transformers": []
},
{
"elementType": "button",
"elementOptions": [
{ "sourceSuffixes": [] },
{ "separator": "" },
{ "cssClasses": "col-xs-6" },
{ "onClick": "removeFromList(this)" },
{ "getStringKey": "Gen_Remove_Last" }
],
"transformers": []
}
]
},
"default_value": ["9"],
"options": [],
"localized": ["name", "description"],
"name": [
{
"language_code": "en_us",
"string": "Ports"
}
],
"description": [
{
"language_code": "en_us",
"string": "Which Ports are used to broadcast the wake on lan message."
}
]
},
{
"function": "CMD",
"type": {
"dataType": "string",
"elements": [
{
"elementType": "input",
"elementOptions": [{ "readonly": "true" }],
"transformers": []
}
]
},
"default_value": "python3 /app/front/plugins/wake_on_lan/wake_on_lan.py",
"options": [],
"localized": ["name", "description"],
"name": [
{
"language_code": "en_us",
"string": "Command"
}
],
"description": [
{
"language_code": "en_us",
"string": "Command to run. This can not be changed"
}
]
},
{
"function": "RUN_TIMEOUT",
"type": {
"dataType": "integer",
"elements": [
{
"elementType": "input",
"elementOptions": [{ "type": "number" }],
"transformers": []
}
]
},
"default_value": 30,
"options": [],
"localized": ["name", "description"],
"name": [
{
"language_code": "en_us",
"string": "Run timeout"
}
],
"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."
}
]
}
],
"database_column_definitions": [
{
"column": "Index",
"css_classes": "col-sm-2",
"show": true,
"type": "none",
"default_value": "",
"options": [],
"localized": ["name"],
"name": [
{
"language_code": "en_us",
"string": "Index"
}
]
},
{
"column": "Object_PrimaryID",
"css_classes": "col-sm-2",
"show": true,
"type": "device_name_mac",
"default_value": "",
"options": [],
"localized": ["name"],
"name": [
{
"language_code": "en_us",
"string": "MAC"
}
]
},
{
"column": "Object_SecondaryID",
"css_classes": "col-sm-2",
"show": true,
"type": "device_ip",
"default_value": "",
"options": [],
"localized": ["name"],
"name": [
{
"language_code": "en_us",
"string": "IP"
}
]
},
{
"column": "Watched_Value1",
"css_classes": "col-sm-2",
"show": true,
"type": "label",
"default_value": "",
"options": [],
"localized": ["name"],
"name": [
{
"language_code": "en_us",
"string": "Name"
}
]
},
{
"column": "Watched_Value2",
"css_classes": "col-sm-2",
"show": true,
"type": "label",
"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": "Device Type"
}
]
},
{
"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"
}
]
},
{
"column": "Dummy",
"css_classes": "col-sm-2",
"show": false,
"type": "label",
"default_value": "",
"options": [],
"localized": ["name"],
"name": [
{
"language_code": "en_us",
"string": "Scan method"
}
]
},
{
"column": "DateTimeCreated",
"css_classes": "col-sm-2",
"show": true,
"type": "label",
"default_value": "",
"options": [],
"localized": ["name"],
"name": [
{
"language_code": "en_us",
"string": "Created"
}
]
},
{
"column": "DateTimeChanged",
"css_classes": "col-sm-2",
"show": true,
"type": "label",
"default_value": "",
"options": [],
"localized": ["name"],
"name": [
{
"language_code": "en_us",
"string": "Changed"
}
]
},
{
"column": "Status",
"css_classes": "col-sm-1",
"show": true,
"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"
}
]
}
]
}

View File

@@ -0,0 +1,122 @@
#!/usr/bin/env python
import os
import pathlib
import sys
import json
import sqlite3
from pytz import timezone
from wakeonlan import send_magic_packet
# Define the installation path and extend the system path for plugin imports
INSTALL_PATH = "/app"
sys.path.extend([f"{INSTALL_PATH}/front/plugins", f"{INSTALL_PATH}/server"])
from plugin_helper import Plugin_Object, Plugin_Objects, decodeBase64
from plugin_utils import get_plugins_configs
from logger import mylog, Logger
from const import pluginsPath, fullDbPath, logPath
from helper import timeNowTZ, get_setting_value
from notification import write_notification
from database import DB
from device import Device_obj
import conf
# Make sure the TIMEZONE for logging is correct
conf.tz = timezone(get_setting_value('TIMEZONE'))
# Make sure log level is initialized correctly
Logger(get_setting_value('LOG_LEVEL'))
pluginName = 'WOL'
# Define the current path and log file paths
LOG_PATH = logPath + '/plugins'
LOG_FILE = os.path.join(LOG_PATH, f'script.{pluginName}.log')
RESULT_FILE = os.path.join(LOG_PATH, f'last_result.{pluginName}.log')
# Initialize the Plugin obj output file
plugin_objects = Plugin_Objects(RESULT_FILE)
def main():
mylog('none', [f'[{pluginName}] In script'])
# Retrieve configuration settings
broadcast_ips = get_setting_value('WOL_broadcast_ips')
devices_to_wake = get_setting_value('WOL_devices_to_wake')
ports = get_setting_value('WOL_ports')
mylog('verbose', [f'[{pluginName}] broadcast_ips value {broadcast_ips}'])
# Create a database connection
db = DB() # instance of class DB
db.open()
# Create a Device_obj instance
device_handler = Device_obj(db)
# Retrieve devices
if 'offline' in devices_to_wake:
devices_to_wake = device_handler.getOffline()
elif 'down' in devices_to_wake:
devices_to_wake = device_handler.getDown()
else:
mylog('verbose', [f'[{pluginName}] Invalid setting WOL_devices_to_wake {devices_to_wake}'])
return
# execute script if something to do
if len(devices_to_wake) > 0:
for device in devices_to_wake:
for ip in broadcast_ips:
for port in ports:
result = execute(port, ip, device['devMac'], device['devName'])
# Process the data into native application tables
if len(result) > 0:
plugin_objects.add_object(
primaryId = device['devMac'],
secondaryId = device['devLastIP'],
watched1 = device['devName'],
watched2 = result,
watched3 = '',
watched4 = '',
extra = '',
foreignKey = device['devMac']
)
# log result
plugin_objects.write_result_file()
else:
mylog('none', [f'[{pluginName}] No devices to wake'])
mylog('none', [f'[{pluginName}] Script finished'])
return 0
# wake
def execute(port, ip, mac, name):
result = 'null'
try:
# Send the magic packet to wake up the device
send_magic_packet(mac, ip_address=ip, port=int(port))
mylog('verbose', [f'[{pluginName}] Magic packet sent to {mac} ({name})'])
result = 'success'
except Exception as e:
result = str(e)
mylog('verbose', [f'[{pluginName}] Failed to send magic packet to {mac} ({name}): {e}'])
# Return the data result
return result
if __name__ == '__main__':
main()