From fbce3e18c250fd859703819fe101cb2d3a96f26f Mon Sep 17 00:00:00 2001 From: Pawel Derehajlo Date: Thu, 16 Jan 2025 03:37:37 +0100 Subject: [PATCH 1/2] Added AsusWRT device import plugin --- Dockerfile | 2 +- Dockerfile.debian | 2 +- docker-compose.yml | 2 +- front/plugins/README.md | 1 + front/plugins/asuswrt_import/README.md | 14 + front/plugins/asuswrt_import/config.json | 528 +++++++++++++++++++++++ front/plugins/asuswrt_import/script.py | 108 +++++ install/install_dependencies.debian.sh | 2 +- 8 files changed, 655 insertions(+), 4 deletions(-) create mode 100755 front/plugins/asuswrt_import/README.md create mode 100755 front/plugins/asuswrt_import/config.json create mode 100755 front/plugins/asuswrt_import/script.py diff --git a/Dockerfile b/Dockerfile index b3377b38..76a9fa13 100755 --- a/Dockerfile +++ b/Dockerfile @@ -14,7 +14,7 @@ ENV PATH="/opt/venv/bin:$PATH" COPY . ${INSTALL_DIR}/ -RUN pip install openwrt-luci-rpc 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 \ +RUN pip install openwrt-luci-rpc asusrouter asyncio aiohttp 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 193fe796..736b61f5 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 openwrt-luci-rpc 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 " +RUN /bin/bash -c "source myenv/bin/activate && update-alternatives --install /usr/bin/python python /usr/bin/python3 10 && pip3 install openwrt-luci-rpc asusrouter asyncio aiohttp 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 " # Create a buildtimestamp.txt to later check if a new version was released RUN date +%s > ${INSTALL_DIR}/front/buildtimestamp.txt diff --git a/docker-compose.yml b/docker-compose.yml index ca180001..a3df2c66 100755 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -72,5 +72,5 @@ services: - TZ=${TZ} - PORT=${PORT} # โ— DANGER ZONE BELOW - Setting ALWAYS_FRESH_INSTALL=true will delete the content of the /db & /config folders - - ALWAYS_FRESH_INSTALL=${ALWAYS_FRESH_INSTALL} + - ALWAYS_FRESH_INSTALL=true diff --git a/front/plugins/README.md b/front/plugins/README.md index 693b0255..f917d905 100755 --- a/front/plugins/README.md +++ b/front/plugins/README.md @@ -40,6 +40,7 @@ Device-detecting plugins insert values into the `CurrentScan` database table. T | `INTRSPD` | โ™ป | Internet speed test | | | Script | [internet_speedtest](/front/plugins/internet_speedtest/) | | `IPNEIGH` | ๐Ÿ” | Scan ARP (IPv4) and NDP (IPv6) tables | | | Script | [ipneigh](/front/plugins/ipneigh/) | | `LUCIRPC` | ๐Ÿ” | Import connected devices from OpenWRT | | | Script | [luci_import](/front/plugins/luci_import/) | +| `ASUSWRT` | ๐Ÿ” | Import connected devices from AsusWRT | | | Script | [asuswrt_import](/front/plugins/asuswrt_import/) | | `MAINT` | โš™ | Maintenance of logs, etc. | | | Script | [maintenance](/front/plugins/maintenance/) | | `MQTT` | โ–ถ๏ธ | MQTT for synching to Home Assistant | | | Script | [_publisher_mqtt](/front/plugins/_publisher_mqtt/) | | `NBTSCAN` | ๐Ÿ†Ž | Nbtscan (NetBIOS-based) name resolution | | | Script | [nbtscan_scan](/front/plugins/nbtscan_scan/) | diff --git a/front/plugins/asuswrt_import/README.md b/front/plugins/asuswrt_import/README.md new file mode 100755 index 00000000..7bcb1b05 --- /dev/null +++ b/front/plugins/asuswrt_import/README.md @@ -0,0 +1,14 @@ +## Overview + +Plugin that imports device IP, MAC, Name, Vendor and Online status from AsusWRT and AsusWRT-Merlin based routers. + +This Plugin is using awesome [asusrouter](https://github.com/Vaskivskyi/asusrouter) library. Please check if your router is supported by it [here](https://github.com/Vaskivskyi/asusrouter?tab=readme-ov-file#supported-devices). + +### Usage + +- Enable the `ASUSWRT` plugin +- Head to **Settings** > **AsusWRT device import** to adjust the default values. + +### Notes + +- In case an existing imported device is renamed in Asus Router it will not be renamed in NetAlertX. In this case it has to be done manually or the device should be removed and it will appear on the next scan. diff --git a/front/plugins/asuswrt_import/config.json b/front/plugins/asuswrt_import/config.json new file mode 100755 index 00000000..f25d08df --- /dev/null +++ b/front/plugins/asuswrt_import/config.json @@ -0,0 +1,528 @@ +{ + "code_name": "asuswrt_import", + "show_ui": true, + "unique_prefix": "ASUSWRT", + "plugin_type": "device_scanner", + "execution_order": "Layer_1", + "enabled": true, + "data_source": "script", + "mapped_to_table": "CurrentScan", + "data_filters": [ + { + "compare_column": "Object_PrimaryID", + "compare_operator": "==", + "compare_field_id": "txtMacFilter", + "compare_js_template": "'{value}'.toString()", + "compare_use_quotes": true + } + ], + "localized": [ + "display_name", + "description", + "icon" + ], + "display_name": [ + { + "language_code": "en_us", + "string": "AsusWRT device import" + } + ], + "description": [ + { + "language_code": "en_us", + "string": "The plugin is used to import connected devices from AsusWRT" + } + ], + "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" + ], + "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 schedule" + } + ] + }, + { + "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 SYNC_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": "CMD", + "type": { + "dataType": "string", + "elements": [ + { + "elementType": "input", + "elementOptions": [ + { + "readonly": "true" + } + ], + "transformers": [] + } + ] + }, + "default_value": "python3 /app/front/plugins/asuswrt_import/script.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." + } + ] + }, + { + "function": "host", + "type": { + "dataType": "string", + "elements": [ + { + "elementType": "input", + "elementOptions": [], + "transformers": [] + } + ] + }, + "default_value": "192.168.1.1", + "options": [], + "localized": [ + "name", + "description" + ], + "name": [ + { + "language_code": "en_us", + "string": "Router ip" + } + ], + "description": [ + { + "language_code": "en_us", + "string": "Router ip(do not include http:// or https://)." + } + ] + }, + { + "function": "user", + "type": { + "dataType": "string", + "elements": [ + { + "elementType": "input", + "elementOptions": [], + "transformers": [] + } + ] + }, + "default_value": "admin", + "options": [], + "localized": [ + "name", + "description" + ], + "name": [ + { + "language_code": "en_us", + "string": "Router user" + } + ], + "description": [ + { + "language_code": "en_us", + "string": "User name used to login into your router." + } + ] + }, + { + "function": "password", + "type": { + "dataType": "string", + "elements": [ + { + "elementType": "input", + "elementOptions": [ + { + "type": "password" + } + ], + "transformers": [] + } + ] + }, + "default_value": "", + "options": [], + "localized": [ + "name", + "description" + ], + "name": [ + { + "language_code": "en_us", + "string": "Router password" + } + ], + "description": [ + { + "language_code": "en_us", + "string": "Password used to login into your router." + } + ] + }, + { + "function": "ssl", + "type": { + "dataType": "boolean", + "elements": [ + { + "elementType": "input", + "elementOptions": [ + { + "type": "checkbox" + } + ], + "transformers": [] + } + ] + }, + "default_value": false, + "options": [], + "localized": [ + "name", + "description" + ], + "name": [ + { + "language_code": "en_us", + "string": "Router SSL" + } + ], + "description": [ + { + "language_code": "en_us", + "string": "If your router enforces SSL connections." + } + ] + } + ], + "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", + "default_value": "", + "localized": [ + "name" + ], + "mapped_to_column": "cur_MAC", + "name": [ + { + "language_code": "en_us", + "string": "MAC address" + }, + { + "language_code": "es_es", + "string": "Direcciรณn MAC" + }, + { + "language_code": "de_de", + "string": "MAC Adresse" + } + ], + "options": [], + "show": true, + "type": "device_mac" + }, + { + "column": "Object_SecondaryID", + "css_classes": "col-sm-2", + "default_value": "", + "localized": [ + "name" + ], + "mapped_to_column": "cur_IP", + "name": [ + { + "language_code": "en_us", + "string": "IP" + }, + { + "language_code": "es_es", + "string": "IP" + }, + { + "language_code": "de_de", + "string": "IP" + } + ], + "options": [], + "show": true, + "type": "device_ip" + }, + { + "column": "Watched_Value1", + "css_classes": "col-sm-2", + "default_value": "", + "localized": [ + "name" + ], + "mapped_to_column": "cur_Name", + "name": [ + { + "language_code": "en_us", + "string": "Hostname" + }, + { + "language_code": "es_es", + "string": "Nombre de host" + }, + { + "language_code": "de_de", + "string": "Name des Hosts" + } + ], + "options": [], + "show": true, + "type": "label" + }, + { + "column": "Watched_Value2", + "mapped_to_column": "cur_Vendor", + "css_classes": "col-sm-2", + "default_value": "", + "localized": [ + "name" + ], + "name": [ + { + "language_code": "en_us", + "string": "Vendor" + }, + { + "language_code": "es_es", + "string": "Proveedor" + }, + { + "language_code": "de_de", + "string": "Hersteller" + } + ], + "options": [], + "show": true, + "type": "label" + }, + { + "column": "Dummy", + "mapped_to_column": "cur_ScanMethod", + "mapped_to_column_data": { + "value": "ASUSWRT" + }, + "css_classes": "col-sm-2", + "show": true, + "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" + } + ] + } + ] +} \ No newline at end of file diff --git a/front/plugins/asuswrt_import/script.py b/front/plugins/asuswrt_import/script.py new file mode 100755 index 00000000..c0b7f3a3 --- /dev/null +++ b/front/plugins/asuswrt_import/script.py @@ -0,0 +1,108 @@ +#!/usr/bin/env python +import os +import sys + +INSTALL_PATH = "/app" +sys.path.extend([f"{INSTALL_PATH}/front/plugins", f"{INSTALL_PATH}/server"]) +pluginName = "ASUSWRT" + +import asyncio + +import aiohttp +import conf +from asusrouter import AsusData, AsusRouter +from asusrouter.modules.connection import ConnectionState +from const import logPath +from helper import get_setting_value +from logger import Logger, mylog +from plugin_helper import (Plugin_Object, Plugin_Objects, decodeBase64, + handleEmpty) +from pytz import timezone + +conf.tz = timezone(get_setting_value("TIMEZONE")) + +Logger(get_setting_value("LOG_LEVEL")) + +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") + +plugin_objects = Plugin_Objects(RESULT_FILE) + + +def main(): + mylog("verbose", [f"[{pluginName}] started."]) + + device_data = get_device_data() + + mylog( + "verbose", + [f"[{pluginName}] Found '{len(device_data)}' devices"], + ) + + filtered_devices = [ + (key, device) + for key, device in device_data.items() + if device.state == ConnectionState.CONNECTED + ] + + mylog( + "verbose", + [f"[{pluginName}] Processing '{len(filtered_devices)}' connected devices"], + ) + + for mac, device in filtered_devices: + entry_mac = str(device.description.mac).lower() + + plugin_objects.add_object( + primaryId=entry_mac, + secondaryId=handleEmpty(device.connection.ip_address), + watched1=handleEmpty(device.description.name), + watched2=handleEmpty(device.description.vendor), + extra=pluginName, + foreignKey=entry_mac, + ) + + plugin_objects.write_result_file() + + mylog("verbose", [f"[{pluginName}] finished"]) + + return 0 + + +def get_device_data(): + # Create a new event loop + loop = asyncio.new_event_loop() + + # Create aiohttp session + session = aiohttp.ClientSession(loop=loop) + + router = AsusRouter( # required - both IP and URL supported + hostname=get_setting_value("ASUSWRT_host"), # required + username=get_setting_value("ASUSWRT_user"), # required + password=get_setting_value("ASUSWRT_password"), # required + use_ssl=get_setting_value("ASUSWRT_ssl"), # optional + session=session, # optional + ) + + # Connect to the router + loop.run_until_complete(router.async_connect()) + + if router.connected: + mylog("verbose", [f"[{pluginName}] logged in successfully."]) + else: + mylog("error", [f"[{pluginName}] failed to login."]) + return [] + + # Now you can use the router object to call methods + clients = loop.run_until_complete(router.async_get_data(AsusData.CLIENTS)) + + # Remember to disconnect and close the session when you're done + loop.run_until_complete(router.async_disconnect()) + loop.run_until_complete(session.close()) + + return clients + + +if __name__ == "__main__": + main() diff --git a/install/install_dependencies.debian.sh b/install/install_dependencies.debian.sh index b0ebe207..3be19064 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 openwrt-luci-rpc 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 +pip3 install openwrt-luci-rpc asusrouter asyncio aiohttp 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 From adb99e5f1a2f0c4dce72b37c7fec0163df1b9255 Mon Sep 17 00:00:00 2001 From: labmonkey Date: Thu, 16 Jan 2025 03:41:26 +0100 Subject: [PATCH 2/2] Revert leftover code --- docker-compose.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docker-compose.yml b/docker-compose.yml index a3df2c66..ca180001 100755 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -72,5 +72,5 @@ services: - TZ=${TZ} - PORT=${PORT} # โ— DANGER ZONE BELOW - Setting ALWAYS_FRESH_INSTALL=true will delete the content of the /db & /config folders - - ALWAYS_FRESH_INSTALL=true + - ALWAYS_FRESH_INSTALL=${ALWAYS_FRESH_INSTALL}