updated to monitor the netowrk and reset wifi if is not working
This commit is contained in:
53
setup_wifi_control.sh
Normal file
53
setup_wifi_control.sh
Normal file
@@ -0,0 +1,53 @@
|
||||
#!/bin/bash
|
||||
# Setup script to allow passwordless sudo for WiFi control commands
|
||||
|
||||
echo "Setting up passwordless sudo for WiFi control..."
|
||||
echo ""
|
||||
|
||||
# Create sudoers file for WiFi commands
|
||||
SUDOERS_FILE="/etc/sudoers.d/kiwy-signage-wifi"
|
||||
|
||||
# Check if running as root
|
||||
if [ "$EUID" -ne 0 ]; then
|
||||
echo "This script must be run as root (use sudo)"
|
||||
echo "Usage: sudo bash setup_wifi_control.sh"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Get the username who invoked sudo
|
||||
ACTUAL_USER="${SUDO_USER:-$USER}"
|
||||
|
||||
echo "Configuring passwordless sudo for user: $ACTUAL_USER"
|
||||
echo ""
|
||||
|
||||
# Create sudoers entry
|
||||
cat > "$SUDOERS_FILE" << EOF
|
||||
# Allow $ACTUAL_USER to control WiFi without password for Kiwy Signage Player
|
||||
$ACTUAL_USER ALL=(ALL) NOPASSWD: /usr/sbin/rfkill block wifi
|
||||
$ACTUAL_USER ALL=(ALL) NOPASSWD: /usr/sbin/rfkill unblock wifi
|
||||
$ACTUAL_USER ALL=(ALL) NOPASSWD: /sbin/ifconfig wlan0 down
|
||||
$ACTUAL_USER ALL=(ALL) NOPASSWD: /sbin/ifconfig wlan0 up
|
||||
$ACTUAL_USER ALL=(ALL) NOPASSWD: /sbin/dhclient wlan0
|
||||
EOF
|
||||
|
||||
# Set correct permissions
|
||||
chmod 0440 "$SUDOERS_FILE"
|
||||
|
||||
echo "✓ Created sudoers file: $SUDOERS_FILE"
|
||||
echo ""
|
||||
|
||||
# Validate the sudoers file
|
||||
if visudo -c -f "$SUDOERS_FILE"; then
|
||||
echo "✓ Sudoers file validated successfully"
|
||||
echo ""
|
||||
echo "Setup complete! User '$ACTUAL_USER' can now control WiFi without password."
|
||||
echo ""
|
||||
echo "Test with:"
|
||||
echo " sudo rfkill block wifi"
|
||||
echo " sudo rfkill unblock wifi"
|
||||
else
|
||||
echo "✗ Error: Sudoers file validation failed"
|
||||
echo "Removing invalid file..."
|
||||
rm -f "$SUDOERS_FILE"
|
||||
exit 1
|
||||
fi
|
||||
35
src/main.py
35
src/main.py
@@ -56,6 +56,7 @@ from get_playlists_v2 import (
|
||||
send_player_error_feedback
|
||||
)
|
||||
from keyboard_widget import KeyboardWidget
|
||||
from network_monitor import NetworkMonitor
|
||||
from kivy.graphics import Color, Line, Ellipse
|
||||
from kivy.uix.floatlayout import FloatLayout
|
||||
from kivy.uix.slider import Slider
|
||||
@@ -1496,6 +1497,8 @@ class SignagePlayer(Widget):
|
||||
# Card reader for authentication
|
||||
self.card_reader = None
|
||||
self._pending_edit_image = None
|
||||
# Network monitor
|
||||
self.network_monitor = None
|
||||
# Paths
|
||||
self.base_dir = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
||||
self.config_dir = os.path.join(self.base_dir, 'config')
|
||||
@@ -1542,6 +1545,9 @@ class SignagePlayer(Widget):
|
||||
# Load configuration
|
||||
self.load_config()
|
||||
|
||||
# Initialize network monitor
|
||||
self.start_network_monitoring()
|
||||
|
||||
# Play intro video first
|
||||
self.play_intro_video()
|
||||
|
||||
@@ -1582,6 +1588,30 @@ class SignagePlayer(Widget):
|
||||
Logger.info("SignagePlayer: Configuration saved")
|
||||
except Exception as e:
|
||||
Logger.error(f"SignagePlayer: Error saving config: {e}")
|
||||
|
||||
def start_network_monitoring(self):
|
||||
"""Initialize and start network monitoring"""
|
||||
try:
|
||||
if self.config and 'server_ip' in self.config:
|
||||
server_url = self.config.get('server_ip', '')
|
||||
|
||||
# Initialize network monitor with:
|
||||
# - Check every 30-45 minutes
|
||||
# - Restart WiFi for 20 minutes on connection failure
|
||||
self.network_monitor = NetworkMonitor(
|
||||
server_url=server_url,
|
||||
check_interval_min=30,
|
||||
check_interval_max=45,
|
||||
wifi_restart_duration=20
|
||||
)
|
||||
|
||||
# Start monitoring
|
||||
self.network_monitor.start_monitoring()
|
||||
Logger.info("SignagePlayer: Network monitoring started")
|
||||
else:
|
||||
Logger.warning("SignagePlayer: Cannot start network monitoring - no server configured")
|
||||
except Exception as e:
|
||||
Logger.error(f"SignagePlayer: Error starting network monitoring: {e}")
|
||||
|
||||
async def async_playlist_update_loop(self):
|
||||
"""Async coroutine to check for playlist updates without blocking UI"""
|
||||
@@ -2363,6 +2393,11 @@ class SignagePlayerApp(App):
|
||||
def on_stop(self):
|
||||
Logger.info("SignagePlayerApp: Application stopped")
|
||||
|
||||
# Stop network monitoring
|
||||
if hasattr(self.root, 'network_monitor') and self.root.network_monitor:
|
||||
self.root.network_monitor.stop_monitoring()
|
||||
Logger.info("SignagePlayerApp: Network monitoring stopped")
|
||||
|
||||
# Cancel all async tasks
|
||||
try:
|
||||
pending = asyncio.all_tasks()
|
||||
|
||||
235
src/network_monitor.py
Normal file
235
src/network_monitor.py
Normal file
@@ -0,0 +1,235 @@
|
||||
"""
|
||||
Network Monitoring Module
|
||||
Checks server connectivity and manages WiFi restart on connection failure
|
||||
"""
|
||||
|
||||
import subprocess
|
||||
import time
|
||||
import random
|
||||
import requests
|
||||
from datetime import datetime
|
||||
from kivy.logger import Logger
|
||||
from kivy.clock import Clock
|
||||
|
||||
|
||||
class NetworkMonitor:
|
||||
"""Monitor network connectivity and manage WiFi restart"""
|
||||
|
||||
def __init__(self, server_url, check_interval_min=30, check_interval_max=45, wifi_restart_duration=20):
|
||||
"""
|
||||
Initialize network monitor
|
||||
|
||||
Args:
|
||||
server_url (str): Server URL to check connectivity (e.g., 'https://digi-signage.moto-adv.com')
|
||||
check_interval_min (int): Minimum minutes between checks (default: 30)
|
||||
check_interval_max (int): Maximum minutes between checks (default: 45)
|
||||
wifi_restart_duration (int): Minutes to keep WiFi off during restart (default: 20)
|
||||
"""
|
||||
self.server_url = server_url.rstrip('/')
|
||||
self.check_interval_min = check_interval_min * 60 # Convert to seconds
|
||||
self.check_interval_max = check_interval_max * 60 # Convert to seconds
|
||||
self.wifi_restart_duration = wifi_restart_duration * 60 # Convert to seconds
|
||||
self.is_monitoring = False
|
||||
self.scheduled_event = None
|
||||
self.consecutive_failures = 0
|
||||
self.max_failures_before_restart = 3 # Restart WiFi after 3 consecutive failures
|
||||
|
||||
def start_monitoring(self):
|
||||
"""Start the network monitoring loop"""
|
||||
if not self.is_monitoring:
|
||||
self.is_monitoring = True
|
||||
Logger.info("NetworkMonitor: Starting network monitoring")
|
||||
self._schedule_next_check()
|
||||
|
||||
def stop_monitoring(self):
|
||||
"""Stop the network monitoring"""
|
||||
self.is_monitoring = False
|
||||
if self.scheduled_event:
|
||||
self.scheduled_event.cancel()
|
||||
self.scheduled_event = None
|
||||
Logger.info("NetworkMonitor: Stopped network monitoring")
|
||||
|
||||
def _schedule_next_check(self):
|
||||
"""Schedule the next connectivity check at a random interval"""
|
||||
if not self.is_monitoring:
|
||||
return
|
||||
|
||||
# Random interval between min and max
|
||||
next_check_seconds = random.randint(self.check_interval_min, self.check_interval_max)
|
||||
next_check_minutes = next_check_seconds / 60
|
||||
|
||||
Logger.info(f"NetworkMonitor: Next check scheduled in {next_check_minutes:.1f} minutes")
|
||||
|
||||
# Schedule using Kivy Clock
|
||||
self.scheduled_event = Clock.schedule_once(
|
||||
lambda dt: self._check_connectivity(),
|
||||
next_check_seconds
|
||||
)
|
||||
|
||||
def _check_connectivity(self):
|
||||
"""Check network connectivity to server"""
|
||||
Logger.info("NetworkMonitor: Checking server connectivity...")
|
||||
|
||||
if self._test_server_connection():
|
||||
Logger.info("NetworkMonitor: ✓ Server connection successful")
|
||||
self.consecutive_failures = 0
|
||||
else:
|
||||
self.consecutive_failures += 1
|
||||
Logger.warning(f"NetworkMonitor: ✗ Server connection failed (attempt {self.consecutive_failures}/{self.max_failures_before_restart})")
|
||||
|
||||
if self.consecutive_failures >= self.max_failures_before_restart:
|
||||
Logger.error("NetworkMonitor: Multiple connection failures detected - initiating WiFi restart")
|
||||
self._restart_wifi()
|
||||
self.consecutive_failures = 0 # Reset counter after restart
|
||||
|
||||
# Schedule next check
|
||||
self._schedule_next_check()
|
||||
|
||||
def _test_server_connection(self):
|
||||
"""
|
||||
Test connection to the server using ping only
|
||||
This works in closed networks where the server is local
|
||||
|
||||
Returns:
|
||||
bool: True if server is reachable, False otherwise
|
||||
"""
|
||||
try:
|
||||
# Extract hostname from server URL (remove http:// or https://)
|
||||
hostname = self.server_url.replace('https://', '').replace('http://', '').split('/')[0]
|
||||
|
||||
Logger.info(f"NetworkMonitor: Pinging server: {hostname}")
|
||||
|
||||
# Ping the server hostname with 3 attempts
|
||||
result = subprocess.run(
|
||||
['ping', '-c', '3', '-W', '3', hostname],
|
||||
capture_output=True,
|
||||
text=True,
|
||||
timeout=10
|
||||
)
|
||||
|
||||
if result.returncode == 0:
|
||||
Logger.info(f"NetworkMonitor: ✓ Server {hostname} is reachable")
|
||||
return True
|
||||
else:
|
||||
Logger.warning(f"NetworkMonitor: ✗ Cannot reach server {hostname}")
|
||||
return False
|
||||
|
||||
except subprocess.TimeoutExpired:
|
||||
Logger.warning(f"NetworkMonitor: ✗ Ping timeout to server")
|
||||
return False
|
||||
except Exception as e:
|
||||
Logger.error(f"NetworkMonitor: Error pinging server: {e}")
|
||||
return False
|
||||
|
||||
def _restart_wifi(self):
|
||||
"""
|
||||
Restart WiFi by turning it off for a specified duration then back on
|
||||
This runs in a separate thread to not block the main application
|
||||
"""
|
||||
def wifi_restart_thread():
|
||||
try:
|
||||
Logger.info("NetworkMonitor: ====================================")
|
||||
Logger.info("NetworkMonitor: INITIATING WIFI RESTART SEQUENCE")
|
||||
Logger.info("NetworkMonitor: ====================================")
|
||||
|
||||
# Turn off WiFi using rfkill (more reliable on Raspberry Pi)
|
||||
Logger.info("NetworkMonitor: Turning WiFi OFF using rfkill...")
|
||||
result = subprocess.run(
|
||||
['sudo', 'rfkill', 'block', 'wifi'],
|
||||
capture_output=True,
|
||||
text=True,
|
||||
timeout=10
|
||||
)
|
||||
|
||||
if result.returncode == 0:
|
||||
Logger.info("NetworkMonitor: ✓ WiFi turned OFF successfully (rfkill)")
|
||||
Logger.info("NetworkMonitor: WiFi is now DISABLED and will remain OFF")
|
||||
else:
|
||||
Logger.error(f"NetworkMonitor: rfkill failed, trying ifconfig...")
|
||||
Logger.error(f"NetworkMonitor: rfkill error: {result.stderr}")
|
||||
|
||||
# Fallback to ifconfig
|
||||
result2 = subprocess.run(
|
||||
['sudo', 'ifconfig', 'wlan0', 'down'],
|
||||
capture_output=True,
|
||||
text=True,
|
||||
timeout=10
|
||||
)
|
||||
|
||||
if result2.returncode == 0:
|
||||
Logger.info("NetworkMonitor: ✓ WiFi turned OFF successfully (ifconfig)")
|
||||
else:
|
||||
Logger.error(f"NetworkMonitor: Failed to turn WiFi off: {result2.stderr}")
|
||||
Logger.error(f"NetworkMonitor: Return code: {result2.returncode}")
|
||||
Logger.error(f"NetworkMonitor: STDOUT: {result2.stdout}")
|
||||
return
|
||||
|
||||
# Wait for the specified duration with WiFi OFF
|
||||
wait_minutes = self.wifi_restart_duration / 60
|
||||
Logger.info(f"NetworkMonitor: ====================================")
|
||||
Logger.info(f"NetworkMonitor: WiFi will remain OFF for {wait_minutes:.0f} minutes")
|
||||
Logger.info(f"NetworkMonitor: Waiting period started at: {datetime.now().strftime('%H:%M:%S')}")
|
||||
Logger.info(f"NetworkMonitor: ====================================")
|
||||
|
||||
# Sleep while WiFi is OFF
|
||||
time.sleep(self.wifi_restart_duration)
|
||||
|
||||
Logger.info(f"NetworkMonitor: Wait period completed at: {datetime.now().strftime('%H:%M:%S')}")
|
||||
|
||||
# Turn WiFi back on after the wait period
|
||||
Logger.info("NetworkMonitor: ====================================")
|
||||
Logger.info("NetworkMonitor: Now turning WiFi back ON...")
|
||||
Logger.info("NetworkMonitor: ====================================")
|
||||
|
||||
# Unblock WiFi using rfkill
|
||||
result = subprocess.run(
|
||||
['sudo', 'rfkill', 'unblock', 'wifi'],
|
||||
capture_output=True,
|
||||
text=True,
|
||||
timeout=10
|
||||
)
|
||||
|
||||
if result.returncode == 0:
|
||||
Logger.info("NetworkMonitor: ✓ WiFi unblocked successfully (rfkill)")
|
||||
else:
|
||||
Logger.error(f"NetworkMonitor: rfkill unblock failed: {result.stderr}")
|
||||
|
||||
# Also bring interface up
|
||||
result2 = subprocess.run(
|
||||
['sudo', 'ifconfig', 'wlan0', 'up'],
|
||||
capture_output=True,
|
||||
text=True,
|
||||
timeout=10
|
||||
)
|
||||
|
||||
if result2.returncode == 0:
|
||||
Logger.info("NetworkMonitor: ✓ WiFi interface brought UP successfully")
|
||||
|
||||
# Wait a bit for connection to establish
|
||||
Logger.info("NetworkMonitor: Waiting 10 seconds for WiFi to initialize...")
|
||||
time.sleep(10)
|
||||
|
||||
# Try to restart DHCP
|
||||
Logger.info("NetworkMonitor: Requesting IP address...")
|
||||
subprocess.run(
|
||||
['sudo', 'dhclient', 'wlan0'],
|
||||
capture_output=True,
|
||||
text=True,
|
||||
timeout=15
|
||||
)
|
||||
|
||||
Logger.info("NetworkMonitor: ====================================")
|
||||
Logger.info("NetworkMonitor: WIFI RESTART SEQUENCE COMPLETED")
|
||||
Logger.info("NetworkMonitor: ====================================")
|
||||
else:
|
||||
Logger.error(f"NetworkMonitor: Failed to turn WiFi on: {result.stderr}")
|
||||
|
||||
except subprocess.TimeoutExpired:
|
||||
Logger.error("NetworkMonitor: WiFi restart command timeout")
|
||||
except Exception as e:
|
||||
Logger.error(f"NetworkMonitor: Error during WiFi restart: {e}")
|
||||
|
||||
# Run in separate thread to not block the application
|
||||
import threading
|
||||
thread = threading.Thread(target=wifi_restart_thread, daemon=True)
|
||||
thread.start()
|
||||
61
src/test_network_monitor.py
Normal file
61
src/test_network_monitor.py
Normal file
@@ -0,0 +1,61 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Test script for network monitor functionality
|
||||
"""
|
||||
|
||||
import sys
|
||||
import time
|
||||
from kivy.app import App
|
||||
from kivy.clock import Clock
|
||||
from network_monitor import NetworkMonitor
|
||||
|
||||
class TestMonitorApp(App):
|
||||
"""Minimal Kivy app to test network monitor"""
|
||||
|
||||
def build(self):
|
||||
"""Build the app"""
|
||||
from kivy.uix.label import Label
|
||||
return Label(text='Network Monitor Test Running\nCheck terminal for output')
|
||||
|
||||
def on_start(self):
|
||||
"""Start monitoring when app starts"""
|
||||
server_url = "https://digi-signage.moto-adv.com"
|
||||
|
||||
print("=" * 60)
|
||||
print("Network Monitor Test")
|
||||
print("=" * 60)
|
||||
print()
|
||||
print(f"Server URL: {server_url}")
|
||||
print("Check interval: 0.5 minutes (30 seconds for testing)")
|
||||
print("WiFi restart duration: 1 minute (for testing)")
|
||||
print()
|
||||
|
||||
# Create monitor with short intervals for testing
|
||||
self.monitor = NetworkMonitor(
|
||||
server_url=server_url,
|
||||
check_interval_min=0.5, # 30 seconds
|
||||
check_interval_max=0.5, # 30 seconds
|
||||
wifi_restart_duration=1 # 1 minute
|
||||
)
|
||||
|
||||
# Perform immediate test
|
||||
print("Performing immediate connectivity test...")
|
||||
self.monitor._check_connectivity()
|
||||
|
||||
# Start monitoring for future checks
|
||||
print("\nStarting periodic network monitoring...")
|
||||
self.monitor.start_monitoring()
|
||||
|
||||
print("\nMonitoring is active. Press Ctrl+C to stop.")
|
||||
print("Next check will occur in ~30 seconds.")
|
||||
print()
|
||||
|
||||
def on_stop(self):
|
||||
"""Stop monitoring when app stops"""
|
||||
if hasattr(self, 'monitor'):
|
||||
self.monitor.stop_monitoring()
|
||||
print("\nNetwork monitoring stopped")
|
||||
print("Test completed!")
|
||||
|
||||
if __name__ == '__main__':
|
||||
TestMonitorApp().run()
|
||||
Reference in New Issue
Block a user