From 1615bda1ef79a85ecacbdcc6b048657ab89aafa2 Mon Sep 17 00:00:00 2001 From: Pyhtagodzilla <1670671958@qq.com> Date: Mon, 18 Aug 2025 02:28:52 +0800 Subject: [PATCH] first commit --- .gitignore | 3 ++ python/config.py | 61 +++++++++++++++++++++++++++++ python/config.toml | 5 +++ python/logger.py | 44 +++++++++++++++++++++ python/main.py | 17 ++++++++ python/mqtt_client.py | 87 +++++++++++++++++++++++++++++++++++++++++ python/requirements.txt | 1 + 7 files changed, 218 insertions(+) create mode 100644 .gitignore create mode 100644 python/config.py create mode 100644 python/config.toml create mode 100644 python/logger.py create mode 100644 python/main.py create mode 100644 python/mqtt_client.py create mode 100644 python/requirements.txt diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..c1ddd3d --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +.idea/ +python/__pycache__/ +python/logs/ diff --git a/python/config.py b/python/config.py new file mode 100644 index 0000000..8b3a2f4 --- /dev/null +++ b/python/config.py @@ -0,0 +1,61 @@ +from pathlib import Path + +import toml + +from logger import get_log + +_log = get_log() + + +class Config: + config_path = Path("config.toml") + + def __init__(self): + if not Config.config_path.exists(): + self.create_config_file() + + config_content = self.load_config() + + broker_settings = config_content.get("broker", {}) + self.broker_address = broker_settings.get("address", "pythagodzilla.pw") + self.broker_port = broker_settings.get("port", 1883) + self.broker_username = broker_settings.get("username", "") + self.broker_password = broker_settings.get("password", "") + + def create_config_file(self): + """ + + """ + default_config = { + "broker": { + "address": "pythagodzilla.pw", + "port": 1883, + "username": "", + "password": "" + } + } + + try: + with open(self.config_path, "w", encoding="utf-8") as config_file: + toml.dump(default_config, config_file) + _log.info(f"已创建默认配置文件{Config.config_path}") + except Exception as e: + _log.exception(f"创建配置文件失败: {e}") + + def load_config(self): + """ + Load the configuration from the config file. + """ + try: + with open(self.config_path, "r") as config_file: + config = toml.load(config_file) + return config + except FileNotFoundError: + _log.exception(f"配置文件 {self.config_path} 不存在。") + return None + except toml.TomlDecodeError as e: + _log.exception(f"配置文件解析错误: {e}") + return None + except Exception as e: + _log.exception(f"加载配置文件失败: {e}") + return None diff --git a/python/config.toml b/python/config.toml new file mode 100644 index 0000000..8ac8ec3 --- /dev/null +++ b/python/config.toml @@ -0,0 +1,5 @@ +[broker] +address = "pythagodzilla.pw" +port = 1883 +username = "" +password = "" diff --git a/python/logger.py b/python/logger.py new file mode 100644 index 0000000..98bf58a --- /dev/null +++ b/python/logger.py @@ -0,0 +1,44 @@ +import logging +from datetime import datetime +from logging.handlers import TimedRotatingFileHandler +from pathlib import Path + + +def get_log(): + """ + + """ + logger = logging.getLogger("WildAssistant") + logger.setLevel(logging.DEBUG) + + # file log handler + file_handler = TimedRotatingFileHandler( + # filename=f"{datetime.now().strftime(" % Y - % m - %d")}.log" + filename=Path("logs") / f"{datetime.now().strftime("%Y-%m-%d")}.log", + when="midnight", + backupCount=90, + encoding="utf-8", + ) + file_handler.setLevel(logging.INFO) + + # console log handler + console_handler = logging.StreamHandler() + console_handler.setLevel(logging.INFO) + + file_formatter = logging.Formatter( + fmt="%(asctime)s - %(name)s - %(filename)10s - %(lineno)4d - %(funcName)10s - %(levelname)8s - %(message)s", + datefmt="%Y-%m-%d %H:%M:%S" + ) + + console_formatter = logging.Formatter( + fmt="%(asctime)s - %(levelname)8s - %(message)s", + datefmt="%Y-%m-%d %H:%M:%S" + ) + + file_handler.setFormatter(file_formatter) + console_handler.setFormatter(console_formatter) + + logger.addHandler(file_handler) + logger.addHandler(console_handler) + + return logger diff --git a/python/main.py b/python/main.py new file mode 100644 index 0000000..07b630b --- /dev/null +++ b/python/main.py @@ -0,0 +1,17 @@ +from fastapi import FastAPI +from contextlib import asynccontextmanager +from mqtt_client import client, mqtt_connect, mqtt_disconnect +from logger import get_log + +_log = get_log() + +@asynccontextmanager +async def lifspan(app: FastAPI): + """ + Application startup and shutdown events. + """ + _log.info("Starting MQTT client...") + await mqtt_connect() + yield + _log.info("Stopping MQTT client...") + await mqtt_disconnect() \ No newline at end of file diff --git a/python/mqtt_client.py b/python/mqtt_client.py new file mode 100644 index 0000000..7916c07 --- /dev/null +++ b/python/mqtt_client.py @@ -0,0 +1,87 @@ +import json + +import paho.mqtt.client as mqtt + +from config import Config +from logger import get_log + +# 创建一个 MQTT 客户端实例,并指定使用 V2 回调 API +# 这将消除 DeprecationWarning 警告 +client = mqtt.Client(mqtt.CallbackAPIVersion.VERSION2) + +_log = get_log() + +topics = [ + "test/topic", + "test/topic2", +] + +user_data = { + "topics": json.dumps(topics).encode("utf-8"), +} + +client.user_data_set(user_data) + + +# 定义连接成功时的回调函数 +def on_connect(client, userdata, flags, rc, properties): + """ + 当客户端成功连接到 MQTT 代理时,这个函数会被调用。 + rc (return code) 的值为 0 表示成功。 + """ + if rc == 0: + _log.info("成功连接到 MQTT 服务器!") + # 订阅一个主题。 + # 当连接成功后,客户端会自动重新订阅之前订阅的主题。 + topic_list = json.loads(userdata["topics"].decode("utf-8")) + + for topic in topic_list: + _log.info(f"订阅主题:{topic}") + client.subscribe(topic) + else: + _log.error(f"连接失败,返回码:{rc}") + + +# 定义接收到消息时的回调函数 +def on_message(client, userdata, msg): + """ + 当客户端接收到来自代理的消息时,这个函数会被调用。 + 消息内容在 msg.payload 中,需要解码成字符串。 + """ + _log.info(f"收到消息来自主题:{msg.topic}") + _log.info(f"消息内容:{msg.payload.decode('utf-8')}") + + +# 绑定回调函数 +client.on_connect = on_connect +client.on_message = on_message + + +# 连接 MQTT +def mqtt_connect(): + """ + + """ + _config = Config() + + # 设置连接参数 + broker_address = _config.broker_address + port = _config.broker_port # MQTT 默认端口 + _log.info(f"正在连接{broker_address}:{port}") + + try: + client.connect(broker_address, port, 60) + client.loop_start() + except Exception as e: + _log.exception(e) + + +# 断开 MQTT 连接的函数 +def mqtt_disconnect(): + """ + 停止 MQTT 客户端循环并断开连接 + """ + _log.info("正在断开 MQTT 连接...") + client.loop_stop() + client.disconnect() + _log.info("MQTT 连接已断开。") diff --git a/python/requirements.txt b/python/requirements.txt new file mode 100644 index 0000000..02695d1 --- /dev/null +++ b/python/requirements.txt @@ -0,0 +1 @@ +paho-mqtt~=2.1.0 \ No newline at end of file