mirror of
https://github.com/jokob-sk/NetAlertX.git
synced 2026-04-11 12:41:36 -07:00
@@ -15,7 +15,7 @@ RUN apk add --no-cache bash shadow python3 python3-dev gcc musl-dev libffi-dev o
|
|||||||
ENV PATH="/opt/venv/bin:$PATH"
|
ENV PATH="/opt/venv/bin:$PATH"
|
||||||
|
|
||||||
|
|
||||||
RUN pip install openwrt-luci-rpc asusrouter asyncio aiohttp graphene flask flask-cors unifi-sm-api tplink-omada-client wakeonlan pycryptodome requests paho-mqtt scapy cron-converter pytz json2table dhcp-leases pyunifi speedtest-cli chardet python-nmap dnspython librouteros yattag git+https://github.com/foreign-sub/aiofreepybox.git
|
RUN pip install openwrt-luci-rpc asusrouter asyncio aiohttp graphene flask flask-cors unifi-sm-api tplink-omada-client wakeonlan pycryptodome requests paho-mqtt scapy cron-converter pytz json2table dhcp-leases pyunifi speedtest-cli chardet python-nmap dnspython librouteros yattag zeroconf git+https://github.com/foreign-sub/aiofreepybox.git
|
||||||
|
|
||||||
# Append Iliadbox certificate to aiofreepybox
|
# Append Iliadbox certificate to aiofreepybox
|
||||||
|
|
||||||
@@ -40,7 +40,7 @@ ENV S6_CMD_WAIT_FOR_SERVICES_MAXTIME=0
|
|||||||
|
|
||||||
RUN apk update --no-cache \
|
RUN apk update --no-cache \
|
||||||
&& apk add --no-cache bash libbsd zip lsblk gettext-envsubst sudo mtr tzdata s6-overlay \
|
&& apk add --no-cache bash libbsd zip lsblk gettext-envsubst sudo mtr tzdata s6-overlay \
|
||||||
&& apk add --no-cache curl arp-scan iproute2 iproute2-ss nmap nmap-scripts traceroute nbtscan avahi avahi-tools openrc dbus net-tools net-snmp-tools bind-tools awake ca-certificates \
|
&& apk add --no-cache curl arp-scan iproute2 iproute2-ss nmap nmap-scripts traceroute nbtscan net-tools net-snmp-tools bind-tools awake ca-certificates \
|
||||||
&& apk add --no-cache sqlite php83 php83-fpm php83-cgi php83-curl php83-sqlite3 php83-session \
|
&& apk add --no-cache sqlite php83 php83-fpm php83-cgi php83-curl php83-sqlite3 php83-session \
|
||||||
&& apk add --no-cache python3 nginx \
|
&& apk add --no-cache python3 nginx \
|
||||||
&& ln -s /usr/bin/awake /usr/bin/wakeonlan \
|
&& ln -s /usr/bin/awake /usr/bin/wakeonlan \
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ ENV PATH="/opt/venv/bin:$PATH"
|
|||||||
|
|
||||||
COPY . ${INSTALL_DIR}/
|
COPY . ${INSTALL_DIR}/
|
||||||
|
|
||||||
RUN pip install openwrt-luci-rpc asusrouter asyncio aiohttp graphene flask flask-cors unifi-sm-api tplink-omada-client wakeonlan pycryptodome requests paho-mqtt scapy cron-converter pytz json2table dhcp-leases pyunifi speedtest-cli chardet python-nmap dnspython librouteros yattag git+https://github.com/foreign-sub/aiofreepybox.git \
|
RUN pip install openwrt-luci-rpc asusrouter asyncio aiohttp graphene flask flask-cors unifi-sm-api tplink-omada-client wakeonlan pycryptodome requests paho-mqtt scapy cron-converter pytz json2table dhcp-leases pyunifi speedtest-cli chardet python-nmap dnspython librouteros yattag zeroconf git+https://github.com/foreign-sub/aiofreepybox.git \
|
||||||
&& bash -c "find ${INSTALL_DIR} -type d -exec chmod 750 {} \;" \
|
&& 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 -exec chmod 640 {} \;" \
|
||||||
&& bash -c "find ${INSTALL_DIR} -type f \( -name '*.sh' -o -name '*.py' -o -name 'speedtest-cli' \) -exec chmod 750 {} \;"
|
&& bash -c "find ${INSTALL_DIR} -type f \( -name '*.sh' -o -name '*.py' -o -name 'speedtest-cli' \) -exec chmod 750 {} \;"
|
||||||
@@ -42,7 +42,7 @@ ENV S6_CMD_WAIT_FOR_SERVICES_MAXTIME=0
|
|||||||
|
|
||||||
RUN apk update --no-cache \
|
RUN apk update --no-cache \
|
||||||
&& apk add --no-cache bash libbsd zip lsblk gettext-envsubst sudo mtr tzdata s6-overlay \
|
&& apk add --no-cache bash libbsd zip lsblk gettext-envsubst sudo mtr tzdata s6-overlay \
|
||||||
&& apk add --no-cache curl arp-scan iproute2 iproute2-ss nmap nmap-scripts traceroute nbtscan avahi avahi-tools openrc dbus net-tools net-snmp-tools bind-tools awake ca-certificates \
|
&& apk add --no-cache curl arp-scan iproute2 iproute2-ss nmap nmap-scripts traceroute nbtscan net-tools net-snmp-tools bind-tools awake ca-certificates \
|
||||||
&& apk add --no-cache sqlite php83 php83-fpm php83-cgi php83-curl php83-sqlite3 php83-session \
|
&& apk add --no-cache sqlite php83 php83-fpm php83-cgi php83-curl php83-sqlite3 php83-session \
|
||||||
&& apk add --no-cache python3 nginx \
|
&& apk add --no-cache python3 nginx \
|
||||||
&& ln -s /usr/bin/awake /usr/bin/wakeonlan \
|
&& ln -s /usr/bin/awake /usr/bin/wakeonlan \
|
||||||
|
|||||||
@@ -33,7 +33,7 @@ COPY --chmod=775 --chown=${USER_ID}:${USER_GID} . ${INSTALL_DIR}/
|
|||||||
RUN apt-get install -y \
|
RUN apt-get install -y \
|
||||||
tini snmp ca-certificates curl libwww-perl arp-scan perl apt-utils cron sudo \
|
tini snmp ca-certificates curl libwww-perl arp-scan perl apt-utils cron sudo \
|
||||||
nginx-light php php-cgi php-fpm php-sqlite3 php-curl sqlite3 dnsutils net-tools php-openssl \
|
nginx-light php php-cgi php-fpm php-sqlite3 php-curl sqlite3 dnsutils net-tools php-openssl \
|
||||||
python3 python3-dev iproute2 nmap python3-pip zip systemctl usbutils traceroute nbtscan avahi avahi-tools openrc dbus
|
python3 python3-dev iproute2 nmap python3-pip zip systemctl usbutils traceroute nbtscan
|
||||||
|
|
||||||
# Alternate dependencies
|
# Alternate dependencies
|
||||||
RUN apt-get install nginx nginx-core mtr php-fpm php8.2-fpm php-cli php8.2 php8.2-sqlite3 -y
|
RUN apt-get install nginx nginx-core mtr php-fpm php8.2-fpm php-cli php8.2 php8.2-sqlite3 -y
|
||||||
@@ -43,7 +43,7 @@ RUN phpenmod -v 8.2 sqlite3
|
|||||||
RUN apt-get install -y python3-venv
|
RUN apt-get install -y python3-venv
|
||||||
RUN python3 -m venv myenv
|
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 asusrouter asyncio aiohttp graphene flask flask-cors unifi-sm-api tplink-omada-client wakeonlan pycryptodome requests paho-mqtt scapy cron-converter pytz json2table dhcp-leases pyunifi speedtest-cli chardet python-nmap dnspython librouteros yattag "
|
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 flask-cors unifi-sm-api tplink-omada-client wakeonlan pycryptodome requests paho-mqtt scapy cron-converter pytz json2table dhcp-leases pyunifi speedtest-cli chardet python-nmap dnspython librouteros yattag zeroconf "
|
||||||
|
|
||||||
# Create a buildtimestamp.txt to later check if a new version was released
|
# Create a buildtimestamp.txt to later check if a new version was released
|
||||||
RUN date +%s > ${INSTALL_DIR}/front/buildtimestamp.txt
|
RUN date +%s > ${INSTALL_DIR}/front/buildtimestamp.txt
|
||||||
|
|||||||
@@ -1,215 +1,133 @@
|
|||||||
#!/usr/bin/env python
|
#!/usr/bin/env python3
|
||||||
|
|
||||||
import os
|
import os
|
||||||
import pathlib
|
|
||||||
import sys
|
import sys
|
||||||
import json
|
import json
|
||||||
import sqlite3
|
import socket
|
||||||
import subprocess
|
import ipaddress
|
||||||
|
from zeroconf import Zeroconf, ServiceBrowser, ServiceInfo, InterfaceChoice, IPVersion
|
||||||
|
from zeroconf.asyncio import AsyncZeroconf
|
||||||
|
|
||||||
# Define the installation path and extend the system path for plugin imports
|
|
||||||
INSTALL_PATH = "/app"
|
INSTALL_PATH = "/app"
|
||||||
sys.path.extend([f"{INSTALL_PATH}/front/plugins", f"{INSTALL_PATH}/server"])
|
sys.path.extend([f"{INSTALL_PATH}/front/plugins", f"{INSTALL_PATH}/server"])
|
||||||
|
|
||||||
from plugin_helper import Plugin_Object, Plugin_Objects, decodeBase64
|
from plugin_helper import Plugin_Objects
|
||||||
from plugin_utils import get_plugins_configs
|
|
||||||
from logger import mylog, Logger
|
from logger import mylog, Logger
|
||||||
from const import pluginsPath, fullDbPath, logPath
|
from const import logPath
|
||||||
from helper import timeNowTZ, get_setting_value
|
from helper import get_setting_value
|
||||||
from messaging.in_app import write_notification
|
|
||||||
from database import DB
|
from database import DB
|
||||||
from models.device_instance import DeviceInstance
|
from models.device_instance import DeviceInstance
|
||||||
import conf
|
import conf
|
||||||
from pytz import timezone
|
from pytz import timezone
|
||||||
|
|
||||||
# Make sure the TIMEZONE for logging is correct
|
# Configure timezone and logging
|
||||||
conf.tz = timezone(get_setting_value('TIMEZONE'))
|
conf.tz = timezone(get_setting_value("TIMEZONE"))
|
||||||
|
Logger(get_setting_value("LOG_LEVEL"))
|
||||||
|
|
||||||
# Make sure log level is initialized correctly
|
pluginName = "AVAHISCAN"
|
||||||
Logger(get_setting_value('LOG_LEVEL'))
|
|
||||||
|
|
||||||
pluginName = 'AVAHISCAN'
|
# Define log paths
|
||||||
|
LOG_PATH = os.path.join(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")
|
||||||
|
|
||||||
# Define the current path and log file paths
|
# Initialize plugin results
|
||||||
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)
|
plugin_objects = Plugin_Objects(RESULT_FILE)
|
||||||
|
|
||||||
|
|
||||||
|
# =============================================================================
|
||||||
|
# Helper functions
|
||||||
|
# =============================================================================
|
||||||
|
|
||||||
|
def resolve_mdns_name(ip: str, timeout: int = 5) -> str:
|
||||||
|
"""
|
||||||
|
Attempts to resolve a hostname via multicast DNS using the Zeroconf library.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
ip (str): The IP address to resolve.
|
||||||
|
timeout (int): Timeout in seconds for mDNS resolution.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
str: Resolved hostname (or empty string if not found).
|
||||||
|
"""
|
||||||
|
mylog("debug", [f"[{pluginName}] Resolving mDNS for {ip}"])
|
||||||
|
|
||||||
|
# Convert string IP to an address object
|
||||||
|
try:
|
||||||
|
addr = ipaddress.ip_address(ip)
|
||||||
|
except ValueError:
|
||||||
|
mylog("none", [f"[{pluginName}] Invalid IP: {ip}"])
|
||||||
|
return ""
|
||||||
|
|
||||||
|
# Reverse lookup name, e.g. "121.1.168.192.in-addr.arpa"
|
||||||
|
if addr.version == 4:
|
||||||
|
rev_name = ipaddress.ip_address(ip).reverse_pointer
|
||||||
|
else:
|
||||||
|
rev_name = ipaddress.ip_address(ip).reverse_pointer
|
||||||
|
|
||||||
|
try:
|
||||||
|
zeroconf = Zeroconf()
|
||||||
|
hostname = socket.getnameinfo((ip, 0), socket.NI_NAMEREQD)[0]
|
||||||
|
zeroconf.close()
|
||||||
|
if hostname and hostname != ip:
|
||||||
|
mylog("debug", [f"[{pluginName}] Found mDNS name: {hostname}"])
|
||||||
|
return hostname
|
||||||
|
except Exception as e:
|
||||||
|
mylog("debug", [f"[{pluginName}] Zeroconf lookup failed for {ip}: {e}"])
|
||||||
|
finally:
|
||||||
|
try:
|
||||||
|
zeroconf.close()
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
|
||||||
|
return ""
|
||||||
|
|
||||||
|
|
||||||
|
# =============================================================================
|
||||||
|
# Main logic
|
||||||
|
# =============================================================================
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
mylog('verbose', [f'[{pluginName}] In script'])
|
mylog("verbose", [f"[{pluginName}] Script started"])
|
||||||
|
|
||||||
# timeout = get_setting_value('AVAHI_RUN_TIMEOUT')
|
timeout = 10
|
||||||
timeout = 20
|
db = DB()
|
||||||
|
|
||||||
# Create a database connection
|
|
||||||
db = DB() # instance of class DB
|
|
||||||
db.open()
|
db.open()
|
||||||
|
|
||||||
# Initialize the Plugin obj output file
|
|
||||||
plugin_objects = Plugin_Objects(RESULT_FILE)
|
|
||||||
|
|
||||||
# Create a DeviceInstance instance
|
|
||||||
device_handler = DeviceInstance(db)
|
device_handler = DeviceInstance(db)
|
||||||
|
devices = (
|
||||||
|
device_handler.getAll()
|
||||||
|
if get_setting_value("REFRESH_FQDN")
|
||||||
|
else device_handler.getUnknown()
|
||||||
|
)
|
||||||
|
|
||||||
# Retrieve devices
|
mylog("verbose", [f"[{pluginName}] Devices count: {len(devices)}"])
|
||||||
if get_setting_value("REFRESH_FQDN"):
|
|
||||||
devices = device_handler.getAll()
|
|
||||||
else:
|
|
||||||
devices = device_handler.getUnknown()
|
|
||||||
|
|
||||||
mylog('verbose', [f'[{pluginName}] Devices count: {len(devices)}'])
|
|
||||||
|
|
||||||
# Mock list of devices (replace with actual device_handler.getUnknown() in production)
|
|
||||||
# devices = [
|
|
||||||
# {'devMac': '00:11:22:33:44:55', 'devLastIP': '192.168.1.121'},
|
|
||||||
# {'devMac': '00:11:22:33:44:56', 'devLastIP': '192.168.1.9'},
|
|
||||||
# {'devMac': '00:11:22:33:44:57', 'devLastIP': '192.168.1.82'},
|
|
||||||
# ]
|
|
||||||
|
|
||||||
if len(devices) > 0:
|
|
||||||
# ensure service is running
|
|
||||||
ensure_avahi_running()
|
|
||||||
|
|
||||||
for device in devices:
|
for device in devices:
|
||||||
domain_name = execute_name_lookup(device['devLastIP'], timeout)
|
ip = device["devLastIP"]
|
||||||
|
mac = device["devMac"]
|
||||||
|
|
||||||
# check if found and not a timeout ('to')
|
hostname = resolve_mdns_name(ip, timeout)
|
||||||
if domain_name != '' and domain_name != 'to':
|
|
||||||
|
if hostname:
|
||||||
plugin_objects.add_object(
|
plugin_objects.add_object(
|
||||||
# "MAC", "IP", "Server", "Name"
|
primaryId=mac,
|
||||||
primaryId = device['devMac'],
|
secondaryId=ip,
|
||||||
secondaryId = device['devLastIP'],
|
watched1="",
|
||||||
watched1 = '', # You can add any relevant info here if needed
|
watched2=hostname,
|
||||||
watched2 = domain_name,
|
watched3="",
|
||||||
watched3 = '',
|
watched4="",
|
||||||
watched4 = '',
|
extra="",
|
||||||
extra = '',
|
foreignKey=mac,
|
||||||
foreignKey = device['devMac'])
|
)
|
||||||
|
|
||||||
plugin_objects.write_result_file()
|
plugin_objects.write_result_file()
|
||||||
|
|
||||||
mylog('verbose', [f'[{pluginName}] Script finished'])
|
mylog("verbose", [f"[{pluginName}] Script finished"])
|
||||||
|
|
||||||
return 0
|
return 0
|
||||||
|
|
||||||
#===============================================================================
|
|
||||||
# Execute scan
|
|
||||||
#===============================================================================
|
|
||||||
def execute_name_lookup(ip, timeout):
|
|
||||||
"""
|
|
||||||
Execute the avahi-resolve command on the IP.
|
|
||||||
"""
|
|
||||||
|
|
||||||
args = ['avahi-resolve', '-a', ip]
|
# =============================================================================
|
||||||
|
# Entrypoint
|
||||||
# Execute command
|
# =============================================================================
|
||||||
output = ""
|
if __name__ == "__main__":
|
||||||
|
|
||||||
try:
|
|
||||||
mylog('debug', [f'[{pluginName}] DEBUG CMD :', args])
|
|
||||||
|
|
||||||
# Run the subprocess with a forced timeout
|
|
||||||
output = subprocess.check_output(args, universal_newlines=True, stderr=subprocess.STDOUT, timeout=timeout)
|
|
||||||
|
|
||||||
mylog('debug', [f'[{pluginName}] DEBUG OUTPUT : {output}'])
|
|
||||||
|
|
||||||
domain_name = ''
|
|
||||||
|
|
||||||
# Split the output into lines
|
|
||||||
lines = output.splitlines()
|
|
||||||
|
|
||||||
# Look for the resolved IP address
|
|
||||||
for line in lines:
|
|
||||||
if ip in line:
|
|
||||||
parts = line.split()
|
|
||||||
if len(parts) > 1:
|
|
||||||
domain_name = parts[1] # Second part is the resolved domain name
|
|
||||||
else:
|
|
||||||
mylog('verbose', [f'[{pluginName}] ⚠ ERROR - Unexpected output format: {line}'])
|
|
||||||
|
|
||||||
mylog('debug', [f'[{pluginName}] Domain Name: {domain_name}'])
|
|
||||||
|
|
||||||
return domain_name
|
|
||||||
|
|
||||||
except subprocess.CalledProcessError as e:
|
|
||||||
mylog('none', [f'[{pluginName}] ⚠ ERROR - {e.output}'])
|
|
||||||
|
|
||||||
except subprocess.TimeoutExpired:
|
|
||||||
mylog('none', [f'[{pluginName}] TIMEOUT - the process forcefully terminated as timeout reached'])
|
|
||||||
|
|
||||||
if output == "":
|
|
||||||
mylog('none', [f'[{pluginName}] Scan: FAIL - check logs'])
|
|
||||||
else:
|
|
||||||
mylog('debug', [f'[{pluginName}] Scan: SUCCESS'])
|
|
||||||
|
|
||||||
return ''
|
|
||||||
|
|
||||||
# Function to ensure Avahi and its dependencies are running
|
|
||||||
def ensure_avahi_running(attempt=1, max_retries=2):
|
|
||||||
"""
|
|
||||||
Ensure that D-Bus is running and the Avahi daemon is started, with recursive retry logic.
|
|
||||||
"""
|
|
||||||
mylog('debug', [f'[{pluginName}] Attempt {attempt} - Ensuring D-Bus and Avahi daemon are running...'])
|
|
||||||
|
|
||||||
# Check rc-status
|
|
||||||
try:
|
|
||||||
subprocess.run(['rc-status'], check=True)
|
|
||||||
except subprocess.CalledProcessError as e:
|
|
||||||
mylog('none', [f'[{pluginName}] ⚠ ERROR - Failed to check rc-status: {e.output}'])
|
|
||||||
return
|
|
||||||
|
|
||||||
# Create OpenRC soft level
|
|
||||||
subprocess.run(['touch', '/run/openrc/softlevel'], check=True)
|
|
||||||
|
|
||||||
# Add Avahi daemon to runlevel
|
|
||||||
try:
|
|
||||||
subprocess.run(['rc-update', 'add', 'avahi-daemon'], check=True)
|
|
||||||
except subprocess.CalledProcessError as e:
|
|
||||||
mylog('none', [f'[{pluginName}] ⚠ ERROR - Failed to add Avahi to runlevel: {e.output}'])
|
|
||||||
return
|
|
||||||
|
|
||||||
# Start the D-Bus service
|
|
||||||
try:
|
|
||||||
subprocess.run(['rc-service', 'dbus', 'start'], check=True)
|
|
||||||
except subprocess.CalledProcessError as e:
|
|
||||||
mylog('none', [f'[{pluginName}] ⚠ ERROR - Failed to start D-Bus: {e.output}'])
|
|
||||||
return
|
|
||||||
|
|
||||||
# Check Avahi status
|
|
||||||
status_output = subprocess.run(['rc-service', 'avahi-daemon', 'status'], capture_output=True, text=True)
|
|
||||||
if 'started' in status_output.stdout:
|
|
||||||
mylog('debug', [f'[{pluginName}] Avahi Daemon is already running.'])
|
|
||||||
return
|
|
||||||
|
|
||||||
mylog('none', [f'[{pluginName}] Avahi Daemon is not running, attempting to start... (Attempt {attempt})'])
|
|
||||||
|
|
||||||
# Start the Avahi daemon
|
|
||||||
try:
|
|
||||||
subprocess.run(['rc-service', 'avahi-daemon', 'start'], check=True)
|
|
||||||
except subprocess.CalledProcessError as e:
|
|
||||||
mylog('none', [f'[{pluginName}] ⚠ ERROR - Failed to start Avahi daemon: {e.output}'])
|
|
||||||
|
|
||||||
# Check status after starting
|
|
||||||
status_output = subprocess.run(['rc-service', 'avahi-daemon', 'status'], capture_output=True, text=True)
|
|
||||||
if 'started' in status_output.stdout:
|
|
||||||
mylog('debug', [f'[{pluginName}] Avahi Daemon successfully started.'])
|
|
||||||
return
|
|
||||||
|
|
||||||
# Retry if not started and attempts are left
|
|
||||||
if attempt < max_retries:
|
|
||||||
mylog('debug', [f'[{pluginName}] Retrying... ({attempt + 1}/{max_retries})'])
|
|
||||||
ensure_avahi_running(attempt + 1, max_retries)
|
|
||||||
else:
|
|
||||||
mylog('none', [f'[{pluginName}] ⚠ ERROR - Avahi Daemon failed to start after {max_retries} attempts.'])
|
|
||||||
|
|
||||||
# rc-update add avahi-daemon
|
|
||||||
# rc-service avahi-daemon status
|
|
||||||
# rc-service avahi-daemon start
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
|
||||||
main()
|
main()
|
||||||
|
|||||||
@@ -30,4 +30,4 @@ source myenv/bin/activate
|
|||||||
update-alternatives --install /usr/bin/python python /usr/bin/python3 10
|
update-alternatives --install /usr/bin/python python /usr/bin/python3 10
|
||||||
|
|
||||||
# install packages thru pip3
|
# install packages thru pip3
|
||||||
pip3 install openwrt-luci-rpc asusrouter asyncio aiohttp graphene flask flask-cors unifi-sm-api tplink-omada-client wakeonlan pycryptodome requests paho-mqtt scapy cron-converter pytz json2table dhcp-leases pyunifi speedtest-cli chardet python-nmap dnspython librouteros yattag git+https://github.com/foreign-sub/aiofreepybox.git
|
pip3 install openwrt-luci-rpc asusrouter asyncio aiohttp graphene flask flask-cors unifi-sm-api tplink-omada-client wakeonlan pycryptodome requests paho-mqtt scapy cron-converter pytz json2table dhcp-leases pyunifi speedtest-cli chardet python-nmap dnspython librouteros yattag zeroconf git+https://github.com/foreign-sub/aiofreepybox.git
|
||||||
|
|||||||
@@ -22,4 +22,5 @@ python-nmap
|
|||||||
dnspython
|
dnspython
|
||||||
librouteros
|
librouteros
|
||||||
yattag
|
yattag
|
||||||
|
zeroconf
|
||||||
git+https://github.com/foreign-sub/aiofreepybox.git
|
git+https://github.com/foreign-sub/aiofreepybox.git
|
||||||
|
|||||||
@@ -22,4 +22,5 @@ python-nmap
|
|||||||
dnspython
|
dnspython
|
||||||
librouteros
|
librouteros
|
||||||
yattag
|
yattag
|
||||||
|
zeroconf
|
||||||
git+https://github.com/foreign-sub/aiofreepybox.git
|
git+https://github.com/foreign-sub/aiofreepybox.git
|
||||||
|
|||||||
Reference in New Issue
Block a user