Report hotspot config
This commit is contained in:
parent
17d665e21a
commit
c443fce24f
8 changed files with 244 additions and 160 deletions
|
|
@ -149,43 +149,6 @@ async function disconnectWifi() {
|
|||
}
|
||||
}
|
||||
|
||||
async function updateHotspot() {
|
||||
const ssid = document.getElementById('hotspotName').value;
|
||||
const password = document.getElementById('hotspotPassword').value;
|
||||
const channel = parseInt(document.getElementById('hotspotChannel').value);
|
||||
|
||||
if (!ssid || ssid.length > 32) {
|
||||
showToast('warning', 'Attention', 'SSID invalide (1-32 caractères)');
|
||||
return;
|
||||
}
|
||||
|
||||
if (!password || password.length < 8 || password.length > 63) {
|
||||
showToast('warning', 'Attention', 'Mot de passe invalide (8-63 caractères)');
|
||||
return;
|
||||
}
|
||||
|
||||
showLoading(true);
|
||||
|
||||
try {
|
||||
const response = await fetch('/api/hotspot/config', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ ssid, password, channel })
|
||||
});
|
||||
|
||||
if (response.ok) {
|
||||
showToast('success', 'Configuration', 'Hotspot configuré avec succès');
|
||||
} else {
|
||||
throw new Error('Configuration failed');
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error configuring hotspot:', error);
|
||||
showToast('error', 'Erreur', 'Échec de la configuration');
|
||||
} finally {
|
||||
showLoading(false);
|
||||
}
|
||||
}
|
||||
|
||||
async function toggleHotspot() {
|
||||
const toggle = document.getElementById('hotspotToggle');
|
||||
const enabled = toggle.checked;
|
||||
|
|
@ -286,9 +249,12 @@ function updateStatusDisplay(status) {
|
|||
const hotspotText = hotspotStatus.querySelector('.status-text');
|
||||
const hotspotToggle = document.getElementById('hotspotToggle');
|
||||
|
||||
if (status.hotspotEnabled) {
|
||||
const isHotspotEnabled = status.hotspotStatus && status.hotspotStatus.state === 'ENABLED';
|
||||
|
||||
if (isHotspotEnabled) {
|
||||
hotspotDot.className = 'status-dot active';
|
||||
hotspotText.textContent = 'Hotspot actif';
|
||||
const numStations = status.hotspotStatus.numStations || 0;
|
||||
hotspotText.textContent = `Hotspot actif (${numStations} client${numStations > 1 ? 's' : ''})`;
|
||||
hotspotToggle.checked = true;
|
||||
} else {
|
||||
hotspotDot.className = 'status-dot offline';
|
||||
|
|
@ -296,7 +262,10 @@ function updateStatusDisplay(status) {
|
|||
hotspotToggle.checked = false;
|
||||
}
|
||||
|
||||
appState.hotspotEnabled = status.hotspotEnabled;
|
||||
appState.hotspotEnabled = isHotspotEnabled;
|
||||
|
||||
// Update hotspot details if available
|
||||
updateHotspotDetails(status.hotspotStatus);
|
||||
|
||||
// Check if connectedSSID changed and refresh WiFi list if needed
|
||||
const connectedSSIDChanged = appState.connectedSSID !== status.connectedSSID;
|
||||
|
|
@ -409,11 +378,21 @@ function addLogEntry(log) {
|
|||
|
||||
const timestamp = new Date(log.timestamp).toLocaleTimeString('fr-FR');
|
||||
|
||||
logEntry.innerHTML = `
|
||||
<span class="log-timestamp">${timestamp}</span>
|
||||
<span class="log-source">[${escapeHtml(log.source)}]</span>
|
||||
<span class="log-message">${escapeHtml(log.message)}</span>
|
||||
`;
|
||||
const timestampSpan = document.createElement('span');
|
||||
timestampSpan.className = 'log-timestamp';
|
||||
timestampSpan.textContent = timestamp;
|
||||
|
||||
const sourceSpan = document.createElement('span');
|
||||
sourceSpan.className = 'log-source';
|
||||
sourceSpan.textContent = `[${log.source}]`;
|
||||
|
||||
const messageSpan = document.createElement('span');
|
||||
messageSpan.className = 'log-message';
|
||||
messageSpan.textContent = log.message;
|
||||
|
||||
logEntry.appendChild(timestampSpan);
|
||||
logEntry.appendChild(sourceSpan);
|
||||
logEntry.appendChild(messageSpan);
|
||||
|
||||
logContainer.appendChild(logEntry);
|
||||
|
||||
|
|
@ -422,6 +401,55 @@ function addLogEntry(log) {
|
|||
}
|
||||
}
|
||||
|
||||
function createHotspotInfoItem(label, value) {
|
||||
const item = document.createElement('div');
|
||||
item.className = 'hotspot-info-item';
|
||||
|
||||
const labelSpan = document.createElement('span');
|
||||
labelSpan.className = 'info-label';
|
||||
labelSpan.textContent = label + ':';
|
||||
|
||||
const valueSpan = document.createElement('span');
|
||||
valueSpan.className = 'info-value';
|
||||
valueSpan.textContent = value;
|
||||
|
||||
item.appendChild(labelSpan);
|
||||
item.appendChild(valueSpan);
|
||||
|
||||
return item;
|
||||
}
|
||||
|
||||
function updateHotspotDetails(hotspotStatus) {
|
||||
const detailsContainer = document.getElementById('hotspotDetails');
|
||||
if (!detailsContainer) return;
|
||||
|
||||
// Clear existing content
|
||||
detailsContainer.innerHTML = '';
|
||||
|
||||
if (!hotspotStatus || hotspotStatus.state !== 'ENABLED') {
|
||||
detailsContainer.appendChild(createHotspotInfoItem('État', 'Inactif'));
|
||||
return;
|
||||
}
|
||||
|
||||
detailsContainer.appendChild(createHotspotInfoItem('État', hotspotStatus.state));
|
||||
|
||||
if (hotspotStatus.ssid) {
|
||||
detailsContainer.appendChild(createHotspotInfoItem('SSID', hotspotStatus.ssid));
|
||||
}
|
||||
|
||||
if (hotspotStatus.channel) {
|
||||
detailsContainer.appendChild(createHotspotInfoItem('Canal', `${hotspotStatus.channel} (${hotspotStatus.frequency} MHz)`));
|
||||
}
|
||||
|
||||
if (hotspotStatus.numStations !== undefined) {
|
||||
detailsContainer.appendChild(createHotspotInfoItem('Clients connectés', hotspotStatus.numStations.toString()));
|
||||
}
|
||||
|
||||
if (hotspotStatus.bssid) {
|
||||
detailsContainer.appendChild(createHotspotInfoItem('BSSID', hotspotStatus.bssid));
|
||||
}
|
||||
}
|
||||
|
||||
// ===== WebSocket Functions =====
|
||||
|
||||
function connectWebSocket() {
|
||||
|
|
|
|||
|
|
@ -133,7 +133,7 @@
|
|||
<svg class="icon" viewBox="0 0 24 24" width="20" height="20">
|
||||
<path fill="currentColor" d="M12,2A10,10 0 0,0 2,12A10,10 0 0,0 12,22A10,10 0 0,0 22,12A10,10 0 0,0 12,2M12,4C16.41,4 20,7.59 20,12C20,16.41 16.41,20 12,20C7.59,20 4,16.41 4,12C4,7.59 7.59,4 12,4M12,6A6,6 0 0,0 6,12A6,6 0 0,0 12,18A6,6 0 0,0 18,12A6,6 0 0,0 12,6M12,8A4,4 0 0,1 16,12A4,4 0 0,1 12,16A4,4 0 0,1 8,12A4,4 0 0,1 12,8Z"/>
|
||||
</svg>
|
||||
Configuration Hotspot
|
||||
Hotspot Status
|
||||
</h2>
|
||||
<div class="toggle-switch">
|
||||
<input type="checkbox" id="hotspotToggle" checked onchange="toggleHotspot()">
|
||||
|
|
@ -141,54 +141,12 @@
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="hotspotName">
|
||||
<svg class="input-icon" viewBox="0 0 24 24" width="16" height="16">
|
||||
<path fill="currentColor" d="M12,6A10,10 0 0,0 2,16C2,16.5 2.04,17 2.11,17.5L6,13.61C6,13.61 9.26,15.45 9.26,15.45L11.12,13C11.12,13 12.19,13.53 12.19,13.53C12.19,13.53 13.72,12 13.72,12L12,10.28C12,10.28 14.97,8.12 14.97,8.12L22,15.15C22,14.77 22,14.39 22,14A10,10 0 0,0 12,6Z"/>
|
||||
</svg>
|
||||
Nom du réseau (SSID)
|
||||
</label>
|
||||
<input type="text" id="hotspotName" value="TravelRouter" placeholder="Nom du hotspot" maxlength="32">
|
||||
<div class="hotspot-details" id="hotspotDetails">
|
||||
<div class="hotspot-info-item">
|
||||
<span class="info-label">État:</span>
|
||||
<span class="info-value">Chargement...</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="hotspotPassword">
|
||||
<svg class="input-icon" viewBox="0 0 24 24" width="16" height="16">
|
||||
<path fill="currentColor" d="M12,17A2,2 0 0,0 14,15C14,13.89 13.1,13 12,13A2,2 0 0,0 10,15A2,2 0 0,0 12,17M18,8A2,2 0 0,1 20,10V20A2,2 0 0,1 18,22H6A2,2 0 0,1 4,20V10C4,8.89 4.9,8 6,8H7V6A5,5 0 0,1 12,1A5,5 0 0,1 17,6V8H18M12,3A3,3 0 0,0 9,6V8H15V6A3,3 0 0,0 12,3Z"/>
|
||||
</svg>
|
||||
Mot de passe (8-63 caractères)
|
||||
</label>
|
||||
<input type="password" id="hotspotPassword" value="travel123" placeholder="Mot de passe du hotspot" minlength="8" maxlength="63">
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="hotspotChannel">
|
||||
<svg class="input-icon" viewBox="0 0 24 24" width="16" height="16">
|
||||
<path fill="currentColor" d="M9,10H7V17H9V10M13,10H11V17H13V10M17,10H15V17H17V10M19,3H18V1H16V3H8V1H6V3H5C3.89,3 3,3.89 3,5V19A2,2 0 0,0 5,21H19A2,2 0 0,0 21,19V5C21,3.89 20.1,3 19,3M19,19H5V8H19V19Z"/>
|
||||
</svg>
|
||||
Canal WiFi (2.4GHz)
|
||||
</label>
|
||||
<select id="hotspotChannel">
|
||||
<option value="1">Canal 1 (2412 MHz)</option>
|
||||
<option value="2">Canal 2 (2417 MHz)</option>
|
||||
<option value="3">Canal 3 (2422 MHz)</option>
|
||||
<option value="4">Canal 4 (2427 MHz)</option>
|
||||
<option value="5">Canal 5 (2432 MHz)</option>
|
||||
<option value="6" selected>Canal 6 (2437 MHz)</option>
|
||||
<option value="7">Canal 7 (2442 MHz)</option>
|
||||
<option value="8">Canal 8 (2447 MHz)</option>
|
||||
<option value="9">Canal 9 (2452 MHz)</option>
|
||||
<option value="10">Canal 10 (2457 MHz)</option>
|
||||
<option value="11">Canal 11 (2462 MHz)</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<button class="btn btn-primary" onclick="updateHotspot()">
|
||||
<svg viewBox="0 0 24 24" width="18" height="18">
|
||||
<path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"/>
|
||||
</svg>
|
||||
Mettre à jour la configuration
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
|
|
|||
|
|
@ -747,6 +747,39 @@ body {
|
|||
background: var(--text-secondary);
|
||||
}
|
||||
|
||||
/* Hotspot Details */
|
||||
.hotspot-details {
|
||||
margin-top: 1.5rem;
|
||||
padding: 1rem;
|
||||
background: var(--background);
|
||||
border-radius: var(--radius-md);
|
||||
border: 1px solid var(--border-color);
|
||||
}
|
||||
|
||||
.hotspot-info-item {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 0.5rem 0;
|
||||
border-bottom: 1px solid var(--border-color);
|
||||
}
|
||||
|
||||
.hotspot-info-item:last-child {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
.info-label {
|
||||
font-weight: 500;
|
||||
color: var(--text-secondary);
|
||||
font-size: 0.875rem;
|
||||
}
|
||||
|
||||
.info-value {
|
||||
font-weight: 600;
|
||||
color: var(--text-primary);
|
||||
font-size: 0.875rem;
|
||||
}
|
||||
|
||||
/* Responsive Design */
|
||||
@media (max-width: 1024px) {
|
||||
.grid {
|
||||
|
|
|
|||
|
|
@ -82,11 +82,11 @@ func ConfigureHotspot(c *gin.Context) {
|
|||
|
||||
// ToggleHotspot handles hotspot enable/disable
|
||||
func ToggleHotspot(c *gin.Context, status *models.SystemStatus) {
|
||||
status.HotspotEnabled = !status.HotspotEnabled
|
||||
enabled := status.HotspotEnabled
|
||||
// Determine current state
|
||||
isEnabled := status.HotspotStatus != nil && status.HotspotStatus.State == "ENABLED"
|
||||
|
||||
var err error
|
||||
if enabled {
|
||||
if !isEnabled {
|
||||
err = hotspot.Start()
|
||||
logging.AddLog("Hotspot", "Hotspot activé")
|
||||
} else {
|
||||
|
|
@ -100,7 +100,10 @@ func ToggleHotspot(c *gin.Context, status *models.SystemStatus) {
|
|||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{"enabled": enabled})
|
||||
// Update status immediately
|
||||
status.HotspotStatus = hotspot.GetDetailedStatus()
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{"enabled": !isEnabled})
|
||||
}
|
||||
|
||||
// GetDevices returns connected devices
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@ import (
|
|||
"github.com/nemunaire/repeater/internal/api"
|
||||
"github.com/nemunaire/repeater/internal/config"
|
||||
"github.com/nemunaire/repeater/internal/device"
|
||||
"github.com/nemunaire/repeater/internal/hotspot"
|
||||
"github.com/nemunaire/repeater/internal/logging"
|
||||
"github.com/nemunaire/repeater/internal/models"
|
||||
"github.com/nemunaire/repeater/internal/wifi"
|
||||
|
|
@ -32,7 +33,7 @@ func New(assets embed.FS) *App {
|
|||
Status: models.SystemStatus{
|
||||
Connected: false,
|
||||
ConnectedSSID: "",
|
||||
HotspotEnabled: true,
|
||||
HotspotStatus: nil,
|
||||
ConnectedCount: 0,
|
||||
DataUsage: 0.0,
|
||||
Uptime: 0,
|
||||
|
|
@ -131,6 +132,9 @@ func (a *App) periodicStatusUpdate() {
|
|||
a.Status.ConnectedSSID = wifi.GetConnectedSSID()
|
||||
a.Status.Uptime = getSystemUptime()
|
||||
|
||||
// Get detailed hotspot status
|
||||
a.Status.HotspotStatus = hotspot.GetDetailedStatus()
|
||||
|
||||
// Get network data usage for WiFi interface
|
||||
if a.Config != nil {
|
||||
rxBytes, txBytes := getInterfaceBytes(a.Config.WifiInterface)
|
||||
|
|
|
|||
|
|
@ -4,6 +4,8 @@ import (
|
|||
"fmt"
|
||||
"os"
|
||||
"os/exec"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/nemunaire/repeater/internal/models"
|
||||
)
|
||||
|
|
@ -36,12 +38,74 @@ rsn_pairwise=CCMP
|
|||
|
||||
// Start starts the hotspot
|
||||
func Start() error {
|
||||
cmd := exec.Command("systemctl", "start", "hostapd")
|
||||
cmd := exec.Command("/etc/init.d/hostapd", "start")
|
||||
return cmd.Run()
|
||||
}
|
||||
|
||||
// Stop stops the hotspot
|
||||
func Stop() error {
|
||||
cmd := exec.Command("systemctl", "stop", "hostapd")
|
||||
cmd := exec.Command("/etc/init.d/hostapd", "stop")
|
||||
return cmd.Run()
|
||||
}
|
||||
|
||||
// Status checks if the hotspot is running.
|
||||
// Returns nil if the service is running, or an error if it's stopped or crashed.
|
||||
func Status() error {
|
||||
cmd := exec.Command("/etc/init.d/hostapd", "status")
|
||||
return cmd.Run()
|
||||
}
|
||||
|
||||
// GetDetailedStatus retrieves detailed status information from hostapd_cli.
|
||||
// Returns nil if hostapd is not running or if there's an error.
|
||||
func GetDetailedStatus() *models.HotspotStatus {
|
||||
cmd := exec.Command("hostapd_cli", "status")
|
||||
output, err := cmd.Output()
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
status := &models.HotspotStatus{}
|
||||
lines := strings.Split(string(output), "\n")
|
||||
|
||||
for _, line := range lines {
|
||||
line = strings.TrimSpace(line)
|
||||
if line == "" || strings.HasPrefix(line, "Selected interface") {
|
||||
continue
|
||||
}
|
||||
|
||||
parts := strings.SplitN(line, "=", 2)
|
||||
if len(parts) != 2 {
|
||||
continue
|
||||
}
|
||||
|
||||
key := strings.TrimSpace(parts[0])
|
||||
value := strings.TrimSpace(parts[1])
|
||||
|
||||
switch key {
|
||||
case "state":
|
||||
status.State = value
|
||||
case "channel":
|
||||
if ch, err := strconv.Atoi(value); err == nil {
|
||||
status.Channel = ch
|
||||
}
|
||||
case "freq":
|
||||
if freq, err := strconv.Atoi(value); err == nil {
|
||||
status.Frequency = freq
|
||||
}
|
||||
case "ssid[0]":
|
||||
status.SSID = value
|
||||
case "bssid[0]":
|
||||
status.BSSID = value
|
||||
case "num_sta[0]":
|
||||
if num, err := strconv.Atoi(value); err == nil {
|
||||
status.NumStations = num
|
||||
}
|
||||
case "hw_mode":
|
||||
status.HWMode = value
|
||||
case "country_code":
|
||||
status.CountryCode = value
|
||||
}
|
||||
}
|
||||
|
||||
return status
|
||||
}
|
||||
|
|
|
|||
|
|
@ -26,11 +26,23 @@ type HotspotConfig struct {
|
|||
Channel int `json:"channel"`
|
||||
}
|
||||
|
||||
// HotspotStatus represents detailed hotspot status
|
||||
type HotspotStatus struct {
|
||||
State string `json:"state"` // ENABLED, DISABLED, etc.
|
||||
SSID string `json:"ssid"` // Current SSID being broadcast
|
||||
BSSID string `json:"bssid"` // MAC address of the AP
|
||||
Channel int `json:"channel"` // Current channel
|
||||
Frequency int `json:"frequency"` // Frequency in MHz
|
||||
NumStations int `json:"numStations"` // Number of connected stations
|
||||
HWMode string `json:"hwMode"` // Hardware mode (g, a, n, ac, etc.)
|
||||
CountryCode string `json:"countryCode"` // Country code
|
||||
}
|
||||
|
||||
// SystemStatus represents overall system status
|
||||
type SystemStatus struct {
|
||||
Connected bool `json:"connected"`
|
||||
ConnectedSSID string `json:"connectedSSID"`
|
||||
HotspotEnabled bool `json:"hotspotEnabled"`
|
||||
HotspotStatus *HotspotStatus `json:"hotspotStatus,omitempty"` // Detailed hotspot status
|
||||
ConnectedCount int `json:"connectedCount"`
|
||||
DataUsage float64 `json:"dataUsage"`
|
||||
Uptime int64 `json:"uptime"`
|
||||
|
|
|
|||
100
openapi.yaml
100
openapi.yaml
|
|
@ -133,42 +133,6 @@ paths:
|
|||
schema:
|
||||
$ref: '#/components/schemas/Error'
|
||||
|
||||
/api/hotspot/config:
|
||||
post:
|
||||
tags:
|
||||
- Hotspot
|
||||
summary: Configure hotspot settings
|
||||
description: |
|
||||
Updates the hotspot (access point) configuration including SSID, password,
|
||||
and WiFi channel. Changes are written to hostapd configuration file.
|
||||
The hotspot needs to be restarted for changes to take effect.
|
||||
operationId: configureHotspot
|
||||
requestBody:
|
||||
required: true
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/HotspotConfig'
|
||||
responses:
|
||||
'200':
|
||||
description: Configuration updated successfully
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/SuccessResponse'
|
||||
'400':
|
||||
description: Invalid configuration data
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/Error'
|
||||
'500':
|
||||
description: Configuration update failed
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/Error'
|
||||
|
||||
/api/hotspot/toggle:
|
||||
post:
|
||||
tags:
|
||||
|
|
@ -176,7 +140,8 @@ paths:
|
|||
summary: Toggle hotspot on/off
|
||||
description: |
|
||||
Enables or disables the hotspot (access point) by starting/stopping
|
||||
the hostapd service. Returns the new enabled state.
|
||||
the hostapd service. Returns the new enabled state and updates
|
||||
the system status with current hostapd_cli information.
|
||||
operationId: toggleHotspot
|
||||
responses:
|
||||
'200':
|
||||
|
|
@ -242,7 +207,8 @@ paths:
|
|||
summary: Get system status
|
||||
description: |
|
||||
Returns comprehensive system status including WiFi connection state,
|
||||
hotspot status, connected device count, data usage, and uptime.
|
||||
detailed hotspot status from hostapd_cli, connected device count,
|
||||
data usage, and uptime.
|
||||
operationId: getStatus
|
||||
responses:
|
||||
'200':
|
||||
|
|
@ -367,32 +333,48 @@ components:
|
|||
- ssid
|
||||
- password
|
||||
|
||||
HotspotConfig:
|
||||
HotspotStatus:
|
||||
type: object
|
||||
description: Hotspot (access point) configuration
|
||||
description: Detailed hotspot status from hostapd_cli
|
||||
properties:
|
||||
state:
|
||||
type: string
|
||||
description: Hotspot state (ENABLED, DISABLED, etc.)
|
||||
example: "ENABLED"
|
||||
ssid:
|
||||
type: string
|
||||
description: Hotspot SSID (network name)
|
||||
minLength: 1
|
||||
maxLength: 32
|
||||
description: Current SSID being broadcast
|
||||
example: "TravelRouter"
|
||||
password:
|
||||
bssid:
|
||||
type: string
|
||||
description: WPA2 password (minimum 8 characters)
|
||||
minLength: 8
|
||||
maxLength: 63
|
||||
example: "secure123"
|
||||
description: MAC address of the access point
|
||||
pattern: '^([0-9a-fA-F]{2}:){5}[0-9a-fA-F]{2}$'
|
||||
example: "4a:e3:4e:09:57:f8"
|
||||
channel:
|
||||
type: integer
|
||||
description: WiFi channel (1-11 for 2.4GHz)
|
||||
description: Current WiFi channel
|
||||
minimum: 1
|
||||
maximum: 14
|
||||
example: 6
|
||||
example: 11
|
||||
frequency:
|
||||
type: integer
|
||||
description: Frequency in MHz
|
||||
example: 2462
|
||||
numStations:
|
||||
type: integer
|
||||
description: Number of connected stations
|
||||
minimum: 0
|
||||
example: 2
|
||||
hwMode:
|
||||
type: string
|
||||
description: Hardware mode (g, a, n, ac, etc.)
|
||||
example: "g"
|
||||
countryCode:
|
||||
type: string
|
||||
description: Country code
|
||||
example: "VN"
|
||||
required:
|
||||
- ssid
|
||||
- password
|
||||
- channel
|
||||
- state
|
||||
|
||||
ConnectedDevice:
|
||||
type: object
|
||||
|
|
@ -440,10 +422,11 @@ components:
|
|||
type: string
|
||||
description: SSID of connected upstream network (empty if not connected)
|
||||
example: "Hotel-Guest"
|
||||
hotspotEnabled:
|
||||
type: boolean
|
||||
description: Whether the hotspot is currently enabled
|
||||
example: true
|
||||
hotspotStatus:
|
||||
allOf:
|
||||
- $ref: '#/components/schemas/HotspotStatus'
|
||||
nullable: true
|
||||
description: Detailed hotspot status (null if hotspot is not running)
|
||||
connectedCount:
|
||||
type: integer
|
||||
description: Number of devices connected to hotspot
|
||||
|
|
@ -452,7 +435,7 @@ components:
|
|||
dataUsage:
|
||||
type: number
|
||||
format: double
|
||||
description: Total data usage in MB (placeholder for future implementation)
|
||||
description: Total data usage in MB
|
||||
example: 145.7
|
||||
uptime:
|
||||
type: integer
|
||||
|
|
@ -467,7 +450,6 @@ components:
|
|||
required:
|
||||
- connected
|
||||
- connectedSSID
|
||||
- hotspotEnabled
|
||||
- connectedCount
|
||||
- dataUsage
|
||||
- uptime
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue