Skip to content

Commit

Permalink
.
Browse files Browse the repository at this point in the history
  • Loading branch information
mahmodaltil committed Jan 11, 2025
1 parent bc9b1ae commit ad7888c
Show file tree
Hide file tree
Showing 4 changed files with 269 additions and 3 deletions.
12 changes: 12 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
FROM mcr.microsoft.com/playwright/python:v1.40.0-jammy

WORKDIR /app

COPY requirements.txt .
RUN pip install -r requirements.txt

COPY . .

EXPOSE 5000

CMD ["python", "app.py"]
134 changes: 134 additions & 0 deletions app.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
from flask import Flask, render_template, jsonify
from flask_socketio import SocketIO
from playwright.sync_api import sync_playwright
import threading
import time
import os
from datetime import datetime

app = Flask(__name__)
socketio = SocketIO(app, cors_allowed_origins="*")

# Global variables for browser state
browser_context = {
'playwright': None,
'browser': None,
'page': None,
'connected': False,
'last_output': '',
'monitor_thread': None,
'stop_monitor': False
}

def setup_browser():
"""Initialize browser in headless mode"""
try:
browser_context['playwright'] = sync_playwright().start()
browser_context['browser'] = browser_context['playwright'].chromium.launch(
headless=True,
args=['--no-sandbox', '--disable-dev-shm-usage']
)
browser_context['page'] = browser_context['browser'].new_page()
return True
except Exception as e:
print(f"Failed to setup browser: {str(e)}")
return False

def connect_to_arduino():
"""Connect to Arduino Web Editor and setup device"""
try:
page = browser_context['page']

# Navigate to Arduino Web Editor
page.goto("https://app.arduino.cc/sketches")

# Wait for device selection button
page.wait_for_selector("._device-name_12ggg_205")
page.click("._device-name_12ggg_205")

# Search for ESP32 device
search_input = page.wait_for_selector("#react-aria6138362191-:r10:")
search_input.type("DOIT ESP32 DEVKIT V1")
page.keyboard.press("Tab")
page.keyboard.press("Enter")

# Open Serial Monitor
page.wait_for_selector("._open-serial-monitor-button_1y7x9_356")
page.click("._open-serial-monitor-button_1y7x9_356")

# Switch to Serial Monitor
page.goto("https://app.arduino.cc/sketches/monitor")

# Set baud rate to 115200
page.wait_for_selector("._x-small_wmean_200")
page.click("._x-small_wmean_200")
page.click('button:text("115200")')

browser_context['connected'] = True
return True
except Exception as e:
print(f"Failed to connect to Arduino: {str(e)}")
return False

def monitor_serial_output():
"""Monitor serial output in a separate thread"""
while not browser_context['stop_monitor']:
try:
if browser_context['connected'] and browser_context['page']:
output_element = browser_context['page'].wait_for_selector(".serial-output")
new_output = output_element.inner_text()

if new_output != browser_context['last_output']:
browser_context['last_output'] = new_output
socketio.emit('serial_data', {'data': new_output})
except Exception as e:
print(f"Error reading serial output: {str(e)}")

time.sleep(1)

@app.route('/')
def index():
return render_template('index.html')

@app.route('/connect', methods=['POST'])
def connect():
if not browser_context['connected']:
if setup_browser() and connect_to_arduino():
# Start monitoring thread
browser_context['stop_monitor'] = False
browser_context['monitor_thread'] = threading.Thread(target=monitor_serial_output)
browser_context['monitor_thread'].start()
return jsonify({'success': True, 'message': 'Connected successfully'})

return jsonify({'success': False, 'message': 'Failed to connect'})

@app.route('/disconnect', methods=['POST'])
def disconnect():
try:
browser_context['stop_monitor'] = True
if browser_context['monitor_thread']:
browser_context['monitor_thread'].join()

if browser_context['page']:
browser_context['page'].close()
if browser_context['browser']:
browser_context['browser'].close()
if browser_context['playwright']:
browser_context['playwright'].stop()

browser_context['connected'] = False
browser_context['last_output'] = ''
return jsonify({'success': True})
except Exception as e:
return jsonify({'success': False, 'message': str(e)})

@app.route('/status')
def status():
return jsonify({
'connected': browser_context['connected'],
'last_update': datetime.now().strftime("%H:%M:%S")
})

