SCLP
This commit is contained in:
85
SCLP/utils/database.py
Normal file
85
SCLP/utils/database.py
Normal file
@@ -0,0 +1,85 @@
|
||||
# -*- coding:utf-8 -*-
|
||||
"""
|
||||
@Author : xuxingchen
|
||||
@Contact : xuxingchen@sinochem.com
|
||||
@Desc : 数据库处理基础方法
|
||||
"""
|
||||
import sqlite3
|
||||
|
||||
from config import DB_PATH
|
||||
from utils.logger import Logger, new_dc
|
||||
|
||||
|
||||
class SQLiteDatabaseEngine:
|
||||
def __init__(self, db_path: str = "demo.db") -> None:
|
||||
self.sqlite3 = sqlite3
|
||||
self.db_path = db_path
|
||||
self.connection = None
|
||||
self.cursor = None
|
||||
self.connect()
|
||||
|
||||
def connect(self):
|
||||
"""连接SQLite 数据库(如果数据库不存在则会自动创建)"""
|
||||
self.connection = self.sqlite3.connect(self.db_path)
|
||||
self.cursor = self.connection.cursor()
|
||||
Logger.init(new_dc(f"🔗 SQLite - {self.db_path} has connect successfully! 🔗", "[1;32m"))
|
||||
|
||||
def disconnect(self):
|
||||
try:
|
||||
self.cursor.close()
|
||||
self.connection.close()
|
||||
except Exception as e:
|
||||
if type(e).__name__ != "ProgrammingError":
|
||||
Logger.error(f"{type(e).__name__}, {e}")
|
||||
Logger.info(new_dc(f"🔌 Disconnect from SQLite - {self.db_path}! 🔌", "[1m"))
|
||||
|
||||
def exist(self, table_name):
|
||||
self.cursor.execute(
|
||||
f"SELECT name FROM sqlite_master WHERE type='table' AND name=?",
|
||||
(table_name,),
|
||||
)
|
||||
result = self.cursor.fetchone()
|
||||
if result:
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
|
||||
|
||||
class BaseTable:
|
||||
def __init__(self, connection=None, cursor=None):
|
||||
self.connection = connection
|
||||
self.cursor = cursor
|
||||
|
||||
def set(self, connection, cursor):
|
||||
self.connection = connection
|
||||
self.cursor = cursor
|
||||
|
||||
def execute(self, sql: str | list, params: tuple | list = ()):
|
||||
if isinstance(sql, list):
|
||||
for i, s in enumerate(sql):
|
||||
self.cursor.execute(s, params[i])
|
||||
else:
|
||||
self.cursor.execute(sql, params)
|
||||
self.connection.commit()
|
||||
|
||||
def executemany(self, sql: str, params: list[tuple]):
|
||||
self.cursor.executemany(sql, params)
|
||||
self.connection.commit()
|
||||
|
||||
def query(self, sql: str, params: tuple = ()):
|
||||
self.cursor.execute(sql, params)
|
||||
|
||||
|
||||
class TableHandlerSingleton:
|
||||
_instance = None
|
||||
|
||||
@classmethod
|
||||
def get_instance(cls):
|
||||
if cls._instance is None:
|
||||
db = SQLiteDatabaseEngine(db_path=DB_PATH)
|
||||
cls._instance = BaseTable(db.connection, db.cursor)
|
||||
return cls._instance
|
||||
|
||||
|
||||
def get_table_handler():
|
||||
return TableHandlerSingleton.get_instance()
|
||||
163
SCLP/utils/logger.py
Normal file
163
SCLP/utils/logger.py
Normal file
@@ -0,0 +1,163 @@
|
||||
# -*- coding:utf-8 -*-
|
||||
"""
|
||||
@Author : xuxingchen
|
||||
@Contact : xuxingchen@sinochem.com
|
||||
@Desc : 日志模块
|
||||
"""
|
||||
import sys
|
||||
import io
|
||||
import time
|
||||
|
||||
DEBUG = None
|
||||
LOGGER_PATH = None
|
||||
TOKEN_ERROR = "token无效"
|
||||
|
||||
|
||||
def log(text: str, log_path: str = None):
|
||||
"""打印日志"""
|
||||
log_path = log_path if log_path else LOGGER_PATH
|
||||
log_line = '[{}] {}'.format(time.strftime("%Y-%m-%d %H:%M:%S", time.localtime()), text)
|
||||
if log_path:
|
||||
log_file = open(log_path, 'a', encoding='utf8')
|
||||
log_file.write("{}\n".format(log_line))
|
||||
log_file.close()
|
||||
print(log_line)
|
||||
|
||||
|
||||
def log_plus(text: str, log_path: str = None, prefix_text: str = None, suffix_text: str = None):
|
||||
"""加强版打印日志,预置了不同文字颜色"""
|
||||
log_path = log_path if log_path else LOGGER_PATH
|
||||
if prefix_text:
|
||||
log_line_start = f"[{time.strftime('%Y-%m-%d %H:%M:%S', time.localtime())}] {prefix_text}"
|
||||
else:
|
||||
log_line_start = f"[{time.strftime('%Y-%m-%d %H:%M:%S', time.localtime())}]"
|
||||
if suffix_text:
|
||||
log_line = f"{log_line_start} {text} {suffix_text}"
|
||||
else:
|
||||
log_line = f"{log_line_start} {text}"
|
||||
if log_path:
|
||||
log_file = open(log_path, 'a', encoding='utf8')
|
||||
log_file.write("{}\n".format(log_line))
|
||||
log_file.close()
|
||||
print(log_line)
|
||||
|
||||
|
||||
def format_text(text_list: list, align_list: list = None) -> str:
|
||||
"""格式化一些文本信息, 可以根据 align_list 调整每列文本的距离"""
|
||||
if not align_list:
|
||||
align_list = [(30, '<'), (60, '<')]
|
||||
formatted_text = []
|
||||
for txt, (int_align, flag) in zip(text_list, align_list):
|
||||
formatted_text.append(f'{txt:{flag}{int_align}}')
|
||||
return ' '.join(formatted_text)
|
||||
|
||||
|
||||
def new_dc(msgs: str | float | int, fore_color: str = "", back_color: str = "") -> str:
|
||||
"""给文本上色
|
||||
|
||||
fore_color格式如下:
|
||||
[{显示方式};{前景色};{背景色}m
|
||||
|
||||
显示方式
|
||||
0(默认值)、1(高亮)、22(非粗体)、4(下划线)、24(非下划线)、 5(闪烁)、25(非闪烁)、7(反显)、27(非反显)
|
||||
前景色
|
||||
30(黑色)、31(红色)、32(绿色)、 33(黄色)、34(蓝色)、35(洋 红)、36(青色)、37(白色)
|
||||
背景色
|
||||
40(黑色)、41(红色)、42(绿色)、 43(黄色)、44(蓝色)、45(洋 红)、46(青色)、47(白色)
|
||||
如:
|
||||
高亮绿色红色背景 [1;32;41m
|
||||
默认-绿字体 [32m
|
||||
"""
|
||||
if fore_color == "":
|
||||
fore_color = '[32m' # 默认绿色
|
||||
return "\033{}{}{}\033[0m".format(back_color, fore_color, str(msgs))
|
||||
|
||||
|
||||
def console_mute(func):
|
||||
"""采用装饰器的方式提供函数的控制台标准输出临时置空,净化控制台环境"""
|
||||
|
||||
def wrapper(*args, **kwargs):
|
||||
original_stdout = sys.stdout
|
||||
try:
|
||||
sys.stdout = io.StringIO()
|
||||
return func(*args, **kwargs)
|
||||
finally:
|
||||
# 恢复原始的 sys.stdout 和 sys.stderr
|
||||
sys.stdout = original_stdout
|
||||
|
||||
return wrapper
|
||||
|
||||
|
||||
def log_speed_ms(start_time: float, suffix: str = "", prefix: str = "", number_color: str = "", decimal: int = 4):
|
||||
"""控制台打印消耗的毫秒时间"""
|
||||
log(f"{suffix}用时:{new_dc(str(round((time.time() - start_time) * 1000, decimal)), number_color)}ms{prefix}")
|
||||
|
||||
|
||||
def speed_ms(start_time: float, decimal: int = 4):
|
||||
"""消耗的毫秒时间"""
|
||||
return round((time.time() - start_time) * 1000, decimal)
|
||||
|
||||
|
||||
class Logger:
|
||||
"""对控制台日志输出的二次封装,对部分常用日志进行预置"""
|
||||
|
||||
@staticmethod
|
||||
def debug(text: str, log_path: str = None):
|
||||
"""预置的debug形式的log"""
|
||||
log_path = log_path if log_path else LOGGER_PATH
|
||||
if isinstance(DEBUG, bool) and DEBUG:
|
||||
log_plus(text, log_path, f"[{new_dc('INFO-DEBUG', '[34m')}]")
|
||||
|
||||
@staticmethod
|
||||
def info(text: str, log_path: str = None):
|
||||
"""预置的info形式的log"""
|
||||
log_path = log_path if log_path else LOGGER_PATH
|
||||
log_plus(text, log_path, f"[{new_dc('INFO', '[34m')}]")
|
||||
|
||||
@staticmethod
|
||||
def error(text: str, log_path: str = None):
|
||||
"""预置的error形式的log"""
|
||||
log_path = log_path if log_path else LOGGER_PATH
|
||||
log_plus(text, log_path, f"[{new_dc('ERROR', '[1;31m')}]")
|
||||
|
||||
@staticmethod
|
||||
def warn(text: str, log_path: str = None):
|
||||
"""预置的warn形式的log"""
|
||||
log_path = log_path if log_path else LOGGER_PATH
|
||||
log_plus(text, log_path, f"[{new_dc('WARN', '[1;33m')}]")
|
||||
|
||||
@staticmethod
|
||||
def init(text: str, log_path: str = None):
|
||||
"""预置的error形式的log"""
|
||||
log_path = log_path if log_path else LOGGER_PATH
|
||||
log_plus(text, log_path, f"[{new_dc('INIT', '[35m')}]")
|
||||
|
||||
@staticmethod
|
||||
def title(text: str, log_path: str = None):
|
||||
"""预置title形式的显目log"""
|
||||
log_path = log_path if log_path else LOGGER_PATH
|
||||
log_plus(new_dc(text, '[1m'), log_path, "🚀", "🚀")
|
||||
|
||||
@staticmethod
|
||||
def complete(text: str, log_path: str = None):
|
||||
"""预置complete形式的显目log"""
|
||||
log_path = log_path if log_path else LOGGER_PATH
|
||||
log_plus(new_dc(text, '[1;32m'), log_path, "✅", "✅")
|
||||
|
||||
@staticmethod
|
||||
def remove(text: str, log_path: str = None):
|
||||
"""预置remove形式的显目log"""
|
||||
log_path = log_path if log_path else LOGGER_PATH
|
||||
log_plus(new_dc(text, '[1m'), log_path, "🚮", "🚮")
|
||||
|
||||
@staticmethod
|
||||
def connect(text: str, log_path: str = None):
|
||||
"""预置connect形式的显目log"""
|
||||
log_path = log_path if log_path else LOGGER_PATH
|
||||
log_plus(new_dc(text, '[1;32m'), log_path, "🔗", "🔗")
|
||||
|
||||
@staticmethod
|
||||
def disconnect(text: str, log_path: str = None):
|
||||
"""预置disconnect形式的显目log"""
|
||||
log_path = log_path if log_path else LOGGER_PATH
|
||||
log_plus(new_dc(text, '[1m'), log_path, "🚫", "🚫")
|
||||
360
SCLP/utils/misc.py
Normal file
360
SCLP/utils/misc.py
Normal file
@@ -0,0 +1,360 @@
|
||||
# -*- coding:utf-8 -*-
|
||||
"""
|
||||
@Author : xuxingchen
|
||||
@Contact : xuxingchen@sinochem.com
|
||||
@Desc : 杂项
|
||||
"""
|
||||
import base64
|
||||
import hashlib
|
||||
import os
|
||||
import time
|
||||
import warnings
|
||||
from io import BytesIO
|
||||
import re
|
||||
import random
|
||||
import string
|
||||
import threading
|
||||
from datetime import datetime, timedelta
|
||||
import socket
|
||||
import psutil
|
||||
from typing import Optional
|
||||
|
||||
import numpy as np
|
||||
import pandas as pd
|
||||
import pytz
|
||||
import requests
|
||||
from PIL import Image
|
||||
import paho.mqtt.client as mqtt
|
||||
from openpyxl.styles import PatternFill, Font, Border, Side
|
||||
from openpyxl.utils.dataframe import dataframe_to_rows
|
||||
from openpyxl.workbook import Workbook
|
||||
from pydantic import BaseModel
|
||||
|
||||
from utils import logger
|
||||
|
||||
|
||||
def get_ip_address(interface_name: str) -> tuple[str, str]:
|
||||
interfaces = psutil.net_if_addrs()
|
||||
interface = interfaces.get(interface_name, None)
|
||||
ipv4_address, ipv6_address = "", ""
|
||||
if interface:
|
||||
for address in interface:
|
||||
if address.family == socket.AF_INET:
|
||||
ipv4_address = address.address
|
||||
elif address.family == socket.AF_INET6 and not address.address.startswith("fe80"):
|
||||
ipv6_address = address.address
|
||||
return ipv4_address, ipv6_address
|
||||
|
||||
def extract_fixed_length_number(number_str: str, fixed_length: int = 2) -> str:
|
||||
"""提取一串存在数值的字符串中的第一个数值,对其从右往左取值定值,不足补0"""
|
||||
pattern = re.compile(r'[0-9]+')
|
||||
number = pattern.search(number_str)
|
||||
if number is None:
|
||||
return "0" * fixed_length
|
||||
else:
|
||||
if len(number[0]) < fixed_length:
|
||||
return "0" * (fixed_length - len(number[0])) + number[0]
|
||||
else:
|
||||
return number[0][-fixed_length:]
|
||||
|
||||
|
||||
def snake2camel(key: str) -> str:
|
||||
"""snake命名风格转成camel命名风格"""
|
||||
parts = key.split('_')
|
||||
return parts[0] + ''.join(word.capitalize() for word in parts[1:])
|
||||
|
||||
|
||||
def snake2camel_list_dict(snake_list: list[dict]) -> list[dict]:
|
||||
"""将list[dict]中所有的snake格式的key转换成camel格式的key命名风格"""
|
||||
camel_list = []
|
||||
for snake_dict in snake_list:
|
||||
camel_dict = {}
|
||||
for key, value in snake_dict.items():
|
||||
camel_dict[snake2camel(key)] = value
|
||||
camel_list.append(camel_dict)
|
||||
return camel_list
|
||||
|
||||
|
||||
def clear_log_file(log_path: str, day: int = 7):
|
||||
"""每7天清除一次日志文件"""
|
||||
creation_time = os.path.getctime(log_path)
|
||||
days_since_creation = (time.time() - creation_time) / (60 * 60 * 24)
|
||||
if os.path.exists(log_path) and days_since_creation >= day:
|
||||
try:
|
||||
f0 = open(log_path, "r", encoding="utf8")
|
||||
f1 = open(f"{log_path}.old", "w", encoding="utf8")
|
||||
f1.write(f0.read())
|
||||
f1.close()
|
||||
f0.close()
|
||||
os.remove(log_path)
|
||||
print(f"日志文件 {logger.LOGGER_PATH} 完成重置")
|
||||
except Exception as e:
|
||||
print(f"日志文件 {logger.LOGGER_PATH} 重置失败: {e}")
|
||||
|
||||
|
||||
def generate_captcha_text(characters: int = 6) -> str:
|
||||
"""生成指定长度的随机文本"""
|
||||
letters_and_digits = string.ascii_letters + string.digits
|
||||
return ''.join(random.choice(letters_and_digits) for _ in range(characters))
|
||||
|
||||
|
||||
def encrypt_number(phone_number: str, key: bytes = b'7A') -> str:
|
||||
"""将电话号码加密为字符串"""
|
||||
phone_bytes = phone_number.encode('utf-8') # 将字符串转换为字节
|
||||
encrypted_bytes = bytes(
|
||||
[byte ^ key[i % len(key)] for i, byte in enumerate(phone_bytes)]
|
||||
)
|
||||
# 使用Base64编码加密后的字节
|
||||
encrypted_number = base64.b64encode(encrypted_bytes).decode('utf-8')
|
||||
return encrypted_number
|
||||
|
||||
|
||||
def decrypt_number(encrypted_number: str, key: bytes = b'7A') -> str:
|
||||
"""将字符串解密为电话号码"""
|
||||
# 使用Base64解码加密后的字符串
|
||||
encrypted_bytes = base64.b64decode(encrypted_number)
|
||||
decrypted_bytes = bytes(
|
||||
[byte ^ key[i % len(key)] for i, byte in enumerate(encrypted_bytes)]
|
||||
)
|
||||
decrypted_number = decrypted_bytes.decode('utf-8')
|
||||
return decrypted_number
|
||||
|
||||
|
||||
def now_tz_datetime(days: int = 0) -> str:
|
||||
future_time = datetime.now() + timedelta(days=days)
|
||||
return future_time.strftime("%Y-%m-%dT%H:%M:%S.") + f"{future_time.microsecond // 1000:03d}Z"
|
||||
|
||||
|
||||
def now_datetime_nanosecond(days: int = 0) -> str:
|
||||
future_time = datetime.now() + timedelta(days=days)
|
||||
return future_time.strftime("%Y-%m-%d %H:%M:%S.%f")
|
||||
|
||||
|
||||
def now_datetime_second(days: int = 0) -> str:
|
||||
future_time = datetime.now() + timedelta(days=days)
|
||||
return future_time.strftime("%Y-%m-%d %H:%M:%S")
|
||||
|
||||
|
||||
def millisecond_timestamp2tz(timestamp_13: str):
|
||||
timestamp = int(timestamp_13) / 1000
|
||||
dt_utc = datetime.fromtimestamp(timestamp, tz=pytz.UTC)
|
||||
# 转换为所需的时区,这里以北京时间(China Standard Time)为例
|
||||
china_tz = pytz.timezone('Asia/Shanghai')
|
||||
return dt_utc.astimezone(china_tz).strftime("%Y-%m-%dT%H:%M:%S.") + f"{dt_utc.microsecond // 1000:03d}Z"
|
||||
|
||||
|
||||
def is_image_url_valid(url: str) -> bool:
|
||||
try:
|
||||
# 发送请求获取URL内容
|
||||
response = requests.get(url)
|
||||
response.raise_for_status() # 如果状态码不是200,会抛出异常
|
||||
|
||||
# 将内容加载为图片
|
||||
image = Image.open(BytesIO(response.content))
|
||||
image.verify() # 验证图像文件是否可读
|
||||
|
||||
# 如果上面的代码没有抛出异常,说明图片存在且格式可读
|
||||
return True
|
||||
except (requests.RequestException, IOError):
|
||||
# 如果有任何异常,说明图片不可用或格式不可读
|
||||
return False
|
||||
|
||||
|
||||
def is_image_valid(path: str) -> bool:
|
||||
try:
|
||||
# 将内容加载为图片
|
||||
image = Image.open(open(path, 'rb'))
|
||||
image.verify() # 验证图像文件是否可读
|
||||
|
||||
# 如果上面的代码没有抛出异常,说明图片存在且格式可读
|
||||
return True
|
||||
except (requests.RequestException, IOError):
|
||||
# 如果有任何异常,说明图片不可用或格式不可读
|
||||
return False
|
||||
|
||||
|
||||
def get_file_md5(file_path):
|
||||
content = open(file_path, 'rb')
|
||||
md5hash = hashlib.md5(content.read())
|
||||
return md5hash.hexdigest()
|
||||
|
||||
|
||||
def sql_export_xls(query,
|
||||
db_connection,
|
||||
save_file_path,
|
||||
sheet_title,
|
||||
sheet_header: Optional[list] = None,
|
||||
header_background_color: str = "808080",
|
||||
header_font_color: str = "ffffff"):
|
||||
df = pd.read_sql_query(query, db_connection)
|
||||
wb = Workbook()
|
||||
ws = wb.active
|
||||
ws.title = sheet_title
|
||||
for i, r in enumerate(dataframe_to_rows(df, index=False, header=True)):
|
||||
if sheet_header is not None and i == 0:
|
||||
ws.append(sheet_header)
|
||||
continue
|
||||
ws.append(r)
|
||||
# 表头样式
|
||||
header_fill = PatternFill(start_color=header_background_color, fill_type="solid")
|
||||
header_font = Font(color=header_font_color, bold=True)
|
||||
for cell in ws[1]:
|
||||
cell.fill = header_fill
|
||||
cell.font = header_font
|
||||
# 边框样式 表头无边框-数据无顶实线框
|
||||
thin_border = Border(
|
||||
left=Side(style='thin'),
|
||||
right=Side(style='thin'),
|
||||
bottom=Side(style='thin')
|
||||
)
|
||||
for i, row in enumerate(ws.iter_rows()):
|
||||
if i == 0:
|
||||
continue
|
||||
for cell in row:
|
||||
cell.border = thin_border
|
||||
# 单元格宽度自适应调整
|
||||
for column in ws.columns:
|
||||
max_length = 0
|
||||
column = list(column)
|
||||
for cell in column:
|
||||
if cell.value is not None:
|
||||
cell_length = len(str(cell.value))
|
||||
if re.search(r'[\u4e00-\u9fff]', str(cell.value)):
|
||||
cell_length += len(re.findall(r'[\u4e00-\u9fff]', str(cell.value)))
|
||||
if cell_length > max_length:
|
||||
max_length = cell_length
|
||||
adjusted_width = (max_length + 2)
|
||||
ws.column_dimensions[column[0].column_letter].width = adjusted_width
|
||||
wb.save(save_file_path)
|
||||
|
||||
|
||||
def valid_xls(file: BytesIO, required_columns: Optional[list]) -> tuple[bool, str, Optional[list]]:
|
||||
"""如果校验通过返回list结构的数据,如果检验不通过返回None"""
|
||||
with warnings.catch_warnings():
|
||||
warnings.simplefilter("ignore")
|
||||
try:
|
||||
df = pd.read_excel(file)
|
||||
df = df.replace(np.nan, None)
|
||||
if all(col in df.columns for col in required_columns):
|
||||
return True, "", df[required_columns].values.tolist()
|
||||
else:
|
||||
missing_cols = [col for col in required_columns if col not in df.columns]
|
||||
return False, f"缺少必要的列: {', '.join(missing_cols)}", None
|
||||
except Exception as e:
|
||||
return False, f"文件解析失败 {type(e).__name__}, {e}", None
|
||||
|
||||
|
||||
class BasicCallback(BaseModel):
|
||||
status: bool
|
||||
message: Optional[str]
|
||||
|
||||
|
||||
class InvalidException(Exception):
|
||||
def __init__(self, message: str):
|
||||
self.message = message
|
||||
|
||||
|
||||
class UserData:
|
||||
def __init__(self):
|
||||
self.table_handle = None
|
||||
self.topic: Optional[str] = None
|
||||
self.topics: list = []
|
||||
self.table_handler = None
|
||||
self.message = None
|
||||
self.token = None
|
||||
self.status: dict = {}
|
||||
self.clients: dict = {}
|
||||
self.lock = threading.Lock() # 添加一个锁用于线程同步
|
||||
|
||||
def set_table_handle(self, value):
|
||||
with self.lock:
|
||||
self.table_handle = value
|
||||
|
||||
def set_topic(self, value: str):
|
||||
with self.lock:
|
||||
self.topic = value
|
||||
|
||||
def set_topics(self, value: list):
|
||||
with self.lock:
|
||||
self.topics = value
|
||||
|
||||
def set_table_handler(self, value):
|
||||
with self.lock:
|
||||
self.table_handler = value
|
||||
|
||||
def set_message(self, value):
|
||||
with self.lock:
|
||||
self.message = value
|
||||
|
||||
def set_token(self, value):
|
||||
with self.lock:
|
||||
self.token = value
|
||||
|
||||
def set_status(self, value: dict):
|
||||
with self.lock:
|
||||
self.status = value
|
||||
|
||||
def set_status_add(self, key, value):
|
||||
with self.lock:
|
||||
self.status[key] = value
|
||||
|
||||
def set_status_remove(self, key):
|
||||
with self.lock:
|
||||
if self.status and key in self.status.keys():
|
||||
self.status.pop(key)
|
||||
|
||||
def get_status(self, key):
|
||||
if self.status and key in self.status.keys():
|
||||
return self.status[key]
|
||||
|
||||
def set_clients(self, value: dict):
|
||||
with self.lock:
|
||||
self.clients = value
|
||||
|
||||
def set_client_add(self, key, value):
|
||||
with self.lock:
|
||||
self.clients[key] = value
|
||||
|
||||
|
||||
def create_mqtt_client(broker_host,
|
||||
broker_port,
|
||||
userdata: UserData,
|
||||
on_message=None,
|
||||
on_publish=None,
|
||||
on_connect=None,
|
||||
on_disconnect=None,
|
||||
client_id: str = "",
|
||||
username: str = "",
|
||||
password: str = ""):
|
||||
if client_id != "":
|
||||
client = mqtt.Client(client_id=client_id)
|
||||
else:
|
||||
client = mqtt.Client()
|
||||
client.user_data_set(userdata)
|
||||
if on_connect:
|
||||
client.on_connect = on_connect
|
||||
if on_disconnect:
|
||||
client.on_disconnect = on_disconnect
|
||||
if on_message:
|
||||
client.on_message = on_message
|
||||
if on_publish:
|
||||
client.on_publish = on_publish
|
||||
client.username_pw_set(username, password)
|
||||
client.connect(broker_host, broker_port)
|
||||
return client
|
||||
|
||||
|
||||
def on_connect(client, userdata, flags, rc):
|
||||
logger.Logger.init(logger.new_dc(f"🔗 Mqtt connection! {{rc: {rc}}} 🔗", '[1;32m'))
|
||||
if userdata.topics:
|
||||
_topics = [(topic, 0) for topic in userdata.topics]
|
||||
client.subscribe(_topics)
|
||||
logger.Logger.debug(f"subscribe topics: {userdata.topics}")
|
||||
|
||||
|
||||
def on_disconnect(client, userdata, rc):
|
||||
logger.Logger.info(logger.new_dc(f"🔌 Break mqtt connection! {{rc: {rc}}} 🔌", "[1m"))
|
||||
|
||||
|
||||
def on_publish(client, userdata, rc):
|
||||
logger.Logger.debug(f"{userdata.topic} <- {userdata.message}")
|
||||
Reference in New Issue
Block a user