This complete feature gives you a production-ready QR rotation system for digital signage with analytics tracking, admin management, and easy integration with any open-source signage platform.
<script> const API_URL = 'http://localhost:5000'; let chart = null; async function loadAnalytics() const response = await fetch(`$API_URL/api/analytics`); const data = await response.json(); document.getElementById('totalScans').textContent = data.total_scans; document.getElementById('scansLastHour').textContent = data.scans_last_hour; document.getElementById('activeCount').textContent = data.qr_performance.length; // Update chart if (chart) chart.destroy(); const ctx = document.getElementById('performanceChart').getContext('2d'); chart = new Chart(ctx, type: 'bar', data: labels: data.qr_performance.map(p => p.name), datasets: [ label: 'Number of Scans', data: data.qr_performance.map(p => p.scans), backgroundColor: '#667eea' ] ); async function addQRContent() const data = name: document.getElementById('qrName').value, url: document.getElementById('qrUrl').value, description: document.getElementById('qrDesc').value, display_duration: parseInt(document.getElementById('qrDuration').value) ; const response = await fetch(`$API_URL/api/qr-content`, method: 'POST', headers: 'Content-Type': 'application/json' , body: JSON.stringify(data) ); if (response.ok) alert('QR content added successfully!'); location.reload(); // Auto-refresh every 10 seconds loadAnalytics(); setInterval(loadAnalytics, 10000); </script> </body> </html> # Dockerfile FROM python:3.9-slim WORKDIR /app
class QRScan(db.Model): id = db.Column(db.Integer, primary_key=True) qr_content_id = db.Column(db.Integer, db.ForeignKey('qr_content.id')) scanned_at = db.Column(db.DateTime, default=datetime.utcnow) ip_address = db.Column(db.String(45)) user_agent = db.Column(db.String(500)) open source digital signage
scan = QRScan( qr_content_id=qr_id, ip_address=request.remote_addr, user_agent=request.headers.get('User-Agent') ) db.session.add(scan) db.session.commit()
with app.app_context(): db.create_all() def generate_qr_base64(url): qr = qrcode.QRCode(version=1, box_size=10, border=4) qr.add_data(url) qr.make(fit=True) img = qr.make_image(fill_color="black", back_color="white") buffered = BytesIO() img.save(buffered, format="PNG") return base64.b64encode(buffered.getvalue()).decode() Get current active QR for display @app.route('/api/current-qr', methods=['GET']) def get_current_qr(): active_qrs = QRContent.query.filter_by(is_active=True).all() This complete feature gives you a production-ready QR
COPY . .
# requirements.txt Flask==2.3.0 flask-cors==4.0.0 flask-sqlalchemy==3.0.5 qrcode==7.4.2 Pillow==10.0.0 For Xibo: Use the HTML package with iframe embedding # requirements
EXPOSE 5000
# Rotate based on time to simulate display rotation # In production, track which one is currently showing current_time = datetime.utcnow() seconds_in_day = (current_time - current_time.replace(hour=0, minute=0, second=0)).seconds index = (seconds_in_day // 30) % len(active_qrs) # Rotate every 30 seconds current_qr = active_qrs[index]
return jsonify( 'total_scans': total_scans, 'scans_last_hour': scans_last_hour, 'qr_performance': ['name': name, 'scans': count for name, count in qr_performance] ) @app.route('/api/qr-content', methods=['POST']) def add_qr_content(): data = request.json qr = QRContent( name=data['name'], url=data['url'], description=data.get('description'), display_duration=data.get('display_duration', 30) ) db.session.add(qr) db.session.commit() return jsonify('id': qr.id, 'message': 'QR content added')
qr_base64 = generate_qr_base64(current_qr.url)