diff --git a/main.py b/main.py index 77ad5d2..1868a4b 100644 --- a/main.py +++ b/main.py @@ -1,11 +1,8 @@ #!/usr/bin/env python3 """ - Gadgetbridge MQTT Step Counter Integration - Extracts sensor data from Gadgetbridge SQLite database and publishes to Home Assistant via MQTT - """ import os @@ -23,12 +20,44 @@ class GadgetbridgeMQTTPublisher: def __init__(self): self.setup_logging() self.db_path = os.getenv("GADGETBRIDGE_DB_PATH", "/data/Gadgetbridge.db") - self.device_name = self.get_device_alias() self.load_config() self.mqtt_client = None self.publish_interval = int(os.getenv("PUBLISH_INTERVAL_SECONDS", "300")) self.max_retries = int(os.getenv("MAX_RETRIES", "5")) self.retry_delay = int(os.getenv("RETRY_DELAY_SECONDS", "30")) + + # Initialize device_name with fallback - don't fail on DB issues during init + try: + self.device_name = self.get_device_alias() + except Exception as e: + self.logger.warning(f"Could not get device alias during init: {e}") + self.device_name = "fitness_tracker" + + # Initialize sensors after device_name is set + self.initialize_sensors() + + def setup_logging(self): + """Setup logging configuration (console only)""" + logging.basicConfig( + level=logging.INFO, + format="%(asctime)s - %(levelname)s - %(message)s", + handlers=[ + logging.StreamHandler(), + ], + ) + self.logger = logging.getLogger(__name__) + + def load_config(self): + """Load MQTT configuration from environment variables""" + self.mqtt_config = { + "broker": os.getenv("MQTT_BROKER", "localhost"), + "port": int(os.getenv("MQTT_PORT", "1883")), + "username": os.getenv("MQTT_USERNAME", ""), + "password": os.getenv("MQTT_PASSWORD", ""), + } + + def initialize_sensors(self): + """Initialize sensor definitions after device_name is available""" self.sensors = [ { "name": "Daily Steps", @@ -139,26 +168,6 @@ class GadgetbridgeMQTTPublisher: }, ] - def setup_logging(self): - """Setup logging configuration (console only)""" - logging.basicConfig( - level=logging.INFO, - format="%(asctime)s - %(levelname)s - %(message)s", - handlers=[ - logging.StreamHandler(), - ], - ) - self.logger = logging.getLogger(__name__) - - def load_config(self): - """Load MQTT configuration from environment variables""" - self.mqtt_config = { - "broker": os.getenv("MQTT_BROKER", "localhost"), - "port": int(os.getenv("MQTT_PORT", "1883")), - "username": os.getenv("MQTT_USERNAME", ""), - "password": os.getenv("MQTT_PASSWORD", ""), - } - async def publish_home_assistant_discovery( self, entity_type: str, entity_id: str, config: Dict ): @@ -340,55 +349,69 @@ class GadgetbridgeMQTTPublisher: self.logger.info(f"Published sensor data: {data}") -async def run_main_loop(self): - """Main execution loop with error recovery - simplified version""" - while True: - try: - async with aiomqtt.Client( - hostname=self.mqtt_config["broker"], - port=self.mqtt_config["port"], - username=self.mqtt_config["username"] or None, - password=self.mqtt_config["password"] or None, - ) as client: - self.mqtt_client = client - self.logger.info("MQTT connection successful") - - await self.setup_home_assistant_entities() - - # Publish immediately on startup - sensor_data = self.get_sensor_data() - await self.publish_sensor_data(sensor_data) - - # Main publishing loop - while True: - await asyncio.sleep(self.publish_interval) + async def run_main_loop(self): + """Main execution loop with error recovery""" + while True: + try: + self.logger.info("Attempting MQTT connection...") + async with aiomqtt.Client( + hostname=self.mqtt_config["broker"], + port=self.mqtt_config["port"], + username=self.mqtt_config["username"] or None, + password=self.mqtt_config["password"] or None, + ) as client: + self.mqtt_client = client + self.logger.info("MQTT connection successful") + + await self.setup_home_assistant_entities() + + # Publish immediately on startup sensor_data = self.get_sensor_data() await self.publish_sensor_data(sensor_data) + self.logger.info(f"Next publish in {self.publish_interval} seconds...") + + # Main publishing loop + while True: + await asyncio.sleep(self.publish_interval) + sensor_data = self.get_sensor_data() + await self.publish_sensor_data(sensor_data) + self.logger.info(f"Next publish in {self.publish_interval} seconds...") - except Exception as e: - self.logger.error(f"Error in main loop: {e}") - self.logger.info(f"Restarting main loop in {self.retry_delay} seconds...") - await asyncio.sleep(self.retry_delay) + except Exception as e: + self.logger.error(f"Error in main loop: {e}") + self.logger.info(f"Restarting main loop in {self.retry_delay} seconds...") + await asyncio.sleep(self.retry_delay) async def run(self): """Main execution method (async) - now with proper error recovery""" self.logger.info("Starting Gadgetbridge MQTT Publisher") - # Initial database check + # Initial database check with wait loop if not os.path.exists(self.db_path): self.logger.error(f"Database file not found: {self.db_path}") - self.logger.error("Waiting for database to become available...") + self.logger.info("Waiting for database to become available...") while not os.path.exists(self.db_path): await asyncio.sleep(30) + self.logger.info("Still waiting for database...") self.logger.info("Database file found, continuing...") + # Try to get proper device name now that DB might be available + try: + new_device_name = self.get_device_alias() + if new_device_name != self.device_name: + self.logger.info(f"Updating device name from {self.device_name} to {new_device_name}") + self.device_name = new_device_name + self.initialize_sensors() # Reinitialize with correct device name + except Exception as e: + self.logger.warning(f"Could not update device alias: {e}, using fallback") + # Run main loop with recovery await self.run_main_loop() def get_device_alias(self) -> str: """Fetch ALIAS from DEVICE table for device_name where NAME contains 'band' or 'watch' (case-insensitive)""" if not os.path.exists(self.db_path): - self.logger.warning(f"Database file not found during init: {self.db_path}") + self.logger.warning(f"Database file not found: {self.db_path}") return "fitness_tracker" try: @@ -418,4 +441,4 @@ async def run_main_loop(self): if __name__ == "__main__": publisher = GadgetbridgeMQTTPublisher() - asyncio.run(publisher.run()) \ No newline at end of file + asyncio.run(publisher.run())