rebased for applying latest changes
This commit is contained in:
@@ -164,6 +164,10 @@
|
|||||||
<td>Firmware Version</td>
|
<td>Firmware Version</td>
|
||||||
<th id="firmwareVersion"></th>
|
<th id="firmwareVersion"></th>
|
||||||
</tr>
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>LTE Category</td>
|
||||||
|
<th id="lteCategory"></th>
|
||||||
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td>Phone Number</td>
|
<td>Phone Number</td>
|
||||||
<th id="phoneNumber"></th>
|
<th id="phoneNumber"></th>
|
||||||
|
|||||||
@@ -24,6 +24,7 @@
|
|||||||
<script src="/js/styles/modal-trigger.js"></script>
|
<script src="/js/styles/modal-trigger.js"></script>
|
||||||
<script src="/js/utils/reboot.js"></script>
|
<script src="/js/utils/reboot.js"></script>
|
||||||
<script src="/js/utils/restart-connection.js"></script>
|
<script src="/js/utils/restart-connection.js"></script>
|
||||||
|
<script src="/js/cell-locking/cell-lock.js"></script>
|
||||||
<script defer src="/js/auth/auth.js"></script>
|
<script defer src="/js/auth/auth.js"></script>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
@@ -156,6 +157,7 @@
|
|||||||
<input
|
<input
|
||||||
class="input"
|
class="input"
|
||||||
type="email"
|
type="email"
|
||||||
|
id="earfcn1"
|
||||||
placeholder="EARFCN"
|
placeholder="EARFCN"
|
||||||
/>
|
/>
|
||||||
<span class="icon is-left">
|
<span class="icon is-left">
|
||||||
@@ -169,7 +171,12 @@
|
|||||||
<div class="field">
|
<div class="field">
|
||||||
<label class="label">PCI 1</label>
|
<label class="label">PCI 1</label>
|
||||||
<div class="control has-icons-left">
|
<div class="control has-icons-left">
|
||||||
<input class="input" type="email" placeholder="PCI" />
|
<input
|
||||||
|
class="input"
|
||||||
|
type="email"
|
||||||
|
placeholder="PCI"
|
||||||
|
id="pci1"
|
||||||
|
/>
|
||||||
<span class="icon is-left">
|
<span class="icon is-left">
|
||||||
<i class="fa-solid fa-signal"></i>
|
<i class="fa-solid fa-signal"></i>
|
||||||
</span>
|
</span>
|
||||||
@@ -187,6 +194,7 @@
|
|||||||
class="input"
|
class="input"
|
||||||
type="email"
|
type="email"
|
||||||
placeholder="EARFCN"
|
placeholder="EARFCN"
|
||||||
|
id="earfcn2"
|
||||||
/>
|
/>
|
||||||
<span class="icon is-left">
|
<span class="icon is-left">
|
||||||
<i class="fas fa-bolt"></i>
|
<i class="fas fa-bolt"></i>
|
||||||
@@ -199,7 +207,12 @@
|
|||||||
<div class="field">
|
<div class="field">
|
||||||
<label class="label">PCI 2</label>
|
<label class="label">PCI 2</label>
|
||||||
<div class="control has-icons-left">
|
<div class="control has-icons-left">
|
||||||
<input class="input" type="email" placeholder="PCI" />
|
<input
|
||||||
|
class="input"
|
||||||
|
type="email"
|
||||||
|
placeholder="PCI"
|
||||||
|
id="pci2"
|
||||||
|
/>
|
||||||
<span class="icon is-left">
|
<span class="icon is-left">
|
||||||
<i class="fa-solid fa-signal"></i>
|
<i class="fa-solid fa-signal"></i>
|
||||||
</span>
|
</span>
|
||||||
@@ -217,6 +230,7 @@
|
|||||||
class="input"
|
class="input"
|
||||||
type="email"
|
type="email"
|
||||||
placeholder="EARFCN"
|
placeholder="EARFCN"
|
||||||
|
id="earfcn3"
|
||||||
/>
|
/>
|
||||||
<span class="icon is-left">
|
<span class="icon is-left">
|
||||||
<i class="fas fa-bolt"></i>
|
<i class="fas fa-bolt"></i>
|
||||||
@@ -229,7 +243,12 @@
|
|||||||
<div class="field">
|
<div class="field">
|
||||||
<label class="label">PCI 3</label>
|
<label class="label">PCI 3</label>
|
||||||
<div class="control has-icons-left">
|
<div class="control has-icons-left">
|
||||||
<input class="input" type="email" placeholder="PCI" />
|
<input
|
||||||
|
class="input"
|
||||||
|
type="email"
|
||||||
|
placeholder="PCI"
|
||||||
|
id="pci3"
|
||||||
|
/>
|
||||||
<span class="icon is-left">
|
<span class="icon is-left">
|
||||||
<i class="fa-solid fa-signal"></i>
|
<i class="fa-solid fa-signal"></i>
|
||||||
</span>
|
</span>
|
||||||
@@ -244,6 +263,7 @@
|
|||||||
<a
|
<a
|
||||||
href="#"
|
href="#"
|
||||||
class="card-footer-item has-text-link has-text-weight-semibold has-text-white"
|
class="card-footer-item has-text-link has-text-weight-semibold has-text-white"
|
||||||
|
id="saveLTE"
|
||||||
>
|
>
|
||||||
Lock LTE Cells
|
Lock LTE Cells
|
||||||
</a>
|
</a>
|
||||||
@@ -251,6 +271,7 @@
|
|||||||
<a
|
<a
|
||||||
href="#"
|
href="#"
|
||||||
class="card-footer-item has-text-link has-text-weight-semibold has-text-white"
|
class="card-footer-item has-text-link has-text-weight-semibold has-text-white"
|
||||||
|
id="resetLTE"
|
||||||
>
|
>
|
||||||
Reset
|
Reset
|
||||||
</a>
|
</a>
|
||||||
@@ -275,6 +296,7 @@
|
|||||||
class="input"
|
class="input"
|
||||||
type="email"
|
type="email"
|
||||||
placeholder="NR-ARFCN"
|
placeholder="NR-ARFCN"
|
||||||
|
id="nr-arfcn"
|
||||||
/>
|
/>
|
||||||
<span class="icon is-left">
|
<span class="icon is-left">
|
||||||
<i class="fas fa-bolt"></i>
|
<i class="fas fa-bolt"></i>
|
||||||
@@ -291,6 +313,7 @@
|
|||||||
class="input"
|
class="input"
|
||||||
type="email"
|
type="email"
|
||||||
placeholder="NR PCI"
|
placeholder="NR PCI"
|
||||||
|
id="nr-pci"
|
||||||
/>
|
/>
|
||||||
<span class="icon is-left">
|
<span class="icon is-left">
|
||||||
<i class="fa-solid fa-signal"></i>
|
<i class="fa-solid fa-signal"></i>
|
||||||
@@ -306,13 +329,13 @@
|
|||||||
<label class="label">SCS</label>
|
<label class="label">SCS</label>
|
||||||
<div class="control has-icons-left">
|
<div class="control has-icons-left">
|
||||||
<span class="select">
|
<span class="select">
|
||||||
<select>
|
<select id="scs">
|
||||||
<option selected>Select SCS</option>
|
<option selected>Select SCS</option>
|
||||||
<option>15 kHz</option>
|
<option value="15">15 kHz</option>
|
||||||
<option>30 kHz</option>
|
<option value="30">30 kHz</option>
|
||||||
<option>60 kHz</option>
|
<option value="60">60 kHz</option>
|
||||||
<option>120 kHz</option>
|
<option value="120">120 kHz</option>
|
||||||
<option>240 kHz</option>
|
<option value="240">240 kHz</option>
|
||||||
</select>
|
</select>
|
||||||
</span>
|
</span>
|
||||||
<span class="icon is-left">
|
<span class="icon is-left">
|
||||||
@@ -330,6 +353,7 @@
|
|||||||
class="input"
|
class="input"
|
||||||
type="email"
|
type="email"
|
||||||
placeholder="NR Band"
|
placeholder="NR Band"
|
||||||
|
id="nr-band"
|
||||||
/>
|
/>
|
||||||
<span class="icon is-left">
|
<span class="icon is-left">
|
||||||
<i class="fa-solid fa-signal"></i>
|
<i class="fa-solid fa-signal"></i>
|
||||||
@@ -345,6 +369,7 @@
|
|||||||
<a
|
<a
|
||||||
href="#"
|
href="#"
|
||||||
class="card-footer-item has-text-link has-text-weight-semibold has-text-white"
|
class="card-footer-item has-text-link has-text-weight-semibold has-text-white"
|
||||||
|
id="saveSA"
|
||||||
>
|
>
|
||||||
Lock SA Cells
|
Lock SA Cells
|
||||||
</a>
|
</a>
|
||||||
@@ -352,6 +377,7 @@
|
|||||||
<a
|
<a
|
||||||
href="#"
|
href="#"
|
||||||
class="card-footer-item has-text-link has-text-weight-semibold has-text-white"
|
class="card-footer-item has-text-link has-text-weight-semibold has-text-white"
|
||||||
|
id="resetSA"
|
||||||
>
|
>
|
||||||
Reset
|
Reset
|
||||||
</a>
|
</a>
|
||||||
@@ -388,11 +414,12 @@
|
|||||||
<span class="icon has-text-info">
|
<span class="icon has-text-info">
|
||||||
<i class="fas fa-info-circle"></i>
|
<i class="fas fa-info-circle"></i>
|
||||||
</span>
|
</span>
|
||||||
<span>Current Active Locked Cells</span>
|
<span>NR-5G Cell Locking</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<p class="block has-text-weight-semibold">
|
<p class="block has-text-weight-semibold">
|
||||||
150/69, 1350/70, 1350/71
|
The NR-5G cell locking feature is only available on 5G-NR SA mode. <br />
|
||||||
|
This will also change the preferred network mode to NR5G.
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -24,6 +24,7 @@
|
|||||||
<script src="/js/styles/modal-trigger.js"></script>
|
<script src="/js/styles/modal-trigger.js"></script>
|
||||||
<script src="/js/utils/reboot.js"></script>
|
<script src="/js/utils/reboot.js"></script>
|
||||||
<script src="/js/utils/restart-connection.js"></script>
|
<script src="/js/utils/restart-connection.js"></script>
|
||||||
|
<script src="/js/cell-scanner/cell-scanner.js"></script>
|
||||||
<script defer src="/js/auth/auth.js"></script>
|
<script defer src="/js/auth/auth.js"></script>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
@@ -149,7 +150,7 @@
|
|||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<div class="card-content">
|
<div class="card-content">
|
||||||
<table class="table is-fullwidth">
|
<!-- <table class="table is-fullwidth">
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th>Network Provider</th>
|
<th>Network Provider</th>
|
||||||
@@ -162,7 +163,7 @@
|
|||||||
<th class="is-hidden-mobile">SINR</th>
|
<th class="is-hidden-mobile">SINR</th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody id="fullCellScanTableBody">
|
||||||
<tr>
|
<tr>
|
||||||
<td>Smart</td>
|
<td>Smart</td>
|
||||||
<td>B1</td>
|
<td>B1</td>
|
||||||
@@ -171,106 +172,46 @@
|
|||||||
<td>623</td>
|
<td>623</td>
|
||||||
<td class="is-hidden-mobile">
|
<td class="is-hidden-mobile">
|
||||||
<div class="tags has-addons">
|
<div class="tags has-addons">
|
||||||
<span class="tag is-size-6"> -103 </span>
|
<span class="tag is-size-6">-103</span>
|
||||||
<span class="tag is-danger is-size-6 has-text-white">
|
<span class="tag is-danger is-size-6 has-text-white"
|
||||||
Poor
|
>Poor</span
|
||||||
</span>
|
>
|
||||||
</div>
|
</div>
|
||||||
</td>
|
</td>
|
||||||
<td class="is-hidden-mobile">
|
<td class="is-hidden-mobile">
|
||||||
<div class="tags has-addons">
|
<div class="tags has-addons">
|
||||||
<span class="tag is-size-6"> -103 </span>
|
<span class="tag is-size-6">-103</span>
|
||||||
<span class="tag is-danger is-size-6 has-text-white">
|
<span class="tag is-danger is-size-6 has-text-white"
|
||||||
Poor
|
>Poor</span
|
||||||
</span>
|
>
|
||||||
</div>
|
</div>
|
||||||
</td>
|
</td>
|
||||||
<td class="is-hidden-mobile">
|
<td class="is-hidden-mobile">
|
||||||
<div class="tags has-addons">
|
<div class="tags has-addons">
|
||||||
<span class="tag is-size-6"> -5 </span>
|
<span class="tag is-size-6">-5</span>
|
||||||
<span class="tag is-danger is-size-6 has-text-white">
|
<span class="tag is-danger is-size-6 has-text-white"
|
||||||
Poor
|
>Poor</span
|
||||||
</span>
|
>
|
||||||
</div>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
|
|
||||||
<tr>
|
|
||||||
<td>DITO</td>
|
|
||||||
<td>B2</td>
|
|
||||||
<td>150</td>
|
|
||||||
<td>10</td>
|
|
||||||
<td>623</td>
|
|
||||||
<td class="is-hidden-mobile">
|
|
||||||
<div class="tags has-addons">
|
|
||||||
<span class="tag is-size-6"> -90 </span>
|
|
||||||
<span class="tag is-warning is-size-6 has-text-white">
|
|
||||||
Medium
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
</td>
|
|
||||||
<td class="is-hidden-mobile">
|
|
||||||
<div class="tags has-addons">
|
|
||||||
<span class="tag is-size-6"> -90 </span>
|
|
||||||
<span class="tag is-warning is-size-6 has-text-white">
|
|
||||||
Medium
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
</td>
|
|
||||||
<td class="is-hidden-mobile">
|
|
||||||
<div class="tags has-addons">
|
|
||||||
<span class="tag is-size-6"> 10 </span>
|
|
||||||
<span class="tag is-warning is-size-6 has-text-white">
|
|
||||||
Medium
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
|
|
||||||
<tr>
|
|
||||||
<td>Globe</td>
|
|
||||||
<td>B3</td>
|
|
||||||
<td>150</td>
|
|
||||||
<td>10</td>
|
|
||||||
<td>623</td>
|
|
||||||
<td class="is-hidden-mobile">
|
|
||||||
<div class="tags has-addons">
|
|
||||||
<span class="tag is-size-6"> -65 </span>
|
|
||||||
<span class="tag is-success is-size-6 has-text-white">
|
|
||||||
Strong
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
</td>
|
|
||||||
<td class="is-hidden-mobile">
|
|
||||||
<div class="tags has-addons">
|
|
||||||
<span class="tag is-size-6"> -65 </span>
|
|
||||||
<span class="tag is-success is-size-6 has-text-white">
|
|
||||||
Strong
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
</td>
|
|
||||||
<td class="is-hidden-mobile">
|
|
||||||
<div class="tags has-addons">
|
|
||||||
<span class="tag is-size-6"> 30 </span>
|
|
||||||
<span class="tag is-success is-size-6 has-text-white">
|
|
||||||
Strong
|
|
||||||
</span>
|
|
||||||
</div>
|
</div>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table> -->
|
||||||
|
<p>
|
||||||
|
Still under development. Please check back later for updates.
|
||||||
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<div class="card-footer">
|
<div class="card-footer">
|
||||||
<a
|
<a
|
||||||
href="#"
|
href="#"
|
||||||
|
id="startFullScanBtn"
|
||||||
class="card-footer-item has-text-link has-text-weight-semibold has-text-white"
|
class="card-footer-item has-text-link has-text-weight-semibold has-text-white"
|
||||||
>
|
>
|
||||||
Start Full Scan
|
Start Full Scan
|
||||||
</a>
|
</a>
|
||||||
|
|
||||||
<a
|
<a
|
||||||
href="#"
|
href="#"
|
||||||
|
id="resetFullScanBtn"
|
||||||
class="card-footer-item has-text-link has-text-weight-semibold has-text-white"
|
class="card-footer-item has-text-link has-text-weight-semibold has-text-white"
|
||||||
>
|
>
|
||||||
Reset
|
Reset
|
||||||
@@ -285,124 +226,38 @@
|
|||||||
<div class="card-header-title">Full Neighbour Cell Scanner</div>
|
<div class="card-header-title">Full Neighbour Cell Scanner</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="card-content">
|
<div class="card-content">
|
||||||
<table class="table is-fullwidth">
|
<table class="table is-fullwidth" id="neighbourCellTable">
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th>Name</th>
|
<th>Type</th>
|
||||||
<th>EARFCN</th>
|
<th>EARFCN</th>
|
||||||
<th>Bandwidth</th>
|
|
||||||
<th>Physical ID</th>
|
<th>Physical ID</th>
|
||||||
<th class="is-hidden-mobile">RSRP</th>
|
<th class="is-hidden-mobile">RSRP</th>
|
||||||
<th class="is-hidden-mobile">RSRQ</th>
|
<th class="is-hidden-mobile">RSRQ</th>
|
||||||
<th class="is-hidden-mobile">SINR</th>
|
<th class="is-hidden-mobile">RSSI</th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody id="neighbourCellTableBody"></tbody>
|
||||||
<tr>
|
|
||||||
<td>B1</td>
|
|
||||||
<td>150</td>
|
|
||||||
<td>10</td>
|
|
||||||
<td>623</td>
|
|
||||||
<td class="is-hidden-mobile">
|
|
||||||
<div class="tags has-addons">
|
|
||||||
<span class="tag is-size-6"> -103 </span>
|
|
||||||
<span class="tag is-danger is-size-6 has-text-white">
|
|
||||||
Poor
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
</td>
|
|
||||||
<td class="is-hidden-mobile">
|
|
||||||
<div class="tags has-addons">
|
|
||||||
<span class="tag is-size-6"> -103 </span>
|
|
||||||
<span class="tag is-danger is-size-6 has-text-white">
|
|
||||||
Poor
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
</td>
|
|
||||||
<td class="is-hidden-mobile">
|
|
||||||
<div class="tags has-addons">
|
|
||||||
<span class="tag is-size-6"> -5 </span>
|
|
||||||
<span class="tag is-danger is-size-6 has-text-white">
|
|
||||||
Poor
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
|
|
||||||
<tr>
|
|
||||||
<td>B2</td>
|
|
||||||
<td>150</td>
|
|
||||||
<td>10</td>
|
|
||||||
<td>623</td>
|
|
||||||
<td class="is-hidden-mobile">
|
|
||||||
<div class="tags has-addons">
|
|
||||||
<span class="tag is-size-6"> -90 </span>
|
|
||||||
<span class="tag is-warning is-size-6 has-text-white">
|
|
||||||
Medium
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
</td>
|
|
||||||
<td class="is-hidden-mobile">
|
|
||||||
<div class="tags has-addons">
|
|
||||||
<span class="tag is-size-6"> -90 </span>
|
|
||||||
<span class="tag is-warning is-size-6 has-text-white">
|
|
||||||
Medium
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
</td>
|
|
||||||
<td class="is-hidden-mobile">
|
|
||||||
<div class="tags has-addons">
|
|
||||||
<span class="tag is-size-6"> 10 </span>
|
|
||||||
<span class="tag is-warning is-size-6 has-text-white">
|
|
||||||
Medium
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
|
|
||||||
<tr>
|
|
||||||
<td>B3</td>
|
|
||||||
<td>150</td>
|
|
||||||
<td>10</td>
|
|
||||||
<td>623</td>
|
|
||||||
<td class="is-hidden-mobile">
|
|
||||||
<div class="tags has-addons">
|
|
||||||
<span class="tag is-size-6"> -65 </span>
|
|
||||||
<span class="tag is-success is-size-6 has-text-white">
|
|
||||||
Strong
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
</td>
|
|
||||||
<td class="is-hidden-mobile">
|
|
||||||
<div class="tags has-addons">
|
|
||||||
<span class="tag is-size-6"> -65 </span>
|
|
||||||
<span class="tag is-success is-size-6 has-text-white">
|
|
||||||
Strong
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
</td>
|
|
||||||
<td class="is-hidden-mobile">
|
|
||||||
<div class="tags has-addons">
|
|
||||||
<span class="tag is-size-6"> 30 </span>
|
|
||||||
<span class="tag is-success is-size-6 has-text-white">
|
|
||||||
Strong
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
</tbody>
|
|
||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
<div class="card-footer">
|
<div class="card-footer">
|
||||||
<a
|
<a
|
||||||
href="#"
|
href="#"
|
||||||
|
id="startLTEScanBtn"
|
||||||
class="card-footer-item has-text-link has-text-weight-semibold has-text-white"
|
class="card-footer-item has-text-link has-text-weight-semibold has-text-white"
|
||||||
>
|
>
|
||||||
Start Neighbourcell Scan
|
Start LTE Neighbourcell Scan
|
||||||
</a>
|
</a>
|
||||||
|
|
||||||
<a
|
<a
|
||||||
href="#"
|
href="#"
|
||||||
|
id="startNR5GScanBtn"
|
||||||
|
class="card-footer-item has-text-link has-text-weight-semibold has-text-white"
|
||||||
|
>
|
||||||
|
Start NR5G Neighbourcell Scan
|
||||||
|
</a>
|
||||||
|
<a
|
||||||
|
href="#"
|
||||||
|
id="resetScanBtn"
|
||||||
class="card-footer-item has-text-link has-text-weight-semibold has-text-white"
|
class="card-footer-item has-text-link has-text-weight-semibold has-text-white"
|
||||||
>
|
>
|
||||||
Reset
|
Reset
|
||||||
@@ -417,47 +272,19 @@
|
|||||||
<footer class="footer">
|
<footer class="footer">
|
||||||
<div class="content">
|
<div class="content">
|
||||||
<div class="fixed-grid has-2-cols has-1-cols-mobile">
|
<div class="fixed-grid has-2-cols has-1-cols-mobile">
|
||||||
<div class="grid">
|
<div class="icon-text">
|
||||||
<div class="cell">
|
<span class="icon has-text-info">
|
||||||
<div class="icon-text">
|
<i class="fas fa-info-circle"></i>
|
||||||
<span class="icon has-text-info">
|
</span>
|
||||||
<i class="fas fa-info-circle"></i>
|
<span>Full Network Provider Cell Scanner</span>
|
||||||
</span>
|
|
||||||
<span>Information</span>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<p class="block has-text-weight-semibold">
|
|
||||||
Full Network Provider Cell Scanner will scan all of the cells in
|
|
||||||
your area even from other network providers.
|
|
||||||
<br />
|
|
||||||
The NR-5G scan will only display SA bands available in your
|
|
||||||
area.
|
|
||||||
<br />
|
|
||||||
Scanning will take a few minutes so please wait patiently.
|
|
||||||
<br />
|
|
||||||
If problem persists after scanning, please reboot the modem.
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="cell">
|
|
||||||
<div class="icon-text">
|
|
||||||
<span class="icon has-text-info">
|
|
||||||
<i class="fas fa-info-circle"></i>
|
|
||||||
</span>
|
|
||||||
<span>Information</span>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<p class="block has-text-weight-semibold">
|
|
||||||
Full Neighbour Cell Scanner will only scan the bands of your
|
|
||||||
active network provider.
|
|
||||||
<br />
|
|
||||||
The NR-5G scan result is based on your current active 5G network
|
|
||||||
mode.
|
|
||||||
<br />
|
|
||||||
If problem persists after scanning, please reboot the modem.
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
<p class="block has-text-weight-semibold">
|
||||||
|
This command is recommended to be used when there is no (U)SIM card.
|
||||||
|
<br />
|
||||||
|
This command does not apply to 5G cells in NSA mode.
|
||||||
|
<br />
|
||||||
|
Scanning will take a few minutes so please wait patiently.
|
||||||
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</footer>
|
</footer>
|
||||||
|
|||||||
@@ -233,6 +233,27 @@
|
|||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="cell">
|
||||||
|
<div class="field">
|
||||||
|
<label class="label">U-SIM Slot Configuration</label>
|
||||||
|
<p class="control has-icons-left">
|
||||||
|
<span class="select">
|
||||||
|
<select id="simSlot">
|
||||||
|
<option value="">Select active SIM slot</option>
|
||||||
|
<option value="1">SIM Slot 1</option>
|
||||||
|
<option value="2">SIM Slot 2</option>
|
||||||
|
</select>
|
||||||
|
</span>
|
||||||
|
<span class="icon is-small is-left">
|
||||||
|
<i class="fas fa-sim-card" id="simCardIcon"></i>
|
||||||
|
</span>
|
||||||
|
</p>
|
||||||
|
<p class="help">
|
||||||
|
Selecting a mode will apply immediately.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -24,6 +24,7 @@
|
|||||||
<script src="/js/styles/modal-trigger.js"></script>
|
<script src="/js/styles/modal-trigger.js"></script>
|
||||||
<script src="/js/utils/reboot.js"></script>
|
<script src="/js/utils/reboot.js"></script>
|
||||||
<script src="/js/utils/restart-connection.js"></script>
|
<script src="/js/utils/restart-connection.js"></script>
|
||||||
|
<script src="/js/cell-sms/sms-manager.js"></script>
|
||||||
<script defer src="/js/auth/auth.js"></script>
|
<script defer src="/js/auth/auth.js"></script>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
@@ -152,150 +153,9 @@
|
|||||||
class="fixed-grid has-1-cols"
|
class="fixed-grid has-1-cols"
|
||||||
style="height: 450px; overflow-y: scroll"
|
style="height: 450px; overflow-y: scroll"
|
||||||
>
|
>
|
||||||
<div class="grid is-gap-4 is-fullwidth">
|
<!-- This is where messages will be inserted -->
|
||||||
<div class="cell">
|
<div id="sms-container" class="grid is-gap-4 is-fullwidth">
|
||||||
<div class="is-flex is-align-items-center">
|
<!-- Messages will be populated here -->
|
||||||
<div class="checkbox mr-6">
|
|
||||||
<input type="checkbox" />
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
class="is-flex is-flex-direction-column is-align-items-start"
|
|
||||||
>
|
|
||||||
<p class="has-text-weight-semibold">
|
|
||||||
Senders Name Here
|
|
||||||
</p>
|
|
||||||
<p>2022-11-20 13:30:00</p>
|
|
||||||
<p>
|
|
||||||
Message content here. Lorem ipsum dolor sit amet
|
|
||||||
consectetur adipisicing elit. Corrupti doloremque
|
|
||||||
voluptatum velit repellendus ipsum delectus
|
|
||||||
blanditiis quis dolores. Maxime labore esse
|
|
||||||
laboriosam inventore error molestiae consequuntur
|
|
||||||
quo, deleniti ea nihil.
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="cell">
|
|
||||||
<div class="is-flex is-align-items-center">
|
|
||||||
<div class="checkbox mr-6">
|
|
||||||
<input type="checkbox" />
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
class="is-flex is-flex-direction-column is-align-items-start"
|
|
||||||
>
|
|
||||||
<p class="has-text-weight-semibold">
|
|
||||||
Senders Name Here
|
|
||||||
</p>
|
|
||||||
<p>2022-11-20 13:30:00</p>
|
|
||||||
<p>
|
|
||||||
Message content here. Lorem ipsum dolor sit amet
|
|
||||||
consectetur adipisicing elit. Corrupti doloremque
|
|
||||||
voluptatum velit repellendus ipsum delectus
|
|
||||||
blanditiis quis dolores. Maxime labore esse
|
|
||||||
laboriosam inventore error molestiae consequuntur
|
|
||||||
quo, deleniti ea nihil.
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="cell">
|
|
||||||
<div class="is-flex is-align-items-center">
|
|
||||||
<div class="checkbox mr-6">
|
|
||||||
<input type="checkbox" />
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
class="is-flex is-flex-direction-column is-align-items-start"
|
|
||||||
>
|
|
||||||
<p class="has-text-weight-semibold">
|
|
||||||
Senders Name Here
|
|
||||||
</p>
|
|
||||||
<p>2022-11-20 13:30:00</p>
|
|
||||||
<p>
|
|
||||||
Message content here. Lorem ipsum dolor sit amet
|
|
||||||
consectetur adipisicing elit. Corrupti doloremque
|
|
||||||
voluptatum velit repellendus ipsum delectus
|
|
||||||
blanditiis quis dolores. Maxime labore esse
|
|
||||||
laboriosam inventore error molestiae consequuntur
|
|
||||||
quo, deleniti ea nihil.
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="cell">
|
|
||||||
<div class="is-flex is-align-items-center">
|
|
||||||
<div class="checkbox mr-6">
|
|
||||||
<input type="checkbox" />
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
class="is-flex is-flex-direction-column is-align-items-start"
|
|
||||||
>
|
|
||||||
<p class="has-text-weight-semibold">
|
|
||||||
Senders Name Here
|
|
||||||
</p>
|
|
||||||
<p>2022-11-20 13:30:00</p>
|
|
||||||
<p>
|
|
||||||
Message content here. Lorem ipsum dolor sit amet
|
|
||||||
consectetur adipisicing elit. Corrupti doloremque
|
|
||||||
voluptatum velit repellendus ipsum delectus
|
|
||||||
blanditiis quis dolores. Maxime labore esse
|
|
||||||
laboriosam inventore error molestiae consequuntur
|
|
||||||
quo, deleniti ea nihil.
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="cell">
|
|
||||||
<div class="is-flex is-align-items-center">
|
|
||||||
<div class="checkbox mr-6">
|
|
||||||
<input type="checkbox" />
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
class="is-flex is-flex-direction-column is-align-items-start"
|
|
||||||
>
|
|
||||||
<p class="has-text-weight-semibold">
|
|
||||||
Senders Name Here
|
|
||||||
</p>
|
|
||||||
<p>2022-11-20 13:30:00</p>
|
|
||||||
<p>
|
|
||||||
Message content here. Lorem ipsum dolor sit amet
|
|
||||||
consectetur adipisicing elit. Corrupti doloremque
|
|
||||||
voluptatum velit repellendus ipsum delectus
|
|
||||||
blanditiis quis dolores. Maxime labore esse
|
|
||||||
laboriosam inventore error molestiae consequuntur
|
|
||||||
quo, deleniti ea nihil.
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="cell">
|
|
||||||
<div class="is-flex is-align-items-center">
|
|
||||||
<div class="checkbox mr-6">
|
|
||||||
<input type="checkbox" />
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
class="is-flex is-flex-direction-column is-align-items-start"
|
|
||||||
>
|
|
||||||
<p class="has-text-weight-semibold">
|
|
||||||
Senders Name Here
|
|
||||||
</p>
|
|
||||||
<p>2022-11-20 13:30:00</p>
|
|
||||||
<p>
|
|
||||||
Message content here. Lorem ipsum dolor sit amet
|
|
||||||
consectetur adipisicing elit. Corrupti doloremque
|
|
||||||
voluptatum velit repellendus ipsum delectus
|
|
||||||
blanditiis quis dolores. Maxime labore esse
|
|
||||||
laboriosam inventore error molestiae consequuntur
|
|
||||||
quo, deleniti ea nihil.
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -303,6 +163,7 @@
|
|||||||
<div class="card-footer">
|
<div class="card-footer">
|
||||||
<a
|
<a
|
||||||
href="#"
|
href="#"
|
||||||
|
id="refresh-sms"
|
||||||
class="card-footer-item has-text-link has-text-weight-semibold has-text-white"
|
class="card-footer-item has-text-link has-text-weight-semibold has-text-white"
|
||||||
>
|
>
|
||||||
Refresh
|
Refresh
|
||||||
@@ -310,6 +171,7 @@
|
|||||||
|
|
||||||
<a
|
<a
|
||||||
href="#"
|
href="#"
|
||||||
|
id="delete-selected-sms"
|
||||||
class="card-footer-item has-text-link has-text-weight-semibold has-text-white"
|
class="card-footer-item has-text-link has-text-weight-semibold has-text-white"
|
||||||
>
|
>
|
||||||
Delete Selected
|
Delete Selected
|
||||||
@@ -328,9 +190,10 @@
|
|||||||
<div class="grid">
|
<div class="grid">
|
||||||
<div class="cell is-col-span-1">
|
<div class="cell is-col-span-1">
|
||||||
<div class="field">
|
<div class="field">
|
||||||
<label class="label">Receipient's Phone Number</label>
|
<label class="label">Recipient's Phone Number</label>
|
||||||
<div class="control has-icons-left">
|
<div class="control has-icons-left">
|
||||||
<input
|
<input
|
||||||
|
id="phone-number-input"
|
||||||
class="input"
|
class="input"
|
||||||
type="text"
|
type="text"
|
||||||
placeholder="Input Phone Number"
|
placeholder="Input Phone Number"
|
||||||
@@ -347,8 +210,9 @@
|
|||||||
<label class="label">Message</label>
|
<label class="label">Message</label>
|
||||||
<div class="control">
|
<div class="control">
|
||||||
<textarea
|
<textarea
|
||||||
|
id="message-input"
|
||||||
class="textarea"
|
class="textarea"
|
||||||
placeholder="Textarea"
|
placeholder="Enter your message here"
|
||||||
></textarea>
|
></textarea>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -359,6 +223,7 @@
|
|||||||
<div class="card-footer">
|
<div class="card-footer">
|
||||||
<a
|
<a
|
||||||
href="#"
|
href="#"
|
||||||
|
id="send-sms"
|
||||||
class="card-footer-item has-text-link has-text-weight-semibold has-text-white"
|
class="card-footer-item has-text-link has-text-weight-semibold has-text-white"
|
||||||
>
|
>
|
||||||
Send SMS
|
Send SMS
|
||||||
@@ -366,6 +231,7 @@
|
|||||||
|
|
||||||
<a
|
<a
|
||||||
href="#"
|
href="#"
|
||||||
|
id="reset-form"
|
||||||
class="card-footer-item has-text-link has-text-weight-semibold has-text-white"
|
class="card-footer-item has-text-link has-text-weight-semibold has-text-white"
|
||||||
>
|
>
|
||||||
Reset
|
Reset
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ escape_json() {
|
|||||||
JSON_RESPONSE="["
|
JSON_RESPONSE="["
|
||||||
|
|
||||||
# List of AT commands to run, one by one
|
# List of AT commands to run, one by one
|
||||||
for COMMAND in 'AT+CGMI' 'AT+CGMM' 'AT+CGMR' 'AT+CNUM' 'AT+CIMI' 'AT+ICCID' 'AT+CGSN' 'AT+QMAP="LANIP"' 'AT+QMAP="WWAN"'; do
|
for COMMAND in 'AT+CGMI' 'AT+CGMM' 'AT+CGMR' 'AT+CNUM' 'AT+CIMI' 'AT+ICCID' 'AT+CGSN' 'AT+QMAP="LANIP"' 'AT+QMAP="WWAN"' 'AT+QGETCAPABILITY'; do
|
||||||
# Write the command to the input file
|
# Write the command to the input file
|
||||||
echo "$COMMAND" > "$INPUT_FILE"
|
echo "$COMMAND" > "$INPUT_FILE"
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,258 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
# Parse POST data
|
||||||
|
read -r QUERY_STRING
|
||||||
|
|
||||||
|
# Function to urldecode
|
||||||
|
urldecode() {
|
||||||
|
echo -e "$(echo "$1" | sed 's/+/ /g;s/%\([0-9A-F][0-9A-F]\)/\\x\1/g')"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Function to send AT commands silently
|
||||||
|
send_at_command() {
|
||||||
|
echo "$1" | atinout - /dev/smd7 - >/dev/null 2>&1
|
||||||
|
}
|
||||||
|
|
||||||
|
# Extract reset flags
|
||||||
|
reset_lte=$(echo "$QUERY_STRING" | grep -o 'reset_lte=[^&]*' | cut -d= -f2)
|
||||||
|
reset_5g=$(echo "$QUERY_STRING" | grep -o 'reset_5g=[^&]*' | cut -d= -f2)
|
||||||
|
|
||||||
|
# Extract LTE values from POST data
|
||||||
|
earfcn1=$(echo "$QUERY_STRING" | grep -o 'earfcn1=[^&]*' | cut -d= -f2)
|
||||||
|
pci1=$(echo "$QUERY_STRING" | grep -o 'pci1=[^&]*' | cut -d= -f2)
|
||||||
|
earfcn2=$(echo "$QUERY_STRING" | grep -o 'earfcn2=[^&]*' | cut -d= -f2)
|
||||||
|
pci2=$(echo "$QUERY_STRING" | grep -o 'pci2=[^&]*' | cut -d= -f2)
|
||||||
|
earfcn3=$(echo "$QUERY_STRING" | grep -o 'earfcn3=[^&]*' | cut -d= -f2)
|
||||||
|
pci3=$(echo "$QUERY_STRING" | grep -o 'pci3=[^&]*' | cut -d= -f2)
|
||||||
|
|
||||||
|
# Extract 5G-SA values from POST data
|
||||||
|
nrarfcn=$(echo "$QUERY_STRING" | grep -o 'nrarfcn=[^&]*' | cut -d= -f2)
|
||||||
|
nrpci=$(echo "$QUERY_STRING" | grep -o 'nrpci=[^&]*' | cut -d= -f2)
|
||||||
|
scs=$(echo "$QUERY_STRING" | grep -o 'scs=[^&]*' | cut -d= -f2)
|
||||||
|
band=$(echo "$QUERY_STRING" | grep -o 'band=[^&]*' | cut -d= -f2)
|
||||||
|
|
||||||
|
# URL decode all values
|
||||||
|
reset_lte=$(urldecode "$reset_lte")
|
||||||
|
reset_5g=$(urldecode "$reset_5g")
|
||||||
|
earfcn1=$(urldecode "$earfcn1")
|
||||||
|
pci1=$(urldecode "$pci1")
|
||||||
|
earfcn2=$(urldecode "$earfcn2")
|
||||||
|
pci2=$(urldecode "$pci2")
|
||||||
|
earfcn3=$(urldecode "$earfcn3")
|
||||||
|
pci3=$(urldecode "$pci3")
|
||||||
|
nrarfcn=$(urldecode "$nrarfcn")
|
||||||
|
nrpci=$(urldecode "$nrpci")
|
||||||
|
scs=$(urldecode "$scs")
|
||||||
|
band=$(urldecode "$band")
|
||||||
|
|
||||||
|
# Send Content-type header before any other output
|
||||||
|
echo "Content-type: application/json"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# Handle reset requests
|
||||||
|
if [ "$reset_lte" = "1" ] || [ "$reset_5g" = "1" ]; then
|
||||||
|
# Remove configuration files
|
||||||
|
rm -f /etc/quecmanager/cell_lock_config.txt
|
||||||
|
rm -f /etc/quecmanager/apply_cell_lock.sh
|
||||||
|
|
||||||
|
# Remove from rc.local if present
|
||||||
|
sed -i '/\/etc\/quecmanager\/apply_cell_lock.sh/d' /etc/rc.local
|
||||||
|
|
||||||
|
if [ "$reset_lte" = "1" ] && [ "$reset_5g" = "1" ]; then
|
||||||
|
send_at_command "AT+QNWLOCK=\"common/4g\",0"
|
||||||
|
send_at_command "AT+QNWLOCK=\"common/5g\",0"
|
||||||
|
send_at_command "AT+QNWPREFCFG=\"mode_pref\",AUTO"
|
||||||
|
sleep 1
|
||||||
|
send_at_command "AT+COPS=2"
|
||||||
|
sleep 1
|
||||||
|
send_at_command "AT+COPS=0"
|
||||||
|
echo '{"status": "success", "message": "All cell lock configurations removed"}'
|
||||||
|
elif [ "$reset_lte" = "1" ]; then
|
||||||
|
send_at_command "AT+QNWLOCK=\"common/4g\",0"
|
||||||
|
sleep 1
|
||||||
|
send_at_command "AT+COPS=2"
|
||||||
|
sleep 1
|
||||||
|
send_at_command "AT+COPS=0"
|
||||||
|
echo '{"status": "success", "message": "LTE cell lock configuration removed"}'
|
||||||
|
else
|
||||||
|
send_at_command "AT+QNWLOCK=\"common/5g\",0"
|
||||||
|
send_at_command "AT+QNWPREFCFG=\"mode_pref\",AUTO"
|
||||||
|
sleep 1
|
||||||
|
send_at_command "AT+COPS=2"
|
||||||
|
sleep 1
|
||||||
|
send_at_command "AT+COPS=0"
|
||||||
|
echo '{"status": "success", "message": "5G cell lock configuration removed"}'
|
||||||
|
fi
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Create the directory structure if it doesn't exist
|
||||||
|
mkdir -p /etc/quecmanager /var/log/quecmanager
|
||||||
|
|
||||||
|
# Create a configuration file to store cell locking profiles
|
||||||
|
cat >/etc/quecmanager/cell_lock_config.txt <<EOF
|
||||||
|
# LTE Cell Locking Configuration
|
||||||
|
earfcn1=$earfcn1
|
||||||
|
pci1=$pci1
|
||||||
|
earfcn2=$earfcn2
|
||||||
|
pci2=$pci2
|
||||||
|
earfcn3=$earfcn3
|
||||||
|
pci3=$pci3
|
||||||
|
|
||||||
|
# 5G-SA Cell Locking Configuration
|
||||||
|
nrarfcn=$nrarfcn
|
||||||
|
nrpci=$nrpci
|
||||||
|
scs=$scs
|
||||||
|
band=$band
|
||||||
|
EOF
|
||||||
|
|
||||||
|
# Create the apply_cell_lock.sh script
|
||||||
|
cat >/etc/quecmanager/apply_cell_lock.sh <<'EOF'
|
||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
# Create log directory if it doesn't exist
|
||||||
|
LOG_DIR="/var/log/quecmanager"
|
||||||
|
mkdir -p "$LOG_DIR"
|
||||||
|
DEBUG_LOG="$LOG_DIR/cell_lock_debug.log"
|
||||||
|
|
||||||
|
# Function to log messages
|
||||||
|
log_message() {
|
||||||
|
local message="$1"
|
||||||
|
local timestamp=$(date '+%Y-%m-%d %H:%M:%S')
|
||||||
|
echo "$timestamp - $message" >> "$DEBUG_LOG"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Verify required tools
|
||||||
|
if ! command -v atinout >/dev/null 2>&1; then
|
||||||
|
log_message "Error: atinout command not found"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ ! -c "/dev/smd7" ]; then
|
||||||
|
log_message "Error: Modem device /dev/smd7 not found"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Function to send AT commands
|
||||||
|
send_at_command() {
|
||||||
|
local command="$1"
|
||||||
|
local description="$2"
|
||||||
|
local retry_count=0
|
||||||
|
local max_retries=3
|
||||||
|
|
||||||
|
log_message "Attempting to send command: $command ($description)"
|
||||||
|
|
||||||
|
while [ $retry_count -lt $max_retries ]; do
|
||||||
|
echo "$command" | atinout - /dev/smd7 - > /tmp/at_response.txt 2>&1
|
||||||
|
local result=$(cat /tmp/at_response.txt)
|
||||||
|
|
||||||
|
log_message "Attempt $((retry_count + 1)) - Response: $result"
|
||||||
|
|
||||||
|
if echo "$result" | grep -q "OK"; then
|
||||||
|
log_message "Command successful: $description"
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
retry_count=$((retry_count + 1))
|
||||||
|
log_message "Command failed, retry $retry_count of $max_retries"
|
||||||
|
sleep 2
|
||||||
|
done
|
||||||
|
|
||||||
|
log_message "Command failed after $max_retries retries: $description"
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
# Function to apply cell lock configuration
|
||||||
|
apply_cell_lock() {
|
||||||
|
local config_file="/etc/quecmanager/cell_lock_config.txt"
|
||||||
|
|
||||||
|
if [ ! -f "$config_file" ]; then
|
||||||
|
log_message "Configuration file not found"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Read configuration values
|
||||||
|
. "$config_file"
|
||||||
|
|
||||||
|
# Test modem responsiveness
|
||||||
|
if ! send_at_command "AT" "Testing modem responsiveness"; then
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Apply LTE configuration if present
|
||||||
|
if [ -n "$earfcn1" ] && [ -n "$pci1" ]; then
|
||||||
|
if [ -n "$earfcn2" ] && [ -n "$pci2" ]; then
|
||||||
|
if [ -n "$earfcn3" ] && [ -n "$pci3" ]; then
|
||||||
|
send_at_command "AT+QNWLOCK=\"common/4g\",3,$earfcn1,$pci1,$earfcn2,$pci2,$earfcn3,$pci3" "Setting LTE lock (3 cells)"
|
||||||
|
else
|
||||||
|
send_at_command "AT+QNWLOCK=\"common/4g\",2,$earfcn1,$pci1,$earfcn2,$pci2" "Setting LTE lock (2 cells)"
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
send_at_command "AT+QNWLOCK=\"common/4g\",1,$earfcn1,$pci1" "Setting LTE lock (1 cell)"
|
||||||
|
fi
|
||||||
|
|
||||||
|
sleep 1
|
||||||
|
if ! send_at_command "AT+COPS=2" "Network Disconnected"; then
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
sleep 1
|
||||||
|
if ! send_at_command "AT+COPS=0" "Network Reconnected"; then
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Apply 5G configuration if present
|
||||||
|
if [ -n "$nrpci" ] && [ -n "$nrarfcn" ] && [ -n "$scs" ] && [ -n "$band" ]; then
|
||||||
|
|
||||||
|
if ! send_at_command "AT+QNWPREFCFG=\"mode_pref\",NR5G" "Setting network to SA only"; then
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
sleep 1
|
||||||
|
|
||||||
|
if ! send_at_command "AT+QNWLOCK=\"common/5g\",$nrpci,$nrarfcn,$scs,$band" "Setting 5G lock"; then
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
sleep 1
|
||||||
|
if ! send_at_command "AT+COPS=2" "Network Disconnected"; then
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
sleep 1
|
||||||
|
if ! send_at_command "AT+COPS=0" "Network Reconnected"; then
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
# Main execution
|
||||||
|
log_message "Starting cell lock configuration"
|
||||||
|
if apply_cell_lock; then
|
||||||
|
log_message "Cell lock configuration applied successfully"
|
||||||
|
exit 0
|
||||||
|
else
|
||||||
|
log_message "Failed to apply cell lock configuration"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
EOF
|
||||||
|
|
||||||
|
# Make the script executable
|
||||||
|
chmod +x /etc/quecmanager/apply_cell_lock.sh
|
||||||
|
|
||||||
|
# Add to rc.local if not already present
|
||||||
|
if ! grep -q "/etc/quecmanager/apply_cell_lock.sh" /etc/rc.local; then
|
||||||
|
sed -i '/exit 0/i sleep 30\n\/etc\/quecmanager\/apply_cell_lock.sh' /etc/rc.local
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Run the script immediately
|
||||||
|
/etc/quecmanager/apply_cell_lock.sh
|
||||||
|
result=$?
|
||||||
|
|
||||||
|
if [ $result -eq 0 ]; then
|
||||||
|
echo '{"status": "success", "message": "Cell lock configurations saved and applied successfully"}'
|
||||||
|
else
|
||||||
|
echo '{"status": "error", "message": "Cell lock configurations saved but failed to apply"}'
|
||||||
|
fi
|
||||||
@@ -0,0 +1,60 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
echo "Content-type: application/json"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
CONFIG_FILE="/etc/quecmanager/cell_lock_config.txt"
|
||||||
|
|
||||||
|
if [ ! -f "$CONFIG_FILE" ]; then
|
||||||
|
echo '{"status": "error", "message": "No cell lock configurations found", "configurations": {}}'
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Function to read config values
|
||||||
|
get_config_value() {
|
||||||
|
local key=$1
|
||||||
|
local value=$(grep "^$key=" "$CONFIG_FILE" | sed "s/^$key=//")
|
||||||
|
# Remove any trailing whitespace or comments
|
||||||
|
value=$(echo "$value" | sed 's/[[:space:]]*#.*$//' | sed 's/[[:space:]]*$//')
|
||||||
|
echo "$value"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Read LTE configuration values
|
||||||
|
earfcn1=$(get_config_value "earfcn1")
|
||||||
|
pci1=$(get_config_value "pci1")
|
||||||
|
earfcn2=$(get_config_value "earfcn2")
|
||||||
|
pci2=$(get_config_value "pci2")
|
||||||
|
earfcn3=$(get_config_value "earfcn3")
|
||||||
|
pci3=$(get_config_value "pci3")
|
||||||
|
|
||||||
|
# Read 5G-SA configuration values
|
||||||
|
nrarfcn=$(get_config_value "nrarfcn")
|
||||||
|
nrpci=$(get_config_value "nrpci")
|
||||||
|
scs=$(get_config_value "scs")
|
||||||
|
band=$(get_config_value "band")
|
||||||
|
|
||||||
|
# Debug output to syslog
|
||||||
|
logger "fetch-cell-lock: earfcn1=$earfcn1 pci1=$pci1 nrarfcn=$nrarfcn nrpci=$nrpci"
|
||||||
|
|
||||||
|
# Construct JSON response
|
||||||
|
cat << EOF
|
||||||
|
{
|
||||||
|
"status": "success",
|
||||||
|
"configurations": {
|
||||||
|
"lte": {
|
||||||
|
"earfcn1": "${earfcn1:-}",
|
||||||
|
"pci1": "${pci1:-}",
|
||||||
|
"earfcn2": "${earfcn2:-}",
|
||||||
|
"pci2": "${pci2:-}",
|
||||||
|
"earfcn3": "${earfcn3:-}",
|
||||||
|
"pci3": "${pci3:-}"
|
||||||
|
},
|
||||||
|
"sa": {
|
||||||
|
"nrarfcn": "${nrarfcn:-}",
|
||||||
|
"nrpci": "${nrpci:-}",
|
||||||
|
"scs": "${scs:-}",
|
||||||
|
"band": "${band:-}"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
EOF
|
||||||
@@ -0,0 +1,120 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
# Enable debug logging
|
||||||
|
# exec 1> >(logger -s -t $(basename $0)) 2>&1
|
||||||
|
# Create log directory if it doesn't exist
|
||||||
|
LOG_DIR="/var/log/quecmanager"
|
||||||
|
mkdir -p "$LOG_DIR"
|
||||||
|
DEBUG_LOG="$LOG_DIR/cell_lock_debug.log"
|
||||||
|
|
||||||
|
# Function to log messages
|
||||||
|
log_message() {
|
||||||
|
local message="$1"
|
||||||
|
local timestamp=$(date '+%Y-%m-%d %H:%M:%S')
|
||||||
|
echo "$timestamp - $message" >> "$DEBUG_LOG"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Verify required tools
|
||||||
|
if ! command -v atinout >/dev/null 2>&1; then
|
||||||
|
log_message "Error: atinout command not found"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ ! -c "/dev/smd11" ]; then
|
||||||
|
log_message "Error: Modem device /dev/smd11 not found"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Function to send AT commands
|
||||||
|
send_at_command() {
|
||||||
|
local command="$1"
|
||||||
|
local description="$2"
|
||||||
|
local retry_count=0
|
||||||
|
local max_retries=3
|
||||||
|
|
||||||
|
log_message "Attempting to send command: $command ($description)"
|
||||||
|
|
||||||
|
while [ $retry_count -lt $max_retries ]; do
|
||||||
|
echo "$command" | atinout - /dev/smd11 - > /tmp/at_response.txt 2>&1
|
||||||
|
local result=$(cat /tmp/at_response.txt)
|
||||||
|
|
||||||
|
log_message "Attempt $((retry_count + 1)) - Response: $result"
|
||||||
|
|
||||||
|
if echo "$result" | grep -q "OK"; then
|
||||||
|
log_message "Command successful: $description"
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
retry_count=$((retry_count + 1))
|
||||||
|
log_message "Command failed, retry $retry_count of $max_retries"
|
||||||
|
sleep 2
|
||||||
|
done
|
||||||
|
|
||||||
|
log_message "Command failed after $max_retries retries: $description"
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
# Function to apply cell lock configuration
|
||||||
|
apply_cell_lock() {
|
||||||
|
local config_file="/etc/quecmanager/cell_lock_config.txt"
|
||||||
|
|
||||||
|
if [ ! -f "$config_file" ]; then
|
||||||
|
log_message "Configuration file not found"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Read configuration values
|
||||||
|
. "$config_file"
|
||||||
|
|
||||||
|
# Test modem responsiveness
|
||||||
|
if ! send_at_command "AT" "Testing modem responsiveness"; then
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Apply LTE configuration if present
|
||||||
|
if [ -n "$earfcn1" ] && [ -n "$pci1" ]; then
|
||||||
|
if ! send_at_command "AT+CFUN=0" "Setting radio off"; then
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
sleep 2
|
||||||
|
|
||||||
|
if [ -n "$earfcn2" ] && [ -n "$pci2" ]; then
|
||||||
|
if [ -n "$earfcn3" ] && [ -n "$pci3" ]; then
|
||||||
|
send_at_command "AT+QNWLOCK=\"common/4g\",3,$earfcn1,$pci1,$earfcn2,$pci2,$earfcn3,$pci3" "Setting LTE lock (3 cells)"
|
||||||
|
else
|
||||||
|
send_at_command "AT+QNWLOCK=\"common/4g\",2,$earfcn1,$pci1,$earfcn2,$pci2" "Setting LTE lock (2 cells)"
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
send_at_command "AT+QNWLOCK=\"common/4g\",1,$earfcn1,$pci1" "Setting LTE lock (1 cell)"
|
||||||
|
fi
|
||||||
|
|
||||||
|
sleep 2
|
||||||
|
if ! send_at_command "AT+CFUN=1" "Setting radio on"; then
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Apply 5G configuration if present
|
||||||
|
if [ -n "$nrpci" ] && [ -n "$nrarfcn" ] && [ -n "$scs" ] && [ -n "$band" ]; then
|
||||||
|
if ! send_at_command "AT+CFUN=0" "Setting radio off"; then
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
sleep 2
|
||||||
|
if ! send_at_command "AT+QNWLOCK=\"common/5g\",$nrpci,$nrarfcn,$scs,$band" "Setting 5G lock"; then
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
sleep 2
|
||||||
|
if ! send_at_command "AT+CFUN=1" "Setting radio on"; then
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# Main execution
|
||||||
|
log_message "Starting cell lock configuration"
|
||||||
|
if apply_cell_lock; then
|
||||||
|
log_message "Cell lock configuration applied successfully"
|
||||||
|
exit 0
|
||||||
|
else
|
||||||
|
log_message "Failed to apply cell lock configuration"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
@@ -29,7 +29,7 @@ escape_json() {
|
|||||||
JSON_RESPONSE="["
|
JSON_RESPONSE="["
|
||||||
|
|
||||||
# List of AT commands to run, one by one
|
# List of AT commands to run, one by one
|
||||||
for COMMAND in 'AT+CGDCONT?' 'AT+QNWPREFCFG="mode_pref"' 'AT+QNWPREFCFG="nr5g_disable_mode"'; do
|
for COMMAND in 'AT+CGDCONT?' 'AT+QNWPREFCFG="mode_pref"' 'AT+QNWPREFCFG="nr5g_disable_mode"' 'AT+QUIMSLOT?'; do
|
||||||
# Write the command to the input file
|
# Write the command to the input file
|
||||||
echo "$COMMAND" > "$INPUT_FILE"
|
echo "$COMMAND" > "$INPUT_FILE"
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
/* */
|
/* */
|
||||||
|
|
||||||
/* import Poppins font */
|
/* import Poppins font */
|
||||||
@import url("https://fonts.googleapis.com/css2?family=Euclid+Flex:wght@400;500;600;700&display=swap");
|
@import url('https://fonts.googleapis.com/css2?family=Poppins:wght@300;400;500;600;700&display=swap');
|
||||||
|
|
||||||
/* use Poppins everywhere */
|
/* use Poppins everywhere */
|
||||||
* {
|
* {
|
||||||
@@ -332,3 +332,23 @@ html.theme-light {
|
|||||||
from { transform: rotate(0deg); }
|
from { transform: rotate(0deg); }
|
||||||
to { transform: rotate(359deg); }
|
to { transform: rotate(359deg); }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.loading-container {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
gap: 1rem;
|
||||||
|
height: 200px;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.loading-container .icon {
|
||||||
|
color: #3273dc;
|
||||||
|
}
|
||||||
|
|
||||||
|
.loading-container p {
|
||||||
|
color: #4a4a4a;
|
||||||
|
font-size: 1.2rem;
|
||||||
|
margin-top: 1rem;
|
||||||
|
}
|
||||||
@@ -66,6 +66,19 @@ const DATA_MAP = {
|
|||||||
}),
|
}),
|
||||||
elementIds: ["IPv4", "IPv6"],
|
elementIds: ["IPv4", "IPv6"],
|
||||||
},
|
},
|
||||||
|
QGETCAPABILITY: {
|
||||||
|
// Changed from LTECATERGORY to match the actual command
|
||||||
|
parse: (response) => {
|
||||||
|
const lines = response.split("\n");
|
||||||
|
for (const line of lines) {
|
||||||
|
if (line.includes("LTE-CATEGORY")) {
|
||||||
|
return `CAT-${line.split(":").pop().trim()}`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return "";
|
||||||
|
},
|
||||||
|
elementId: "lteCategory",
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
// DOM Element Selectors
|
// DOM Element Selectors
|
||||||
@@ -240,39 +253,6 @@ async function saveIMEISetting() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Data Parsing Functions
|
|
||||||
function parseDeviceData(response, key) {
|
|
||||||
const dataMap = {
|
|
||||||
CGMI: (response) => response.split("\n")[1].trim(),
|
|
||||||
CGMM: (response) => response.split("\n")[1].trim(),
|
|
||||||
CGMR: (response) => response.split("\n")[1].trim(),
|
|
||||||
CNUM: (response) =>
|
|
||||||
response
|
|
||||||
.split("\n")[1]
|
|
||||||
.split(":")[1]
|
|
||||||
.split(",")[1]
|
|
||||||
.replace(/"/g, "")
|
|
||||||
.trim(),
|
|
||||||
CIMI: (response) => response.split("\n")[1].trim(),
|
|
||||||
ICCID: (response) => response.split("\n")[1].split(":")[1].trim(),
|
|
||||||
CGSN: (response) => response.split("\n")[1].trim(),
|
|
||||||
LANIP: (response) =>
|
|
||||||
response.split("\n")[1].split(":")[1].split(",")[3].trim(),
|
|
||||||
WWAN: (response) => ({
|
|
||||||
IPv4: response
|
|
||||||
.split("\n")[1]
|
|
||||||
.split(":")[1]
|
|
||||||
.split(",")[4]
|
|
||||||
.replace(/"/g, "")
|
|
||||||
.trim(),
|
|
||||||
IPv6: response.split("\n")[2].split(",")[4].replace(/"/g, "").trim(),
|
|
||||||
}),
|
|
||||||
};
|
|
||||||
|
|
||||||
return dataMap[key]?.(response);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Data Fetching and Display
|
|
||||||
// Data Parsing and Update Functions
|
// Data Parsing and Update Functions
|
||||||
function updateDeviceInfo(key, value) {
|
function updateDeviceInfo(key, value) {
|
||||||
const mapping = DATA_MAP[key];
|
const mapping = DATA_MAP[key];
|
||||||
@@ -317,6 +297,7 @@ async function fetchAboutData() {
|
|||||||
Object.keys(DATA_MAP).forEach((key) => {
|
Object.keys(DATA_MAP).forEach((key) => {
|
||||||
if (item.response.includes(key)) {
|
if (item.response.includes(key)) {
|
||||||
const value = DATA_MAP[key].parse(item.response);
|
const value = DATA_MAP[key].parse(item.response);
|
||||||
|
console.log("Parsed value:", value);
|
||||||
updateDeviceInfo(key, value);
|
updateDeviceInfo(key, value);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -274,7 +274,7 @@ const eventHandlers = {
|
|||||||
|
|
||||||
if (selectedMode !== currentMode) {
|
if (selectedMode !== currentMode) {
|
||||||
const commands = {
|
const commands = {
|
||||||
"Disabled": 'AT+QMAP="MPDN_rule",0;+QPOWD=1',
|
"Disabled": 'AT+QMAP="MPDN_rule",0;+QMAPWAC=1;+QPOWD=1',
|
||||||
"ETH Only": `AT+QMAP="MPDN_rule",0,1,0,1,1,"${selectedDeviceMAC}";+QPOWD=1`,
|
"ETH Only": `AT+QMAP="MPDN_rule",0,1,0,1,1,"${selectedDeviceMAC}";+QPOWD=1`,
|
||||||
"USB Only": `AT+QMAP="MPDN_rule",0,1,0,3,1,"${selectedDeviceMAC}";+QPOWD=1`
|
"USB Only": `AT+QMAP="MPDN_rule",0,1,0,3,1,"${selectedDeviceMAC}";+QPOWD=1`
|
||||||
};
|
};
|
||||||
@@ -299,10 +299,10 @@ const eventHandlers = {
|
|||||||
|
|
||||||
if (selectedProtocol !== currentProtocol) {
|
if (selectedProtocol !== currentProtocol) {
|
||||||
const commands = {
|
const commands = {
|
||||||
"RMNET": 'AT+QCFG="usbnet",0;+CFUN=1,1',
|
"RMNET": 'AT+QCFG="usbnet",0;+QPOWD=1',
|
||||||
"ECM (Recommended)": 'AT+QCFG="usbnet",1;+CFUN=1,1',
|
"ECM (Recommended)": 'AT+QCFG="usbnet",1;+QPOWD=1',
|
||||||
"MBIM": 'AT+QCFG="usbnet",2;+CFUN=1,1',
|
"MBIM": 'AT+QCFG="usbnet",2;+QPOWD=1',
|
||||||
"RNDIS": 'AT+QCFG="usbnet",3;+CFUN=1,1'
|
"RNDIS": 'AT+QCFG="usbnet",3;+QPOWD=1'
|
||||||
};
|
};
|
||||||
|
|
||||||
const command = commands[selectedProtocol];
|
const command = commands[selectedProtocol];
|
||||||
|
|||||||
@@ -0,0 +1,363 @@
|
|||||||
|
document.addEventListener("DOMContentLoaded", function () {
|
||||||
|
// State management
|
||||||
|
const state = {
|
||||||
|
isLTECellLockEnabled: false,
|
||||||
|
is5GCellLockEnabled: false
|
||||||
|
};
|
||||||
|
|
||||||
|
// Constants
|
||||||
|
const CONSTANTS = {
|
||||||
|
NOTIFICATION_TIMEOUT: 4000,
|
||||||
|
SCS_DEFAULT: 'Select SCS',
|
||||||
|
ENDPOINTS: {
|
||||||
|
CELL_LOCK: '/cgi-bin/cell-locking/cell-lock.sh',
|
||||||
|
FETCH_CONFIG: '/cgi-bin/cell-locking/fetch-cell-lock.sh'
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// DOM Elements
|
||||||
|
const elements = {
|
||||||
|
lteFields: ['earfcn1', 'pci1', 'earfcn2', 'pci2', 'earfcn3', 'pci3'],
|
||||||
|
saFields: ['nr-arfcn', 'nr-pci', 'nr-band'],
|
||||||
|
buttons: {
|
||||||
|
saveLTE: document.getElementById('saveLTE'),
|
||||||
|
saveSA: document.getElementById('saveSA'),
|
||||||
|
resetLTE: document.getElementById('resetLTE'),
|
||||||
|
resetSA: document.getElementById('resetSA'),
|
||||||
|
refresh: document.getElementById('refreshConfig')
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// UI Utilities
|
||||||
|
const UI = {
|
||||||
|
showNotification: (message, isError = false) => {
|
||||||
|
const existingNotification = document.querySelector('.notification');
|
||||||
|
if (existingNotification) {
|
||||||
|
existingNotification.remove();
|
||||||
|
}
|
||||||
|
|
||||||
|
const notification = document.createElement('div');
|
||||||
|
notification.className = `notification ${isError ? 'is-danger' : 'is-success'} is-light`;
|
||||||
|
notification.innerHTML = `
|
||||||
|
<button class="delete"></button>
|
||||||
|
${message}
|
||||||
|
`;
|
||||||
|
|
||||||
|
document.querySelector('.column-margin').insertAdjacentElement('beforebegin', notification);
|
||||||
|
|
||||||
|
const deleteButton = notification.querySelector('.delete');
|
||||||
|
deleteButton.addEventListener('click', () => notification.remove());
|
||||||
|
|
||||||
|
setTimeout(() => notification.remove(), CONSTANTS.NOTIFICATION_TIMEOUT);
|
||||||
|
},
|
||||||
|
|
||||||
|
setButtonLoading: (buttonId, isLoading, text = '') => {
|
||||||
|
const button = document.getElementById(buttonId);
|
||||||
|
if (!button) return;
|
||||||
|
|
||||||
|
button.disabled = isLoading;
|
||||||
|
button.innerHTML = isLoading ? `
|
||||||
|
<span class="icon is-small">
|
||||||
|
<i class="fas fa-spinner fa-pulse"></i>
|
||||||
|
</span>
|
||||||
|
<span class="ml-2">Processing...</span>
|
||||||
|
` : text;
|
||||||
|
},
|
||||||
|
|
||||||
|
toggleInputs: (disabled) => {
|
||||||
|
document.querySelectorAll('input, select').forEach(input => {
|
||||||
|
input.disabled = disabled;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
clearInputs: (fields) => {
|
||||||
|
fields.forEach(fieldId => {
|
||||||
|
const element = document.getElementById(fieldId);
|
||||||
|
if (element) {
|
||||||
|
if (element.tagName === 'SELECT') {
|
||||||
|
element.selectedIndex = 0;
|
||||||
|
} else {
|
||||||
|
element.value = '';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Validation Utilities
|
||||||
|
const Validator = {
|
||||||
|
validateNumeric: (value, fieldName) => {
|
||||||
|
if (value && !/^\d+$/.test(value)) {
|
||||||
|
UI.showNotification(`${fieldName} must be a numeric value`, true);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
},
|
||||||
|
|
||||||
|
validateLTEInputs: () => {
|
||||||
|
const earfcn1 = document.getElementById('earfcn1').value;
|
||||||
|
const pci1 = document.getElementById('pci1').value;
|
||||||
|
|
||||||
|
if (!earfcn1 && !pci1) return true;
|
||||||
|
|
||||||
|
if (!Validator.validateNumeric(earfcn1, 'EARFCN 1')) return false;
|
||||||
|
if (!Validator.validateNumeric(pci1, 'PCI 1')) return false;
|
||||||
|
|
||||||
|
if ((earfcn1 && !pci1) || (!earfcn1 && pci1)) {
|
||||||
|
UI.showNotification('Both EARFCN and PCI must be provided for each pair', true);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
},
|
||||||
|
|
||||||
|
validate5GInputs: () => {
|
||||||
|
const nrArfcn = document.getElementById('nr-arfcn').value;
|
||||||
|
const nrPci = document.getElementById('nr-pci').value;
|
||||||
|
const scs = document.getElementById('scs').value;
|
||||||
|
const nrBand = document.getElementById('nr-band').value;
|
||||||
|
|
||||||
|
if (!nrArfcn && !nrPci && scs === CONSTANTS.SCS_DEFAULT && !nrBand) return true;
|
||||||
|
|
||||||
|
if (!Validator.validateNumeric(nrArfcn, 'NR ARFCN')) return false;
|
||||||
|
if (!Validator.validateNumeric(nrPci, 'NR PCI')) return false;
|
||||||
|
if (!Validator.validateNumeric(nrBand, 'NR Band')) return false;
|
||||||
|
|
||||||
|
if (scs === CONSTANTS.SCS_DEFAULT) {
|
||||||
|
UI.showNotification('Please select an SCS value', true);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Data Utilities
|
||||||
|
const DataUtils = {
|
||||||
|
hasValues: (fields) => {
|
||||||
|
return fields.some(field => {
|
||||||
|
const element = document.getElementById(field);
|
||||||
|
if (element.tagName === 'SELECT') {
|
||||||
|
return element.value !== CONSTANTS.SCS_DEFAULT;
|
||||||
|
}
|
||||||
|
return element.value.trim() !== '';
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
getFormData: (fields) => {
|
||||||
|
return fields.reduce((acc, field) => {
|
||||||
|
acc[field] = document.getElementById(field).value;
|
||||||
|
return acc;
|
||||||
|
}, {});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// API Handlers
|
||||||
|
const API = {
|
||||||
|
async makeRequest(endpoint, method = 'GET', body = null) {
|
||||||
|
try {
|
||||||
|
const response = await fetch(endpoint, {
|
||||||
|
method,
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/x-www-form-urlencoded'
|
||||||
|
},
|
||||||
|
...(body && { body: new URLSearchParams(body).toString() })
|
||||||
|
});
|
||||||
|
return await response.json();
|
||||||
|
} catch (error) {
|
||||||
|
console.error('API Error:', error);
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
async saveLTEConfiguration(formData) {
|
||||||
|
return API.makeRequest(CONSTANTS.ENDPOINTS.CELL_LOCK, 'POST', formData);
|
||||||
|
},
|
||||||
|
|
||||||
|
async save5GConfiguration(formData) {
|
||||||
|
return API.makeRequest(CONSTANTS.ENDPOINTS.CELL_LOCK, 'POST', formData);
|
||||||
|
},
|
||||||
|
|
||||||
|
async resetConfiguration(type) {
|
||||||
|
return API.makeRequest(CONSTANTS.ENDPOINTS.CELL_LOCK, 'POST', {
|
||||||
|
[`reset_${type}`]: '1'
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
async fetchConfigurations() {
|
||||||
|
return API.makeRequest(CONSTANTS.ENDPOINTS.FETCH_CONFIG);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Event Handlers
|
||||||
|
const EventHandlers = {
|
||||||
|
async handleLTESave(e) {
|
||||||
|
e.preventDefault();
|
||||||
|
if (!Validator.validateLTEInputs()) return;
|
||||||
|
|
||||||
|
if (state.is5GCellLockEnabled || DataUtils.hasValues(elements.saFields)) {
|
||||||
|
UI.showNotification('LTE cell lock cannot be configured when 5G-SA cell lock is enabled', true);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
UI.toggleInputs(true);
|
||||||
|
UI.setButtonLoading('saveLTE', true);
|
||||||
|
|
||||||
|
const formData = DataUtils.getFormData(elements.lteFields);
|
||||||
|
const response = await API.saveLTEConfiguration(formData);
|
||||||
|
|
||||||
|
if (response.status === 'success') {
|
||||||
|
state.isLTECellLockEnabled = true;
|
||||||
|
state.is5GCellLockEnabled = false;
|
||||||
|
UI.showNotification('LTE cell lock configured successfully');
|
||||||
|
} else {
|
||||||
|
UI.showNotification(response.message || 'Error configuring LTE cell lock', true);
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
UI.showNotification(`Error configuring LTE cell lock: ${error.message}`, true);
|
||||||
|
} finally {
|
||||||
|
UI.toggleInputs(false);
|
||||||
|
UI.setButtonLoading('saveLTE', false, 'Lock LTE Cells');
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
async handle5GSave(e) {
|
||||||
|
e.preventDefault();
|
||||||
|
if (!Validator.validate5GInputs()) return;
|
||||||
|
|
||||||
|
if (state.isLTECellLockEnabled || DataUtils.hasValues(elements.lteFields)) {
|
||||||
|
UI.showNotification('5G-SA cell lock cannot be configured when LTE cell lock is enabled', true);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
UI.toggleInputs(true);
|
||||||
|
UI.setButtonLoading('saveSA', true);
|
||||||
|
|
||||||
|
const scsValue = document.getElementById('scs').value;
|
||||||
|
const formData = {
|
||||||
|
nrarfcn: document.getElementById('nr-arfcn').value,
|
||||||
|
nrpci: document.getElementById('nr-pci').value,
|
||||||
|
scs: scsValue === CONSTANTS.SCS_DEFAULT ? '' : scsValue.split(' ')[0],
|
||||||
|
band: document.getElementById('nr-band').value
|
||||||
|
};
|
||||||
|
|
||||||
|
const response = await API.save5GConfiguration(formData);
|
||||||
|
|
||||||
|
if (response.status === 'success') {
|
||||||
|
state.is5GCellLockEnabled = true;
|
||||||
|
state.isLTECellLockEnabled = false;
|
||||||
|
UI.showNotification('5G-SA cell lock configured successfully');
|
||||||
|
} else {
|
||||||
|
UI.showNotification(response.message || 'Error configuring 5G-SA cell lock', true);
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
UI.showNotification(`Error configuring 5G-SA cell lock: ${error.message}`, true);
|
||||||
|
} finally {
|
||||||
|
UI.toggleInputs(false);
|
||||||
|
UI.setButtonLoading('saveSA', false, 'Lock 5G-SA Cells');
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
async handleLTEReset(e) {
|
||||||
|
e.preventDefault();
|
||||||
|
try {
|
||||||
|
UI.setButtonLoading('resetLTE', true);
|
||||||
|
const response = await API.resetConfiguration('lte');
|
||||||
|
|
||||||
|
if (response.status === 'success') {
|
||||||
|
UI.clearInputs(elements.lteFields);
|
||||||
|
state.isLTECellLockEnabled = false;
|
||||||
|
UI.showNotification('LTE cell lock reset successfully');
|
||||||
|
} else {
|
||||||
|
UI.showNotification(response.message || 'Error resetting LTE cell lock', true);
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
UI.showNotification(`Error resetting LTE cell lock: ${error.message}`, true);
|
||||||
|
} finally {
|
||||||
|
UI.setButtonLoading('resetLTE', false, 'Reset LTE Cells');
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
async handle5GReset(e) {
|
||||||
|
e.preventDefault();
|
||||||
|
try {
|
||||||
|
UI.setButtonLoading('resetSA', true);
|
||||||
|
const response = await API.resetConfiguration('5g');
|
||||||
|
|
||||||
|
if (response.status === 'success') {
|
||||||
|
UI.clearInputs([...elements.saFields, 'scs']);
|
||||||
|
state.is5GCellLockEnabled = false;
|
||||||
|
UI.showNotification('5G-SA cell lock reset successfully');
|
||||||
|
} else {
|
||||||
|
UI.showNotification(response.message || 'Error resetting 5G-SA cell lock', true);
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
UI.showNotification(`Error resetting 5G-SA cell lock: ${error.message}`, true);
|
||||||
|
} finally {
|
||||||
|
UI.setButtonLoading('resetSA', false, 'Reset 5G-SA Cells');
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
async handleRefresh(e) {
|
||||||
|
e?.preventDefault();
|
||||||
|
try {
|
||||||
|
const data = await API.fetchConfigurations();
|
||||||
|
|
||||||
|
if (data.status === 'success' && data.configurations) {
|
||||||
|
const { lte, sa } = data.configurations;
|
||||||
|
|
||||||
|
if (lte) {
|
||||||
|
state.isLTECellLockEnabled = true;
|
||||||
|
state.is5GCellLockEnabled = false;
|
||||||
|
elements.lteFields.forEach(field => {
|
||||||
|
if (lte[field]) document.getElementById(field).value = lte[field];
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (sa) {
|
||||||
|
state.is5GCellLockEnabled = true;
|
||||||
|
state.isLTECellLockEnabled = false;
|
||||||
|
elements.saFields.forEach(field => {
|
||||||
|
if (sa[field.replace('-', '')]) {
|
||||||
|
document.getElementById(field).value = sa[field.replace('-', '')];
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (sa.scs) {
|
||||||
|
const scsSelect = document.getElementById('scs');
|
||||||
|
Array.from(scsSelect.options).some((option, index) => {
|
||||||
|
if (option.value === sa.scs) {
|
||||||
|
scsSelect.selectedIndex = index;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error fetching configurations:', error);
|
||||||
|
UI.showNotification(`Error fetching configurations: ${error.message}`, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Initialize event listeners
|
||||||
|
function initializeEventListeners() {
|
||||||
|
elements.buttons.saveLTE?.addEventListener('click', EventHandlers.handleLTESave);
|
||||||
|
elements.buttons.saveSA?.addEventListener('click', EventHandlers.handle5GSave);
|
||||||
|
elements.buttons.resetLTE?.addEventListener('click', EventHandlers.handleLTEReset);
|
||||||
|
elements.buttons.resetSA?.addEventListener('click', EventHandlers.handle5GReset);
|
||||||
|
elements.buttons.refresh?.addEventListener('click', EventHandlers.handleRefresh);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Initialize the application
|
||||||
|
function initialize() {
|
||||||
|
initializeEventListeners();
|
||||||
|
EventHandlers.handleRefresh();
|
||||||
|
}
|
||||||
|
|
||||||
|
initialize();
|
||||||
|
});
|
||||||
@@ -0,0 +1,246 @@
|
|||||||
|
class NeighbourCellScanner {
|
||||||
|
constructor() {
|
||||||
|
this.tableBody = document.getElementById("neighbourCellTableBody");
|
||||||
|
this.tableHeaders = document.querySelector("#neighbourCellTable thead tr");
|
||||||
|
this.lteScanBtn = document.getElementById("startLTEScanBtn");
|
||||||
|
this.nr5gScanBtn = document.getElementById("startNR5GScanBtn");
|
||||||
|
this.resetBtn = document.getElementById("resetScanBtn");
|
||||||
|
|
||||||
|
this.bindEvents();
|
||||||
|
}
|
||||||
|
|
||||||
|
bindEvents() {
|
||||||
|
this.lteScanBtn.addEventListener("click", () => this.startLTEScan());
|
||||||
|
this.nr5gScanBtn.addEventListener("click", () => this.startNR5GScan());
|
||||||
|
this.resetBtn.addEventListener("click", () => this.resetTable());
|
||||||
|
}
|
||||||
|
|
||||||
|
updateTableHeaders(mode) {
|
||||||
|
if (mode === "LTE") {
|
||||||
|
this.tableHeaders.innerHTML = `
|
||||||
|
<th>Type</th>
|
||||||
|
<th>EARFCN</th>
|
||||||
|
<th>Physical ID</th>
|
||||||
|
<th class="is-hidden-mobile">RSRP</th>
|
||||||
|
<th class="is-hidden-mobile">RSRQ</th>
|
||||||
|
<th class="is-hidden-mobile">RSSI</th>
|
||||||
|
`;
|
||||||
|
} else if (mode === "NR5G") {
|
||||||
|
this.tableHeaders.innerHTML = `
|
||||||
|
<th>Type</th>
|
||||||
|
<th>ARFCN</th>
|
||||||
|
<th>Physical ID</th>
|
||||||
|
<th class="is-hidden-mobile">RSRP</th>
|
||||||
|
<th class="is-hidden-mobile">RSSI</th>
|
||||||
|
<th class="is-hidden-mobile">--</th>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async sendCommand(command) {
|
||||||
|
try {
|
||||||
|
const response = await fetch("/cgi-bin/atinout_handler.sh", {
|
||||||
|
method: "POST",
|
||||||
|
headers: {
|
||||||
|
"Content-Type": "application/x-www-form-urlencoded",
|
||||||
|
},
|
||||||
|
body: `command=${encodeURIComponent(command)}`,
|
||||||
|
});
|
||||||
|
// remove the initial table row
|
||||||
|
this.tableBody.innerHTML = "";
|
||||||
|
return await response.json();
|
||||||
|
} catch (error) {
|
||||||
|
console.error("API Error:", error);
|
||||||
|
// add the initial table row again
|
||||||
|
this.addPlaceholderRow();
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
getSignalQuality(value) {
|
||||||
|
if (value > -90) return "is-success";
|
||||||
|
if (value > -100) return "is-warning";
|
||||||
|
return "is-danger";
|
||||||
|
}
|
||||||
|
|
||||||
|
getSignalText(value) {
|
||||||
|
if (value > -90) return "Good";
|
||||||
|
if (value > -100) return "Fair";
|
||||||
|
return "Poor";
|
||||||
|
}
|
||||||
|
|
||||||
|
createSignalTag(value) {
|
||||||
|
const quality = this.getSignalQuality(value);
|
||||||
|
const text = this.getSignalText(value);
|
||||||
|
return `
|
||||||
|
<div class="tags has-addons">
|
||||||
|
<span class="tag is-size-6">${value}</span>
|
||||||
|
<span class="tag ${quality} is-size-6 has-text-white">${text}</span>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
|
parseLTEResponse(response) {
|
||||||
|
const output = response.output;
|
||||||
|
const lines = output.split("\n");
|
||||||
|
const results = [];
|
||||||
|
|
||||||
|
for (const line of lines) {
|
||||||
|
if (line.startsWith("+QENG:")) {
|
||||||
|
const match = line.match(
|
||||||
|
/"([^"]+)","LTE",(\d+),(\d+),(-?\d+),(-?\d+),(-?\d+)/
|
||||||
|
);
|
||||||
|
if (match) {
|
||||||
|
// Extract just 'intra' or 'inter' from the type
|
||||||
|
const fullType = match[1];
|
||||||
|
const type = fullType.includes("intra") ? "intra" : "inter";
|
||||||
|
|
||||||
|
results.push({
|
||||||
|
type: type,
|
||||||
|
earfcn: match[2],
|
||||||
|
pci: match[3],
|
||||||
|
rsrq: parseInt(match[4]),
|
||||||
|
rsrp: parseInt(match[5]),
|
||||||
|
rssi: parseInt(match[6]),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return results;
|
||||||
|
}
|
||||||
|
|
||||||
|
parseNR5GResponse(response) {
|
||||||
|
const output = response.output;
|
||||||
|
const lines = output.split("\n");
|
||||||
|
const results = [];
|
||||||
|
|
||||||
|
for (const line of lines) {
|
||||||
|
if (line.startsWith("+QNWCFG:")) {
|
||||||
|
const match = line.match(/\d+,(\d+),(\d+),(-?\d+),(-?\d+)/);
|
||||||
|
if (match) {
|
||||||
|
results.push({
|
||||||
|
arfcn: match[1],
|
||||||
|
pci: match[2],
|
||||||
|
rsrp: parseInt(match[3]),
|
||||||
|
rssi: parseInt(match[4]),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return results;
|
||||||
|
}
|
||||||
|
|
||||||
|
addPlaceholderRow() {
|
||||||
|
const row = document.createElement("tr");
|
||||||
|
row.innerHTML = `
|
||||||
|
<td>--</td>
|
||||||
|
<td>--</td>
|
||||||
|
<td>--</td>
|
||||||
|
<td class="is-hidden-mobile">
|
||||||
|
<div class="tags has-addons">
|
||||||
|
<span class="tag is-size-6">--</span>
|
||||||
|
<span class="tag is-light is-size-6">No Data</span>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
<td class="is-hidden-mobile">
|
||||||
|
<div class="tags has-addons">
|
||||||
|
<span class="tag is-size-6">--</span>
|
||||||
|
<span class="tag is-light is-size-6">No Data</span>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
<td class="is-hidden-mobile">
|
||||||
|
<div class="tags has-addons">
|
||||||
|
<span class="tag is-size-6">--</span>
|
||||||
|
<span class="tag is-light is-size-6">No Data</span>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
`;
|
||||||
|
this.tableBody.appendChild(row);
|
||||||
|
}
|
||||||
|
|
||||||
|
async startLTEScan() {
|
||||||
|
try {
|
||||||
|
const response = await this.sendCommand('AT+QENG="neighbourcell"');
|
||||||
|
const results = this.parseLTEResponse(response);
|
||||||
|
|
||||||
|
// Clear the table and update headers first
|
||||||
|
this.tableBody.innerHTML = "";
|
||||||
|
this.updateTableHeaders("LTE");
|
||||||
|
|
||||||
|
if (results.length === 0) {
|
||||||
|
this.addPlaceholderRow();
|
||||||
|
} else {
|
||||||
|
results.forEach((result) => {
|
||||||
|
const row = document.createElement("tr");
|
||||||
|
row.innerHTML = `
|
||||||
|
<td>${result.type}</td>
|
||||||
|
<td>${result.earfcn}</td>
|
||||||
|
<td>${result.pci}</td>
|
||||||
|
<td class="is-hidden-mobile">${this.createSignalTag(
|
||||||
|
result.rsrp
|
||||||
|
)}</td>
|
||||||
|
<td class="is-hidden-mobile">${this.createSignalTag(
|
||||||
|
result.rsrq
|
||||||
|
)}</td>
|
||||||
|
<td class="is-hidden-mobile">${this.createSignalTag(
|
||||||
|
result.rssi
|
||||||
|
)}</td>
|
||||||
|
`;
|
||||||
|
this.tableBody.appendChild(row);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error("LTE Scan failed:", error);
|
||||||
|
this.resetTable();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async startNR5GScan() {
|
||||||
|
try {
|
||||||
|
const response = await this.sendCommand(
|
||||||
|
'AT+QNWCFG="nr5g_meas_info",1;+QNWCFG="nr5g_meas_info"'
|
||||||
|
);
|
||||||
|
const results = this.parseNR5GResponse(response);
|
||||||
|
|
||||||
|
// Clear the table and update headers first
|
||||||
|
this.tableBody.innerHTML = "";
|
||||||
|
this.updateTableHeaders("NR5G");
|
||||||
|
|
||||||
|
if (results.length === 0) {
|
||||||
|
this.addPlaceholderRow();
|
||||||
|
} else {
|
||||||
|
results.forEach((result) => {
|
||||||
|
const row = document.createElement("tr");
|
||||||
|
row.innerHTML = `
|
||||||
|
<td>NR5G</td>
|
||||||
|
<td>${result.arfcn}</td>
|
||||||
|
<td>${result.pci}</td>
|
||||||
|
<td class="is-hidden-mobile">${this.createSignalTag(
|
||||||
|
result.rsrp
|
||||||
|
)}</td>
|
||||||
|
<td class="is-hidden-mobile">${this.createSignalTag(
|
||||||
|
result.rssi
|
||||||
|
)}</td>
|
||||||
|
<td class="is-hidden-mobile">--</td>
|
||||||
|
`;
|
||||||
|
this.tableBody.appendChild(row);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error("NR5G Scan failed:", error);
|
||||||
|
this.resetTable();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
resetTable() {
|
||||||
|
this.tableBody.innerHTML = "";
|
||||||
|
this.updateTableHeaders("LTE"); // Reset to default LTE headers
|
||||||
|
this.addPlaceholderRow(); // Add placeholder row after reset
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Initialize the scanner when the document is ready
|
||||||
|
document.addEventListener("DOMContentLoaded", () => {
|
||||||
|
const scanner = new NeighbourCellScanner();
|
||||||
|
scanner.resetTable(); // Show initial placeholder row
|
||||||
|
});
|
||||||
1598
ipk-source/sdxpinn-quecmanager/root/www/js/cell-scanner/mcc-mnc.txt
Normal file
1598
ipk-source/sdxpinn-quecmanager/root/www/js/cell-scanner/mcc-mnc.txt
Normal file
File diff suppressed because it is too large
Load Diff
@@ -19,7 +19,7 @@ document.addEventListener('DOMContentLoaded', function() {
|
|||||||
form.insertAdjacentElement('beforebegin', notification);
|
form.insertAdjacentElement('beforebegin', notification);
|
||||||
|
|
||||||
// Remove notification after 5 seconds
|
// Remove notification after 5 seconds
|
||||||
setTimeout(() => notification.remove(), 5000);
|
setTimeout(() => notification.remove(), 4000);
|
||||||
|
|
||||||
// Allow manual close
|
// Allow manual close
|
||||||
notification.querySelector('.delete').addEventListener('click', () => notification.remove());
|
notification.querySelector('.delete').addEventListener('click', () => notification.remove());
|
||||||
|
|||||||
@@ -15,6 +15,9 @@ let currentNetworkMode = "";
|
|||||||
let currentNr5GModeControl = "";
|
let currentNr5GModeControl = "";
|
||||||
let updatedNr5GModeControl = "";
|
let updatedNr5GModeControl = "";
|
||||||
|
|
||||||
|
let updatedSlot = "";
|
||||||
|
let currentSlot = "";
|
||||||
|
|
||||||
// Function to check if settings have changed
|
// Function to check if settings have changed
|
||||||
function haveSettingsChanged() {
|
function haveSettingsChanged() {
|
||||||
return (
|
return (
|
||||||
@@ -37,6 +40,13 @@ function haveNr5GModeControlChanged() {
|
|||||||
return currentNr5GModeControl !== updatedNr5GModeControl;
|
return currentNr5GModeControl !== updatedNr5GModeControl;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Function to check if SIM slot has changed
|
||||||
|
function haveSimSlotChanged() {
|
||||||
|
console.log("Current SIM slot:", currentSlot);
|
||||||
|
console.log("Updated SIM slot:", updatedSlot);
|
||||||
|
return currentSlot !== updatedSlot;
|
||||||
|
}
|
||||||
|
|
||||||
// Function to apply network mode changes immediately
|
// Function to apply network mode changes immediately
|
||||||
async function applyNetworkModeChange() {
|
async function applyNetworkModeChange() {
|
||||||
if (!haveNetworkModeChanged()) {
|
if (!haveNetworkModeChanged()) {
|
||||||
@@ -75,6 +85,40 @@ async function applyNr5GModeControlChange() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Function to apply SIM slot changes immediately
|
||||||
|
async function applySimSlotChange() {
|
||||||
|
if (!haveSimSlotChanged()) {
|
||||||
|
alert("No changes detected in the SIM slot.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const atCommand = `AT+QUIMSLOT=${updatedSlot}`;
|
||||||
|
console.log("Sending AT command for SIM slot change:", atCommand);
|
||||||
|
const response = await sendATCommand(atCommand);
|
||||||
|
console.log("AT command response:", response);
|
||||||
|
|
||||||
|
// Disable the select input while the SIM slot is being applied
|
||||||
|
const simSlotSelect = document.getElementById("simSlot");
|
||||||
|
simSlotSelect.disabled = true;
|
||||||
|
|
||||||
|
// Send network deregistration command to apply SIM slot changes after 1 second
|
||||||
|
await new Promise((resolve) => setTimeout(resolve, 1000));
|
||||||
|
await sendATCommand("AT+COPS=2");
|
||||||
|
// Wait for 2 seconds before turning on the modem
|
||||||
|
await new Promise((resolve) => setTimeout(resolve, 2000));
|
||||||
|
await sendATCommand("AT+COPS=0");
|
||||||
|
|
||||||
|
// re-enable the select input after the SIM slot is applied
|
||||||
|
simSlotSelect.disabled = false;
|
||||||
|
|
||||||
|
alert("SIM slot applied successfully!");
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Error applying SIM slot:", error);
|
||||||
|
alert("Error applying SIM slot. Please try again.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Function to send settings to the modem
|
// Function to send settings to the modem
|
||||||
async function saveSettings() {
|
async function saveSettings() {
|
||||||
if (!haveSettingsChanged()) {
|
if (!haveSettingsChanged()) {
|
||||||
@@ -281,6 +325,35 @@ async function fetchCellSettings() {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
} else if (item.response.includes("QUIMSLOT")) {
|
||||||
|
const slot = item.response
|
||||||
|
.split("\n")[1]
|
||||||
|
.split(":")[1]
|
||||||
|
.split(",")[0]
|
||||||
|
.trim();
|
||||||
|
|
||||||
|
console.log("Slot:", slot);
|
||||||
|
|
||||||
|
currentSlot = slot;
|
||||||
|
updatedSlot = slot;
|
||||||
|
|
||||||
|
const slotInput = document.getElementById("simSlot");
|
||||||
|
if (slotInput) {
|
||||||
|
// Explicitly set the value and update the selected option
|
||||||
|
slotInput.value = slot;
|
||||||
|
|
||||||
|
// Add event listener for slot changes if not already added
|
||||||
|
if (!slotInput.hasListener) {
|
||||||
|
slotInput.hasListener = true;
|
||||||
|
slotInput.addEventListener("change", (e) => {
|
||||||
|
updatedSlot = e.target.value;
|
||||||
|
if (updatedSlot) {
|
||||||
|
// Only apply if a valid slot is selected
|
||||||
|
applySimSlotChange();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
|||||||
@@ -0,0 +1,251 @@
|
|||||||
|
class SMSManager {
|
||||||
|
constructor() {
|
||||||
|
this.initializeElements();
|
||||||
|
this.bindEvents();
|
||||||
|
this.init();
|
||||||
|
}
|
||||||
|
|
||||||
|
initializeElements() {
|
||||||
|
this.smsContainer = document.getElementById("sms-container");
|
||||||
|
this.refreshButton = document.getElementById("refresh-sms");
|
||||||
|
this.deleteSelectedButton = document.getElementById("delete-selected-sms");
|
||||||
|
this.phoneNumberInput = document.getElementById("phone-number-input");
|
||||||
|
this.messageTextarea = document.getElementById("message-input");
|
||||||
|
this.sendSMSButton = document.getElementById("send-sms");
|
||||||
|
this.resetButton = document.getElementById("reset-form");
|
||||||
|
|
||||||
|
const elements = {
|
||||||
|
"SMS Container": this.smsContainer,
|
||||||
|
"Refresh Button": this.refreshButton,
|
||||||
|
"Delete Selected Button": this.deleteSelectedButton,
|
||||||
|
"Phone Number Input": this.phoneNumberInput,
|
||||||
|
"Message Textarea": this.messageTextarea,
|
||||||
|
"Send SMS Button": this.sendSMSButton,
|
||||||
|
"Reset Button": this.resetButton,
|
||||||
|
};
|
||||||
|
|
||||||
|
for (const [name, element] of Object.entries(elements)) {
|
||||||
|
if (!element) {
|
||||||
|
console.error(`${name} element not found!`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bindEvents() {
|
||||||
|
if (this.refreshButton) {
|
||||||
|
this.refreshButton.addEventListener("click", (e) => {
|
||||||
|
e.preventDefault();
|
||||||
|
this.refreshSMS();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.deleteSelectedButton) {
|
||||||
|
this.deleteSelectedButton.addEventListener("click", (e) => {
|
||||||
|
e.preventDefault();
|
||||||
|
this.deleteSelectedSMS();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.sendSMSButton) {
|
||||||
|
this.sendSMSButton.addEventListener("click", (e) => {
|
||||||
|
e.preventDefault();
|
||||||
|
this.sendSMS();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.resetButton) {
|
||||||
|
this.resetButton.addEventListener("click", (e) => {
|
||||||
|
e.preventDefault();
|
||||||
|
this.resetForm();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async sendCommand(command) {
|
||||||
|
try {
|
||||||
|
const response = await fetch("/cgi-bin/atinout_handler.sh", {
|
||||||
|
method: "POST",
|
||||||
|
headers: {
|
||||||
|
"Content-Type": "application/x-www-form-urlencoded",
|
||||||
|
},
|
||||||
|
body: `command=${encodeURIComponent(command)}`,
|
||||||
|
});
|
||||||
|
return await response.json();
|
||||||
|
} catch (error) {
|
||||||
|
console.error("AT command failed:", error);
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async init() {
|
||||||
|
try {
|
||||||
|
await this.sendCommand("AT+CMGF=1");
|
||||||
|
await this.refreshSMS();
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Initialization failed:", error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
showLoadingState() {
|
||||||
|
this.smsContainer.innerHTML = `
|
||||||
|
<div class="loading-container">
|
||||||
|
<span class="icon is-large">
|
||||||
|
<i class="fas fa-spinner fa-pulse fa-2x"></i>
|
||||||
|
</span>
|
||||||
|
<p class="mt-2">Fetching SMS...</p>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
|
async refreshSMS() {
|
||||||
|
this.showLoadingState();
|
||||||
|
try {
|
||||||
|
const response = await this.sendCommand('AT+CMGL="ALL"');
|
||||||
|
|
||||||
|
let rawData;
|
||||||
|
if (typeof response === "string") {
|
||||||
|
rawData = response;
|
||||||
|
} else if (response && response.result) {
|
||||||
|
rawData = response.result;
|
||||||
|
} else if (response && response.output) {
|
||||||
|
rawData = response.output;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!rawData) {
|
||||||
|
console.error("No valid data received from AT command");
|
||||||
|
this.displayMessages([]);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const messages = this.parseSMSData(rawData);
|
||||||
|
this.displayMessages(messages);
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Failed to refresh SMS:", error);
|
||||||
|
this.displayMessages([]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
parseSMSData(data) {
|
||||||
|
const messages = [];
|
||||||
|
const lines = data.split("\n");
|
||||||
|
let currentMessage = null;
|
||||||
|
|
||||||
|
for (let i = 0; i < lines.length; i++) {
|
||||||
|
const line = lines[i].trim();
|
||||||
|
if (!line || line === "OK" || line === 'AT+CMGL="ALL"') continue;
|
||||||
|
|
||||||
|
if (line.startsWith("+CMGL:")) {
|
||||||
|
if (currentMessage && currentMessage.message) {
|
||||||
|
messages.push(currentMessage);
|
||||||
|
}
|
||||||
|
|
||||||
|
const headerMatch = line.match(
|
||||||
|
/\+CMGL:\s*(\d+),"([^"]*?)","([^"]*?)",,"([^"]*?)"/
|
||||||
|
);
|
||||||
|
if (headerMatch) {
|
||||||
|
currentMessage = {
|
||||||
|
index: headerMatch[1],
|
||||||
|
status: headerMatch[2],
|
||||||
|
sender: headerMatch[3],
|
||||||
|
date: headerMatch[4].replace("+32", ""),
|
||||||
|
message: "",
|
||||||
|
};
|
||||||
|
}
|
||||||
|
} else if (currentMessage) {
|
||||||
|
currentMessage.message += (currentMessage.message ? "\n" : "") + line;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (currentMessage && currentMessage.message) {
|
||||||
|
messages.push(currentMessage);
|
||||||
|
}
|
||||||
|
|
||||||
|
return messages;
|
||||||
|
}
|
||||||
|
|
||||||
|
createMessageElement(message, index) {
|
||||||
|
const formattedDate = message.date.replace(
|
||||||
|
/(\d{2})\/(\d{2})\/(\d{2}),(\d{2}:\d{2}:\d{2})/,
|
||||||
|
"20$3-$2-$1 $4"
|
||||||
|
);
|
||||||
|
|
||||||
|
return `
|
||||||
|
<div class="cell" id="sms-message-${index}">
|
||||||
|
<div class="is-flex is-align-items-center">
|
||||||
|
<div class="checkbox mr-6">
|
||||||
|
<input type="checkbox"
|
||||||
|
id="sms-checkbox-${index}"
|
||||||
|
data-index="${message.index}" />
|
||||||
|
</div>
|
||||||
|
<div class="is-flex is-flex-direction-column is-align-items-start">
|
||||||
|
<p class="has-text-weight-semibold" id="sms-sender-${index}">${message.sender}</p>
|
||||||
|
<p id="sms-date-${index}">${formattedDate}</p>
|
||||||
|
<p id="sms-content-${index}">${message.message}</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
|
displayMessages(messages) {
|
||||||
|
if (!this.smsContainer) {
|
||||||
|
console.error("SMS container not found!");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.smsContainer.innerHTML =
|
||||||
|
messages.length === 0
|
||||||
|
? '<div class="cell" id="no-messages">No messages found</div>'
|
||||||
|
: messages
|
||||||
|
.map((msg, index) => this.createMessageElement(msg, index))
|
||||||
|
.join("");
|
||||||
|
}
|
||||||
|
|
||||||
|
async deleteSelectedSMS() {
|
||||||
|
const selectedCheckboxes = document.querySelectorAll(
|
||||||
|
'input[type="checkbox"]:checked'
|
||||||
|
);
|
||||||
|
const indices = Array.from(selectedCheckboxes).map(
|
||||||
|
(cb) => cb.dataset.index
|
||||||
|
);
|
||||||
|
|
||||||
|
if (indices.length === 0) return;
|
||||||
|
|
||||||
|
try {
|
||||||
|
for (const index of indices) {
|
||||||
|
await this.sendCommand(`AT+CMGD=${index}`);
|
||||||
|
}
|
||||||
|
await this.refreshSMS();
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Failed to delete messages:", error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async sendSMS() {
|
||||||
|
const phoneNumber = this.phoneNumberInput.value.trim();
|
||||||
|
const message = this.messageTextarea.value.trim();
|
||||||
|
|
||||||
|
if (!phoneNumber || !message) {
|
||||||
|
alert("Please enter both phone number and message");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
await this.sendCommand(`AT+CMGS="${phoneNumber}"`);
|
||||||
|
await this.sendCommand(`${message}\x1A`);
|
||||||
|
this.resetForm();
|
||||||
|
await this.refreshSMS();
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Failed to send SMS:", error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
resetForm() {
|
||||||
|
if (this.phoneNumberInput) this.phoneNumberInput.value = "";
|
||||||
|
if (this.messageTextarea) this.messageTextarea.value = "";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
document.addEventListener("DOMContentLoaded", () => {
|
||||||
|
window.smsManager = new SMSManager();
|
||||||
|
});
|
||||||
@@ -54,7 +54,7 @@ let atCommandInterval;
|
|||||||
let connectionStatusInterval;
|
let connectionStatusInterval;
|
||||||
let trafficStatsInterval;
|
let trafficStatsInterval;
|
||||||
const DEFAULT_REFRESH_RATE = 5000; // 5 seconds
|
const DEFAULT_REFRESH_RATE = 5000; // 5 seconds
|
||||||
const TRAFFIC_STATS_REFRESH_RATE = 1000; // 1 second
|
const TRAFFIC_STATS_REFRESH_RATE = 5000; // 5 seconds
|
||||||
const CONNECTION_CHECK_MULTIPLIER = 6; // Will make connection check 6 times slower
|
const CONNECTION_CHECK_MULTIPLIER = 6; // Will make connection check 6 times slower
|
||||||
const STORAGE_KEY = "modemRefreshRate";
|
const STORAGE_KEY = "modemRefreshRate";
|
||||||
|
|
||||||
@@ -958,26 +958,31 @@ function processWANIPData(jsonData) {
|
|||||||
|
|
||||||
async function fetchTrafficStats() {
|
async function fetchTrafficStats() {
|
||||||
try {
|
try {
|
||||||
const response = await fetch("/cgi-bin/home/traffic_stats.sh", {
|
// Send the AT command to the CGI handler
|
||||||
method: "GET",
|
const response = await fetch("/cgi-bin/atinout_handler.sh", {
|
||||||
|
method: "POST",
|
||||||
headers: {
|
headers: {
|
||||||
"Content-Type": "application/json",
|
"Content-Type": "application/x-www-form-urlencoded",
|
||||||
},
|
},
|
||||||
|
body: `command=${encodeURIComponent("AT+QGDNRCNT?")}`,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Get the raw text response
|
||||||
const rawData = await response.text();
|
const rawData = await response.text();
|
||||||
|
|
||||||
if (!rawData || rawData.trim() === "") {
|
if (!rawData || rawData.trim() === "") {
|
||||||
throw new Error("Empty or malformed response");
|
throw new Error("Empty or malformed response");
|
||||||
}
|
}
|
||||||
|
|
||||||
const jsonData = JSON.parse(rawData);
|
// Extract the upload and download values using regex
|
||||||
|
const match = rawData.match(/\+QGDNRCNT: (\d+),(\d+)/);
|
||||||
|
if (!match || match.length < 3) {
|
||||||
|
throw new Error("Unexpected response format");
|
||||||
|
}
|
||||||
|
|
||||||
console.log("Traffic stats fetched successfully");
|
// Parse the upload and download values
|
||||||
|
const upload = parseInt(match[1], 10);
|
||||||
// Parse rx (download) and tx (upload) values
|
const download = parseInt(match[2], 10);
|
||||||
const download = jsonData.download;
|
|
||||||
const upload = jsonData.upload;
|
|
||||||
|
|
||||||
// Convert to human-readable format
|
// Convert to human-readable format
|
||||||
const downloadFormatted = formatBytes(download);
|
const downloadFormatted = formatBytes(download);
|
||||||
|
|||||||
@@ -75,7 +75,7 @@ document.addEventListener('DOMContentLoaded', function() {
|
|||||||
headers: {
|
headers: {
|
||||||
'Content-Type': 'application/x-www-form-urlencoded',
|
'Content-Type': 'application/x-www-form-urlencoded',
|
||||||
},
|
},
|
||||||
body: 'command=' + encodeURIComponent('AT+QPOWD=1')
|
body: 'command=' + encodeURIComponent('AT+CFUN=1,1')
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!response.ok) {
|
if (!response.ok) {
|
||||||
|
|||||||
Reference in New Issue
Block a user