diff --git a/Dockerfile b/Dockerfile index 2f372649..13a330b4 100755 --- a/Dockerfile +++ b/Dockerfile @@ -14,7 +14,7 @@ ENV PATH="/opt/venv/bin:$PATH" COPY . ${INSTALL_DIR}/ -RUN pip install graphene flask netifaces tplink-omada-client pycryptodome requests paho-mqtt scapy cron-converter pytz json2table dhcp-leases pyunifi speedtest-cli chardet python-nmap dnspython librouteros git+https://github.com/foreign-sub/aiofreepybox.git \ +RUN pip install graphene flask netifaces tplink-omada-client wakeonlan pycryptodome requests paho-mqtt scapy cron-converter pytz json2table dhcp-leases pyunifi speedtest-cli chardet python-nmap dnspython librouteros git+https://github.com/foreign-sub/aiofreepybox.git \ && bash -c "find ${INSTALL_DIR} -type d -exec chmod 750 {} \;" \ && bash -c "find ${INSTALL_DIR} -type f -exec chmod 640 {} \;" \ && bash -c "find ${INSTALL_DIR} -type f \( -name '*.sh' -o -name '*.py' -o -name 'speedtest-cli' \) -exec chmod 750 {} \;" diff --git a/Dockerfile.debian b/Dockerfile.debian index 42f6e910..789fa351 100755 --- a/Dockerfile.debian +++ b/Dockerfile.debian @@ -43,7 +43,7 @@ RUN phpenmod -v 8.2 sqlite3 RUN apt-get install -y python3-venv RUN python3 -m venv myenv -RUN /bin/bash -c "source myenv/bin/activate && update-alternatives --install /usr/bin/python python /usr/bin/python3 10 && pip3 install tplink-omada-client pycryptodome requests paho-mqtt scapy cron-converter pytz json2table dhcp-leases pyunifi speedtest-cli chardet python-nmap dnspython librouteros " +RUN /bin/bash -c "source myenv/bin/activate && update-alternatives --install /usr/bin/python python /usr/bin/python3 10 && pip3 install tplink-omada-client wakeonlan pycryptodome requests paho-mqtt scapy cron-converter pytz json2table dhcp-leases pyunifi speedtest-cli chardet python-nmap dnspython librouteros " # Create a buildtimestamp.txt to later check if a new version was released RUN date +%s > ${INSTALL_DIR}/front/buildtimestamp.txt diff --git a/front/plugins/__template/rename_me.py b/front/plugins/__template/rename_me.py index 5c844768..279a5fab 100755 --- a/front/plugins/__template/rename_me.py +++ b/front/plugins/__template/rename_me.py @@ -116,7 +116,7 @@ def get_device_data(some_setting): } ] - # Return the data to be detected by teh main application + # Return the data to be detected by the main application return device_data if __name__ == '__main__': diff --git a/front/plugins/wake_on_lan/README.md b/front/plugins/wake_on_lan/README.md new file mode 100755 index 00000000..11b845f5 --- /dev/null +++ b/front/plugins/wake_on_lan/README.md @@ -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. + diff --git a/front/plugins/wake_on_lan/config.json b/front/plugins/wake_on_lan/config.json new file mode 100755 index 00000000..43c16a1f --- /dev/null +++ b/front/plugins/wake_on_lan/config.json @@ -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": "" + } + ], + "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 always_after_scan, on_new_device, on_notification" + } + ] + }, + { + "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 schedule in the WOL_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." + } + ] + }, + { + "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": "
" + }, + { + "equals": "watched-changed", + "replacement": "
" + }, + { + "equals": "new", + "replacement": "
" + }, + { + "equals": "missing-in-last-scan", + "replacement": "
" + } + ], + "localized": ["name"], + "name": [ + { + "language_code": "en_us", + "string": "Status" + } + ] + } + ] +} diff --git a/front/plugins/wake_on_lan/wake_on_lan.py b/front/plugins/wake_on_lan/wake_on_lan.py new file mode 100755 index 00000000..03e89654 --- /dev/null +++ b/front/plugins/wake_on_lan/wake_on_lan.py @@ -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() diff --git a/install/install_dependencies.debian.sh b/install/install_dependencies.debian.sh index bfe895f2..d91cbdcd 100755 --- a/install/install_dependencies.debian.sh +++ b/install/install_dependencies.debian.sh @@ -30,5 +30,5 @@ source myenv/bin/activate update-alternatives --install /usr/bin/python python /usr/bin/python3 10 # install packages thru pip3 -pip3 install graphene flask netifaces tplink-omada-client pycryptodome requests paho-mqtt scapy cron-converter pytz json2table dhcp-leases pyunifi speedtest-cli chardet python-nmap dnspython librouteros git+https://github.com/foreign-sub/aiofreepybox.git +pip3 install graphene flask netifaces tplink-omada-client wakeonlan pycryptodome requests paho-mqtt scapy cron-converter pytz json2table dhcp-leases pyunifi speedtest-cli chardet python-nmap dnspython librouteros git+https://github.com/foreign-sub/aiofreepybox.git diff --git a/scripts/db_cleanup/README.md b/scripts/db_cleanup/README.md old mode 100644 new mode 100755 diff --git a/server/device.py b/server/device.py index f309f7a5..80c75031 100755 --- a/server/device.py +++ b/server/device.py @@ -40,6 +40,23 @@ class Device_obj: return result[column_name] if result else None + # Get all down + def getDown(self): + self.db.sql.execute(""" + SELECT * FROM Devices WHERE devAlertDown = 1 and devPresentLastScan = 0 + """) + return self.db.sql.fetchall() + + # Get all down + def getOffline(self): + self.db.sql.execute(""" + SELECT * FROM Devices WHERE devPresentLastScan = 0 + """) + return self.db.sql.fetchall() + + + + #------------------------------------------------------------------------------- # Removing devices from the CurrentScan DB table which the user chose to ignore by MAC or IP