import sys import os import json import math from PySide6.QtGui import QGuiApplication from PySide6.QtQml import QQmlApplicationEngine from PySide6.QtCore import QObject, Property, Signal, Slot, QTimer # Try importing w1thermsensor, handle failure for dev environments try: from w1thermsensor import W1ThermSensor, Sensor HAS_W1 = True except ImportError: HAS_W1 = False print("Warning: w1thermsensor not found. Running in simulation mode.") class Backend(QObject): leftTempChanged = Signal() rightTempChanged = Signal() def __init__(self, demo_mode=False): super().__init__() self.demo_mode = demo_mode self._left_temp = 0.0 self._right_temp = 0.0 self.left_sensor = None self.right_sensor = None # Load Config self.config = self.load_config() # Initialize Sensors self.init_sensors() # Timer for polling self.timer = QTimer() self.timer.timeout.connect(self.update_temps) self.timer.start(2000) # Poll every 2 seconds def load_config(self): # Determine path based on run mode (Script vs Frozen/Compiled) if getattr(sys, 'frozen', False): # If run as a compiled exe, look in the same directory as the executable application_path = os.path.dirname(sys.executable) else: # If run as a script, look in the directory of the script application_path = os.path.dirname(os.path.abspath(__file__)) config_path = os.path.join(application_path, "config.json") if os.path.exists(config_path): try: with open(config_path, 'r') as f: return json.load(f) except Exception as e: print(f"Error loading config: {e}") return {} def init_sensors(self): if not HAS_W1: print("W1ThermSensor not installed.") return print(f"Loading sensors using config: {self.config}") try: available_sensors = W1ThermSensor.get_available_sensors() print(f"Available W1 sensors: {[s.id for s in available_sensors]}") # Map Left Sensor left_id = self.config.get("left_sensor_id") if left_id: try: # Check if ID exists in available to be sure found = next((s for s in available_sensors if s.id == left_id), None) if found: self.left_sensor = found print(f"✅ Successfully bound Left Sensor to {left_id}") else: print(f"❌ Configured Left Sensor {left_id} not found in available list!") # Try direct init anyway just in case self.left_sensor = W1ThermSensor(sensor_id=left_id) except Exception as e: print(f"❌ Failed to bind Left Sensor {left_id}: {e}") # Auto-assign fallback if not self.left_sensor and len(available_sensors) > 0: self.left_sensor = available_sensors[0] print(f"⚠️ Auto-assigned Left Sensor to {self.left_sensor.id}") # Map Right Sensor right_id = self.config.get("right_sensor_id") if right_id: try: self.right_sensor = W1ThermSensor(sensor_id=right_id) print(f"Bound Right Sensor to {right_id}") except Exception as e: print(f"Failed to bind Right Sensor {right_id}: {e}") except Exception as e: print(f"Error initializing sensors: {e}") def update_temps(self): # Simulation counter if not hasattr(self, '_sim_step'): self._sim_step = 0 self._sim_step += 0.1 # Left if self.left_sensor: try: t = self.left_sensor.get_temperature() print(f"Left Sensor ({self.left_sensor.id}) Read: {t}°C") self._set_left_temp(t) except Exception as e: print(f"Error reading left sensor: {e}") elif self.demo_mode: # Simulation print("Left Sensor Missing - Simulating...") sim_val = 5 + 10 * math.sin(self._sim_step) self._set_left_temp(sim_val) # Right if self.right_sensor: try: t = self.right_sensor.get_temperature() self._set_right_temp(t) except Exception as e: print(f"Error reading right sensor: {e}") elif self.demo_mode: # Simulation sim_val = 10 + 10 * math.cos(self._sim_step) self._set_right_temp(sim_val) @Property(float, notify=leftTempChanged) def leftTemp(self): return self._left_temp def _set_left_temp(self, val): if abs(self._left_temp - val) > 0.1: self._left_temp = val self.leftTempChanged.emit() @Property(float, notify=rightTempChanged) def rightTemp(self): return self._right_temp def _set_right_temp(self, val): if abs(self._right_temp - val) > 0.1: self._right_temp = val self.rightTempChanged.emit() def main(): # Redirect output to log.txt sys.stdout = open("log.txt", "w", buffering=1) sys.stderr = sys.stdout print("--- Volvo Display Log ---") # Check for demo mode demo_mode = "--demo" in sys.argv if demo_mode: print("Starting in DEMO MODE (Simulation Enabled)") app = QGuiApplication(sys.argv) engine = QQmlApplicationEngine() backend = Backend(demo_mode=demo_mode) engine.rootContext().setContextProperty("backend", backend) # Get absolute path to main.qml current_dir = os.path.dirname(os.path.abspath(__file__)) qml_file = os.path.join(current_dir, "main.qml") engine.load(qml_file) if not engine.rootObjects(): sys.exit(-1) sys.exit(app.exec()) if __name__ == "__main__": main()