Archive Simpleadmin 1.0

This commit is contained in:
Cameron Thompson
2024-10-30 03:22:39 +00:00
parent 74d7d3f3ef
commit 7ed145d7bc
44 changed files with 141 additions and 0 deletions

View File

@@ -0,0 +1,141 @@
# ORIGINAL PROJECT PRESERVED HERE
# RGMII Toolkit
## Software deployment Toolkit for Quectel RM5xxx series 5G modems utilizing an m.2 to RJ45 adapter (RGMII)
Current Branch: **Main**
Please Fork and PR with/to [Development](https://github.com/iamromulan/quectel-rgmii-toolkit/tree/development) instead of main, it has the latest changes and is meant to be commits ahead of main for testing. :)
"Don't push to production 😂"
Fork development, and PR development to development :)
#### [JUMP TO HOW TO USE](#how-to-use)
**Currently:** This will allow you to install or if already installed, update, remove, or modify:
- Simple Admin: A simple web interface for managing your Quectel m.2 modem through it's gateway address
- It will install socat-at-bridge: sets up ttyOUT and ttyOUT2 for AT commands. You'll be able to use the `atcmd` command as well for an interactive at command session from adb, ssh, or ttyd
- It will install simplefirewall: A simple firewall that blocks definable incoming ports and a TTL mangle option/modifier. As of now only the TTL is controllable through Simple Admin. You can edit port block options and TTL from the 3rd option in the toolkit
- Tailscale: A magic VPN for accessing Simple Admin, SSH, and ttyd on the go. The Toolkit installs the Tailscale client directly to the modem and allows you to login and configure other settings. Head over to tailscale.com to sign up for a free account and learn more.
- Schedule a Daily Reboot at a specified time
- A fix for certain modems that don't start in CFUN=1 mode
- Entware/OPKG: A package installer/manager/repo
- Run `opkg help` to see how to use it
- These packages are installable: https://bin.entware.net/armv7sf-k3.2/Packages.html
- TTYd: A shell session right from your browser
- Currently this uses port 443 but SSL/TLS is not in use (http only for now)
- Entware/OPKG is required so it will install it if it isn't installed
- This will replace the stock Quectel login and passwd binaries with ones from entware
**My goal** is for this to also include any new useful scripts or software for this modem and others that support RGMII mode.
## Screenshots
![Toolkit](https://github.com/iamromulan/quectel-rgmii-configuration-notes/blob/main/images/iamromulantoolkit.png?raw=true)
![Home Page](https://github.com/iamromulan/quectel-rgmii-configuration-notes/blob/main/images/iamromulansimpleindex.png?raw=true)
![AT Commands](https://github.com/iamromulan/quectel-rgmii-configuration-notes/blob/main/images/iamromulanatcommands.png?raw=true)
![AT Commands](https://github.com/iamromulan/quectel-rgmii-configuration-notes/blob/main/images/iamromulansimplenetwork.png?raw=true)
![TTL](https://github.com/iamromulan/quectel-rgmii-configuration-notes/blob/main/images/iamromulansimpleTTL.png?raw=true)
![TTL](https://github.com/iamromulan/quectel-rgmii-configuration-notes/blob/main/images/iamromulanspeedtest.png?raw=true)
## How to Use
**To run the Toolkit:**
- Open ADB & Fastboot++ covered in [Using ADB](https://github.com/iamromulan/quectel-rgmii-configuration-notes?tab=readme-ov-file#unlocking-and-using-adb) or just use adb
- Make sure your modem is connected by USB to your computer
- Run `adb devices` to make sure your modem is detected by adb
- Run `adb shell ping 8.8.8.8` to make sure the shell can access the internet. If you get an error, make sure the modem is connected to a cellular network and make sure `AT+QMAPWAC=1` as covered in the troubleshooting section: [I Can't get internet access from the Ethernet port (Common)](https://github.com/iamromulan/quectel-rgmii-configuration-notes/tree/main?tab=readme-ov-file#i-cant-get-internet-access-from-the-ethernet-port-common)
- If you don't get an error you should be getting replies back endlessly, press `CTRL-C` to stop it.
- Simply Copy/Paste this into your Command Prompt/Shell
```bash
adb shell "cd /tmp && wget -O RMxxx_rgmii_toolkit.sh https://raw.githubusercontent.com/iamromulan/quectel-rgmii-toolkit/main/RMxxx_rgmii_toolkit.sh && chmod +x RMxxx_rgmii_toolkit.sh && ./RMxxx_rgmii_toolkit.sh" && cd /
```
**Or, if you want to stay in the modems shell when you are done**
```
adb shell
```
Then run
```
cd /tmp && wget -O RMxxx_rgmii_toolkit.sh https://raw.githubusercontent.com/iamromulan/quectel-rgmii-toolkit/main/RMxxx_rgmii_toolkit.sh && chmod +x RMxxx_rgmii_toolkit.sh && ./RMxxx_rgmii_toolkit.sh && cd /
```
**You should see:**
![Toolkit](https://github.com/iamromulan/quectel-rgmii-configuration-notes/blob/main/images/iamromulantoolkit.png?raw=true)
## Tailscale Installation and Config
> :warning: Your modem must already be connected to the internet for this to install
### Installation:
Open up the toolkit main menu and **press 4** to enter the Tailscale menu
![Toolkit](https://github.com/iamromulan/quectel-rgmii-configuration-notes/blob/main/images/tailscalemenu.png?raw=true)
**Press 1, wait for it to install. This is a very large file for the system so give it some time.**
**Once done and it says Tailscale installed successfully press 2/enter to configure it.**
![Toolkit](https://github.com/iamromulan/quectel-rgmii-configuration-notes/blob/main/images/tailscaleconfig.png?raw=true)
If you want to, enable the Tailscale Web UI on port 8088 for configuration from the browser later by **pressing 1/enter**.
To do it in the toolkit:
First time connecting you'll be given a link to login with
- Press 3 to just connect only.
- Press 4 to connect and enable SSH access (remote command line) over tailscale.
- Press 5 to reconnect with SSH off while connected with SSH on
- Press 6 to disconnect
- Press 7 to Logout
That's it! From another device running tailscale you should be able to access your modem through the IP assigned to it by your tailnet. To access SSH from another device on the tailnet, open a terminal/command prompt and type
tailscale ssh root@(IP or Hostname)
IP or Hostname being the IP or hostname assigned to it in your tailnet
- Note that your SSH client must be able to give you a link to sign in with upon connecting. That's how the session is authorized. Works fine in Windows CMD or on Android use JuiceSSH.
### Entware/OPKG installation
- After installing, the `opkg` command will work
- You can run `opkg list` to see a list of installable packages, or head over to https://bin.entware.net/armv7sf-k3.2/Packages.html
- Everything opkg does is installed to /opt
- `/opt` is actually located at `/usrdata/opt` to save space but is
mounted at `/opt`
- To properly use entware/opkg run the `login` command first if using adb to use stufff installed by entware/opkg
- Be aware of space and CPU limits, run `dfc` to see what the available space in `/opt` is. Run `htop` to see CPU usage and memory usage
### TTYd installation
- This listens on port 443 for http requests (no SSL/TLS yet)
- This will automaticly install entware and patch the login and passwd binaries with ones from entware\so you can login
- It will ask you to set a password for the `root` user account
- TTYd doesn't seem to be too mobile friendly for now but I optimized it the best i could for now so it is at least usable through a smartphone browser. Hopefully the startup script can be improved even more later. Some people have a better experience depending on mobile browser
## Acknowledgements
### GitHub Users/Individuals:
Thank You to:
[Nate Carlson](https://github.com/natecarlson) for the Original Telnet Deamon/socat bridge usage and the Original RGMII Notes
[aesthernr](https://github.com/aesthernr) for creating the Original Simple Admin
[rbflurry](https://github.com/rbflurry/) for inital Simple Admin fixes
[dr-dolomite](https://github.com/dr-dolomite) for some major stat page improvements and this repos first approved external PR!
### Existing projects:
Simpleadmin heavily uses the AT Command Parsing Scripts (Basically a copy with new changes and tweaks) of Dairyman's Rooter Source https://github.com/ofmodemsandmen/ROOterSource2203
Tailscale was obtained through Tailscale's static build page. Since these modems have a 32-bit ARM processor on-board I used the arm package. https://pkgs.tailscale.com/stable/#static
Entware/opkg was obtained through [Entware's wiki](https://github.com/Entware/Entware/wiki/Alternative-install-vs-standard) and the installer heavily modified by [iamromulan](https://github.com/iamromulan) for use with Quectel modems
TTYd was obtained from the [TTYd Project](https://github.com/tsl0922/ttyd)

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,250 @@
#!/bin/sh
# Modified by iamromlan to set up a proper entware environment for Quectel RM5xx series m.2 modems
TYPE='generic'
#|---------|-----------------|
#| TARGET | Quectel Modem |
#| ARCH | armv7sf-k3.2 |
#| LOADER | ld-linux.so.3 |
#| GLIBC | 2.27 |
#|---------|-----------------|
unset LD_LIBRARY_PATH
unset LD_PRELOAD
ARCH=armv7sf-k3.2
LOADER=ld-linux.so.3
GLIBC=2.27
PRE_OPKG_PATH=$(which opkg)
# Remount filesystem as read-write
mount -o remount,rw /
uninstall_entware() {
echo -e '\033[31mInfo: Starting Entware/OPKG uninstallation...\033[0m'
# Stop services
systemctl stop rc.unslung.service
/opt/etc/init.d/rc.unslung stop
rm /lib/systemd/system/multi-user.target.wants/rc.unslung.service
rm /lib/systemd/system/rc.unslung.service
systemctl stop opt.mount
rm /lib/systemd/system/multi-user.target.wants/start-opt-mount.service
rm /lib/systemd/system/opt.mount
rm /lib/systemd/system/start-opt-mount.service
# Unmount /opt if mounted
mountpoint -q /opt && umount /opt
# Remove Entware installation directory
rm -rf /usrdata/opt
rm -rf /opt
# Reload systemctl daemon
systemctl daemon-reload
# Optionally, clean up any modifications to /etc/profile or other system files
# Restore original link to login binary compiled by Quectel
rm /bin/login
ln /bin/login.shadow /bin/login
echo -e '\033[32mInfo: Entware/OPKG has been uninstalled successfully.\033[0m'
}
# Check if /opt exists
if [ -d /opt ]; then
echo -e "\033[32mDo you want to uninstall Entware/OPKG first? It is already installed.\033[0m"
echo -e "\033[32mThis will also resore your login process to Quectel Stock\033[0m"
echo -e "\033[32m1) Yes\033[0m"
echo -e "\033[32m2) No\033[0m"
echo -e "\033[32m3) Cancel\033[0m"
read -p "Select an option: " choice
case $choice in
1)
# Call the uninstall function
uninstall_entware
exit 0
;;
2)
# Continue with the script
echo "Continuing with the script..."
;;
3)
echo "Canceling. Exiting script."
exit 0
;;
*)
echo "Invalid option. Please select 1, 2, or 3."
;;
esac
fi
create_opt_mount() {
# Bind /usrdata/opt to /opt
echo -e '\033[32mInfo: Setting up /opt mount to /usrdata/opt...\033[0m'
cat <<EOF > /lib/systemd/system/opt.mount
[Unit]
Description=Bind /usrdata/opt to /opt
[Mount]
What=/usrdata/opt
Where=/opt
Type=none
Options=bind
[Install]
WantedBy=multi-user.target
EOF
systemctl daemon-reload
systemctl start opt.mount
# Additional systemd service to ensure opt.mount starts at boot
echo -e '\033[32mInfo: Creating service to start opt.mount at boot...\033[0m'
cat <<EOF > /lib/systemd/system/start-opt-mount.service
[Unit]
Description=Ensure opt.mount is started at boot
After=network.target
[Service]
Type=oneshot
ExecStart=/bin/systemctl start opt.mount
[Install]
WantedBy=multi-user.target
EOF
systemctl daemon-reload
ln -s /lib/systemd/system/start-opt-mount.service /lib/systemd/system/multi-user.target.wants/start-opt-mount.service
}
if [ -n "$PRE_OPKG_PATH" ]; then
while true; do
echo -e "\033[32mopkg already exists at: $PRE_OPKG_PATH\033[0m"
echo -e "\033[32mDo you want to rename it to opkg_old?\033[0m"
echo -e "\033[32m1) Yes (Highly Recommended)\033[0m"
echo -e "\033[32m2) No (The opkg command may not work)\033[0m"
read -p "Select an option (1 or 2): " user_choice
case $user_choice in
1)
mv "$PRE_OPKG_PATH" "${PRE_OPKG_PATH}_old"
echo "Factory/Already existing opkg has been renamed to opkg_old."
break
;;
2)
echo "Proceeding without renaming opkg."
break
;;
*)
echo "Invalid option. Please select 1 or 2."
;;
esac
done
else
echo "Info: no existing opkg binary detected, proceeding with installation"
fi
echo -e '\033[32mInfo: Creating /opt mount pointed to /usrdata/opt ...\033[0m'
create_opt_mount
echo -e '\033[32mInfo: Proceeding with main installation ...\033[0m'
# no need to create many folders. entware-opt package creates most
for folder in bin etc lib/opkg tmp var/lock
do
if [ -d "/opt/$folder" ]; then
echo -e '\033[31mWarning: Folder /opt/$folder exists!\033[0m'
echo -e '\033[31mWarning: If something goes wrong please clean /opt folder and try again.\033[0m'
else
mkdir -p /opt/$folder
fi
done
echo -e '\033[32mInfo: Opkg package manager deployment...\033[0m'
URL=http://bin.entware.net/${ARCH}/installer
wget $URL/opkg -O /opt/bin/opkg
chmod 755 /opt/bin/opkg
wget $URL/opkg.conf -O /opt/etc/opkg.conf
echo -e '\033[32mInfo: Basic packages installation...\033[0m'
/opt/bin/opkg update
/opt/bin/opkg install entware-opt
# Fix for multiuser environment
chmod 777 /opt/tmp
for file in passwd group shells shadow gshadow; do
if [ $TYPE = 'generic' ]; then
if [ -f /etc/$file ]; then
ln -sf /etc/$file /opt/etc/$file
else
[ -f /opt/etc/$file.1 ] && cp /opt/etc/$file.1 /opt/etc/$file
fi
else
if [ -f /opt/etc/$file.1 ]; then
cp /opt/etc/$file.1 /opt/etc/$file
fi
fi
done
[ -f /etc/localtime ] && ln -sf /etc/localtime /opt/etc/localtime
# Create and enable rc.unslung service
echo -e '\033[32mInfo: Creating rc.unslung (Entware init.d service)...\033[0m'
cat <<EOF > /lib/systemd/system/rc.unslung.service
[Unit]
Description=Start Entware services
[Service]
Type=oneshot
# Add a delay to give /opt time to mount
ExecStartPre=/bin/sleep 5
ExecStart=/opt/etc/init.d/rc.unslung start
RemainAfterExit=yes
[Install]
WantedBy=multi-user.target
EOF
systemctl daemon-reload
ln -s /lib/systemd/system/rc.unslung.service /lib/systemd/system/multi-user.target.wants/rc.unslung.service
systemctl start rc.unslung.service
echo -e '\033[32mInfo: Congratulations!\033[0m'
echo -e '\033[32mInfo: If there are no errors above then Entware was successfully initialized.\033[0m'
echo -e '\033[32mInfo: Add /opt/bin & /opt/sbin to $PATH variable\033[0m'
echo -e '\033[32mInfo: Run export PATH=/opt/bin:/opt/sbin:$PATH to do it for this session only\033[0m'
echo -e '\033[32mInfo: opkg at /opt/bin will be linked to /bin but any package you install with opkg will not be automatically.\033[0m'
ln -sf /opt/bin/opkg /bin
opkg update && opkg install shadow-login shadow-passwd
if [ "$?" -ne 0 ]; then
echo -e "\e[1;31mPackage installation failed. Please check your internet connection and try again.\e[0m"
exit 1
fi
# Replace the login and passwd binaries and set home for root to a writable directory
rm /opt/etc/shadow
rm /opt/etc/passwd
cp /etc/shadow /opt/etc/
cp /etc/passwd /opt/etc
mkdir /usrdata/root
mkdir /usrdata/root/bin
touch /usrdata/root/.profile
echo "# Set PATH for all shells" > /usrdata/root/.profile
echo "export PATH=/bin:/usr/sbin:/usr/bin:/sbin:/opt/sbin:/opt/bin:/usrdata/root/bin" >> /usrdata/root/.profile
chmod +x /usrdata/root/.profile
sed -i '1s|/home/root:/bin/sh|/usrdata/root:/bin/bash|' /opt/etc/passwd
rm /bin/login /usr/bin/passwd
ln -sf /opt/bin/login /bin
ln -sf /opt/bin/passwd /usr/bin/
echo -e "\e[1;31mPlease set your system login password.\e[0m"
/usr/bin/passwd
# Install basic and useful utilites
opkg install mc
ln -sf /opt/bin/mc /bin
opkg install htop
ln -sf /opt/bin/htop /bin
opkg install dfc
ln -sf /opt/bin/dfc /bin
opkg install lsof
ln -sf /opt/bin/lsof /bin
# Remount filesystem as read-only
mount -o remount,ro /

View File

@@ -0,0 +1,28 @@
#!/bin/bash
while true; do
# Run AT+CGMM to get the modem model
echo -en "AT+CGMM\r\n" | microcom -t 1000 /dev/ttyOUT > /tmp/modemmodel.txt
sleep 2
# Run AT+CGCONTRDP once then proceed to while loop
echo -en "AT+CGCONTRDP=1\r\n" | microcom -t 1000 /dev/ttyOUT > /tmp/apn.txt
sleep 2
# Run AT+QUIMSLOT? to get the current sim slot
echo -en "AT+QUIMSLOT?\r\n" | microcom -t 1000 /dev/ttyOUT > /tmp/simslot.txt
sleep 2
# Send request to modem and wait 3 seconds for data
echo -en "AT+QSPN;+CEREG=2;+CEREG?;+CEREG=0;+C5GREG=2;+C5GREG?;+C5GREG=0;+CSQ;+QENG=\"servingcell\";+QRSRP;+QCAINFO;+QNWPREFCFG=\"mode_pref\";+QTEMP\r\n" \
| microcom -t 3000 /dev/ttyOUT > /tmp/modemstatus.txt
if [ $? -eq 0 ]
then
# Parse
if [ -f /tmp/modemstatus.txt ]
then
/usrdata/simpleadmin/scripts/modemstatus_parse.sh
fi
fi
sleep 25 # Add a sleep to avoid CPU overload
done

View File

@@ -0,0 +1,540 @@
#!/bin/bash
# Adapted to work with RJ45 / Quectel Board Dev
# Quectel AT Parsing Original source ROOter2203
# https://github.com/ofmodemsandmen/ROOterSource2203/blob/6636758b945ff16b6c5b54494de04b74b011c204/package/rooter/ext-rooter-basic/files/usr/lib/rooter/common/quecteldata.sh
#
rspr2rssi() {
echo ${RSCP} ${BW_N} | awk '{printf "%.0f\n", (($1+10*log(12*$2)/log(10)))}'
}
lte_bw() {
BW=$(echo $BW | grep -o "[0-5]\{1\}")
case $BW in
"0")
BW="1.4" ;;
"1")
BW="3" ;;
"2"|"3"|"4"|"5")
BW=$((($(echo $BW) - 1) * 5)) ;;
esac
}
nr_bw() {
BW=$(echo $BW | grep -o "[0-9]\{1,2\}")
case $BW in
"0"|"1"|"2"|"3"|"4"|"5")
BW=$((($(echo $BW) + 1) * 5)) ;;
"6"|"7"|"8"|"9"|"10"|"11"|"12")
BW=$((($(echo $BW) - 2) * 10)) ;;
"13")
BW="200" ;;
"14")
BW="400" ;;
esac
}
# Function to get the secondary LTE & NR5G bands
get_secondary_bands_lte() {
# Extract the LTE BANDs from SCC lines from /tmp/modemstatus.txt.
# If there are multiple bands, they will be concatenated with <br/> tags.
SC_BANDS=$(grep -o '"LTE BAND [0-9]\+"' /tmp/modemstatus.txt | tr -d '"' | sed '1d' | sed ':a;N;$!ba;s/\n/<br\/>/g')
# If there are no LTE bands or NR5G bands, set SC_BANDS to empty
if [ -z "$SC_BANDS" ]; then
SC_BANDS="-"
fi
}
# Function to get the secondary NR5G bands for NR5G NSA
get_secondary_bands_nsa() {
# Extract the NR5G NSA BANDs from SCC lines from /tmp/modemstatus.txt.
# If there are multiple bands, they will be concatenated with <br/> tags.
SC_BANDS_NSA=$(grep -o '"NR5G BAND [0-9]\+"' /tmp/modemstatus.txt | tr -d '"' | sed ':a;N;$!ba;s/\n/<br\/>/g')
echo $SC_BANDS_NSA > /tmp/scbands.txt
# If there are no NR5G NSA bands, set SC_BANDS_NSA to empty
if [ -z "$SC_BANDS_NSA" ]; then
SC_BANDS_NSA="-"
fi
}
get_secondary_bands_sa() {
# Extract the NR5G SA BANDs from SCC lines from /tmp/modemstatus.txt.
# If there are multiple bands, they will be concatenated with <br/> tags.
SC_BANDS=$(grep -o '"NR5G BAND [0-9]\+"' /tmp/modemstatus.txt | tr -d '"' | sed '1d' | sed ':a;N;$!ba;s/\n/<br\/>/g')
# If there are no NR5G SA bands, set SC_BANDS to empty
if [ -z "$SC_BANDS" ]; then
SC_BANDS="-"
fi
}
# Get the modem model from /tmp/modemmodel.txt and parse it
MODEM_MODEL=$(</tmp/modemmodel.txt)
# Get the model name from the modem model (they either start with RG or RM)
MODEM_MODEL=$(echo "$MODEM_MODEL" | grep -o "RG[^ ]\+\|RM[^ ]\+")
# Get the APN from /tmp/apn.txt and parse it
APN=$(grep "^+CGCONTRDP" /tmp/apn.txt | awk -F',' '{gsub(/"/, "", $3); print $3}')
# Get the SIM slot from /tmp/simslot.txt and parse it
# simslot.txt looks like this: +QUIMSLOT: 1
SIMSLOT=$(</tmp/simslot.txt)
SIMSLOT=$(echo "$SIMSLOT" | grep -o "[0-9]")
# Append SIM before the SIM slot number
SIMSLOT="SIM "$SIMSLOT
# Read File
OX=$(</tmp/modemstatus.txt)
OX=$(echo $OX | tr 'a-z' 'A-Z')
RSRP=""
RSRQ=""
CHANNEL="-"
ECIO="-"
RSCP="-"
ECIO1=" "
RSCP1=" "
MODE="-"
MODTYPE="-"
NETMODE="-"
LBAND="-"
PCI="-"
CTEMP="-"
SINR="-"
COPS="-"
COPS_MCC="-"
COPS_MNC="-"
CID=""
CID5=""
RAT=""
QSPN=$(echo $OX | grep -o '+QSPN: "[^"]*","[^"]*","[^"]*",[^"]*,"[^"]*"' | cut -c 8-)
# GET MCCMNC from the last field of QSPN
MCCMNC=$(echo $QSPN | cut -d, -f5 | tr -d '"')
PROVIDER=$(echo $QSPN | cut -d, -f1 | tr -d '"')
PROVIDER_ID=$(echo $QSPN | cut -d, -f5 | tr -d '"')
CSQ=$(echo $OX | grep -o "+CSQ: [0-9]\{1,2\}" | grep -o "[0-9]\{1,2\}")
if [ "$CSQ" = "99" ]; then
CSQ=""
fi
if [ -n "$CSQ" ]; then
CSQ_PER=$(($CSQ * 100/31))"%"
CSQ_RSSI=$((2 * CSQ - 113))" dBm"
else
CSQ="-"
CSQ_PER="-"
CSQ_RSSI="-"
fi
NR_NSA=$(echo $OX | grep -o "+QENG:[ ]\?\"NR5G-NSA\",")
NR_SA=$(echo $OX | grep -o "+QENG: \"SERVINGCELL\",[^,]\+,\"NR5G-SA\",\"[DFT]\{3\}\",")
if [ -n "$NR_NSA" ]; then
QENG=",,"$(echo $OX" " | grep -o "+QENG: \"LTE\".\+\"NR5G-NSA\"," | tr " " ",")
if [ -z "$QENG5" ]; then
# Fixed an issue where the last 2 digits were not included in the regex
QENG5=$(echo $OX | grep -o "+QENG:[ ]\?\"NR5G-NSA\",[0-9]\{3\},[0-9]\{2,3\},[0-9]\{1,5\},-[0-9]\{2,3\},[-0-9]\{1,3\},-[0-9]\{2,3\},[0-9]\{1,6\},[0-9]\{1,3\},[0-9]\{1,3\},[0-9]\{1,3\}")
if [ -n "$QENG5" ]; then
QENG5=$QENG5",,"
fi
fi
elif [ -n "$NR_SA" ]; then
QENG=$(echo $NR_SA | tr " " ",")
QENG5=$(echo $OX | grep -o "+QENG: \"SERVINGCELL\",[^,]\+,\"NR5G-SA\",\"[DFT]\{3\}\",[ 0-9]\{3,4\},[0-9]\{2,3\},[0-9A-F]\{1,10\},[0-9]\{1,5\},[0-9A-F]\{2,6\},[0-9]\{6,7\},[0-9]\{1,3\},[0-9]\{1,2\},-[0-9]\{2,5\},-[0-9]\{2,3\},[-0-9]\{1,3\}")
else
QENG=$(echo $OX" " | grep -o "+QENG: [^ ]\+ " | tr " " ",")
fi
QCA=$(echo $OX" " | grep -o "+QCAINFO: \"S[CS]\{2\}\".\+NWSCANMODE" | tr " " ",")
QNSM=$(echo $OX | grep -o "+QCFG: \"NWSCANMODE\",[0-9]")
QNWP=$(echo $OX | grep -o "+QNWPREFCFG: \"MODE_PREF\",[A-Z5:]\+" | cut -d, -f2)
QTEMP=$(echo $OX | grep -o "+QTEMP: [0-9]\{1,3\}")
if [ -z "$QTEMP" ]; then
QTEMP=$(echo $OX | grep -o "+QTEMP:[ ]\?\"XO[_-]THERM[_-][^,]\+,[\"]\?[0-9]\{1,3\}" | grep -o "[0-9]\{1,3\}")
fi
if [ -z "$QTEMP" ]; then
QTEMP=$(echo $OX | grep -o "+QTEMP:[ ]\?\"MDM-CORE-USR.\+[0-9]\{1,3\}\"" | cut -d\" -f4)
fi
if [ -z "$QTEMP" ]; then
QTEMP=$(echo $OX | grep -o "+QTEMP:[ ]\?\"MDMSS.\+[0-9]\{1,3\}\"" | cut -d\" -f4)
fi
if [ -n "$QTEMP" ]; then
CTEMP=$(echo $QTEMP | grep -o "[0-9]\{1,3\}")$(printf "\xc2\xb0")"C"
fi
RAT=$(echo $QENG | cut -d, -f4 | grep -o "[-A-Z5]\{3,7\}")
rm -f /tmp/modnetwork
case $RAT in
"GSM")
MODE="GSM"
;;
"WCDMA")
MODE="WCDMA"
CHANNEL=$(echo $QENG | cut -d, -f9)
RSCP=$(echo $QENG | cut -d, -f12)
RSCP="-"$(echo $RSCP | grep -o "[0-9]\{1,3\}")
ECIO=$(echo $QENG | cut -d, -f13)
ECIO="-"$(echo $ECIO | grep -o "[0-9]\{1,3\}")
;;
"LTE"|"CAT-M"|"CAT-NB")
MODE=$(echo $QENG | cut -d, -f5 | grep -o "[DFT]\{3\}")
if [ -n "$MODE" ]; then
MODE="$RAT $MODE"
else
MODE="$RAT"
fi
get_secondary_bands_lte
PCI=$(echo $QENG | cut -d, -f9)
CHANNEL=$(echo $QENG | cut -d, -f10)
LBAND=$(echo $QENG | cut -d, -f11 | grep -o "[0-9]\{1,3\}")
BW=$(echo $QENG | cut -d, -f12)
lte_bw
BWU=$BW
BW=$(echo $QENG | cut -d, -f13)
lte_bw
BWD=$BW
if [ -z "$BWD" ]; then
BWD="unknown"
fi
if [ -z "$BWU" ]; then
BWU="unknown"
fi
if [ -n "$LBAND" ]; then
PC_BAND="LTE BAND "$LBAND
LBAND="B"$LBAND" (Bandwidth $BWD MHz Down | $BWU MHz Up)"
fi
RSRP=$(echo $QENG | cut -d, -f15 | grep -o "[0-9]\{1,3\}")
if [ -n "$RSRP" ]; then
RSCP="-"$RSRP
RSRPLTE=$RSCP
fi
RSRQ=$(echo $QENG | cut -d, -f16 | grep -o "[0-9]\{1,3\}")
if [ -n "$RSRQ" ]; then
ECIO="-"$RSRQ
fi
RSSI=$(echo $QENG | cut -d, -f17 | grep -o "\-[0-9]\{1,3\}")
if [ -n "$RSSI" ]; then
CSQ_RSSI=$RSSI" dBm"
fi
SINRR=$(echo $QENG | cut -d, -f18 | grep -o "[0-9]\{1,3\}")
if [ -n "$SINRR" ]; then
if [ $SINRR -le 25 ]; then
SINR=$((($(echo $SINRR) * 2) -20))" dB"
fi
fi
if [ -n "$(echo $QENG | cut -d, -f21)" ]; then
CQI=$(echo $QENG | cut -d, -f19 | grep "^[0-9]\+$")
if [ -n "$SINR" -a -n "$CQI" -a "$CQI" != "0" ]; then
SINR=$SINR" (CQI $CQI)"
fi
fi
if [ -n "$NR_NSA" ]; then
# Changed network mode to NR5G NSA for easy identification
MODE="NR5G NSA"
echo "0" > /tmp/modnetwork
if [ -n "$QENG5" ]; then
QENG5=$QENG5",,"
get_secondary_bands_nsa
# Append the SC_BANDS_NSA to SC_BANDS with <br /> tags
SC_BANDS=$SC_BANDS"<br />"$SC_BANDS_NSA
PCI="$PCI, "$(echo $QENG5 | cut -d, -f4)
SCHV=$(echo $QENG5 | cut -d, -f8)
SLBV=$(echo $QENG5 | cut -d, -f9) # Now correctly captures the NR band
BW=$(echo $QENG5 | cut -d, -f10) # Now gets the correct BW
if [ -n "$SLBV" ]; then
LBAND=$LBAND"<br />n"$SLBV
if [ -n "$BW" ]; then
nr_bw
LBAND=$LBAND" (Bandwidth $BW MHz)"
fi
if [ "$SCHV" -ge 123400 ]; then
CHANNEL=$CHANNEL", "$SCHV
else
CHANNEL=$CHANNEL", -"
fi
else
# removed the (unknown NR5G BAND) and replaced with No NR5G Band to avoid confusion
LBAND=$LBAND"<br />No NR5G Band Detected"
CHANNEL=$CHANNEL", -"
fi
RSCP=$RSCP" dBm<br />"$(echo $QENG5 | cut -d, -f5)
SINRR=$(echo $QENG5 | cut -d, -f6 | grep -o "[0-9]\{1,3\}")
if [ -n "$SINRR" ]; then
if [ $SINRR -le 30 ]; then
SINR=$SINR"<br />"$((($(echo $SINRR) * 2) -20))" dB"
fi
fi
ECIO=$ECIO" (4G) dB<br />"$(echo $QENG5 | cut -d, -f7)" (5G) "
fi
fi
if [ -z "$LBAND" ]; then
LBAND="-"
else
if [ -n "$QCA" ]; then
QCA=$(echo $QCA | grep -o "\"S[CS]\{2\}\"[-0-9A-Z,\"]\+")
for QCAL in $(echo "$QCA"); do
if [ $(echo "$QCAL" | cut -d, -f7) = "2" ]; then
SCHV=$(echo $QCAL | cut -d, -f2 | grep -o "[0-9]\+")
SRATP="B"
if [ -n "$SCHV" ]; then
CHANNEL="$CHANNEL, $SCHV"
if [ "$SCHV" -gt 123400 ]; then
SRATP="n"
fi
fi
SLBV=$(echo $QCAL | cut -d, -f6 | grep -o "[0-9]\{1,2\}")
if [ -n "$SLBV" ]; then
LBAND=$LBAND"<br />"$SRATP$SLBV
BWD=$(echo $QCAL | cut -d, -f3 | grep -o "[0-9]\{1,3\}")
if [ -n "$BWD" ]; then
UPDOWN=$(echo $QCAL | cut -d, -f13)
case "$UPDOWN" in
"UL" )
CATYPE="CA"$(printf "\xe2\x86\x91") ;;
"DL" )
CATYPE="CA"$(printf "\xe2\x86\x93") ;;
* )
CATYPE="CA" ;;
esac
if [ $BWD -gt 14 ]; then
LBAND=$LBAND" ("$CATYPE", Bandwidth "$(($(echo $BWD) / 5))" MHz)"
else
LBAND=$LBAND" ("$CATYPE", Bandwidth 1.4 MHz)"
fi
fi
LBAND=$LBAND
fi
PCI="$PCI, "$(echo $QCAL | cut -d, -f8)
fi
done
fi
fi
if [ $RAT = "CAT-M" ] || [ $RAT = "CAT-NB" ]; then
LBAND="B$(echo $QENG | cut -d, -f11) ($RAT)"
fi
;;
"NR5G-SA")
MODE="NR5G-SA"
echo "0" > /tmp/modnetwork
if [ -n "$QENG5" ]; then
MODE="$RAT $(echo $QENG5 | cut -d, -f4)"
PCI=$(echo $QENG5 | cut -d, -f8)
get_secondary_bands_sa
# Apply | sed '1d' to NR_BAND
# Temporarily removed the sed command for testing
CHANNEL=$(echo $QENG5 | cut -d, -f10)
LBAND=$(echo $QENG5 | cut -d, -f11)
PC_BAND="NR5G BAND "$LBAND
BW=$(echo $QENG5 | cut -d, -f12)
nr_bw
LBAND="n"$LBAND" (Bandwidth $BW MHz)"
RSCP=$(echo $QENG5 | cut -d, -f13)
ECIO=$(echo $QENG5 | cut -d, -f14)
if [ "$CSQ_PER" = "-" ]; then
BW_N=($BW * 5)
RSSI=$(rspr2rssi)
CSQ_PER=$((100 - (($RSSI + 51) * 100/-62)))"%"
CSQ=$((($RSSI + 113) / 2))
CSQ_RSSI=$RSSI" dBm"
fi
SINRR=$(echo $QENG5 | cut -d, -f15 | grep -o "[0-9]\{1,3\}")
if [ -n "$SINRR" ]; then
if [ $SINRR -le 30 ]; then
SINR=$((($(echo $SINRR) * 2) -20))" dB"
fi
fi
fi
;;
esac
QRSRP=$(echo "$OX" | grep -o "+QRSRP:[^,]\+,-[0-9]\{1,5\},-[0-9]\{1,5\},-[0-9]\{1,5\}[^ ]*")
if [ -n "$QRSRP" ] && [ "$RAT" != "WCDMA" ]; then
QRSRP1=$(echo $QRSRP | cut -d, -f1 | grep -o "[-0-9]\+")
QRSRP2=$(echo $QRSRP | cut -d, -f2)
QRSRP3=$(echo $QRSRP | cut -d, -f3)
QRSRP4=$(echo $QRSRP | cut -d, -f4)
QRSRPtype=$(echo $QRSRP | cut -d, -f5)
if [ "$QRSRPtype" == "NR5G" ]; then
if [ -n "$NR_SA" ]; then
RSCP=$QRSRP1
if [ -n "$QRSRP2" -a "$QRSRP2" != "-32768" ]; then
RSCP1="RxD "$QRSRP2
fi
if [ -n "$QRSRP3" -a "$QRSRP3" != "-32768" -a "$QRSRP3" != "-44" ]; then
RSCP=$RSCP" dBm<br />"$QRSRP3
fi
if [ -n "$QRSRP4" -a "$QRSRP4" != "-32768" -a "$QRSRP4" != "-44" ]; then
RSCP1="RxD "$QRSRP4
fi
else
RSCP=$RSRPLTE
if [ -n "$QRSRP1" -a "$QRSRP1" != "-32768" -a "$QRSRP1" != "-44" ]; then
RSCP=$RSCP" (4G) dBm<br />"$QRSRP1
if [ -n "$QRSRP2" -a "$QRSRP2" != "-32768" -a "$QRSRP2" != "-44" ]; then
RSCP="$RSCP, $QRSRP2"
if [ -n "$QRSRP3" -a "$QRSRP3" != "-32768" -a "$QRSRP3" != "-44" ]; then
RSCP="$RSCP, $QRSRP3"
if [ -n "$QRSRP4" -a "$QRSRP4" != "-32768" -a "$QRSRP4" != "-44" ]; then
RSCP="$RSCP, $QRSRP4"
fi
fi
RSCP=$RSCP" (5G) "
fi
fi
fi
elif [ "$QRSRP2$QRSRP3$QRSRP4" != "-44-44-44" -a -z "$QENG5" ]; then
RSCP=$QRSRP1
if [ "$QRSRP3$QRSRP4" == "-140-140" -o "$QRSRP3$QRSRP4" == "-44-44" -o "$QRSRP3$QRSRP4" == "-32768-32768" ]; then
RSCP1="RxD "$(echo $QRSRP | cut -d, -f2)
else
RSCP=$RSCP" dBm (RxD "$QRSRP2" dBm)<br />"$QRSRP3
RSCP1="RxD "$QRSRP4
fi
fi
fi
QNSM=$(echo "$QNSM" | grep -o "[0-9]")
if [ -n "$QNSM" ]; then
MODTYPE="6"
case $QNSM in
"0" )
NETMODE="1" ;;
"1" )
NETMODE="3" ;;
"2"|"5" )
NETMODE="5" ;;
"3" )
NETMODE="7" ;;
esac
fi
if [ -n "$QNWP" ]; then
MODTYPE="6"
case $QNWP in
"AUTO" )
NETMODE="1" ;;
"WCDMA" )
NETMODE="5" ;;
"LTE" )
NETMODE="7" ;;
"LTE:NR5G" )
NETMODE="8" ;;
"NR5G" )
NETMODE="9" ;;
esac
fi
OX=$(echo "${OX//[ \"]/}")
REGV=$(echo "$OX" | grep -o "+C5GREG:2,[0-9],[A-F0-9]\{2,6\},[A-F0-9]\{5,10\},[0-9]\{1,2\}")
if [ -n "$REGV" ]; then
LAC5=$(echo "$REGV" | cut -d, -f3)
LAC5=$LAC5" ($(printf "%d" 0x$LAC5))"
CID5=$(echo "$REGV" | cut -d, -f4)
CID5L=$(printf "%010X" 0x$CID5)
RNC5=${CID5L:1:6}
RNC5=$RNC5" ($(printf "%d" 0x$RNC5))"
CID5=${CID5L:7:3}
CID5="Short $(printf "%X" 0x$CID5) ($(printf "%d" 0x$CID5)), Long $(printf "%X" 0x$CID5L) ($(printf "%d" 0x$CID5L))"
RAT=$(echo "$REGV" | cut -d, -f5)
fi
REGV=$(echo "$OX" | grep -o "+CEREG:2,[0-9],[A-F0-9]\{2,4\},[A-F0-9]\{5,8\}")
REGFMT="3GPP"
if [ -z "$REGV" ]; then
REGV=$(echo "$OX" | grep -o "+CEREG:2,[0-9],[A-F0-9]\{2,4\},[A-F0-9]\{1,3\},[A-F0-9]\{5,8\}")
REGFMT="SW"
fi
if [ -n "$REGV" ]; then
LAC=$(echo "$REGV" | cut -d, -f3)
LAC=$(printf "%04X" 0x$LAC)" ($(printf "%d" 0x$LAC))"
if [ $REGFMT = "3GPP" ]; then
CID=$(echo "$REGV" | cut -d, -f4)
else
CID=$(echo "$REGV" | cut -d, -f5)
fi
CIDL=$(printf "%08X" 0x$CID)
RNC=${CIDL:1:5}
RNC=$RNC" ($(printf "%d" 0x$RNC))"
CID=${CIDL:6:2}
CID="Short $(printf "%X" 0x$CID) ($(printf "%d" 0x$CID)), Long $(printf "%X" 0x$CIDL) ($(printf "%d" 0x$CIDL))"
else
REGV=$(echo "$OX" | grep -o "+CREG:2,[0-9],[A-F0-9]\{2,4\},[A-F0-9]\{2,8\}")
if [ -n "$REGV" ]; then
LAC=$(echo "$REGV" | cut -d, -f3)
CID=$(echo "$REGV" | cut -d, -f4)
if [ ${#CID} -gt 4 ]; then
LAC=$(printf "%04X" 0x$LAC)" ($(printf "%d" 0x$LAC))"
CIDL=$(printf "%08X" 0x$CID)
RNC=${CIDL:1:3}
CID=${CIDL:4:4}
CID="Short $(printf "%X" 0x$CID) ($(printf "%d" 0x$CID)), Long $(printf "%X" 0x$CIDL) ($(printf "%d" 0x$CIDL))"
else
LAC=""
fi
else
LAC=""
fi
fi
REGSTAT=$(echo "$REGV" | cut -d, -f2)
if [ "$REGSTAT" == "5" -a "$COPS" != "-" ]; then
COPS_MNC=$COPS_MNC" (Roaming)"
fi
if [ -n "$CID" -a -n "$CID5" ] && [ "$RAT" == "13" -o "$RAT" == "10" ]; then
LAC="4G $LAC, 5G $LAC5"
CID="4G $CID<br />5G $CID5"
RNC="4G $RNC, 5G $RNC5"
elif [ -n "$CID5" ]; then
LAC=$LAC5
CID=$CID5
RNC=$RNC5
fi
if [ -z "$LAC" ]; then
LAC="-"
CID="-"
RNC="-"
fi
LUPDATE=$(date +%s)
rm -fR /tmp/signal.txt
MODEZ=$(echo $MODE | tr -d '"')
{
echo 'PROVIDER="'"$PROVIDER"'"'
echo 'CSQ="'"$CSQ"'"'
echo 'CSQ_PER="'"$CSQ_PER"'"'
echo 'CSQ_RSSI="'"$CSQ_RSSI"'"'
echo 'ECIO="'"$ECIO"'"'
echo 'RSCP="'"$RSCP"'"'
echo 'ECIO1="'"$ECIO1"'"'
echo 'RSCP1="'"$RSCP1"'"'
echo 'MODE="'"$MODEZ"'"'
echo 'MODTYPE="'"$MODTYPE"'"'
echo 'NETMODE="'"$NETMODE"'"'
echo 'CHANNEL="'"$CHANNEL"'"'
echo 'LBAND="'"$LBAND"'"'
echo 'PC_BAND="'"$PC_BAND"'"'
echo 'SC_BANDS="'"$SC_BANDS"'"'
echo 'APN="'"$APN"'"'
echo 'MODEM_MODEL="'"$MODEM_MODEL"'"'
echo 'SIMSLOT="'"$SIMSLOT"'"'
echo 'PCI="'"$PCI"'"'
echo 'TEMP="'"$CTEMP"'"'
echo 'SINR="'"$SINR"'"'
echo 'LASTUPDATE="'"$LUPDATE"'"'
echo 'COPS="'"$COPS"'"'
echo 'COPS_MCC="'"$COPS_MCC"'"'
echo 'COPS_MNC="'"$COPS_MNC"'"'
echo 'MCCMNC="'"$MCCMNC"'"'
echo 'LAC="'"$LAC"'"'
echo 'LAC_NUM="'""'"'
echo 'CID="'"$CID"'"'
echo 'CID_NUM="'""'"'
echo 'RNC="'"$RNC"'"'
echo 'RNC_NUM="'""'"'
} > /tmp/signal.txt
# Pregenerate JSON File
/usrdata/simpleadmin/scripts/tojson.sh /tmp/signal.txt > /tmp/modemstatus.json

View File

@@ -0,0 +1,48 @@
#!/bin/bash
# sarav (hello@grity.com)
# convert key=value to json
# Created at Gritfy ( Devops Junction )
# Updated by: dr-dolomite to make it more robust since it was failing on some casess
file_name="$1"
echo "{"
last_line=$(wc -l < "$file_name")
first_line=true
while IFS='=' read -r key value || [[ -n "$key" ]]; do
# Skip empty lines and comments
if [[ -z "$key" || "$key" == \#* ]]; then
continue
fi
# Trim leading and trailing whitespace from key and value
key=$(echo "$key" | awk '{$1=$1};1')
value=$(echo "$value" | awk '{$1=$1};1')
# Check if value includes double quotes inside it like: "value,"value"". If there is, remove the inner double quotes.
if [[ "$value" == *\"* ]]; then
value=$(echo "$value" | sed 's/\"//g')
# enclose the value in double quotes again
value="\"$value\""
fi
# Check if value is empty, if so, skip printing this key-value pair
if [[ -z "$value" ]]; then
continue
fi
# Print comma before each pair except for the first one
if $first_line; then
first_line=false
else
printf ','
fi
# Print key-value pair in JSON format without surrounding double quotes on value
printf ' "%s" : %s' "$key" "$value"
printf '\n'
done < "$file_name"
echo "}"

View File

@@ -0,0 +1,9 @@
[Unit]
Description=Simpleadmin service to generate status from modem
[Service]
ExecStart=/usrdata/simpleadmin/scripts/build_modem_status
Restart=always
[Install]
WantedBy=multi-user.target

View File

@@ -0,0 +1,11 @@
[Unit]
Description=SimpleAdmin httpd service
After=network.target
[Service]
Type=simple
ExecStart=/usr/sbin/httpd -f -h /usrdata/simpleadmin/www -p 8080
ExecStop=/bin/kill -WINCH ${MAINPID}
[Install]
WantedBy=multi-user.target

View File

@@ -0,0 +1,352 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>Simple Settings</title>
<script src="/js/alpinejs.min.js" defer></script>
<link rel="stylesheet" href="/css/bulma.css" />
<link rel="stylesheet" type="text/css" href="/css/admin.css" />
<link rel="stylesheet" href="styles.css" />
</head>
<body>
<!-- START NAV -->
<nav class="navbar is-black" x-data="{ isOpen: false }">
<div class="container">
<div class="navbar-brand">
<a class="navbar-item brand-text" href="/"> Simple Admin </a>
<a
role="button"
class="navbar-burger burger"
@click="isOpen = !isOpen"
>
<span aria-hidden="true"></span>
<span aria-hidden="true"></span>
<span aria-hidden="true"></span>
</a>
</div>
<div
id="navMenu"
class="navbar-menu"
:class="isOpen ? 'is-active' : ''"
>
<div class="navbar-start">
<a class="navbar-item" href="/"> Connection Info </a>
<a class="navbar-item" href="/atcommander.html"> AT Commands </a>
<a class="navbar-item" href="/bandlock.html"> Simple Network </a>
<a class="navbar-item" href="/ttl.html"> TTL Changer </a>
<a class="navbar-item" href="/speedtest.html"> OpenSpeedtest </a>
</div>
</div>
</div>
</nav>
<!-- END NAV -->
<div class="container" x-data="atCommands()">
<div class="columns">
<div class="column is-12">
<div class="columns">
<div class="column is-4">
<div class="card">
<header class="card-header">
<p class="card-header-title">AT Command</p>
</header>
<div class="card-content">
<div class="content">
<div class="field">
<label class="label">AT Command</label>
<div class="control">
<input
class="input"
type="text"
placeholder="ATI"
x-model="atcmd"
x-ref="atCmdInput"
@keydown.enter="sendAtCommand()"
/>
</div>
</div>
<div class="field">
<p class="control">
<button
class="button is-success"
@click="sendAtCommand()"
:disabled="isLoading"
>
Send AT Command
</button>
<button
class="button is-danger"
@click="clearResponses()"
:disabled="atCommandResponse === ''"
>
Clear
</button>
</p>
</div>
</div>
</div>
</div>
</div>
<div class="column is-8">
<div class="card">
<header class="card-header">
<p class="card-header-title">AT Command Response</p>
</header>
<div class="card-content">
<div class="content">
<textarea
class="textarea"
placeholder="Multiple commands should be separated by a semicolon. Example: AT+CGMR;+GSN"
rows="10"
x-text="isLoading ? 'Fetching response, please wait...' : atCommandResponse"
></textarea>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- START Useful Commands Section -->
<div class="container" x-data="atCommands()">
<div class="columns">
<div class="column is-12">
<div class="card">
<header class="card-header">
<p class="card-header-title">Useful One Click Commands</p>
</header>
<div class="card-content">
<div class="content">
<div style="display: flex; flex-direction: row; flex-wrap: wrap; margin-top: 1rem; justify-content: space-between;">
<div class="field">
<div class="control">
<button
class="button is-danger"
@click="sendRebootCommand()"
:disabled="isRebooting"
>
Reboot
</button>
</div>
</div>
<!-- <div class="field">
<div class="control">
<button
class="button is-danger"
@click="sendRebootCommand()"
:disabled="isRebooting"
>
Reboot
</button>
</div>
</div> -->
</div>
<p>Here are some useful commands:</p>
<ul>
<li>
<!-- Open to another tab -->
See <a href="https://github.com/iamromulan/RM520N-GL#at-commands" target="_blank" style="cursor: pointer;">https://github.com/iamromulan/RM520N-GL#at-commands</a> for
more
</li>
<li>AT+CFUN=1,1 (reboot)</li>
<li>
AT+QMAPWAC? (get current status of auto connect, 0=disabled
1=enabled)
</li>
<li>
AT+QMAPWAC=1 (enable auto connect internet for ethernet)
</li>
<li>
AT+QMAPWAC=0 (disable auto connect for ethernet; use when
you want internet over usb to work; IPPT must be disabled)
</li>
<li>
AT+QUIMSLOT? (get active sim slot; 1=Slot 1; 2=Slot 2)
</li>
<li>AT+QUIMSLOT=1 (switch to sim slot 1)</li>
<li>AT+QUIMSLOT=2 (switch to sim slot 2)</li>
<li>AT+CGDCONT? (Get active APN profle list 1 through 8)</li>
<li>
AT+CGDCONT=1,"IPV4V6","APNHERE" (Sets APN profle 1 to
APNHERE using both IPV4 and IPV6)
</li>
<li>AT+GSN (Show current IMEI)</li>
<li>AT+EGMR=1,7,"IMEIGOESHERE" (sets/repairs IMEI)</li>
<li>AT+QCAINFO (Show all connected bands/CA info)</li>
<li>
AT+QNWPREFCFG="mode_pref" (Check what the current network
search mode is set to)
</li>
<li>
AT+QNWPREFCFG="mode_pref",AUTO (Set network search mode to
automatic)
</li>
<li>
AT+QNWPREFCFG="mode_pref",NR5G:LTE (Set network search mode
to 5G/NR and 4G/LTE only)
</li>
<li>
AT+QNWPREFCFG="mode_pref",NR5G (Set network search mode to
5G/NR only)
</li>
<li>
AT+QNWPREFCFG="mode_pref",LTE (Set network search mode to
4G/LTE only)
</li>
<li>
AT+QNWPREFCFG="nr5g_disable_mode" (Check to see if SA or NSA
NR5G is disabled)
</li>
<li>
AT+QNWPREFCFG="nr5g_disable_mode",0 (Enable Both SA and NSA
5G/NR)
</li>
<li>
AT+QNWPREFCFG="nr5g_disable_mode",1 (Disable SA 5G/NR only)
</li>
<li>
AT+QNWPREFCFG="nr5g_disable_mode",2 (Disable NSA 5G/NR only)
</li>
<li>
AT+QNWPREFCFG="nr5g_band" (Get current 5G/NR bandlock
settings)
</li>
<li>
AT+QNWPREFCFG="nr5g_band",1:2:3:4:5:6 (Example: Lock to
5G/NR bands n1,n2,n3,n4,n5, and n6)
</li>
<li>
AT+QNWPREFCFG="lte_band" (Get current 4G/LTE bandlock
settings)
</li>
<li>
AT+QNWPREFCFG="lte_band",1:2:3:4:5:6 (Example: Lock to
4G/LTE bands 1,2,3,4,5, and 6)
</li>
<li>
AT+QMAP="WWAN" (Show currently assigned IPv4 and IPv6 from
the provider)
</li>
<li>
AT+QMAP="LANIP" (Show current DHCP range and Gateway address
for VLAN0)
</li>
<li>
AT+QMAP="LANIP",IP_start_range,IP_end_range,Gateway_IP (Set
IPv4 Start/End range and Gateway IP of DHCP for VLAN0)
</li>
<li>
AT+QMAP="DHCPV4DNS","disable" (disable the onboard DNS
proxy; recommended for IPPT)
</li>
<li>
AT+QMAP="MPDN_rule",0,1,0,1,1,"FF:FF:FF:FF:FF:FF" (Turn on
IP Passthrough for Ethernet)
</li>
<li>
AT+QMAP="MPDN_rule",0 (turn off IPPT/clear MPDN rule 0;
Remember to run AT+QMAPWAC=1 and reboot after)
</li>
</ul>
</div>
</div>
</div>
</div>
</div>
<!-- Loading modal -->
<div x-show="isRebooting" class="modal-overlay">
<div class="loading-modal">
<div class="spinner"></div>
<div
class="loading-text"
style="display: flex; flex-direction: column"
>
<h3>Rebooting...</h3>
<p style="margin-top: 0.5rem">
Please wait for
<span x-text="countdown" style="font-weight: 500"></span> seconds
before refreshing the page.
</p>
</div>
</div>
</div>
</div>
<script>
function atCommands() {
return {
isLoading: false,
isRebooting: false,
countdown: 40, // Total waiting time in seconds
atcmd: null,
defaultAtCommand: "ATI",
atCommandResponse: "",
sendAtCommand() {
if (!this.atcmd) {
// Use ATI as default command
this.atcmd = "ATI";
console.log(
"AT Command is empty, using ATI as default command: ",
this.atcmd
);
}
this.isLoading = true;
fetch(
"/cgi-bin/get_atcommand?" +
new URLSearchParams({
atcmd: this.atcmd,
})
)
.then((res) => {
return res.text();
})
.then((data) => {
this.atCommandResponse = data;
this.isLoading = false;
})
.finally(() => {
this.isLoading = false;
});
},
clearResponses() {
this.atCommandResponse = "";
},
sendRebootCommand() {
this.atcmd = "AT+CFUN=1,1";
this.isRebooting = true;
console.log("Reboot command sent: ", this.atcmd);
fetch(
"/cgi-bin/get_atcommand?" +
new URLSearchParams({
atcmd: this.atcmd,
})
)
.then((res) => {
return res.text();
})
.then((data) => {
this.atCommandResponse =
"Rebooting... Please wait a few seconds before refreshing the page.";
})
.finally(() => {
let timer = setInterval(() => {
this.countdown--;
if (this.countdown <= 0) {
clearInterval(timer);
this.isRebooting = false;
}
}, 1000); // Update countdown every second
});
},
};
}
</script>
</body>
</html>

View File

@@ -0,0 +1,875 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>Simple Network</title>
<script src="/js/alpinejs.min.js" defer></script>
<link rel="stylesheet" href="/css/bulma.css" />
<link rel="stylesheet" type="text/css" href="/css/admin.css" />
<link rel="stylesheet" href="styles.css" />
<style>
.container {
display: flex;
justify-content: center;
}
.card-column {
margin-bottom: 20px;
}
</style>
</head>
<body>
<!-- START NAV -->
<nav class="navbar is-black" x-data="{ isOpen: false }">
<div class="container">
<div class="navbar-brand">
<a class="navbar-item brand-text" href="/"> Simple Admin </a>
<a
role="button"
class="navbar-burger burger"
@click="isOpen = !isOpen"
>
<span aria-hidden="true"></span>
<span aria-hidden="true"></span>
<span aria-hidden="true"></span>
</a>
</div>
<div
id="navMenu"
class="navbar-menu"
:class="isOpen ? 'is-active' : ''"
>
<div class="navbar-start">
<a class="navbar-item" href="/"> Connection Info </a>
<a class="navbar-item" href="/atcommander.html"> AT Commands </a>
<a class="navbar-item" href="/bandlock.html"> Simple Network </a>
<a class="navbar-item" href="/ttl.html"> TTL Changer </a>
<a class="navbar-item" href="/speedtest.html"> OpenSpeedtest </a>
</div>
</div>
</div>
</nav>
<!-- END NAV -->
<div
class="container"
style="margin: auto"
x-data="atCommands()"
x-init="atCommands()"
>
<div class="columns is-multiline">
<!-- First Box -->
<div class="column is-8-tablet is-6-desktop card-column">
<div class="card">
<header class="card-header">
<p class="card-header-title">Band Locking</p>
</header>
<div class="card-content">
<!-- Create a drop down -->
<div class="select is-primary">
<select id="networkMode" x-model="networkMode">
<option>Select Network Mode</option>
<option>LTE</option>
<option>NR5G-NSA</option>
<option>NR5G-SA</option>
</select>
</div>
<input
class="input is-primary"
type="text"
placeholder="Example: 1,3,41"
style="margin-top: 1rem"
x-model="bandNumbers"
/>
<div style="margin-top: 1rem; display: flex; flex-direction: row">
<button
class="button is-primary"
@click="lockBands()"
:disabled="isLocking"
>
Lock Bands
</button>
<button
class="button is-warning"
style="margin-left: 1rem"
@click="restoreBands()"
:disabled="isLocking"
>
Restore Bands
</button>
</div>
</div>
<div class="card-footer" style="padding: 0.25rem">
<p class="card-footer-item">
Please first select your desired network band from the dropdown
menu. To lock onto specific bands, please type the band numbers
separated by commas (,) and then click the lock button.
</p>
</div>
</div>
</div>
<!-- Second Box -->
<div class="column is-8-tablet is-6-desktop card-column">
<div class="card">
<header class="card-header">
<p class="card-header-title">LTE and NR5G Cell Locking</p>
</header>
<div class="card-content">
<div style="display: flex; flex-direction: row">
<div class="select is-info">
<select id="networkModeSelect" x-model="networkMode2">
<option>Select Network Mode</option>
<option>LTE</option>
<option>NR5G-SA</option>
</select>
</div>
<div
id="numFreqContainer"
style="margin-left: 1rem"
x-show="networkMode2 == 'LTE'"
>
<input
class="input is-info"
type="number"
id="numFreqInput"
min="1"
max="10"
placeholder="1-10"
x-model="cellNum"
/>
</div>
</div>
<div id="freqNumbersContainer"></div>
<!-- For NR5G-SA -->
<div style="margin-top: 1rem" x-show="networkMode2 == 'NR5G-SA'">
<div style="display: flex; flex-direction: row">
<input
class="input is-info"
type="text"
placeholder="EARFCN"
x-model="earfcn1"
/>
<input
class="input is-info"
type="text"
placeholder="PCI"
x-model="pci1"
style="margin-left: 1rem"
/>
<input
class="input is-info"
type="text"
placeholder="SCS"
x-model="scs"
style="margin-left: 1rem"
/>
<input
class="input is-info"
type="text"
placeholder="BAND"
x-model="band"
style="margin-left: 1rem"
/>
</div>
</div>
<div style="margin-top: 1rem; display: flex; flex-direction: row">
<button
class="button is-info"
@click="cellLock()"
:disabled="isLocking"
>
Lock Cell
</button>
<button
class="button is-warning"
style="margin-left: 1rem"
@click="restoreCell()"
:disabled="isLocking"
>
Restore Cell
</button>
</div>
</div>
<div class="card-footer" style="padding: 0.25rem">
<p class="card-footer-item">
To utilize cell locking, first, select the network mode, then
specify the number of cells you wish to lock onto (max 10) if
you are using LTE. Next, input the EARFCN and PCI values for
each cell number. If you are locking through NR5G-SA instead,
simply fill up all the required parameters.
</p>
</div>
</div>
</div>
<!-- Third Box -->
<div class="column is-8-tablet is-6-desktop card-column">
<div class="card">
<header class="card-header">
<p class="card-header-title">Network Utilities</p>
</header>
<div class="card-content">
<div style="display: flex; flex-direction: column">
<div>
<p>Set Network APN</p>
<div
style="
display: flex;
flex-direction: row;
margin-top: 0.5rem;
"
>
<input
class="input is-link"
type="text"
placeholder="APN"
x-model="apnInput"
/>
<button
class="button is-link"
style="margin-left: 1rem"
@click="setAPN()"
:disabled="isLoading"
>
Set APN
</button>
</div>
</div>
</div>
<div style="margin-top: 1rem">
<p>Set Preferred Network Mode</p>
<div
style="display: flex; flex-direction: row; margin-top: 0.5rem"
>
<div class="select is-info">
<select id="prefNetworkMode" x-model="prefNetworkMode">
<option>Select Network</option>
<option>AUTO</option>
<option>WCDMA</option>
<option>LTE ONLY</option>
<option>NR5G-NSA</option>
<option>NR5G-SA</option>
</select>
</div>
<button
class="button is-link"
style="margin-left: 1rem"
@click="setPrefNetwork()"
:disabled="isLoading"
>
Set Network
</button>
</div>
</div>
<div style="margin-top: 1rem">
<p>Set Sim Slot</p>
<div
style="display: flex; flex-direction: row; margin-top: 0.5rem"
>
<div class="select is-info">
<select id="simSlot" x-model="simSlot">
<option>Select Sim</option>
<option>1</option>
<option>2</option>
</select>
</div>
<button
class="button is-link"
style="margin-left: 1rem"
@click="setSimSlot()"
:disabled="isLoading"
>
Set Sim
</button>
</div>
</div>
<div style="margin-top: 1rem">
<p>NR5G Mode Control</p>
<div
style="display: flex; flex-direction: row; margin-top: 0.5rem"
>
<div class="select is-info">
<select id="nrMode" x-model="nrMode">
<option>Select NR5G Mode</option>
<option>Enable All</option>
<option>Disable NSA</option>
<option>Disable SA</option>
</select>
</div>
<button
class="button is-link"
style="margin-left: 1rem"
@click="nr5GMode()"
:disabled="isLoading"
>
Set Mode
</button>
</div>
</div>
</div>
<div class="card-footer" style="padding: 0.25rem">
<p class="card-footer-item">
This section allows you to set the APN, preferred network mode,
and sim slot. Simply input the desired values and click the
respective buttons to apply the changes. (Sim slot is only
applicable for dual sim devices.)
</p>
</div>
</div>
</div>
<!-- Fourth Box -->
<div class="column is-8-tablet is-6-desktop card-column">
<div class="card">
<header class="card-header">
<p class="card-header-title">Query Network Parameters</p>
</header>
<div class="card-content">
<textarea
class="textarea"
rows="5"
placeholder="Query Response"
x-text=" isLoading ? 'Getting Response...' : queryCommandResponse"
></textarea>
<div style="margin-top: 1.5rem">
<p>Check Locked Bands</p>
<div class="select is-info" style="margin-top: 0.5rem">
<select id="lockedBands" x-model="lockedBands">
<option>Locked Bands</option>
<option>LTE</option>
<option>NSA</option>
<option>SA</option>
</select>
</div>
<button
class="button is-link"
style="margin-left: 1rem; margin-top: 0.5rem"
@click="showLockedBands()"
:disabled="isLoading"
>
Check Bands
</button>
</div>
<div style="margin-top: 1rem">
<p>Check Cell Lock</p>
<div class="select is-info" style="margin-top: 0.5rem">
<select id="cellState" x-model="cellState">
<option>Cell Lock</option>
<option>LTE</option>
<option>NR5G-SA</option>
</select>
</div>
<button
class="button is-link"
style="margin-left: 1rem; margin-top: 0.5rem"
@click="showCellState()"
:disabled="isLoading"
>
Check Cell
</button>
</div>
<div style="margin-top: 2rem">
<p>Other Network Query</p>
</div>
<div class="buttons" style="margin-top: 0.5rem">
<button
class="button is-link mr-2"
@click="showSupportedBands()"
:disabled="isLoading"
>
Show Supported Bands
</button>
<button
class="button is-link mr-2"
@click="showCurrentSim()"
:disabled="isLoading"
>
Show Current Sim
</button>
<button
class="button is-link"
@click="showPrefNetwork()"
:disabled="isLoading"
>
Show Pref Network
</button>
<button
class="button is-link"
@click="showCaInfo()"
:disabled="isLoading"
>
Carrier Aggregation Info
</button>
<button
class="button is-link"
@click="servingCellInfo()"
:disabled="isLoading"
>
Serving Cell Info
</button>
<button
class="button is-link"
@click="scanNeighbourCells()"
:disabled="isLoading"
>
Scan Neighbour Cells
</button>
<button
class="button is-link"
@click="scanNsaCells()"
:disabled="isLoading"
>
Scan NSA Cells
</button>
<button
class="button is-link"
@click="fullNetworkScan()"
:disabled="isLoading"
>
Full Network Scan
</button>
</div>
</div>
<div class="card-footer" style="padding: 0.25rem">
<p class="card-footer-item">
This section allows you to query the network parameters. Simply
click the respective buttons to view the network parameters.
</p>
</div>
</div>
</div>
</div>
<!-- Loading Modal for Locking Band -->
<div x-show="isLocking" class="modal-overlay">
<div class="loading-modal">
<div class="spinner"></div>
<div
class="loading-text"
style="display: flex; flex-direction: column"
>
<h3>Initializing Network...</h3>
<p style="margin-top: 0.5rem">
Please wait for
<span x-text="countdown" style="font-weight: 500"></span> seconds.
</p>
</div>
</div>
</div>
<div x-show="isError" class="modal-overlay">
<div class="loading-modal">
<div class="spinner"></div>
<div
class="loading-text"
style="display: flex; flex-direction: column"
>
<h3>Request Error</h3>
<p style="margin-top: 0.5rem">
Please follow the instructions properly. Exiting in
<span x-text="countdown" style="font-weight: 500"></span> seconds.
</p>
</div>
</div>
</div>
</div>
<script>
function atCommands() {
return {
isLoading: false,
isQuerying: false,
isLocking: false,
isError: false,
countdown: 5, // Initial countdown value
networkMode: null,
networkMode2: null,
bandNumbers: null,
cellNum: null,
earfcn1: null,
pci1: null,
earfcn2: null,
pci2: null,
earfcn3: null,
pci3: null,
earfcn4: null,
pci4: null,
earfcn5: null,
pci5: null,
earfcn6: null,
pci6: null,
earfcn7: null,
pci7: null,
earfcn8: null,
pci8: null,
earfcn9: null,
pci9: null,
earfcn10: null,
pci10: null,
band: null,
scs: null,
apnInput: null,
prefNetworkMode: null,
simSlot: null,
nrMode: null,
lockedBands: null,
cellState: null,
invalidCellNum: "",
atCommandResponse: "",
queryCommandResponse: "",
lockBands() {
if (!this.networkMode || !this.bandNumbers) {
this.isError = true;
this.startCountdown();
console.error("Network mode and band numbers are required.");
return;
}
// Remove commas and replace with colons
const parsedBandNumbers = this.bandNumbers.replace(/,/g, ":");
// Construct the atcmd based on the selected network mode
let atcmd;
switch (this.networkMode) {
case "LTE":
atcmd = `AT+QNWPREFCFG="lte_band",${parsedBandNumbers}`;
console.log(atcmd);
break;
case "NR5G-NSA":
atcmd = `AT+QNWPREFCFG="nsa_nr5g_band",${parsedBandNumbers}`;
console.log(atcmd);
break;
case "NR5G-SA":
atcmd = `AT+QNWPREFCFG="nr5g_band",${parsedBandNumbers}`;
console.log(atcmd);
break;
default:
console.error("Invalid network mode.");
return;
}
this.isLoading = true;
fetch(
"/cgi-bin/get_atcommand?" +
new URLSearchParams({
atcmd: atcmd,
})
)
.then((res) => res.text())
.then((data) => {
this.atCommandResponse = data;
this.isLocking = true;
this.startCountdown();
})
.catch((error) => {
this.isError = true;
this.startCountdown();
console.error("Error sending AT command:", error);
})
.finally(() => {
this.isLoading = false;
});
},
cellLock() {
// Error handlers
if (!this.networkMode2) {
this.isError = true;
this.startCountdown();
console.error("Network mode is required.");
return;
}
// Construct the AT command based on the selected network mode
let atcmd;
if (this.networkMode2 === "LTE") {
if (parseInt(this.cellNum) > 10) {
this.invalidCellNum = "Invalid";
this.isError = true;
this.startCountdown();
console.error("Invalid frequency number.");
return;
}
this.isLocking = true;
atcmd = `AT+QNWLOCK="common/4g",${this.cellNum}`;
for (let i = 1; i <= parseInt(this.cellNum); i++) {
atcmd += `,${this["earfcn" + i]},${this["pci" + i]}`;
}
console.log(atcmd);
} else if (this.networkMode2 === "NR5G-SA") {
this.isLocking = true;
atcmd = `AT+QNWLOCK="common/5g",${this.pci1},${this.earfcn1},${this.scs},${this.band}`;
console.log(atcmd);
}
this.sendAtCommand(atcmd);
},
restoreBands() {
const restoreCmd = 'AT+QNWPREFCFG="restore_band"';
this.sendAtCommand(restoreCmd);
},
restoreCell() {
const restoreCmd = 'AT+QNWLOCK="common/4g",0';
this.sendAtCommand(restoreCmd);
},
setAPN() {
const apnCmd = `AT+CGDCONT=1,"IPV4V6","${this.apnInput}"`;
this.sendAtCommand(apnCmd);
},
setPrefNetwork() {
// AT+QNWPREFCFG="mode_pref",<mode>
let prefCmd;
switch (this.prefNetworkMode) {
case "AUTO":
prefCmd = 'AT+QNWPREFCFG="mode_pref",AUTO';
break;
case "WCDMA":
prefCmd = 'AT+QNWPREFCFG="mode_pref",WCDMA';
break;
case "LTE ONLY":
prefCmd = 'AT+QNWPREFCFG="mode_pref",LTE';
break;
case "NR5G-NSA":
prefCmd = 'AT+QNWPREFCFG="mode_pref",LTE:NR5G';
break;
case "NR5G-SA":
prefCmd = 'AT+QNWPREFCFG="mode_pref",NR5G';
break;
default:
console.error("Invalid network mode.");
return;
}
this.sendAtCommand(prefCmd);
},
setSimSlot() {
const simCmd = `AT+QUIMSLOT=${this.simSlot}`;
this.sendAtCommand(simCmd);
},
nr5GMode() {
let nrCmd;
switch (this.nrMode) {
case "Enable All":
nrCmd = 'AT+QNWPREFCFG="nr5g_disable_mode",0';
break;
case "Disable NSA":
nrCmd = 'AT+QNWPREFCFG="nr5g_disable_mode",2';
break;
case "Disable SA":
nrCmd = 'AT+QNWPREFCFG="nr5g_disable_mode",1';
break;
default:
console.error("Invalid NR5G mode.");
return;
}
this.queryATCommand(nrCmd);
},
showLockedBands() {
let lockedCmd;
switch (this.lockedBands) {
case "LTE":
lockedCmd = 'AT+QNWPREFCFG="lte_band"';
break;
case "NSA":
lockedCmd = 'AT+QNWPREFCFG="nsa_nr5g_band"';
break;
case "SA":
lockedCmd = 'AT+QNWPREFCFG="nr5g_band"';
break;
default:
console.error("Invalid network mode.");
return;
}
this.queryATCommand(lockedCmd);
},
showCellState() {
let cellCmd;
switch (this.cellState) {
case "LTE":
cellCmd = 'AT+QNWLOCK="common/4g"';
break;
case "NR5G-SA":
cellCmd = 'AT+QNWLOCK="common/5g"';
break;
default:
console.error("Invalid network mode.");
return;
}
this.queryATCommand(cellCmd);
},
showSupportedBands() {
const supportedCmd = 'AT+QNWPREFCFG="policy_band"';
this.queryATCommand(supportedCmd);
},
showCurrentSim() {
const simCmd = "AT+QUIMSLOT?";
this.queryATCommand(simCmd);
},
showPrefNetwork() {
const prefCmd = 'AT+QNWPREFCFG="mode_pref"';
this.queryATCommand(prefCmd);
},
showCaInfo() {
const caCmd = "AT+QCAINFO";
this.queryATCommand(caCmd);
},
servingCellInfo() {
const servingCmd = 'AT+QENG="servingcell"';
this.queryATCommand(servingCmd);
},
scanNeighbourCells() {
const scanCmd = 'AT+QENG="neighbourcell"';
this.queryATCommand(scanCmd);
},
scanNsaCells() {
const enableCmd = 'AT+QNWCFG="nr5g_meas_info",1';
const queryScanCmd = 'AT+QNWCFG="nr5g_meas_info"';
// Send the enable command first without getting the response
fetch(
"/cgi-bin/get_atcommand?" +
new URLSearchParams({
atcmd: enableCmd,
})
)
.then(() => {
console.log("Enable Success.");
// Show a temporary loading query message
this.queryCommandResponse =
"Enabling NSA cell scan. Please wait...";
this.queryATCommand(queryScanCmd);
})
.catch((error) => {
this.isError = true;
this.startCountdown();
console.error("Error sending AT command:", error);
});
},
fullNetworkScan() {
const scanCmd = "AT+QSCAN=3,1";
// Show a temporary loading query message
this.queryCommandResponse =
"Full network scan initiated. This takes about a minute. Please wait...";
this.queryATCommand(scanCmd);
},
queryATCommand(atcmd) {
this.isQuerying = true;
fetch(
"/cgi-bin/get_atcommand?" +
new URLSearchParams({
atcmd: atcmd,
})
)
.then((res) => res.text())
.then((data) => {
this.queryCommandResponse = data;
console.log(data);
})
.catch((error) => {
this.isError = true;
this.startCountdown();
console.error("Error sending AT command:", error);
})
.finally(() => {
this.isQuerying = false;
});
},
sendAtCommand(atcmd) {
this.isLoading = true;
this.startCountdown();
console.log(atcmd);
fetch(
"/cgi-bin/get_atcommand?" +
new URLSearchParams({
atcmd: atcmd,
})
)
.then((res) => res.text())
.then((data) => {
this.atCommandResponse = data;
console.log(data);
})
.catch((error) => {
this.isError = true;
this.startCountdown();
console.error("Error sending AT command:", error);
})
.finally(() => {
this.isLoading = false;
});
},
startCountdown() {
// Decrease countdown every second
const interval = setInterval(() => {
this.countdown--;
if (this.countdown <= 0) {
clearInterval(interval);
// Reset values
this.isLocking = false;
this.isError = false;
this.isLoading = false;
this.isQuerying = false;
this.countdown = 5;
}
}, 1000);
},
};
}
const freqNumbersContainer = document.getElementById(
"freqNumbersContainer"
);
function generateFreqNumberInputs(num) {
let html = "";
const maxFields = Math.min(num, 10); // Limit to a maximum of 10 fields
for (let i = 1; i <= maxFields; i++) {
html += `
<div style="margin-top: 1rem" x-show="cellNum >= ${i} && networkMode2 == 'LTE'">
<div style="display: flex; flex-direction: row">
<input class="input is-info" type="text" placeholder="EARFCN" x-model="earfcn${i}" />
<input class="input is-info" type="text" placeholder="PCI" x-model="pci${i}" style="margin-left: 1rem" />
</div>
</div>
`;
}
return html;
}
document
.getElementById("numFreqInput")
.addEventListener("input", function () {
const cellNum = parseInt(this.value);
freqNumbersContainer.innerHTML = generateFreqNumberInputs(cellNum);
});
</script>
</body>
</html>

View File

@@ -0,0 +1,35 @@
#!/bin/bash
QUERY_STRING=$(echo "${QUERY_STRING}" | sed 's/;//g')
function urldecode() { : "${*//+/ }"; echo -e "${_//%/\\x}"; }
if [ "${QUERY_STRING}" ]; then
export IFS="&"
for cmd in ${QUERY_STRING}; do
if [ "$(echo $cmd | grep '=')" ]; then
key=$(echo $cmd | awk -F '=' '{print $1}')
value=$(echo $cmd | awk -F '=' '{print $2}')
eval $key=$value
fi
done
fi
MYATCMD=$(printf '%b\n' "${atcmd//%/\\x}")
if [ -n "${MYATCMD}" ]; then
x=$(urldecode "$atcmd")
# Initialize wait time to 1 second
wait_time=1000
while true; do
runcmd=$(echo -en "$x\r\n" | microcom -t $wait_time /dev/ttyOUT2)
# Check if "OK" or "ERROR" is present in the response
if [[ $runcmd =~ "OK" ]] || [[ $runcmd =~ "ERROR" ]]; then
break # Exit the loop if "OK" or "ERROR" is found
fi
# If neither "OK" nor "ERROR" is found, increment wait time by 1 second
((wait_time++))
done
fi
echo "Content-type: text/plain"
echo $x
echo ""
echo $runcmd

View File

@@ -0,0 +1,13 @@
#!/bin/bash
if [ ! -f /tmp/modemstatus.json ]
then
/usrdata/simpleadmin/scripts/modemstatus_parse.sh > /dev/null
fi
runcmd=$(</tmp/modemstatus.json)
echo "Content-type: text/json"
echo ""
cat <<EOT
$runcmd

View File

@@ -0,0 +1,19 @@
#!/bin/bash
# Check iptables for ttlvalue
ttlvalue=$(iptables -t mangle -vnL | grep TTL | awk '{print $13}')
ttlenabled=true;
# Set Variables
if [ -z "${ttlvalue}" ]; then
ttlvalue=0
ttlenabled=false
fi
echo "Content-type: text/json"
echo ""
cat <<EOT
{
"isEnabled": $ttlenabled,
"ttl": $ttlvalue
}

View File

@@ -0,0 +1,61 @@
#!/bin/bash
# Get query
QUERY_STRING=$(echo "${QUERY_STRING}" | sed 's/;//g')
if [ "${QUERY_STRING}" ]; then
export IFS="&"
for cmd in ${QUERY_STRING}; do
if [ "$(echo $cmd | grep '=')" ]; then
key=$(echo $cmd | awk -F '=' '{print $1}')
value=$(echo $cmd | awk -F '=' '{print $2}')
eval $key=$value
fi
done
fi
setTTL=$(printf '%b\n' "${ttlvalue//%/\\x}")
if [ -n "${setTTL}" ]; then
# Stop Service To Remove Rules
/usrdata/simplefirewall/ttl-override stop
# Check iptables is still set
ttlcheck=$(iptables -t mangle -vnL | grep TTL | awk '{print $13}')
# If TTL is still set manually remove values
if [ !-z "${ttlcheck}" ]; then
iptables -t mangle -D POSTROUTING -o rmnet+ -j TTL --ttl-set ${ttlcheck} &>/dev/null || true
ip6tables -t mangle -D POSTROUTING -o rmnet+ -j HL --hl-set ${ttlcheck} &>/dev/null || true
fi
# Echo TTL to file
echo $setTTL > /usrdata/simplefirewall/ttlvalue
# Set Start Service
/usrdata/simplefirewall/ttl-override start
fi
# Check iptables for ttlvalue
ttlvalue=$(iptables -t mangle -vnL | grep TTL | awk '{print $13}')
ttlenabled=true;
# Set Variables
if [ -z "${ttlvalue}" ]; then
ttlvalue=0
ttlenabled=false
fi
echo "Content-type: text/json"
echo ""
cat <<EOT
{
"isEnabled": $ttlenabled,
"ttl": $ttlvalue
}

View File

@@ -0,0 +1,86 @@
:root{
::-webkit-scrollbar{height:10px;width:10px}::-webkit-scrollbar-track{background:#efefef;border-radius:6px}::-webkit-scrollbar-thumb{background:#d5d5d5;border-radius:6px}::-webkit-scrollbar-thumb:hover{background:#c4c4c4}
}
html, body {
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen-Sans, Ubuntu, Cantarell, "Helvetica Neue", sans-serif;
font-size: 16px;
line-height: 1.5;
height: 100%;
background: #ECF0F3;
}
nav.navbar {
border-top: 4px solid #276cda;
margin-bottom: 1rem;
}
.navbar-item.brand-text {
font-weight: 300;
}
.navbar-item, .navbar-link {
font-size: 14px;
font-weight: 700;
}
.columns {
width: 100%;
height: 100%;
margin-left: 0;
}
.menu-label {
color: #8F99A3;
letter-spacing: 1.3;
font-weight: 700;
}
.menu-list a {
color: #0F1D38;
font-size: 14px;
font-weight: 700;
}
.menu-list a:hover {
background-color: transparent;
color: #276cda;
}
.menu-list a.is-active {
background-color: transparent;
color: #276cda;
font-weight: 700;
}
.card {
box-shadow: 0px 2px 4px rgba(0, 0, 0, 0.18);
margin-bottom: 2rem;
}
.card-header-title {
color: #8F99A3;
font-weight: 400;
}
.info-tiles {
margin: 1rem 0;
}
.info-tiles .subtitle {
font-weight: 300;
color: #8F99A3;
}
.hero.welcome.is-info {
background: #36D1DC;
background: -webkit-linear-gradient(to right, #5B86E5, #36D1DC);
background: linear-gradient(to right, #5B86E5, #36D1DC);
}
.hero.welcome .title, .hero.welcome .subtitle {
color: hsl(192, 17%, 99%);
}
.card .content {
font-size: 14px;
}
.card-footer-item {
font-size: 14px;
font-weight: 700;
color: #8F99A3;
}
.card-footer-item:hover {
}
.card-table .table {
margin-bottom: 0;
}
.events-card .card-table {
height: 330px;
overflow-y: scroll;
}

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,284 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<!-- change to a much simpler tab title -->
<title>Simple Admin</title>
<script src="/js/alpinejs.min.js" defer></script>
<link rel="stylesheet" href="/css/bulma.css" />
<link rel="stylesheet" type="text/css" href="/css/admin.css" />
<link rel="stylesheet" href="styles.css" />
</head>
<body>
<!-- START NAV -->
<nav class="navbar is-black" x-data="{ isOpen: false }">
<div class="container">
<div class="navbar-brand">
<a class="navbar-item brand-text" href="/"> Simple Admin </a>
<a
role="button"
class="navbar-burger burger"
@click="isOpen = !isOpen"
>
<span aria-hidden="true"></span>
<span aria-hidden="true"></span>
<span aria-hidden="true"></span>
</a>
</div>
<div
id="navMenu"
class="navbar-menu"
:class="isOpen ? 'is-active' : ''"
>
<div class="navbar-start">
<a class="navbar-item" href="/"> Connection Info </a>
<a class="navbar-item" href="/atcommander.html"> AT Commands </a>
<a class="navbar-item" href="/bandlock.html"> Simple Network </a>
<a class="navbar-item" href="/ttl.html"> TTL Changer </a>
<a class="navbar-item" href="/speedtest.html"> OpenSpeedtest </a>
</div>
</div>
</div>
</nav>
<!-- END NAV -->
<div class="container">
<div class="columns">
<div class="column is-12" x-data="getSignalData()" x-init="init()">
<section class="hero is-info welcome is-small">
<div class="hero-body">
<div class="container">
<!-- Fetches the correct Model Name -->
<h1 class="title">
<span x-text="csqData.MODEM_MODEL"></span> Connection
Info
</h1>
<h2 class="subtitle">
Data Updated: <span x-text="lastUpdate"></span>
</h2>
</div>
</div>
</section>
<section class="info-tiles">
<div class="tile is-ancestor has-text-centered">
<div class="tile is-parent">
<article class="tile is-child box">
<!-- added APN -->
<p class="title" x-text="csqData.APN"></p>
<p class="subtitle">Current APN</p>
<p
class="title"
style="margin-top: 1rem"
x-html="csqData.SIMSLOT"
></p>
<p class="subtitle">Current SIM</p>
</article>
</div>
<div class="tile is-parent">
<article class="tile is-child box">
<!-- added APN -->
<p class="title" x-text="csqData.MODE"></p>
<p class="subtitle">Network</p>
<p
class="title"
x-text="csqData.PC_BAND"
style="margin-top: 1rem"
></p>
<p class="subtitle">Primary Band</p>
</article>
</div>
<!-- added primary band and secondary bands value -->
<div class="tile is-parent">
<article class="tile is-child box">
<p class="title" x-html="csqData.SC_BANDS"></p>
<p class="subtitle">Aggregated Bands</p>
</article>
</div>
<div class="tile is-parent">
<article class="tile is-child box">
<p class="title" x-text="csqData.CSQ_PER"></p>
<p class="subtitle">Signal Strength</p>
<p
class="title"
x-text="csqData.TEMP"
style="margin-top: 1rem"
></p>
<p class="subtitle">Modem Temperature</p>
</article>
</div>
</div>
</section>
<div class="columns">
<div class="column is-6">
<div class="card events-card">
<header class="card-header">
<p class="card-header-title">Signal Information</p>
</header>
<div class="card-table">
<div class="content">
<table class="table is-fullwidth is-striped">
<tbody>
<tr>
<th>Provider</th>
<td x-text="csqData.PROVIDER"></td>
</tr>
<tr>
<th>CSQ</th>
<td x-text="csqData.CSQ"></td>
</tr>
<tr>
<th>Signal Strength</th>
<td x-text="csqData.CSQ_PER"></td>
</tr>
<tr>
<th>RSSI</th>
<td x-text="csqData.CSQ_RSSI"></td>
</tr>
<tr>
<th>
ECIO<sup>3G</sup>/RSRQ<sup>4G</sup>/SS_RSRQ<sup
>5G</sup
>
</th>
<td x-html="csqData.ECIO"></td>
</tr>
<tr>
<th>
RSCP<sup>3G</sup>/RSRP<sup>4G</sup>/SS_RSRP<sup
>5G</sup
>
</th>
<td x-html="csqData.RSCP"></td>
</tr>
<tr>
<th>SINR</th>
<td x-html="csqData.SINR"></td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
</div>
<div class="column is-6">
<div class="card events-card">
<header class="card-header">
<p class="card-header-title">Cell Information</p>
</header>
<div class="card-table">
<div class="content">
<table class="table is-fullwidth is-striped">
<tbody>
<tr>
<th>MCC MNC</th>
<td>
<span x-text="csqData.MCCMNC"></span>
</td>
</tr>
<tr>
<th>RNC<sup>3G</sup>/eNB ID<sup>4G/5G</sup></th>
<td>
<span x-text="csqData.RNC"></span>
<span x-text="csqData.RNC_NUM"></span>
</td>
</tr>
<tr>
<th>Lag<sup>3G</sup>/TAC<sup>4G/5G</sup></th>
<td>
<span x-text="csqData.LAC"></span>
<span x-text="csqData.LAC_NUM"></span>
</td>
</tr>
<tr>
<th>Cell ID</th>
<td x-text="csqData.CID"></td>
</tr>
<tr>
<th>Band</th>
<td x-html="csqData.LBAND"></td>
</tr>
<tr>
<th>Channel</th>
<td x-text="csqData.CHANNEL"></td>
</tr>
<tr>
<th>PCI</th>
<td x-text="csqData.PCI"></td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<script>
function signalInfo() {
return {
isLoading: false,
atcmd: 'AT+QSPN;+CEREG=2;+CEREG?;+CEREG=0;+C5GREG=2;+C5GREG?;+C5GREG=0;+CSQ;+QENG=\"servingcell\";+QRSRP;+QCAINFO;+QNWPREFCFG=\"mode_pref\";+QTEMP\r\n',
atCommandResponse: null,
refreshSignal() {
this.isLoading = true; // Set loading state to true before fetching data
fetch(
"/cgi-bin/get_atcommand?" +
new URLSearchParams({
atcmd: this.atcmd,
})
)
.then((res) => {
return res.text();
})
.then((data) => {
this.atCommandResponse = data;
// Split the response into individual messages
const messages = data.trim().split("\n\n");
// Convert the messages into a JSON file
//TODO: Add the JSON conversion here
// Log the parsed messages array as JSON to the console
console.log(JSON.stringify(parsedMessages, null, 2));
})
.catch((error) => {
console.error("Something went wrong", error);
})
.finally(() => {
this.isLoading = false; // Set loading state to false after fetching data
});
},
};
}
function getSignalData() {
return {
csqData: {},
lastUpdate: new Date().toLocaleString(),
getcsq() {
fetch("/cgi-bin/get_csq")
.then((res) => res.json())
.then((data) => {
this.csqData = data;
this.lastUpdate = new Date(
data.LASTUPDATE * 1000
).toLocaleString();
});
},
init() {
this.getcsq();
setInterval(() => {
this.getcsq();
}, 30000);
},
};
}
</script>
</body>
</html>

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,81 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>Speedtest</title>
<script src="/js/alpinejs.min.js" defer></script>
<link rel="stylesheet" href="/css/bulma.css" />
<link rel="stylesheet" type="text/css" href="/css/admin.css" />
<link rel="stylesheet" href="styles.css" />
</head>
<body>
<!-- START NAV -->
<nav class="navbar is-black" x-data="{ isOpen: false }">
<div
class="container"
>
<div class="navbar-brand">
<a class="navbar-item brand-text" href="/"> Simple Admin </a>
<a
role="button"
class="navbar-burger burger"
@click="isOpen = !isOpen"
>
<span aria-hidden="true"></span>
<span aria-hidden="true"></span>
<span aria-hidden="true"></span>
</a>
</div>
<div
id="navMenu"
class="navbar-menu"
:class="isOpen ? 'is-active' : ''"
>
<div class="navbar-start">
<a class="navbar-item" href="/"> Connection Info </a>
<a class="navbar-item" href="/atcommander.html"> AT Commands </a>
<a class="navbar-item" href="/bandlock.html"> Simple Network </a>
<a class="navbar-item" href="/ttl.html"> TTL Changer </a>
<a class="navbar-item" href="/speedtest.html"> OpenSpeedtest </a>
</div>
</div>
</div>
</nav>
<!-- END NAV -->
<div class="container">
<!--OST Widget code start-->
<div style="text-align: right">
<div style="min-height: 360px">
<div
style="
width: 100%;
height: 0;
padding-bottom: 50%;
position: relative;
"
>
<iframe
style="
border: none;
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
min-height: 360px;
border: none;
overflow: hidden !important;
"
src="//openspeedtest.com/speedtest"
></iframe>
</div>
</div>
</div>
<!-- OST Widget code end -->
</div>
</body>
</html>

View File

@@ -0,0 +1,48 @@
/* Google Poppins */
@import url('https://fonts.googleapis.com/css2?family=Poppins:wght@300;400;500;600;700&display=swap');
*{
font-family: 'Poppins', sans-serif;
}
.modal-overlay {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background-color: rgba(0, 0, 0, 0.5);
z-index: 1000;
display: flex;
justify-content: center;
align-items: center;
}
.loading-modal {
background-color: #fff;
padding: 20px;
border-radius: 10px;
text-align: center;
}
.spinner {
border: 4px solid rgba(0, 0, 0, 0.1);
border-left-color: #333;
border-radius: 50%;
width: 40px;
height: 40px;
animation: spin 1s linear infinite;
margin: 0 auto 10px auto;
}
.loading-text {
font-size: 18px;
color: #333;
}
@keyframes spin {
to {
transform: rotate(360deg);
}
}

View File

@@ -0,0 +1,132 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>TTL Changer</title>
<script src="/js/alpinejs.min.js" defer></script>
<link rel="stylesheet" href="/css/bulma.css">
<link rel="stylesheet" type="text/css" href="/css/admin.css">
<link rel="stylesheet" href="styles.css" />
</head>
<body>
<!-- START NAV -->
<nav class="navbar is-black" x-data="{ isOpen: false }">
<div class="container">
<div class="navbar-brand">
<a class="navbar-item brand-text" href="/">
Simple Admin
</a>
<a role="button" class="navbar-burger burger" @click="isOpen = !isOpen">
<span aria-hidden="true"></span>
<span aria-hidden="true"></span>
<span aria-hidden="true"></span>
</a>
</div>
<div id="navMenu" class="navbar-menu" :class="isOpen ? 'is-active' : ''">
<div class="navbar-start">
<a class="navbar-item" href="/"> Connection Info </a>
<a class="navbar-item" href="/atcommander.html"> AT Commands </a>
<a class="navbar-item" href="/bandlock.html"> Simple Network </a>
<a class="navbar-item" href="/ttl.html"> TTL Changer </a>
<a class="navbar-item" href="/speedtest.html"> OpenSpeedtest </a>
</div>
</div>
</div>
</nav>
<!-- END NAV -->
<div class="container" x-data="ttlCommands()" x-init="init">
<div class="columns">
<div class="column is-12">
<div class="columns">
<div class="column is-8">
<div class="card">
<header class="card-header">
<p class="card-header-title">
TTL Enabler
</p>
</header>
<div class="card-content">
<div class="content">
<p>
<h2>TTL Status</h2> <br>
TTL is <span class="tag is-large"
:class="ttldata.isEnabled ? 'is-success' : 'is-danger'"
x-text="ttldata.isEnabled == true ? 'ON' : 'OFF'"></span>
<br />
TTL Set to <span x-text="ttldata.ttl"></span>
</p>
<div class="field">
<label class="label">Set TTL</label>
<div class="control">
<input class="input" type="number" placeholder="64" x-model="newTTL">
</div>
</div>
<div class="field">
<div class="control">
<button class="button is-link" @click="setTTL()">Set TTL</button>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="column is-4">
<div class="card">
<header class="card-header">
<p class="card-header-title">
Common TTL For Providers
</p>
</header>
<div class="card-content">
<div class="content">
<ul>
<li>Magenta: 65</li>
<li>Red: 88</li>
</ul>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<script>
function ttlCommands() {
return {
isLoading: false,
ttldata: null,
newTTL: 0,
init() {
fetch('/cgi-bin/get_ttl_status')
.then((res) => res.json())
.then((data) => {
this.ttldata = data
});
},
setTTL() {
fetch('/cgi-bin/set_ttl?' + new URLSearchParams({
ttlvalue: this.newTTL,
}))
.then((res) => res.json())
.then((data) => {
this.ttldata = data
})
}
}
}
</script>
</body>
</html>

View File

@@ -0,0 +1,16 @@
#!/bin/bash
# Define the ports you want to block
PORTS=("80" "8080" "8088" "443") # Default ports, will be modified by the install script
# First, allow specified ports on bridge0, eth0, and tailscale0
for port in "${PORTS[@]}"; do
iptables -A INPUT -i bridge0 -p tcp --dport $port -j ACCEPT
iptables -A INPUT -i eth0 -p tcp --dport $port -j ACCEPT
iptables -A INPUT -i tailscale0 -p tcp --dport $port -j ACCEPT
done
# Then, block specified ports on all other interfaces
for port in "${PORTS[@]}"; do
iptables -A INPUT -p tcp --dport $port -j DROP
done

View File

@@ -0,0 +1,11 @@
[Unit]
Description=Simple Firewall Setup
After=network.target
[Service]
Type=oneshot
ExecStart=/bin/bash /usrdata/simplefirewall/simplefirewall.sh
RemainAfterExit=yes
[Install]
WantedBy=multi-user.target

View File

@@ -0,0 +1,12 @@
[Unit]
Description=TTL Override
After=ql-netd.service
DefaultDependencies=no
[Service]
Type=oneshot
ExecStart=/usrdata/simplefirewall/ttl-override start
User=root
[Install]
WantedBy=multi-user.target

View File

@@ -0,0 +1,53 @@
#! /bin/bash
# Adapted from https://github.com/natecarlson/quectel-rgmii-configuration-notes/blob/main/files/ttl-override
# Uses ttlvalue file to read what ttl should be set to
if [ -f /usrdata/simplefirewall/ttlvalue ];
then
ttlfile=$(</usrdata/simplefirewall/ttlvalue)
TTLVALUE=$(echo $ttlfile | grep -o "[0-9]\{1,3\}")
if [ -z "${TTLVALUE}" ]; then
echo "Couldnt get proper ttl value from file" >&2
exit 1
fi
else
# Couldnt find ttlvalue file, lets generate one with 0 ttlvalue (0 = disabled)
touch /usrdata/simplefirewall/ttlvalue && echo '0' > /usrdata/simplefirewall/ttlvalue
exit 1
fi
case "$1" in
start)
if (( $TTLVALUE > 0 )); then
echo "Adding TTL override rules: "
iptables -t mangle -I POSTROUTING -o rmnet+ -j TTL --ttl-set ${TTLVALUE}
ip6tables -t mangle -I POSTROUTING -o rmnet+ -j HL --hl-set ${TTLVALUE}
else
echo "TTLVALUE set to 0, nothing to do..."
fi
echo "done"
;;
stop)
if (( $TTLVALUE > 0 )); then
echo "Removing TTL override rules: "
iptables -t mangle -D POSTROUTING -o rmnet+ -j TTL --ttl-set ${TTLVALUE} &>/dev/null || true
ip6tables -t mangle -D POSTROUTING -o rmnet+ -j HL --hl-set ${TTLVALUE} &>/dev/null || true
else
echo "TTLVALUE set to 0, nothing to do..."
fi
echo "done"
;;
restart)
$0 stop
$0 start
;;
*)
echo "Usage ttl-override { start | stop | restart }" >&2
exit 1
;;
esac
exit 0

View File

@@ -0,0 +1 @@
0

View File

@@ -0,0 +1,69 @@
#!/bin/bash
DEVICE=/dev/ttyOUT2
BAUD=115200
# Function to setup device communication parameters
setup_device() {
stty -F $DEVICE cs8 $BAUD ignbrk -brkint -icrnl -imaxbel \
-opost -onlcr -isig -icanon -iexten -echo -echoe -echok \
-echoctl -echoke noflsh -ixon -crtscts
}
# Function to send AT command and capture the output
send_at_command() {
local command="$1"
# Clear the device buffer before sending a new command
echo -n > $DEVICE
# Send the AT command, preserving the integrity of the input
echo -e "$command\r" > $DEVICE
# Use a temporary file to capture the command output
tmpfile=$(mktemp)
# Start reading the device output to the temporary file
cat $DEVICE > "$tmpfile" &
CAT_PID=$!
# Monitor the output file for "OK" or "ERROR"
while ! grep -qe "OK" -e "ERROR" "$tmpfile"; do
sleep 1
done
# Kill the `cat` process after capturing the response
kill $CAT_PID
wait $CAT_PID 2>/dev/null
# Display the response
cat "$tmpfile" | while IFS= read -r line; do
echo -e "\033[0;32m$line\033[0m"
done
# Clean up
rm "$tmpfile"
}
# Prepare the device for communication
setup_device
# Check if an AT command is provided as an argument
if [ $# -gt 0 ]; then
# Concatenate all arguments to handle commands with spaces and/or quotes correctly
FULL_CMD="$*"
send_at_command "$FULL_CMD"
else
echo -e "\033[0;36mType 'exit' to end the session.\033[0m"
while true; do
echo -en "\033[0;36mEnter AT Command: \033[0m"
read user_input
if [[ "$user_input" == "exit" ]]; then
echo -e "\033[0;32mExiting...\033[0m"
break
fi
send_at_command "$user_input"
done
fi

View File

@@ -0,0 +1,4 @@
#!/bin/bash
# Look for the process by its command and kill it so socat can have smd7 instead
pkill -f "/usr/bin/port_bridge smd7 at_usb2 1"

View File

@@ -0,0 +1,9 @@
[Unit]
Description=Kill port_bridge process on boot
[Service]
Type=oneshot
ExecStart=/usrdata/socat-at-bridge/killsmd7bridge
[Install]
WantedBy=multi-user.target

View File

@@ -0,0 +1,14 @@
[Unit]
Description=Read from /dev/ttyIN and write to smd11
BindsTo=socat-smd11.service
After=socat-smd11.service
[Service]
ExecStart=/bin/bash -c "/bin/cat /dev/ttyIN > /dev/smd11"
ExecStartPost=/bin/sleep 2s
StandardInput=tty-force
Restart=always
RestartSec=1s
[Install]
WantedBy=multi-user.target

View File

@@ -0,0 +1,14 @@
[Unit]
Description=Read from /dev/smd11 and write to ttyIN
BindsTo=socat-smd11.service
After=socat-smd11.service
[Service]
ExecStart=/bin/bash -c "/bin/cat /dev/smd11 > /dev/ttyIN"
ExecStartPost=/bin/sleep 2s
StandardInput=tty-force
Restart=always
RestartSec=1s
[Install]
WantedBy=multi-user.target

View File

@@ -0,0 +1,13 @@
[Unit]
Description=Socat Serial Emulation for smd11
After=ql-netd.service
[Service]
ExecStart=/usrdata/socat-at-bridge/socat-armel-static -d -d pty,link=/dev/ttyIN,raw,echo=0 pty,link=/dev/ttyOUT,raw,echo=1
# Add a delay to prevent the clients from starting too early
ExecStartPost=/bin/sleep 2s
Restart=always
RestartSec=1s
[Install]
WantedBy=multi-user.target

View File

@@ -0,0 +1,14 @@
[Unit]
Description=Read from /dev/ttyIN2 and write to smd7
BindsTo=socat-smd7.service
After=socat-smd7.service
[Service]
ExecStart=/bin/bash -c "/bin/cat /dev/ttyIN2 > /dev/smd7"
ExecStartPost=/bin/sleep 2s
StandardInput=tty-force
Restart=always
RestartSec=1s
[Install]
WantedBy=multi-user.target

View File

@@ -0,0 +1,14 @@
[Unit]
Description=Read from /dev/smd7 and write to ttyIN2
BindsTo=socat-smd7.service
After=socat-smd7.service
[Service]
ExecStart=/bin/bash -c "/bin/cat /dev/smd7 > /dev/ttyIN2"
ExecStartPost=/bin/sleep 2s
StandardInput=tty-force
Restart=always
RestartSec=1s
[Install]
WantedBy=multi-user.target

View File

@@ -0,0 +1,13 @@
[Unit]
Description=Socat Serial Emulation for smd7
After=ql-netd.service
[Service]
ExecStart=/usrdata/socat-at-bridge/socat-armel-static -d -d pty,link=/dev/ttyIN2,raw,echo=0 pty,link=/dev/ttyOUT2,raw,echo=1
# Add a delay to prevent the clients from starting too early
ExecStartPost=/bin/sleep 2s
Restart=always
RestartSec=1s
[Install]
WantedBy=multi-user.target

View File

@@ -0,0 +1,8 @@
[Unit]
Description=Trigger the Tailscale Web UI
After=tailscaled.service
[Service]
Type=oneshot
ExecStart=/bin/systemctl start tailscale-webui
RemainAfterExit=yes

View File

@@ -0,0 +1,12 @@
Description=Tailscale Web Interface
After=tailscaled.service
Requires=tailscaled.service
[Service]
Type=simple
ExecStartPre=/bin/sleep 5
ExecStart=/usrdata/tailscale/tailscale web --listen 0.0.0.0:8088
RemainAfterExit=yes
[Install]
WantedBy=multi-user.target

View File

@@ -0,0 +1,8 @@
# Set the port to listen on for incoming VPN packets.
# Remote nodes will automatically be informed about the new port number,
# but you might want to configure this in order to set external firewall
# settings.
PORT="41641"
# Extra flags you might want to pass to tailscaled.
FLAGS=""

View File

@@ -0,0 +1,16 @@
[Unit]
Description=Tailscale node agent
Documentation=https://tailscale.com/kb/
Wants=network-pre.target
After=network-pre.target NetworkManager.service systemd-resolved.service
[Service]
EnvironmentFile=/usrdata/tailscale/systemd/tailscaled.defaults
ExecStartPre=/usrdata/tailscale/tailscaled --cleanup
ExecStart=/usrdata/tailscale/tailscaled --statedir=/usrdata/tailscale/ --port=${PORT} $FLAGS
ExecStopPost=/usrdata/tailscale/tailscaled --cleanup
Restart=on-failure
Type=notify
[Install]
WantedBy=multi-user.target

View File

@@ -0,0 +1,101 @@
#!/bin/sh
# Configuration
DEVICE_FILE="/dev/smd7"
ICCID_FILE="/path/to/iccid_master_file" # Path to the ICCID-APN-IPType master file
TMP_DIR="/tmp"
TIMEOUT=4
# Start listening to device file
start_listening() {
cat "$DEVICE_FILE" > "$TMP_DIR/device_readout" &
CAT_PID=$!
}
# Send AT command
send_at_command() {
local command=$1
echo -e "${command}\r" > "$DEVICE_FILE"
}
# Wait for and process response
wait_for_response() {
local start_time=$(date +%s)
local current_time
local elapsed_time
while true; do
if grep -q "OK" "$TMP_DIR/device_readout" || grep -q "ERROR" "$TMP_DIR/device_readout"; then
RESPONSE=$(cat "$TMP_DIR/device_readout")
echo "Response received: $RESPONSE"
return 0
fi
current_time=$(date +%s)
elapsed_time=$((current_time - start_time))
if [ "$elapsed_time" -ge "$TIMEOUT" ]; then
echo "Error: Response timed out."
return 1
fi
sleep 1
done
}
# Cleanup function
cleanup() {
kill "$CAT_PID"
wait "$CAT_PID" 2>/dev/null
rm -f "$TMP_DIR/device_readout"
}
# Function to send AT command and wait for response
send_and_wait() {
send_at_command "$1"
wait_for_response
}
# Function to update the APN
update_apn() {
local slot=$1
local apn=$2
local iptype=$3
send_and_wait "AT+CGDCONT=$slot,\"$iptype\",\"$apn\""
if [ $? -eq 0 ]; then
echo "APN updated successfully."
else
echo "Failed to update APN."
fi
}
# Main Execution
if [ -c "$DEVICE_FILE" ]; then
start_listening
# Get ICCID
send_and_wait "AT+CCID"
ICCID=$(echo "$RESPONSE" | grep "+CCID" | cut -d ':' -f2 | tr -d '[:space:]')
echo "ICCID: $ICCID"
# Check ICCID in master file
if grep -q "$ICCID" "$ICCID_FILE"; then
APN=$(grep "$ICCID" "$ICCID_FILE" | cut -d ',' -f2)
IP_TYPE=$(grep "$ICCID" "$ICCID_FILE" | cut -d ',' -f3)
IP_TYPE=${IP_TYPE:-"IPV4V6"} # Default to IPV4V6 if not specified
# Get current APN settings
send_and_wait "AT+CGDCONT?"
CURRENT_APN=$(echo "$RESPONSE" | grep "+CGDCONT: 1" | cut -d ',' -f3 | tr -d '"')
# Compare and update APN if necessary
if [ "$APN" != "$CURRENT_APN" ]; then
update_apn 1 "$APN" "$IP_TYPE"
else
echo "No APN update needed."
fi
else
echo "ICCID not found in the master file."
fi
cleanup
else
echo "Error: Device $DEVICE_FILE does not exist or is not a character special file."
fi

View File

@@ -0,0 +1,21 @@
edit_iccid_file() {
local iccid_file="/path/to/iccid_master_file" # Path to the ICCID-APN-IPType master file
echo "Enter ICCID to add or edit:"
read iccid
echo "Enter APN for $iccid:"
read apn
echo "Enter IP Type (IPV4, IPV6, IPV4V6) for $iccid [Default: IPV4V6]:"
read iptype
iptype=${iptype:-"IPV4V6"} # Default to IPV4V6 if not specified
# Check if ICCID already exists
if grep -q "$iccid" "$iccid_file"; then
# Update existing ICCID's APN and IP Type
sed -i "/$iccid/c\\$iccid,$apn,$iptype" "$iccid_file"
else
# Add new ICCID, APN, and IP Type
echo "$iccid,$apn,$iptype" >> "$iccid_file"
fi
echo "ICCID file updated."
}

View File

@@ -0,0 +1,93 @@
#!/bin/bash
# Read the serial number
serial_number=$(/usrdata/socat-at-bridge/atcmd 'AT+EGMR=0,5' | grep '+EGMR:' | cut -d '"' -f2)
# Read the firmware revision
firmware_revision=$(/usrdata/socat-at-bridge/atcmd 'AT+CGMR' | grep -o 'RM[0-9A-Z]*' | head -1)
echo "=============================================================="
echo "=============================================================="
echo "=============================================================="
echo "=============================================================="
echo "=============================================================="
echo "=============================================================="
echo "=============================================================="
echo "=============================================================="
echo "=============================================================="
echo "=============================================================="
echo "=============================================================="
echo "=============================================================="
echo "=============================================================="
echo "=============================================================="
echo "=============================================================="
echo "=============================================================="
echo "=============================================================="
# Start your Actual echo output here, 17 lines omitted for mobile compatibility. ttyd font needs to be size 25.
# Echo "Logo"
echo " .%+: "
echo " .*@@@-. "
echo " :@@@@- "
echo " @@@@#. "
echo " -@@@@#. "
echo " :. %@@@@: -# "
echo " .+- #@@@@%.+@- "
echo " .#- . +@@@@# #@- "
echo " -@*@*@% @@@@@::@@= "
echo ".+%@@@@@@@@@%=. =@@@@# #@@- .. "
echo " .@@@@@: :@@@@@ =@@@..%= "
echo " -::@-.+. @@@@@.=@@@- =@- "
echo " .@- .@@@@@:.@@@* @@. "
echo " .%- -@@@@@:=@@@@ @@# "
echo " .#- .%@@@@@@#. +@@@@@.#@@@@ @@@."
echo " .*- .@@@@@@@@@@=. @@@@@@ @@@@@ @@@:"
echo " :. .%@@@@@@@@@@@%. .@@@@@+:@@@@@ @@@-"
echo " -@@@@@@@@@@@@@@@..@@@@@@.-@@@@@ .@@@-"
echo " -@@@@@@@@@@%. .@@@@@@. @@@@@+ =@@@="
echo " =@@@@@@@@* .@@@@@@. @@@@@@..@@@@-"
echo " #@@@@@@@@-*@@@@@%..@@@@@@+ #@@@@-"
echo " @@@@@@:.-@@@@@@. @@@@@@= %@@@@@."
echo " .@@@@. *@@@@@@- .+@@@@@@-.@@@@@@+ "
echo " %@@. =@@@@@*. +@@@@@@%.-@@@@@@% "
echo " .@@ .@@@@@= :@@@@@@@@..@@@@@@@= "
echo " =@.+@@@@@. -@@@@@@@*.:@@@@@@@*. "
echo " %.*@@@@= .@@@@@@@-.:@@@@@@@+. "
echo " ..@@@@= .@@@@@@: #@@@@@@@: "
echo " .@@@@ +@@@@..%@@@@@+. "
echo " .@@@. @@@@.:@@@@+. "
echo " @@@. @@@. @@@* .@. "
echo " :@@@ %@@..@@#. *@ "
echo " -*: .@@* :@@. @@. -..@@ "
echo " =@@@@@@.*@- :@% @* =@:=@# "
echo " .@@@-+@@@@:%@..%- ...@%:@@: "
echo " .@@. @@-%@: .%@@*@@%. "
echo " :@@ :+ *@ *@@#*@@@. "
echo " =@@@.@@@@ "
echo " .*@@@:=@@@@: "
echo " .@@@@:.@@@@@: "
echo " .@@@@#.-@@@@@. "
echo " #@@@@: =@@@@@- "
echo " .@@@@@..@@@@@@* "
echo " -@@@@@. @@@@@@#. "
echo " -@@@@@ @@@@@@% "
echo " @@@@@. #@@@@@@. "
echo " :@@@@# =@@@@@@% "
echo " @@@@@: @@@@@@@: "
echo " *@@@@ @@@@@@@. "
echo " .@@@@ @@@@@@@ "
echo " #@@@. @@@@@@* "
echo " @@@# @@@@@@@ "
echo " .@@+=@@@@@@. "
echo " *@@@@@@ "
echo " :@@@@@= "
echo " .@@@@@@. "
echo " :@@@@@*. "
echo " .=@@@@@- "
echo " :+##+. "
echo "=============================================================="
echo "TTYd session file by iamromulan v1.0"
echo "Firmware Revision: $firmware_revision"
echo "Serial Number: $serial_number"
echo "=============================================================="
# Start a login session
exec /bin/login

View File

@@ -0,0 +1,12 @@
[Unit]
Description=TTYD Service
After=network.target
[Service]
Type=simple
ExecStartPre=/bin/sleep 5
ExecStart=/usrdata/ttyd/ttyd -p 443 -t 'theme={"foreground":"white","background":"black"}' -t fontSize=25 --writable /usrdata/ttyd/scripts/ttyd.bash
Restart=on-failure
[Install]
WantedBy=multi-user.target