connectcore-demo-example: add network configuration section

Signed-off-by: David Escalona <david.escalona@digi.com>
This commit is contained in:
David Escalona 2022-09-05 17:38:04 +02:00
parent 14f849fb8d
commit 4f9717a2d3
10 changed files with 1465 additions and 26 deletions

View File

@ -29,10 +29,10 @@ import socketserver
import stat import stat
import subprocess import subprocess
from digi.apix.bluetooth import BluetoothException, BluetoothDevice from digi.apix.bluetooth import BluetoothException, BluetoothDevice, BluetoothProfile
from digi.apix.exceptions import DigiAPIXException from digi.apix.exceptions import DigiAPIXException
from digi.apix.network import NetworkException, NetStatus, NetworkInterface from digi.apix.network import IPMode, NetworkException, NetStatus, NetworkInterface, NetworkProfile
from digi.apix.wifi import WifiException, WifiInterface from digi.apix.wifi import SecurityMode, WifiException, WifiInterface, WifiProfile
from logging.handlers import SysLogHandler from logging.handlers import SysLogHandler
from subprocess import call, TimeoutExpired from subprocess import call, TimeoutExpired
from threading import Thread from threading import Thread
@ -56,6 +56,13 @@ ZERO_MAC = "00:00:00:00:00:00"
ZERO_IP = "0.0.0.0" ZERO_IP = "0.0.0.0"
NOT_AVAILABLE = "N/A" NOT_AVAILABLE = "N/A"
ELEMENT_BLUETOOTH = "bluetooth"
ELEMENT_ETHERNET = "ethernet"
ELEMENT_WIFI = "wifi"
PREFIX_ETHERNET = "eth"
PREFIX_WIFI = "wlan"
# Variables. # Variables.
log = logging.getLogger(APP_NAME) log = logging.getLogger(APP_NAME)
last_cpu_work = 0 last_cpu_work = 0
@ -210,7 +217,7 @@ class RequestHandler(http.server.SimpleHTTPRequestHandler):
# Fill bluetooth devices data. # Fill bluetooth devices data.
try: try:
bt_devices = BluetoothDevice.list_devices() bt_devices = BluetoothDevice.list_devices() or []
for device in bt_devices: for device in bt_devices:
try: try:
bt_device = BluetoothDevice.get(device) bt_device = BluetoothDevice.get(device)
@ -226,7 +233,7 @@ class RequestHandler(http.server.SimpleHTTPRequestHandler):
# Fill ethernet interfaces data. # Fill ethernet interfaces data.
try: try:
interfaces = NetworkInterface.list_interfaces() interfaces = NetworkInterface.list_interfaces() or []
for iface in interfaces: for iface in interfaces:
try: try:
net_iface = NetworkInterface.get(iface) net_iface = NetworkInterface.get(iface)
@ -242,7 +249,7 @@ class RequestHandler(http.server.SimpleHTTPRequestHandler):
# Fill WiFi interfaces data. # Fill WiFi interfaces data.
try: try:
interfaces = WifiInterface.list_interfaces() interfaces = WifiInterface.list_interfaces() or []
for iface in interfaces: for iface in interfaces:
try: try:
wifi_iface = WifiInterface.get(iface) wifi_iface = WifiInterface.get(iface)
@ -567,7 +574,34 @@ class RequestHandler(http.server.SimpleHTTPRequestHandler):
progress = 100 progress = 100
if fw_process and fw_process.poll() is None: if fw_process and fw_process.poll() is None:
progress = "?" progress = "?"
self.wfile.write(json.dumps({"progress": progress, "message": "Updating firmare"}).encode(encoding="utf_8")) self.wfile.write(json.dumps({"progress": progress, "message": "Updating firmware"}).encode(encoding="utf_8"))
elif re.search("/ajax/get_config", self.path) is not None:
# Set the response headers.
self._set_headers(200)
# Get the JSON data.
data = self.rfile.read(int(self.headers["Content-Length"]))
elements = json.loads(data.decode("utf-8")).get("elements", None)
# Fill configuration data.
config_data = get_elements_configuration(elements)
# Send the answer.
if "error" in config_data:
self.wfile.write(json.dumps(config_data).encode(encoding="utf_8"))
else:
self.wfile.write(json.dumps(
{"data": json.dumps(config_data)}).encode(encoding="utf_8"))
elif re.search("/ajax/set_config", self.path) is not None:
# Set the response headers.
self._set_headers(200)
# Get the JSON data.
data = self.rfile.read(int(self.headers["Content-Length"]))
configuration = json.loads(data.decode("utf-8")).get("configuration", None)
# Apply configuration.
result = set_configuration(configuration)
# Send the answer.
if "error" in result:
self.wfile.write(json.dumps(result).encode(encoding="utf_8"))
else:
self.wfile.write(json.dumps({"data": json.dumps(result)}).encode(encoding="utf_8"))
else: else:
# Forbidden. # Forbidden.
self._set_headers(403) self._set_headers(403)
@ -612,7 +646,7 @@ def filter_by_extension(name, filters):
if name.endswith(ext): if name.endswith(ext):
return True return True
return False; return False
def get_uptime(): def get_uptime():
@ -945,6 +979,257 @@ def set_audio_volume(value):
return None return None
def get_elements_configuration(elements):
"""
Gets the configuration for the given elements.
Args:
elements (List): Elements for which to get the configuration.
Returns:
Dictionary: The elements configuration data.
"""
data = {}
for element in elements:
if element == ELEMENT_BLUETOOTH:
data[element] = get_bluetooth_element_configuration()
elif element in (ELEMENT_ETHERNET, ELEMENT_WIFI):
data[element] = get_network_element_configuration(element)
else:
data["error"] = f"Unknown element '{element}'"
return data
def get_network_element_configuration(element):
"""
Gets the configuration for the given network element.
Args:
element (String): Network element for which to get the configuration.
Returns:
Dictionary: The network element configuration data.
"""
data = {}
prefix = PREFIX_ETHERNET
if element == ELEMENT_WIFI:
prefix = PREFIX_WIFI
try:
ifaces = NetworkInterface.list_interfaces() or []
for iface in ifaces:
if prefix in iface:
data[iface] = get_network_iface_configuration(iface)
except DigiAPIXException as exc:
data["error"] = f"Error listing network interfaces: '{str(exc)}'"
return data
def get_network_iface_configuration(interface):
"""
Returns the network configuration of the given interface.
Args:
interface (String): Network interface to get its configuration.
Returns:
Dictionary: dictionary with the interface configuration.
"""
data = {}
try:
net_iface = NetworkInterface.get(interface)
data["enable"] = 1 if net_iface.status == NetStatus.CONNECTED else 0
data["mac"] = mac_to_human_string(net_iface.mac)
data["type"] = 0 if net_iface.ip_mode == IPMode.STATIC else 1
data["ip"] = str(net_iface.ipv4)
data["netmask"] = str(net_iface.netmask)
data["dns1"] = str(net_iface.dns1)
data["dns2"] = str(net_iface.dns2)
data["gateway"] = str(net_iface.gateway)
if PREFIX_WIFI in interface:
try:
wifi_iface = WifiInterface.get(interface)
data["ssid"] = wifi_iface.ssid
data["frequency"] = wifi_iface.frequency
data["channel"] = wifi_iface.channel
data["sec_mode"] = 0 if wifi_iface.sec_mode == SecurityMode.UNKNOWN \
else wifi_iface.sec_mode.code
except WifiException as exc:
raise NetworkException({str(exc)}) from exc
except NetworkException as exc2:
data["error"] = f"Error reading interface data: {str(exc2)}"
return data
def set_configuration(config_data):
"""
Applies the given configuration.
Args:
config_data (Dictionary): Configuration to apply.
Returns:
Dictionary: The operation result.
"""
data = {}
for element in config_data:
if element == ELEMENT_BLUETOOTH:
data[element] = set_bluetooth_element_configuration(config_data[element])
elif element in (ELEMENT_ETHERNET, ELEMENT_WIFI):
data[element] = set_network_element_configuration(config_data[element])
else:
data["error"] = f"Unknown element '{element}'"
return data
def set_network_element_configuration(config_data):
"""
Applies the given network configuration.
Args:
config_data (Dictionary): Network configuration to apply.
Returns:
Dictionary: The operation result.
"""
data = {}
for iface in config_data:
data[iface] = set_network_iface_configuration(iface, config_data[iface])
return data
def set_network_iface_configuration(interface, config_data):
"""
Applies the given network configuration to the given interface.
Args:
interface (String): Network interface to apply configuration to.
config_data (Dictionary): Network configuration to apply.
Returns:
Dictionary: Operation result.
"""
data = {}
profile = NetworkProfile()
if PREFIX_WIFI in interface:
profile = WifiProfile()
profile.connect = config_data.get("enable", None)
profile.mode = IPMode.get(config_data["type"]) if "type" in config_data else None
profile.ipv4 = config_data.get("ip", None)
profile.netmask = config_data.get("netmask", None)
profile.gateway = config_data.get("gateway", None)
profile.dns1 = config_data.get("dns1", None)
profile.dns2 = config_data.get("dns2", None)
if PREFIX_WIFI in interface:
profile.ssid = config_data.get("ssid", None)
profile.sec_mode = (SecurityMode.get(config_data["sec_mode"])
if "sec_mode" in config_data else None)
profile.psk = config_data.get("psk", None)
try:
if PREFIX_ETHERNET in interface:
net_iface = NetworkInterface.get(interface)
elif PREFIX_WIFI in interface:
net_iface = WifiInterface.get(interface)
else:
raise DigiAPIXException(f"Unknown interface '{interface}'")
net_iface.configure(profile)
data["status"] = 0
except DigiAPIXException as exc2:
data["status"] = 1
data["desc"] = f"Error configuring interface: {str(exc2)}"
return data
def get_bluetooth_element_configuration():
"""
Gets the configuration for the bluetooth element.
Returns:
Dictionary: The bluetooth element configuration data.
"""
data = {}
try:
bt_devices = BluetoothDevice.list_devices() or []
for bt_device in bt_devices:
data[bt_device] = get_bluetooth_device_configuration(bt_device)
except DigiAPIXException as exc:
data["error"] = f"Error listing bluetooth devices: '{str(exc)}'"
return data
def get_bluetooth_device_configuration(device):
"""
Returns the configuration of the given bluetooth device.
Args:
device (String): Bluetooth device to get its configuration.
Returns:
Dictionary: dictionary with the device configuration.
"""
data = {}
try:
bt_device = BluetoothDevice.get(device)
data["device_id"] = bt_device.device_id
data["advert_name"] = str(bt_device.advert_name)
data["mac"] = mac_to_human_string(bt_device.mac)
data["enable"] = 1 if bt_device.is_enabled else 0
data["running"] = 1 if bt_device.is_running else 0
except BluetoothException as exc:
data["error"] = f"Error reading device data: {str(exc)}"
return data
def set_bluetooth_element_configuration(config_data):
"""
Applies the given bluetooth configuration.
Args:
config_data (Dictionary): Bluetooth configuration to apply.
Returns:
Dictionary: The operation result.
"""
data = {}
for device in config_data:
data[device] = set_bluetooth_device_configuration(device, config_data[device])
return data
def set_bluetooth_device_configuration(device, config_data):
"""
Applies the given bluetooth configuration to the given device.
Args:
device (String): Bluetooth device to apply configuration to.
config_data (Dictionary): Bluetooth configuration to apply.
Returns:
Dictionary: Operation result.
"""
data = {}
try:
bt_device = BluetoothDevice.get(device)
profile = BluetoothProfile()
profile.enable = config_data.get("enable", None)
profile.advert_name = config_data.get("advert_name", None)
bt_device.configure(profile)
data["status"] = 0
except DigiAPIXException as exc2:
data["status"] = 1
data["desc"] = f"Error configuring interface: {str(exc2)}"
return data
def mac_to_human_string(mac, num_bytes=6): def mac_to_human_string(mac, num_bytes=6):
""" """
Transforms the given MAC address into a human readable string. Transforms the given MAC address into a human readable string.

