import eventlet eventlet.monkey_patch() from flask import Flask, render_template, jsonify, request from flask_socketio import SocketIO from influxdb import InfluxDBClient import threading import time from datetime import datetime import config import os app = Flask(__name__) app.config['SECRET_KEY'] = config.FLASK_CONFIG['secret_key'] socketio = SocketIO(app, cors_allowed_origins="*", async_mode='eventlet', ping_timeout=60, ping_interval=25) def get_influx_client(): client = InfluxDBClient( host=config.INFLUXDB_CONFIG['host'], port=config.INFLUXDB_CONFIG['port'], database=config.INFLUXDB_CONFIG['database'] ) if config.INFLUXDB_CONFIG['username']: client.switch_user(config.INFLUXDB_CONFIG['username'], config.INFLUXDB_CONFIG['password']) return client def get_current_voltage(phase_id): client = get_influx_client() entity_id = config.PHASES[phase_id]['entity_id'] query = f'SELECT "value" FROM "{config.MEASUREMENT}" WHERE "entity_id" = \'{entity_id}\' ORDER BY time DESC LIMIT 1' try: result = client.query(query) points = list(result.get_points()) if points: return {'voltage': round(float(points[0]['value']), 2), 'timestamp': points[0]['time']} except Exception as e: print(f"Influx Error Phase {phase_id}: {e}") finally: client.close() return {'voltage': 0, 'timestamp': None} @app.route('/') def index(): return render_template('index.html', phases=config.PHASES, time_ranges=config.TIME_RANGES, default_range=config.DEFAULT_TIME_RANGE, footer=config.FOOTER) @app.route('/api/timeseries/') def api_timeseries(phase_id): if phase_id not in config.PHASES: return jsonify({'error': 'Invalid phase'}), 400 client = get_influx_client() t_range = request.args.get('range', config.DEFAULT_TIME_RANGE) cfg = config.TIME_RANGES.get(t_range, config.TIME_RANGES['24h']) query = config.PHASES[phase_id]['query'].replace('$timeFilter', cfg['filter']).replace('$__interval', cfg['interval']) try: result = client.query(query) data = [{'time': p['time'], 'voltage': round(p['mean'], 2)} for p in result.get_points() if p.get('mean') is not None] return jsonify(data) except Exception as e: print(f"History Error: {e}") return jsonify([]) finally: client.close() def background_voltage_update(): print("Background worker started...") while True: try: voltages = {'timestamp': None} for pid in config.PHASES.keys(): data = get_current_voltage(pid) voltages[f'phase{pid}'] = data['voltage'] if data['timestamp']: voltages['timestamp'] = data['timestamp'] socketio.emit('voltage_update', voltages) except Exception as e: print(f"Worker Loop Error: {e}") eventlet.sleep(config.CHART_CONFIG['update_interval'] / 1000) if __name__ == '__main__': eventlet.spawn(background_voltage_update) socketio.run(app, host='0.0.0.0', port=config.FLASK_CONFIG['port'])