if __name__ == '__main__':
port = int(os.environ.get('PORT', 5000))
socketio.run(app, host='0.0.0.0', port=port)
7 changes: 4 additions & 3 deletions requirements.txt
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
streamlit>=1.29.0
requests>=2.31.0
python-dotenv>=1.0.0
flask>=3.0.0
playwright>=1.40.0
python-dotenv>=1.0.0
flask-socketio>=5.3.6
119 changes: 119 additions & 0 deletions templates/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Smart Entrance Control System</title>
<script src="https://cdnjs.cloudflare.com/ajax/libs/socket.io/4.0.1/socket.io.js"></script>
<link href="https://cdn.jsdelivr.net/npm/tailwindcss@2.2.19/dist/tailwind.min.css" rel="stylesheet">
</head>
<body class="bg-gray-100">
<div class="container mx-auto px-4 py-8">
<h1 class="text-4xl font-bold text-center mb-8">Smart Entrance Control System</h1>

<div class="max-w-4xl mx-auto bg-white rounded-lg shadow-lg p-6">
<!-- Connection Status -->
<div class="flex justify-between items-center mb-6">
<div>
<span class="font-semibold">Status:</span>
<span id="connection-status" class="ml-2 text-red-500">Disconnected</span>
</div>
<div>
<span class="font-semibold">Last Update:</span>
<span id="last-update" class="ml-2">-</span>
</div>
</div>

<!-- Connection Controls -->
<div class="flex space-x-4 mb-6">
<button id="connect-btn" class="bg-green-500 text-white px-4 py-2 rounded hover:bg-green-600 focus:outline-none">
Connect
</button>
<button id="disconnect-btn" class="bg-red-500 text-white px-4 py-2 rounded hover:bg-red-600 focus:outline-none" disabled>
Disconnect
</button>
</div>

<!-- Serial Monitor -->
<div class="border rounded-lg p-4 bg-black">
<h2 class="text-xl font-semibold mb-4 text-white">Serial Monitor</h2>
<pre id="serial-output" class="font-mono text-green-400 whitespace-pre-wrap h-64 overflow-y-auto">Waiting for connection...</pre>
</div>
</div>
</div>

<script>
const socket = io();
let connected = false;

// DOM Elements
const connectBtn = document.getElementById('connect-btn');
const disconnectBtn = document.getElementById('disconnect-btn');
const statusText = document.getElementById('connection-status');
const lastUpdateText = document.getElementById('last-update');
const serialOutput = document.getElementById('serial-output');

// Connect Button
connectBtn.addEventListener('click', async () => {
connectBtn.disabled = true;
const response = await fetch('/connect', { method: 'POST' });
const result = await response.json();

if (result.success) {
connected = true;
updateConnectionStatus();
} else {
connectBtn.disabled = false;
alert('Failed to connect: ' + result.message);
}
});

// Disconnect Button
disconnectBtn.addEventListener('click', async () => {
disconnectBtn.disabled = true;
const response = await fetch('/disconnect', { method: 'POST' });
const result = await response.json();

if (result.success) {
connected = false;
updateConnectionStatus();
serialOutput.textContent = 'Waiting for connection...';
} else {
disconnectBtn.disabled = false;
alert('Failed to disconnect: ' + result.message);
}
});

// Update Connection Status
function updateConnectionStatus() {
statusText.textContent = connected ? 'Connected' : 'Disconnected';
statusText.className = connected ? 'ml-2 text-green-500' : 'ml-2 text-red-500';
connectBtn.disabled = connected;
disconnectBtn.disabled = !connected;
}

// Socket.io Events
socket.on('serial_data', (data) => {
serialOutput.textContent = data.data;
lastUpdateText.textContent = new Date().toLocaleTimeString();
});

// Status Updates
async function updateStatus() {
try {
const response = await fetch('/status');
const status = await response.json();
connected = status.connected;
updateConnectionStatus();
lastUpdateText.textContent = status.last_update;
} catch (error) {
console.error('Failed to update status:', error);
}
}

// Update status every 5 seconds
setInterval(updateStatus, 5000);
updateStatus();
</script>
</body>
</html>

0 comments on commit ad7888c

Please sign in to comment.