View File

@ -34,7 +34,6 @@ Digi Demo - Dashboard
<p id="banner-text">ConnectCore Demo</p> <p id="banner-text">ConnectCore Demo</p>
</div> </div>
</a> </a>
<div class="nav-right-container"> <div class="nav-right-container">
<div> <div>
<img src="./static/images/board.png" class="device-title-img" title="Device"> <img src="./static/images/board.png" class="device-title-img" title="Device">
@ -69,6 +68,14 @@ Digi Demo - Dashboard
</div> </div>
</a> </a>
</li> </li>
<li id="section_network">
<a data-pjax href="network.html" class="list-group-item list-group-item-action d-flex align-items-center">
<div class="d-flex w-100 justify-content-start align-items-center">
<span class="digi-menu-icon fas fa-network-wired fa-fw fa-lg mr-3"></span>
<span class="menu-collapsed">Network</span>
</div>
</a>
</li>
<li id="section_management"> <li id="section_management">
<a data-pjax href="management.html" class="list-group-item list-group-item-action d-flex align-items-center"> <a data-pjax href="management.html" class="list-group-item list-group-item-action d-flex align-items-center">
<div class="d-flex w-100 justify-content-start align-items-center"> <div class="d-flex w-100 justify-content-start align-items-center">
@ -754,6 +761,7 @@ Digi Demo - Dashboard
<script type="text/javascript" src="./static/js/ccimx8m-nano.js"></script> <script type="text/javascript" src="./static/js/ccimx8m-nano.js"></script>
<script type="text/javascript" src="./static/js/ccimx8x-sbc-pro.js"></script> <script type="text/javascript" src="./static/js/ccimx8x-sbc-pro.js"></script>
<script type="text/javascript" src="./static/js/dashboard.js"></script> <script type="text/javascript" src="./static/js/dashboard.js"></script>
<script type="text/javascript" src="./static/js/network.js"></script>
<script type="text/javascript" src="./static/js/multimedia.js"></script> <script type="text/javascript" src="./static/js/multimedia.js"></script>
<script type="text/javascript" src="./static/js/management.js"></script> <script type="text/javascript" src="./static/js/management.js"></script>
<script type="text/javascript" src="./static/js/file-system.js"></script> <script type="text/javascript" src="./static/js/file-system.js"></script>

View File

@ -34,13 +34,12 @@ Digi Demo - Management
<p id="banner-text">ConnectCore Demo</p> <p id="banner-text">ConnectCore Demo</p>
</div> </div>
</a> </a>
<div class="nav-right-container">
<div class="nav-right-container"> <div>
<img src="./static/images/board.png" class="device-title-img" title="Device">
<div> </div>
<img src="./static/images/board.png" class="device-title-img" title="Device"> <div id="device-name">DEY DEVICE</div>
</div> </div>
<div id="device-name">DEY DEVICE</div>
</div> </div>
</nav> </nav>
@ -69,6 +68,14 @@ Digi Demo - Management
</div> </div>
</a> </a>
</li> </li>
<li id="section_network">
<a data-pjax href="network.html" class="list-group-item list-group-item-action d-flex align-items-center">
<div class="d-flex w-100 justify-content-start align-items-center">
<span class="digi-menu-icon fas fa-network-wired fa-fw fa-lg mr-3"></span>
<span class="menu-collapsed">Network</span>
</div>
</a>
</li>
<li id="section_management"> <li id="section_management">
<a data-pjax href="management.html" class="list-group-item list-group-item-action d-flex align-items-center"> <a data-pjax href="management.html" class="list-group-item list-group-item-action d-flex align-items-center">
<div class="d-flex w-100 justify-content-start align-items-center"> <div class="d-flex w-100 justify-content-start align-items-center">
@ -293,6 +300,7 @@ Digi Demo - Management
<script type="text/javascript" src="./static/js/ccimx8m-nano.js"></script> <script type="text/javascript" src="./static/js/ccimx8m-nano.js"></script>
<script type="text/javascript" src="./static/js/ccimx8x-sbc-pro.js"></script> <script type="text/javascript" src="./static/js/ccimx8x-sbc-pro.js"></script>
<script type="text/javascript" src="./static/js/dashboard.js"></script> <script type="text/javascript" src="./static/js/dashboard.js"></script>
<script type="text/javascript" src="./static/js/network.js"></script>
<script type="text/javascript" src="./static/js/multimedia.js"></script> <script type="text/javascript" src="./static/js/multimedia.js"></script>
<script type="text/javascript" src="./static/js/management.js"></script> <script type="text/javascript" src="./static/js/management.js"></script>
<script type="text/javascript" src="./static/js/file-system.js"></script> <script type="text/javascript" src="./static/js/file-system.js"></script>

View File

@ -34,13 +34,12 @@ Digi Demo - Multimedia
<p id="banner-text">ConnectCore Demo</p> <p id="banner-text">ConnectCore Demo</p>
</div> </div>
</a> </a>
<div class="nav-right-container">
<div class="nav-right-container"> <div>
<img src="./static/images/board.png" class="device-title-img" title="Device">
<div> </div>
<img src="./static/images/board.png" class="device-title-img" title="Device"> <div id="device-name">DEY DEVICE</div>
</div> </div>
<div id="device-name">DEY DEVICE</div>
</div> </div>
</nav> </nav>
@ -69,6 +68,14 @@ Digi Demo - Multimedia
</div> </div>
</a> </a>
</li> </li>
<li id="section_network">
<a data-pjax href="network.html" class="list-group-item list-group-item-action d-flex align-items-center">
<div class="d-flex w-100 justify-content-start align-items-center">
<span class="digi-menu-icon fas fa-network-wired fa-fw fa-lg mr-3"></span>
<span class="menu-collapsed">Network</span>
</div>
</a>
</li>
<li id="section_management"> <li id="section_management">
<a data-pjax href="management.html" class="list-group-item list-group-item-action d-flex align-items-center"> <a data-pjax href="management.html" class="list-group-item list-group-item-action d-flex align-items-center">
<div class="d-flex w-100 justify-content-start align-items-center"> <div class="d-flex w-100 justify-content-start align-items-center">
@ -234,6 +241,7 @@ Digi Demo - Multimedia
<script type="text/javascript" src="./static/js/ccimx8m-nano.js"></script> <script type="text/javascript" src="./static/js/ccimx8m-nano.js"></script>
<script type="text/javascript" src="./static/js/ccimx8x-sbc-pro.js"></script> <script type="text/javascript" src="./static/js/ccimx8x-sbc-pro.js"></script>
<script type="text/javascript" src="./static/js/dashboard.js"></script> <script type="text/javascript" src="./static/js/dashboard.js"></script>
<script type="text/javascript" src="./static/js/network.js"></script>
<script type="text/javascript" src="./static/js/multimedia.js"></script> <script type="text/javascript" src="./static/js/multimedia.js"></script>
<script type="text/javascript" src="./static/js/management.js"></script> <script type="text/javascript" src="./static/js/management.js"></script>
<script type="text/javascript" src="./static/js/file-system.js"></script> <script type="text/javascript" src="./static/js/file-system.js"></script>

View File

@ -0,0 +1,508 @@
<!DOCTYPE html>
<html>
<head>
<title>
Digi Demo - Network
</title>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" href="./static/css/stackpath.bootstrapcdn.com/bootstrap/4.1.3/css/bootstrap.min.css" integrity="sha384-MCw98/SFnGE8fJT3GXwEOngsV7Zt27NXFoaoApmYm81iuXoPkFOJwJ8ERdknLPMO" crossorigin="anonymous">
<link href="./static/css/cdn.jsdelivr.net/gh/gitbrent/bootstrap4-toggle@3.6.1/css/bootstrap4-toggle.min.css" rel="stylesheet">
<link href="./static/css/cdnjs.cloudflare.com/ajax/libs/bootstrap-slider/9.7.3/css/bootstrap-slider.min.css" rel="stylesheet">
<link href="./static/css/fontawesome5.15.4/all.min.css" rel="stylesheet">
<link rel="stylesheet" href="./static/css/general.css">
<link rel="stylesheet" href="./static/css/toastr.css">
<!-- JS, Popper.js, and jQuery -->
<script src="./static/js/code.jquery.com/jquery-3.5.1.min.js" integrity="sha256-9/aliU8dGd2tb6OSsuzixeV4y/faTqgFtohetphbbj0=" crossorigin="anonymous"></script>
<script src="./static/js/cdn.jsdelivr.net/npm/popper.js@1.16.1/dist/umd/popper.min.js" integrity="sha384-9/reFTGAW83EW2RDu2S0VKaIzap3H66lZH81PoYlFhbGU+6BZp6G7niu735Sk7lN" crossorigin="anonymous"></script>
<script src="./static/js/stackpath.bootstrapcdn.com/bootstrap/4.5.2/js/bootstrap.min.js" integrity="sha384-B4gt1jrGC7Jh4AgTPSdUtOBvfO8shuf57BaghqFfPlYxofvL8/KUEfYiJOMMV+rV" crossorigin="anonymous"></script>
<script src="./static/js/cdn.jsdelivr.net/gh/gitbrent/bootstrap4-toggle@3.6.1/js/bootstrap4-toggle.min.js"></script>
<script src="./static/js/cdnjs.cloudflare.com/ajax/libs/bootstrap-slider/9.7.3/bootstrap-slider.min.js"></script>
<script type="text/javascript" src="./static/js/common.js"></script>
<script type="text/javascript" src="./static/js/devices.js"></script>
<script type="text/javascript" src="./static/js/jquery.pjax.js"></script>
<script type="text/javascript" src="./static/js/jquery.matchHeight-min.js"></script>
<script type="text/javascript" src="./static/js/toastr.min.js"></script>
</head>
<body>
<nav id="topBar" class="navbar navbar-expand-lg navbar-light bg-white shadow-sm">
<div class="nav-container">
<a id="banner-link" class="navbar-brand align-middle" href="/">
<div class="d-flex align-items-baseline">
<img id="banner-logo" class="banner-icon" src="./static/images/Digi_logo_banner.png">
<p id="banner-text">ConnectCore Demo</p>
</div>
</a>
<div class="nav-right-container">
<div>
<img src="./static/images/board.png" class="device-title-img" title="Device">
</div>
<div id="device-name">DEY DEVICE</div>
</div>
</div>
</nav>
<div class="bg-light" id="body-row">
<!-- Sidebar https://www.codeply.com/go/3e0RAjccRO/bootstrap-4-collapsing-sidebar-menu# -->
<div id="sidebar-container" class="sidebar-expanded d-none d-md-block">
<!-- d-* hides the Sidebar in smaller devices. Its items can be kept on the Navbar 'Menu' -->
<div id="sidebar-contents">
<!-- Bootstrap List Group -->
<ul id="sections" data-pjax class="list-group">
<li id="section_dashboard">
<a data-pjax href="index.html" class="list-group-item list-group-item-action d-flex align-items-center">
<div class="d-flex w-100 justify-content-start align-items-center">
<span class="digi-menu-icon fas fa-tachometer-alt fa-fw fa-lg mr-3"></span>
<span class="menu-collapsed">Dashboard</span>
</div>
</a>
</li>
<li id="section_multimedia">
<a data-pjax href="multimedia.html" class="list-group-item list-group-item-action d-flex align-items-center">
<div class="d-flex w-100 justify-content-start align-items-center">
<span class="digi-menu-icon fas fa-film fa-fw fa-lg mr-3"></span>
<span class="menu-collapsed">Multimedia</span>
</div>
</a>
</li>
<li id="section_network">
<a data-pjax href="network.html" class="list-group-item list-group-item-action d-flex align-items-center">
<div class="d-flex w-100 justify-content-start align-items-center">
<span class="digi-menu-icon fas fa-network-wired fa-fw fa-lg mr-3"></span>
<span class="menu-collapsed">Network</span>
</div>
</a>
</li>
<li id="section_management">
<a data-pjax href="management.html" class="list-group-item list-group-item-action d-flex align-items-center">
<div class="d-flex w-100 justify-content-start align-items-center">
<span class="digi-menu-icon fas fa-cog fa-fw fa-lg mr-3"></span>
<span class="menu-collapsed">Management</span>
</div>
</a>
</li>
</ul>
<ul id="collapsator" class="list-group">
<a href="#top" data-toggle="sidebar-collapse" class="list-group-item list-group-item-action d-flex align-items-center">
<div class="d-flex w-100 justify-content-start align-items-center">
<span id="collapse-icon" class="digi-menu-icon fas fa-fw fa-lg mr-3"></span>
<span id="collapse-text" class="menu-collapsed">Collapse</span>
</div>
</a>
</ul>
</div>
</div>
<div class="container-fluid">
<div id="loading">
<img id="loading-image" src="./static/images/loading.gif" alt="Loading..." />
</div>
<div id="pjax-container">
<div class="row justify-content-lg-center">
<div class="col-lg-12 col-xl-12">
<div id="loading_popup" class="popup popup-loading shadow">
<img class="popup-item" src="./static/images/loading.gif" alt="Loading..." />
<div id="loading_popup_message" class="popup-text">Loading configuration...</div>
</div>
<div id="info_popup" class="popup popup-info shadow d-none">
<div id="info_popup_title" class="popup-title">Error</div>
<div id="info_popup_message" class="popup-text">-</div>
</div>
<div id="confirm_dialog" class="confirm-dialog d-none">
<div class="device-card-header">
<span class="fas fa-question-circle fa-lg mr-2"></span>
<span id="confirm_dialog_title"></span>
<div class="fas fa-window-close fa-lg device-card-header-button" onclick="showPopup(ID_LOADING_WRAPPER, ID_CONFIRM_DIALOG, false)"></div>
</div>
<div id="confirm_dialog_message" class="confirm-dialog-message"></div>
<div class="confirm-dialog-buttons-container">
<div id="confirm_dialog_no_button" class="device-card-button confirm-dialog-button">No</div>
<div id="confirm_dialog_yes_button" class="device-card-button confirm-dialog-button">Yes</div>
</div>
</div>
<div id="loading_wrapper" class="loading-wrapper">
<div class="card shadow-sm" style="padding: 0px">
<div id="eth0_panel_header" class="interface-header" style="cursor: pointer;" onclick="togglePanelVisibility('eth0')">
<span class="fas fa-ethernet fa-lg mr-2"></span>
<span id="eth0_title" class="card-title interface-title">Ethernet 0</span>
<div id="eth0_toggle_button" class="fas fa-caret-up fa-lg device-card-header-button"></div>
</div>
<div id="eth0_panel_container" class="interface-panel-container">
<div>Configure ethernet parameters.</div>
<div class="param-container">
<span class="param-label">MAC Address:</span>
<span id="eth0_mac" class="param-value"></span>
</div>
<div class="param-container">
<span class="param-label">Connected:</span>
<label class="switch-control">
<input id="eth0_enable" type="checkbox">
<span class="slider-control round"></span>
</label>
</div>
<div id="eth0_ip_mode_param" class="param-container">
<span class="param-label">IP mode:</span>
<select name="eth0_ip_mode" id="eth0_ip_mode" class="select-control">
<option value="static">Static</option>
<option value="dhcp" selected="selected">DHCP</option>
</select>
</div>
<div id="eth0_ip_addr_param" class="param-container">
<span class="param-label">IP address:</span>
<input id="eth0_ip_addr" class="input-control input-control-wide" type="text"/>
<div id="eth0_ip_addr_error" class="error-label"></div>
</div>
<div id="eth0_subnet_mask_param" class="param-container">
<span class="param-label">Subnet mask:</span>
<input id="eth0_subnet_mask" class="input-control input-control-wide" type="text"/>
<div id="eth0_subnet_mask_error" class="error-label"></div>
</div>
<div id="eth0_default_gateway_param" class="param-container">
<span class="param-label">Default gateway:</span>
<input id="eth0_default_gateway" class="input-control input-control-wide" type="text"/>
<div id="eth0_default_gateway_error" class="error-label"></div>
</div>
<div id="eth0_dns1_addr_param" class="param-container">
<span class="param-label">DNS1 address:</span>
<input id="eth0_dns1_addr" class="input-control input-control-wide" type="text"/>
<div id="eth0_dns1_addr_error" class="error-label"></div>
</div>
<div id="eth0_dns2_addr_param" class="param-container">
<span class="param-label">DNS2 address:</span>
<input id="eth0_dns2_addr" class="input-control input-control-wide" type="text"/>
<div id="eth0_dns2_addr_error" class="error-label"></div>
</div>
<div class="buttons-container">
<div id="eth0_refresh_button" class="device-card-button config-button" onclick="readConfiguration(['ethernet'], 'eth0')">Refresh</div>
<div id="eth0_save_button" class="device-card-button config-button config-button-disabled" onclick="saveInterface('ethernet', 'eth0')">Save</div>
</div>
</div>
</div>
<div id="eth1" class="card shadow-sm" style="padding: 0px">
<div id="eth1_panel_header" class="interface-header" style="cursor: pointer;" onclick="togglePanelVisibility('eth1')">
<span class="fas fa-ethernet fa-lg mr-2"></span>
<span id="eth1_title" class="card-title interface-title">Ethernet 1</span>
<div id="eth1_toggle_button" class="fas fa-caret-down fa-lg device-card-header-button"></div>
</div>
<div id="eth1_panel_container" class="interface-panel-container" style="display: none;">
<div>Configure ethernet 1 parameters.</div>
<div class="param-container">
<span class="param-label">MAC Address:</span>
<span id="eth1_mac" class="param-value"></span>
</div>
<div class="param-container">
<span class="param-label">Connected:</span>
<label class="switch-control">
<input id="eth1_enable" type="checkbox">
<span class="slider-control round"></span>
</label>
</div>
<div id="eth1_ip_mode_param" class="param-container">
<span class="param-label">IP mode:</span>
<select name="eth1_ip_mode" id="eth1_ip_mode" class="select-control">
<option value="static">Static</option>
<option value="dhcp" selected="selected">DHCP</option>
</select>
</div>
<div id="eth1_ip_addr_param" class="param-container">
<span class="param-label">IP address:</span>
<input id="eth1_ip_addr" class="input-control input-control-wide" type="text"/>
<div id="eth1_ip_addr_error" class="error-label"></div>
</div>
<div id="eth1_subnet_mask_param" class="param-container">
<span class="param-label">Subnet mask:</span>
<input id="eth1_subnet_mask" class="input-control input-control-wide" type="text"/>
<div id="eth1_subnet_mask_error" class="error-label"></div>
</div>
<div id="eth1_default_gateway_param" class="param-container">
<span class="param-label">Default gateway:</span>
<input id="eth1_default_gateway" class="input-control input-control-wide" type="text"/>
<div id="eth1_default_gateway_error" class="error-label"></div>
</div>
<div id="eth1_dns1_addr_param" class="param-container">
<span class="param-label">DNS1 address:</span>
<input id="eth1_dns1_addr" class="input-control input-control-wide" type="text"/>
<div id="eth1_dns1_addr_error" class="error-label"></div>
</div>
<div id="eth1_dns2_addr_param" class="param-container">
<span class="param-label">DNS2 address:</span>
<input id="eth1_dns2_addr" class="input-control input-control-wide" type="text"/>
<div id="eth1_dns2_addr_error" class="error-label"></div>
</div>
<div class="buttons-container">
<div id="eth1_refresh_button" class="device-card-button config-button" onclick="readConfiguration(['ethernet'], 'eth1')">Refresh</div>
<div id="eth1_save_button" class="device-card-button config-button config-button-disabled" onclick="saveInterface('ethernet', 'eth1')">Save</div>
</div>
</div>
</div>
<div id="wlan0" class="card shadow-sm" style="padding: 0px;">
<div id="wlan0_panel_header" class="interface-header" style="cursor: pointer;" onclick="togglePanelVisibility('wlan0')">
<span class="fas fa-wifi fa-lg mr-2"></span>
<span id="wlan0_title" class="card-title interface-title">Wi-Fi</span>
<div id="wlan0_toggle_button" class="fas fa-caret-down fa-lg device-card-header-button"></div>
</div>
<div id="wlan0_panel_container" class="interface-panel-container" style="display: none;">
<div>Configure wifi parameters.</div>
<div class="param-container">
<span class="param-label">MAC Address:</span>
<span id="wlan0_mac" class="param-value"></span>
</div>
<div class="param-container">
<span class="param-label">Connected:</span>
<label class="switch-control">
<input id="wlan0_enable" type="checkbox">
<span class="slider-control round"></span>
</label>
</div>
<div id="wlan0_ssid_param" class="param-container">
<span class="param-label">SSID:</span>
<input id="wlan0_ssid" class="input-control input-control-wide" type="text"/>
<div id="wlan0_ssid_error" class="error-label"></div>
</div>
<div id="wlan0_enc_type_param" class="param-container">
<span class="param-label">Encryption type:</span>
<select name="wlan0_enc_type" id="wlan0_enc_type" class="select-control">
<option value="0">Open</option>
<option value="1">WPA</option>
<option value="2">WPA2</option>
<option value="3">WPA3</option>
</select>
</div>
<div id="wlan0_password_param" class="param-container">
<span class="param-label">Password:</span>
<input id="wlan0_password" class="input-control input-control-wide" type="password"/>
<div id="wlan0_password_error" class="error-label"></div>
</div>
<div id="wlan0_ip_mode_param" class="param-container">
<span class="param-label">IP mode:</span>
<select name="wlan0_ip_mode" id="wlan0_ip_mode" class="select-control">
<option value="static">Static</option>
<option value="dhcp" selected="selected">DHCP</option>
</select>
</div>
<div id="wlan0_ip_addr_param" class="param-container">
<span class="param-label">IP address:</span>
<input id="wlan0_ip_addr" class="input-control input-control-wide" type="text"/>
<div id="wlan0_ip_addr_error" class="error-label"></div>
</div>
<div id="wlan0_subnet_mask_param" class="param-container">
<span class="param-label">Subnet mask:</span>
<input id="wlan0_subnet_mask" class="input-control input-control-wide" type="text"/>
<div id="wlan0_subnet_mask_error" class="error-label"></div>
</div>
<div id="wlan0_default_gateway_param" class="param-container">
<span class="param-label">Default gateway:</span>
<input id="wlan0_default_gateway" class="input-control input-control-wide" type="text"/>
<div id="wlan0_default_gateway_error" class="error-label"></div>
</div>
<div id="wlan0_dns1_addr_param" class="param-container">
<span class="param-label">DNS1 address:</span>
<input id="wlan0_dns1_addr" class="input-control input-control-wide" type="text"/>
<div id="wlan0_dns1_addr_error" class="error-label"></div>
</div>
<div id="wlan0_dns2_addr_param" class="param-container">
<span class="param-label">DNS2 address:</span>
<input id="wlan0_dns2_addr" class="input-control input-control-wide" type="text"/>
<div id="wlan0_dns2_addr_error" class="error-label"></div>
</div>
<div class="buttons-container">
<div id="wlan0_refresh_button" class="device-card-button config-button" onclick="readConfiguration(['wifi'], 'wlan0')">Refresh</div>
<div id="wlan0_save_button" class="device-card-button config-button config-button-disabled" onclick="saveInterface('wifi', 'wlan0')">Save</div>
</div>
</div>
</div>
</div>
</div>
</div>
<script>
$(document).ready(function() {
// Reset variables.
readingNetworkInfo = false;
// Register input changed events.
$('#eth0_enable').click(function(){
updateInterfaceControls("eth0");
});
$("#eth0_ip_mode").on("change", function(event) {
updateInterfaceControls("eth0");
});
$("#eth0_ip_addr").on("input", function(event) {
validateInterface("eth0");
});
$("#eth0_subnet_mask").on("input", function(event) {
validateInterface("eth0");
});
$("#eth0_default_gateway").on("input", function(event) {
validateInterface("eth0");
});
$("#eth0_dns1_addr").on("input", function(event) {
validateInterface("eth0");
});
$("#eth0_dns2_addr").on("input", function(event) {
validateInterface("eth0");
});
$('#eth1_enable').click(function(){
updateInterfaceControls("eth1");
});
$("#eth1_ip_mode").on("change", function(event) {
updateInterfaceControls("eth1");
});
$("#eth1_ip_addr").on("input", function(event) {
validateInterface("eth1");
});
$("#eth1_subnet_mask").on("input", function(event) {
validateInterface("eth1");
});
$('#eth1_enable').click(function(){
updateInterfaceControls("eth1");
});
$("#eth1_ip_mode").on("change", function(event) {
updateInterfaceControls("eth1");
});
$("#eth1_ip_addr").on("input", function(event) {
validateInterface("eth1");
});
$("#eth1_subnet_mask").on("input", function(event) {
validateInterface("eth1");
});
$("#eth1_default_gateway").on("input", function(event) {
validateInterface("eth1");
});
$("#eth1_dns1_addr").on("input", function(event) {
validateInterface("eth1");
});
$("#eth1_dns2_addr").on("input", function(event) {
validateInterface("eth1");
});
$('#wlan0_enable').click(function(){
updateInterfaceControls("wlan0");
});
$("#wlan0_ssid").on("input", function(event) {
validateInterface("wlan0");
});
$("#wlan0_enc_type").on("change", function(event) {
document.getElementById("wlan0_password").value = "";
passwordChanged = true;
updateInterfaceControls("wlan0");
});
$("#wlan0_password").on("input", function(event) {
passwordChanged = true;
validateInterface("wlan0");
});
$("#wlan0_ip_mode").on("change", function(event) {
updateInterfaceControls("wlan0");
});
$("#wlan0_ip_addr").on("input", function(event) {
validateInterface("wlan0");
});
$("#wlan0_subnet_mask").on("input", function(event) {
validateInterface("wlan0");
});
$("#wlan0_default_gateway").on("input", function(event) {
validateInterface("wlan0");
});
$("#wlan0_dns1_addr").on("input", function(event) {
validateInterface("wlan0");
});
$("#wlan0_dns2_addr").on("input", function(event) {
validateInterface("wlan0");
});
// Gray out page.
document.getElementById("loading_wrapper").classList.add("element-grayed");
// Update all controls.
updateAllControls();
// Initialize page.
initializeNetworkPage();
});
</script>
</div> <!-- pjax-container -->
</div> <!-- container fluid -->
</div> <!--bg light -->
<script>
$(document).ready(function() {
$("#banner-link").on({
"mouseover" : function() {
$("#banner-logo").attr("src", "./static/images/Digi_logo_banner_gray.png");
},
"mouseout" : function() {
$("#banner-logo").attr("src", "./static/images/Digi_logo_banner.png");
}
});
});
// Enable the tooltip library.
$(function() {
$('[data-toggle="tooltip"]').tooltip();
});
</script>
<!-- Local JS files and functions -->
<script type="text/javascript" src="./static/js/sidebar.js"></script>
<script type="text/javascript" src="./static/js/ccmp157-dvk.js"></script>
<script type="text/javascript" src="./static/js/ccimx6ulsbc.js"></script>
<script type="text/javascript" src="./static/js/ccimx8mm-dvk.js"></script>
<script type="text/javascript" src="./static/js/ccimx8m-nano.js"></script>
<script type="text/javascript" src="./static/js/ccimx8x-sbc-pro.js"></script>
<script type="text/javascript" src="./static/js/dashboard.js"></script>
<script type="text/javascript" src="./static/js/network.js"></script>
<script type="text/javascript" src="./static/js/multimedia.js"></script>
<script type="text/javascript" src="./static/js/management.js"></script>
<script type="text/javascript" src="./static/js/file-system.js"></script>
<script>
$(document).ready(function() {
// Don't show the loading spinner at the beginning. Initial page
// load is full, so not relying on AJAX.
$("#loading").hide();
// Set up PJAX.
if ($.support.pjax) {
$.pjax.defaults.timeout = 20000;
$.pjax.defaults.fragment = "#pjax-container";
$(document).pjax("a[data-pjax]", "#pjax-container");
$(document).on("pjax:beforeSend", function() {
// Do not load new content if there are unsaved changes.
if ($("#save-schedule").length && !$("#save-schedule").attr("disabled") && !confirm("There are unsaved changes. Do you want to exit?"))
return false;
});
$(document).on("pjax:send", function(e) {
setSelectedSection(e.currentTarget.activeElement);
$("#pjax-container").hide();
$("#loading").show();
});
$(document).on("pjax:complete", function() {
$("#loading").hide();
$("#pjax-container").show();
});
}
// Append the URL parameters to the section links.
var params = new URLSearchParams(window.location.search).toString();
if (params) {
$("#sections li").each(function(i, n) {
n.children[0].href += "?" + params;
});
}
// Update available sections.
updateAvailableSections();
// Set the selected section.
setSelectedSection();
});
</script>
</body>
</html>

View File

@ -373,7 +373,7 @@ body {
/* POPUP */ /* POPUP */
.popup { .popup {
z-index: 999; z-index: 999;
position: absolute; position: fixed;
transform: translate(-50%, -50%); transform: translate(-50%, -50%);
background-color: white; background-color: white;
padding: 20px 10px; padding: 20px 10px;
@ -1895,6 +1895,11 @@ body {
width: 170px; width: 170px;
} }
.param-value {
display: inline-block;
position: relative;
}
.input-control { .input-control {
position: relative; position: relative;
-webkit-transition: none !important; -webkit-transition: none !important;
@ -1905,12 +1910,99 @@ body {
width: 80px !important; width: 80px !important;
padding: 0px !important; padding: 0px !important;
margin: 0px !important; margin: 0px !important;
text-align: left !important;
padding-left: 5px !important;
}
.input-control-wide {
width: 180px !important;
} }
.input-control-error { .input-control-error {
background-color: #f3b1b1 !important; background-color: #f3b1b1 !important;
} }
.input-control-disabled {
background-color: transparent !important;
border: none !important;
pointer-events: none !important;
color: #939393 !important;
}
.select-control {
position: relative;
-webkit-transition: none !important;
-moz-transition: none !important;
-ms-transition: none !important;
-o-transition: none !important;
transition: none !important;
width: 80px !important;
padding: 0px !important;
margin: 0px !important;
text-align-last: left !important;
padding-left: 5px !important;
}
.switch-control {
position: relative;
display: inline-block;
width: 48px;
height: 22px;
margin-bottom: 0px;
}
.switch-control input {
opacity: 0;
width: 0;
height: 0;
}
.slider-control {
position: absolute;
cursor: pointer;
top: 0;
left: 0;
right: 0;
bottom: 0;
background-color: #ccc;
-webkit-transition: .4s;
transition: .4s;
}
.slider-control:before {
position: absolute;
content: "";
height: 16px;
width: 16px;
left: 3px;
bottom: 3px;
background-color: white;
-webkit-transition: .4s;
transition: .4s;
}
input:checked + .slider-control {
background-color: #2196F3;
}
input:focus + .slider-control {
box-shadow: 0 0 1px #2196F3;
}
input:checked + .slider-control:before {
-webkit-transform: translateX(26px);
-ms-transform: translateX(26px);
transform: translateX(26px);
}
.slider-control.round {
border-radius: 11px;
}
.slider-control.round:before {
border-radius: 50%;
}
.error-label { .error-label {
color: red; color: red;
font-size: 12px; font-size: 12px;
@ -1919,6 +2011,11 @@ body {
padding-left: 10px; padding-left: 10px;
} }
.buttons-container {
with: 100%;
position: relative;
}
.config-button { .config-button {
padding: 5px; padding: 5px;
cursor: pointer; cursor: pointer;
@ -1935,6 +2032,43 @@ body {
background-color: #c3c3c3; background-color: #c3c3c3;
pointer-events: none; pointer-events: none;
} }
input[type=text], input[type=password], select {
background-color: #f6f6f6;
border: none;
color: #0d0d0d;
padding: 12px 30px;
text-align: center;
text-decoration: none;
display: inline-block;
font-size: 14px;
margin: 0px 0px 25px 0px;
width: 85%;
border: 2px solid #f6f6f6;
-webkit-transition: all 0.5s ease-in-out;
-moz-transition: all 0.5s ease-in-out;
-ms-transition: all 0.5s ease-in-out;
-o-transition: all 0.5s ease-in-out;
transition: all 0.5s ease-in-out;
-webkit-border-radius: 5px 5px 5px 5px;
border-radius: 5px 5px 5px 5px;
}
input[type=text]:focus, input[type=password]:focus, select:focus {
background-color: #fff;
border-bottom: 2px solid var(--digi-green);
}
select {
-moz-appearance:none; /* Firefox */
-webkit-appearance:none; /* Safari and Chrome */
appearance:none;
text-align-last: center;
background-image: url("../images/combo_arrow_gray.png");
background-repeat: no-repeat, repeat;
background-position: right .7em top 50%;
background-size: .65em auto;
}
/* END CONFIG CONTROLS */ /* END CONFIG CONTROLS */
/* MANAGEMENT */ /* MANAGEMENT */
@ -2206,3 +2340,20 @@ body {
margin-top: 5px; margin-top: 5px;
} }
/* END MANAGEMENT */ /* END MANAGEMENT */
/* NETWORK */
.interface-header {
padding: 15px 20px 10px 20px;
align-items: center;
font-size: 16px;
font-weight: bold;
}
.interface-panel-container {
padding: 0px 20px 15px 20px;
}
.interface-title {
font-size: 1.25em;
}
/* END NETWORK */

View File

@ -42,6 +42,7 @@ const ID_CPU_TEMPERATURE = "cpu_temperature";
const ID_CPU_UPTIME = "cpu_uptime"; const ID_CPU_UPTIME = "cpu_uptime";
const ID_CURRENT_DIR = "current_dir"; const ID_CURRENT_DIR = "current_dir";
const ID_DATA = "data"; const ID_DATA = "data";
const ID_DESC = "desc";
const ID_DEVICE_NAME = "device-name"; const ID_DEVICE_NAME = "device-name";
const ID_DEVICE_TYPE = "device_type"; const ID_DEVICE_TYPE = "device_type";
const ID_DEVICES = "devices"; const ID_DEVICES = "devices";
@ -133,9 +134,12 @@ const VALUE_SUCCESSFUL = "successful";
const VALUE_TOP = "top"; const VALUE_TOP = "top";
const VALUE_UNKNOWN = "unknown"; const VALUE_UNKNOWN = "unknown";
const CLASS_ARROW_DOWN = "fa-caret-down";
const CLASS_ARROW_UP = "fa-caret-up";
const CLASS_D_NONE = "d-none"; const CLASS_D_NONE = "d-none";
const CLASS_DISABLED_DIV = "disabled-div"; const CLASS_DISABLED_DIV = "disabled-div";
const CLASS_ELEMENT_GRAYED = "element-grayed"; const CLASS_ELEMENT_GRAYED = "element-grayed";
const CLASS_INPUT_DISABLED = "input-control-disabled";
const CLASS_INPUT_ERROR = "input-control-error"; const CLASS_INPUT_ERROR = "input-control-error";
const CLASS_CONFIG_BUTTON_DISABLED = "config-button-disabled"; const CLASS_CONFIG_BUTTON_DISABLED = "config-button-disabled";
const CLASS_SELECTED = "selected"; const CLASS_SELECTED = "selected";
@ -487,6 +491,11 @@ function isDashboardShowing() {
return window.location.pathname.indexOf("index") > -1; return window.location.pathname.indexOf("index") > -1;
} }
// Returns whether the network page is showing or not.
function isNetworkShowing() {
return window.location.pathname.indexOf("network") > -1;
}
// Returns whether the management page is showing or not. // Returns whether the management page is showing or not.
function isManagementShowing() { function isManagementShowing() {
return window.location.pathname.indexOf("management") > -1; return window.location.pathname.indexOf("management") > -1;

View File

@ -92,8 +92,6 @@ const STREAM_BT_STATE = PREFIX_STREAM + IFACE_BT + "/state";
const PANEL_ARROW_WIDTH_100 = 20; const PANEL_ARROW_WIDTH_100 = 20;
const PANEL_BOARD_WIDTH_100 = 1200; const PANEL_BOARD_WIDTH_100 = 1200;
const CLASS_ARROW_DOWN = "fa-caret-down";
const CLASS_ARROW_UP = "fa-caret-up";
const CLASS_LED_PANEL_AREA_ON = "led-panel-area-on"; const CLASS_LED_PANEL_AREA_ON = "led-panel-area-on";
const CLASS_PANEL_AREA_SELECTED = "panel-area-selected"; const CLASS_PANEL_AREA_SELECTED = "panel-area-selected";
const CLASS_PANEL_AREA_ICON_SELECTED = "panel-area-icon-selected"; const CLASS_PANEL_AREA_ICON_SELECTED = "panel-area-icon-selected";

View File

@ -325,6 +325,11 @@ class ConnectCoreDevice {
return this.#ethernetIP[index]; return this.#ethernetIP[index];
} }
// Returns whether the device supports Wifi or not.
hasWifi() {
return this.#wifiMAC != null && this.#wifiMAC != "undefined"
}
// Returns the device WiFi MAC address. // Returns the device WiFi MAC address.
getWifiMAC() { getWifiMAC() {
return this.#wifiMAC; return this.#wifiMAC;

View File

@ -0,0 +1,459 @@
/*
* Copyright 2022, Digi International Inc.
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
// Constants.
const ELEMENT_ETHERNET = "ethernet";
const ELEMENT_WIFI = "wifi";
const FIELD_DEFAULT_GATEWAY = "_default_gateway";
const FIELD_DNS1_ADDRESS = "_dns1_addr";
const FIELD_DNS2_ADDRESS = "_dns2_addr";
const FIELD_ENABLE = "_enable";
const FIELD_ENCRYPTION_TYPE = "_enc_type";
const FIELD_ERROR = "_error";
const FIELD_MAC_ADDRESS = "_mac";
const FIELD_IP_ADDRESS = "_ip_addr";
const FIELD_IP_MODE = "_ip_mode";
const FIELD_PARAM = "_param";
const FIELD_PASSWORD = "_password";
const FIELD_SAVE_BUTTON = "_save_button";
const FIELD_SSID = "_ssid";
const FIELD_SUBNET_MASK = "_subnet_mask";
const ID_DNS1 = "dns1";
const ID_DNS2 = "dns2";
const ID_ENABLE = "enable";
const ID_ETH0_TITLE = "eth0_title";
const ID_GATEWAY = "gateway";
const ID_IP = "ip";
const ID_IP_MODE = "type";
const ID_INTERFACE = "interface";
const ID_MAC = "mac";
const ID_NETMASK = "netmask";
const ID_PANEL_CONTAINER = "_panel_container";
const ID_PASSWORD = "psk";
const ID_SECURITY_MODE = "sec_mode";
const ID_SSID = "ssid";
const ID_TOGGLE_BUTTON = "_toggle_button";
const IP_MODE_DHCP = "dhcp";
const IP_MODE_STATIC = "static";
const ERROR_IP_VALUE_FORMAT = "Value must follow this format: XXX.XXX.XXX.XXX";
const ERROR_PASSWORD_INVALID = "Password must be between 8 and 63 characters long";
const ERROR_SSID_INVALID = "SSID value is not valid";
const ERROR_UNKNOWN = "Unknown error saving configuration.";
const MESSAGE_LOADING_CONFIGURATION = "Loading configuration...";
const MESSAGE_SAVING_CONFIGURATION = "Saving configuration...";
const MESSAGE_CONFIGURATION_SAVED = "Configuration saved successfully!";
const PREFIX_ETHERNET = "eth";
const PREFIX_WIFI = "wlan";
const REGEX_IP = '^(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$';
const REGEX_SSID = '^[^!#;+\\]/"\t][^+\\]/"\t]{0,31}$';
const REGEX_PASSWORD = '^[\u0020-\u007e]{8,63}$';
const ALL_ELEMENTS = "all";
// Variables.
var readingNetworkInfo = false;
var passwordChanged = false;
// Initializes the network page.
function initializeNetworkPage() {
// Sanity checks.
if (!isNetworkShowing())
return;
// Read network configuration.
readElements = [ELEMENT_ETHERNET, ELEMENT_WIFI];
readConfiguration(readElements, ALL_ELEMENTS);
}
// Gets the device configuration the given elements.
function readConfiguration(readElements, parseElement) {
// Execute only in the network page.
if (!isNetworkShowing() || readingNetworkInfo)
return;
// Flag reading variable.
readingNetworkInfo = true;
// Hide the info popup.
showInfoPopup(false);
// Show the loading popup.
showLoadingPopup(true, MESSAGE_LOADING_CONFIGURATION);
// Send request to retrieve configuration.
$.post(
"http://" + getServerAddress() + "/ajax/get_config",
JSON.stringify({
"elements": readElements
}),
function(data) {
// Process only in the network page.
if (!isNetworkShowing())
return;
// Process answer.
processReadConfigurationResponse(data, parseElement);
}
).fail(function(response) {
// Flag reading variable.
readingNetworkInfo = false;
// Clear the element being read.
elementReading = "";
// Process only in the network page.
if (!isNetworkShowing())
return;
// Hide the loading panel.
showLoadingPopup(false);
// Process error.
processAjaxErrorResponse(response);
// Update controls.
updateAllControls();
});
}
// Processes the response of the read configuration request.
function processReadConfigurationResponse(response, parseElement) {
// Check if there was any error in the request.
if (!checkErrorResponse(response, false)) {
// Fill network info.
fillNetworkInfo(JSON.parse(response[ID_DATA]), parseElement);
} else {
// Flag reading variable.
readingNetworkInfo = false;
// Hide the loading panel.
showLoadingPopup(false);
}
}
// Fills network information.
function fillNetworkInfo(response, parseElement) {
var numEthernetIfaces = 0;
var numWifiIfaces = 0;
for (const [element, elementData] of Object.entries(response)) {
if (element != ELEMENT_ETHERNET && element != ELEMENT_WIFI)
continue;
for (const [iface, ifaceData] of Object.entries(elementData)) {
if (!iface.startsWith(PREFIX_WIFI) && !iface.startsWith(PREFIX_ETHERNET))
continue;
if (parseElement != ALL_ELEMENTS && iface != parseElement)
continue;
var enabled = ifaceData[ID_ENABLE];
var ipMode = ifaceData[ID_IP_MODE];
if (iface.startsWith(PREFIX_ETHERNET))
numEthernetIfaces += 1;
else if (iface.startsWith(PREFIX_WIFI))
numWifiIfaces += 1;
// Fill MAC.
document.getElementById(iface + FIELD_MAC_ADDRESS).innerText = ifaceData[ID_MAC];
// Set enable state.
document.getElementById(iface + FIELD_ENABLE).checked = enabled;
// Set IP mode.
if (ipMode == 0)
document.getElementById(iface + FIELD_IP_MODE).value = IP_MODE_STATIC;
else
document.getElementById(iface + FIELD_IP_MODE).value = IP_MODE_DHCP;
// Fill the rest of the fields.
document.getElementById(iface + FIELD_IP_ADDRESS).value = ifaceData[ID_IP];
document.getElementById(iface + FIELD_SUBNET_MASK).value = ifaceData[ID_NETMASK];
document.getElementById(iface + FIELD_DEFAULT_GATEWAY).value = ifaceData[ID_GATEWAY];
document.getElementById(iface + FIELD_DNS1_ADDRESS).value = ifaceData[ID_DNS1];
document.getElementById(iface + FIELD_DNS2_ADDRESS).value = ifaceData[ID_DNS2];
// Specific Wifi fields.
if (iface.startsWith(PREFIX_WIFI)) {
document.getElementById(iface + FIELD_SSID).value = ifaceData[ID_SSID];
document.getElementById(iface + FIELD_ENCRYPTION_TYPE).selectedIndex = ifaceData[ID_SECURITY_MODE];
if (ifaceData[ID_SECURITY_MODE] != 0)
document.getElementById(iface + FIELD_PASSWORD).value = "dummy_password";
passwordChanged = false;
}
// Update controls.
updateInterfaceControls(iface);
}
}
// Update interfaces visibility.
if (parseElement == ALL_ELEMENTS) {
if (numEthernetIfaces == 1) {
document.getElementById(IFACE_ETH1).style.display = "none";
document.getElementById(ID_ETH0_TITLE).innerHTML = "Ethernet";
}
if (numWifiIfaces == 0)
document.getElementById(IFACE_WIFI).style.display = "none";
}
// Flag reading variable.
readingNetworkInfo = false;
// Hide the loading panel.
showLoadingPopup(false);
}
// Updates all controls of the page.
function updateAllControls() {
updateInterfaceControls(IFACE_ETH0);
updateInterfaceControls(IFACE_ETH1);
updateInterfaceControls(IFACE_WIFI);
}
// Updates the given interface controls based on its configuration.
function updateInterfaceControls(iface) {
// Initialize variables.
var ipGroup = document.getElementById(iface + FIELD_IP_ADDRESS + FIELD_PARAM);
var ipField = document.getElementById(iface + FIELD_IP_ADDRESS);
var subnetGroup = document.getElementById(iface + FIELD_SUBNET_MASK + FIELD_PARAM);
var subnetField = document.getElementById(iface + FIELD_SUBNET_MASK);
var gatewayGroup = document.getElementById(iface + FIELD_DEFAULT_GATEWAY + FIELD_PARAM);
var gatewayField = document.getElementById(iface + FIELD_DEFAULT_GATEWAY);
var dns1Group = document.getElementById(iface + FIELD_DNS1_ADDRESS + FIELD_PARAM);
var dns1Field = document.getElementById(iface + FIELD_DNS1_ADDRESS);
var dns2Group = document.getElementById(iface + FIELD_DNS2_ADDRESS + FIELD_PARAM);
var dns2Field = document.getElementById(iface + FIELD_DNS2_ADDRESS);
var ipModeGroup = document.getElementById(iface + FIELD_IP_MODE + FIELD_PARAM);
var ipModeField = document.getElementById(iface + FIELD_IP_MODE);
var enableField = document.getElementById(iface + FIELD_ENABLE);
var enabled = enableField.checked;
var ipMode = ipModeField.value;
var ssidGroup, encryptionTypeGroup, encryptionTypeField, passwordGroup, usePassword;
if (iface.startsWith(PREFIX_WIFI)) {
ssidGroup = document.getElementById(iface + FIELD_SSID + FIELD_PARAM);
encryptionTypeGroup = document.getElementById(iface + FIELD_ENCRYPTION_TYPE + FIELD_PARAM);
encryptionTypeField = document.getElementById(iface + FIELD_ENCRYPTION_TYPE);
passwordGroup = document.getElementById(iface + FIELD_PASSWORD + FIELD_PARAM);
usePassword = encryptionTypeField.value != 0;
if (usePassword)
passwordGroup.style.display = "block";
else
passwordGroup.style.display = "none";
}
// Determine if IP fields can be edited.
if (ipMode == IP_MODE_DHCP) {
if (!ipField.classList.contains(CLASS_INPUT_DISABLED))
ipField.classList.add(CLASS_INPUT_DISABLED);
if (!subnetField.classList.contains(CLASS_INPUT_DISABLED))
subnetField.classList.add(CLASS_INPUT_DISABLED);
if (!gatewayField.classList.contains(CLASS_INPUT_DISABLED))
gatewayField.classList.add(CLASS_INPUT_DISABLED);
if (!dns1Field.classList.contains(CLASS_INPUT_DISABLED))
dns1Field.classList.add(CLASS_INPUT_DISABLED);
if (!dns2Field.classList.contains(CLASS_INPUT_DISABLED))
dns2Field.classList.add(CLASS_INPUT_DISABLED);
} else if (ipMode == IP_MODE_STATIC) {
if (ipField.classList.contains(CLASS_INPUT_DISABLED))
ipField.classList.remove(CLASS_INPUT_DISABLED);
if (subnetField.classList.contains(CLASS_INPUT_DISABLED))
subnetField.classList.remove(CLASS_INPUT_DISABLED);
if (gatewayField.classList.contains(CLASS_INPUT_DISABLED))
gatewayField.classList.remove(CLASS_INPUT_DISABLED);
if (dns1Field.classList.contains(CLASS_INPUT_DISABLED))
dns1Field.classList.remove(CLASS_INPUT_DISABLED);
if (dns2Field.classList.contains(CLASS_INPUT_DISABLED))
dns2Field.classList.remove(CLASS_INPUT_DISABLED);
}
// Validate the network interface.
validateInterface(iface);
}
// Validates the given network interface.
function validateInterface(iface) {
// Initialize vars.
var valid = true;
var saveButton = document.getElementById(iface + FIELD_SAVE_BUTTON);
var enableField = document.getElementById(iface + FIELD_ENABLE);
var ipModeField = document.getElementById(iface + FIELD_IP_MODE);
var ipField = document.getElementById(iface + FIELD_IP_ADDRESS);
var subnetField = document.getElementById(iface + FIELD_SUBNET_MASK);
var gatewayField = document.getElementById(iface + FIELD_DEFAULT_GATEWAY);
var dns1Field = document.getElementById(iface + FIELD_DNS1_ADDRESS);
var dns2Field = document.getElementById(iface + FIELD_DNS2_ADDRESS);
var ipMode = ipModeField.value;
var forceValid = false;
// Validate fields.
if (ipMode == IP_MODE_DHCP)
forceValid = true;
valid &= validateIPField(iface + FIELD_IP_ADDRESS, iface + FIELD_IP_ADDRESS + FIELD_ERROR, forceValid);
valid &= validateIPField(iface + FIELD_SUBNET_MASK, iface + FIELD_SUBNET_MASK + FIELD_ERROR, forceValid);
valid &= validateIPField(iface + FIELD_DEFAULT_GATEWAY, iface + FIELD_DEFAULT_GATEWAY + FIELD_ERROR, forceValid);
valid &= validateIPField(iface + FIELD_DNS1_ADDRESS, iface + FIELD_DNS1_ADDRESS + FIELD_ERROR, forceValid);
valid &= validateIPField(iface + FIELD_DNS2_ADDRESS, iface + FIELD_DNS2_ADDRESS + FIELD_ERROR, forceValid);
if (iface.startsWith(PREFIX_WIFI)) {
valid &= validateField(iface + FIELD_SSID, iface + FIELD_SSID + FIELD_ERROR, REGEX_SSID, ERROR_SSID_INVALID, false);
if (parseInt(document.getElementById(iface + FIELD_ENCRYPTION_TYPE).value) != 0)
valid &= validateField(iface + FIELD_PASSWORD, iface + FIELD_PASSWORD + FIELD_ERROR, REGEX_PASSWORD, ERROR_PASSWORD_INVALID, false);
}
// Check errors.
if (!valid) {
if (!saveButton.classList.contains(CLASS_CONFIG_BUTTON_DISABLED))
saveButton.classList.add(CLASS_CONFIG_BUTTON_DISABLED);
} else {
if (saveButton.classList.contains(CLASS_CONFIG_BUTTON_DISABLED))
saveButton.classList.remove(CLASS_CONFIG_BUTTON_DISABLED);
}
}
// Validates the given IP field.
function validateIPField(fieldID, errorID, forceValid) {
return validateField(fieldID, errorID, REGEX_IP, ERROR_IP_VALUE_FORMAT, forceValid);
}
// Validates the given field.
function validateField(fieldID, errorID, regexPattern, errorMessage, forceValid) {
// Initialize vars.
var field = document.getElementById(fieldID);
var fieldError = document.getElementById(errorID);
var isValid = true;
var error = "";
// Sanity checks.
if (field == null || fieldError == null)
return false;
// Check if value is valid.
if (!forceValid) {
var value = field.value;
if (value.length == 0) {
isValid = false;
error = ERROR_FIELD_EMPTY;
} else if (!value.match(regexPattern)) {
isValid = false;
error = errorMessage;
}
}
// Update controls.
if (isValid) {
if (field.classList.contains(CLASS_INPUT_ERROR))
field.classList.remove(CLASS_INPUT_ERROR);
fieldError.innerHTML = "&nbsp;";
fieldError.style.display = "none";
} else {
if (!field.classList.contains(CLASS_INPUT_ERROR))
field.classList.add(CLASS_INPUT_ERROR);
fieldError.innerHTML = error;
fieldError.style.display = "block";
}
return isValid;
}
// Retrieves the given network interface form data.
function getInterfaceData(element, iface) {
// Initialize variables.
var data = {};
var ifaceData = {};
var networkData = {};
var enable = document.getElementById(iface + FIELD_ENABLE).checked;
var ipMode = document.getElementById(iface + FIELD_IP_MODE).value;
// Fill network data.
networkData[ID_ENABLE] = enable;
if (ipMode == IP_MODE_STATIC) {
networkData[ID_IP_MODE] = 0;
networkData[ID_IP] = document.getElementById(iface + FIELD_IP_ADDRESS).value;
networkData[ID_NETMASK] = document.getElementById(iface + FIELD_SUBNET_MASK).value;
networkData[ID_GATEWAY] = document.getElementById(iface + FIELD_DEFAULT_GATEWAY).value;
networkData[ID_DNS1] = document.getElementById(iface + FIELD_DNS1_ADDRESS).value;
networkData[ID_DNS2] = document.getElementById(iface + FIELD_DNS2_ADDRESS).value;
} else {
networkData[ID_IP_MODE] = 1;
}
if (iface.startsWith(PREFIX_WIFI)) {
networkData[ID_SSID] = document.getElementById(iface + FIELD_SSID).value;
networkData[ID_SECURITY_MODE] = parseInt(document.getElementById(iface + FIELD_ENCRYPTION_TYPE).value);
if (networkData[ID_SECURITY_MODE] != 0 && passwordChanged)
networkData[ID_PASSWORD] = document.getElementById(iface + FIELD_PASSWORD).value;
}
ifaceData[iface] = networkData;
data[element] = ifaceData;
return data;
}
// Saves the network settings for the given interface.
function saveInterface(element, iface) {
// Execute only in the network page.
if (!isNetworkShowing())
return;
// Hide the info popup.
showInfoPopup(false);
// Show the loading popup.
showLoadingPopup(true, MESSAGE_SAVING_CONFIGURATION);
// Send request to set new configuration.
$.post(
"http://" + getServerAddress() + "/ajax/set_config",
JSON.stringify({
"configuration": getInterfaceData(element, iface)
}),
function(data) {
// Process only in the network page.
if (!isNetworkShowing())
return;
// Process answer.
processSaveInterfaceResponse(data, element, iface);
}
).fail(function(response) {
// Process only in the network page.
if (!isNetworkShowing())
return;
// Hide the loading panel.
showLoadingPopup(false);
// Process error.
processAjaxErrorResponse(response);
});
}
// Processes the save network request response.
function processSaveInterfaceResponse(response, element, iface) {
// Check for error in the response.
if (!checkErrorResponse(response, false)) {
var statusFound = false;
// Check status in the response.
if (response[ID_DATA] != null) {
data = JSON.parse(response[ID_DATA]);
for (const [elementEntry, elementData] of Object.entries(data)) {
if (elementEntry != element)
continue;
for (const [ifaceEntry, ifaceData] of Object.entries(elementData)) {
if (ifaceEntry != iface)
continue;
if (ifaceData[ID_STATUS] != null) {
statusFound = true;
if (ifaceData[ID_STATUS] != 0)
toastr.error(ifaceData[ID_DESC]);
else
toastr.success(MESSAGE_CONFIGURATION_SAVED);
}
}
}
}
if (!statusFound)
toastr.error(ERROR_UNKNOWN);
}
// Hide the loading panel.
showLoadingPopup(false);
}
// Toggles the visibility of the given interface panel.
function togglePanelVisibility(interface) {
// Initialize variables.
var panelButton = document.getElementById(interface + ID_TOGGLE_BUTTON);
// Sanity checks.
if (panelButton == null)
return;
// Check visibility.
if (panelButton.classList.contains(CLASS_ARROW_UP)) {
$("#" + interface + ID_PANEL_CONTAINER).slideUp("fast", function() {
panelButton.classList.remove(CLASS_ARROW_UP);
panelButton.classList.add(CLASS_ARROW_DOWN);
});
} else {
$("#" + interface + ID_PANEL_CONTAINER).slideDown("fast", function() {
panelButton.classList.remove(CLASS_ARROW_DOWN);
panelButton.classList.add(CLASS_ARROW_UP);
});
}
}