from flask import Flask, render_template, redirect, url_for, request, flash
import paho.mqtt.client as mqtt
import json
from threading import Thread
from flask_sqlalchemy import SQLAlchemy
from flask_login import LoginManager, UserMixin, login_user, login_required, logout_user, current_user
from werkzeug.security import generate_password_hash, check_password_hash
from flask_socketio import SocketIO
from datetime import datetime
from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes
from cryptography.hazmat.primitives import padding
from cryptography.hazmat.backends import default_backend
import base64
from flask import request, jsonify
from vosk import Model, KaldiRecognizer
import wave
import os
from paddleocr import PaddleOCR
from paddlehub.module.module import Module
import cv2
# 初始化 Flask 和扩展
app = Flask(__name__)
socketio = SocketIO(app) # 初始化 Flask-SocketIO
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///iot_130.db'
app.config['SECRET_KEY'] = 'your_secret_key'
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
db = SQLAlchemy(app)
login_manager = LoginManager(app)
login_manager.login_view = 'login'
# AES 配置
SECRET_KEY = b"your_secret_key".ljust(32, b'\0') # AES-256密钥(32字节)
IV = b"16_byte_iv_12345" # 16字节的初始化向量
# AES解密函数
def decrypt_data(encrypted_data, key):
backend = default_backend()
cipher = Cipher(algorithms.AES(key), modes.CBC(IV), backend=backend)
decryptor = cipher.decryptor()
unpadder = padding.PKCS7(128).unpadder()
decrypted = decryptor.update(base64.b64decode(encrypted_data)) + decryptor.finalize()
unpadded_data = unpadder.update(decrypted) + unpadder.finalize()
return unpadded_data.decode()
# AES加密函数
def encrypt_data(data, key):
backend = default_backend()
cipher = Cipher(algorithms.AES(key), modes.CBC(IV), backend=backend)
encryptor = cipher.encryptor()
padder = padding.PKCS7(128).padder()
padded_data = padder.update(data) + padder.finalize()
encrypted = encryptor.update(padded_data) + encryptor.finalize()
return base64.b64encode(encrypted).decode()
# User 表
class User(UserMixin, db.Model):
__tablename__ = 'User'
id = db.Column(db.Integer, primary_key=True, autoincrement=True)
username = db.Column(db.String(150), unique=True, nullable=False)
password = db.Column(db.String(150), nullable=False)
role = db.Column(db.String(50), default='user')
# Device 表
class Device(db.Model):
__tablename__ = 'Device'
id = db.Column(db.Integer, primary_key=True, autoincrement=True)
name = db.Column(db.String(150), nullable=False)
type = db.Column(db.String(150), nullable=False)
status = db.Column(db.String(50), default='offline')
last_seen = db.Column(db.DateTime, default=None)
# SensorData 表
class SensorData(db.Model):
__tablename__ = 'SensorData'
id = db.Column(db.Integer, primary_key=True, autoincrement=True)
device_id = db.Column(db.Integer, db.ForeignKey('Device.id'), nullable=False)
value = db.Column(db.Float, nullable=False)
timestamp = db.Column(db.DateTime, default=datetime.utcnow)
# Command 表
class Command(db.Model):
__tablename__ = 'Command'
id = db.Column(db.Integer, primary_key=True, autoincrement=True)
device_id = db.Column(db.Integer, db.ForeignKey('Device.id'), nullable=False)
command = db.Column(db.String(150), nullable=False)
status = db.Column(db.String(50), default='pending')
timestamp = db.Column(db.DateTime, default=datetime.utcnow)
# 初始化数据库
with app.app_context():
db.create_all()
@login_manager.user_loader
def load_user(user_id):
return User.query.get(int(user_id))
@app.route('/register', methods=['GET', 'POST'])
def register():
if request.method == 'POST':
username = request.form['username']
password = request.form['password']
hashed_password = generate_password_hash(password)
# 检查用户名是否已存在
if User.query.filter_by(username=username).first():
flash('用户名已存在!')
return redirect(url_for('register'))
# 创建新用户
new_user = User(username=username, password=hashed_password)
db.session.add(new_user)
db.session.commit()
flash('注册成功!请登录。')
return redirect(url_for('login'))
return render_template('register.html')
@app.route('/login', methods=['GET', 'POST'])
def login():
if request.method == 'POST':
username = request.form['username']
password = request.form['password']
user = User.query.filter_by(username=username).first()
if user and check_password_hash(user.password, password):
login_user(user)
return redirect(url_for('index'))
flash('用户名或密码错误!')
return render_template('login.html')
@app.route('/logout')
@login_required
def logout():
logout_user()
return redirect(url_for('login'))
# 上传页面
@app.route('/upload', methods=['GET', 'POST'])
@login_required
def upload():
if request.method == 'POST':
# 检查是否有文件上传
if 'file' not in request.files:
flash('没有选择文件!')
return redirect(request.url)
file = request.files['file']
if file.filename == '':
flash('没有选择文件!')
return redirect(request.url)
# 保存文件到 wav 目录
if file and file.filename.endswith('.wav'):
filepath = os.path.join('wav', file.filename)
file.save(filepath)
# 使用 Vosk 模型进行语音识别
text = transcribe_audio(filepath)
# 返回识别结果
return render_template('upload.html', text=text)
return render_template('upload.html', text=None)
@app.route('/image_upload', methods=['GET', 'POST'])
@login_required
def image_upload():
if request.method == 'POST':
if 'image' not in request.files:
flash('没有选择图像文件!')
return redirect(request.url)
image_file = request.files['image']
if image_file.filename == '':
flash('没有选择图像文件!')
return redirect(request.url)
if image_file and image_file.filename.lower().endswith(('.png', '.jpg', '.jpeg')):
image_path = os.path.join('static/uploads/images', image_file.filename)
image_file.save(image_path)
# 使用 PP-OCRv5 模型进行文字识别和图像检测
recognized_text = recognize_text(image_path)
return render_template('image_upload.html', text=recognized_text)
return render_template('image_upload.html', text=None)
@app.route('/video_upload', methods=['GET', 'POST'])
@login_required
def video_upload():
if request.method == 'POST':
if 'video' not in request.files:
flash('没有选择视频文件!')
return redirect(request.url)
video_file = request.files['video']
if video_file.filename == '':
flash('没有选择视频文件!')
return redirect(request.url)
if video_file and video_file.filename.lower().endswith(('.mp4', '.avi', '.mov')):
video_path = os.path.join('static/uploads/videos', video_file.filename)
video_file.save(video_path)
# 使用 PaddleHub 模型进行宠物分类
classification_result = classify_pets_in_video(video_path)
return render_template('video_upload.html', result=classification_result)
return render_template('video_upload.html', result=None)
def classify_pets_in_video(video_path):
"""使用 PaddleHub 模型对视频中的宠物进行分类"""
try:
# 加载 PaddleHub 的宠物分类模型
module = Module(name="resnet50_vd_animals")
except Exception as e:
print(f"模型加载失败: {e}")
return
# 打开视频文件
cap = cv2.VideoCapture(video_path)
if not cap.isOpened():
print(f"无法打开视频文件: {video_path}")
return
frame_count = 0
results = []
while cap.isOpened():
ret, frame = cap.read()
if not ret:
break
# 每隔一定帧数进行分类
if frame_count % 30 == 0: # 每30帧处理一次
print(f"正在处理第 {frame_count} 帧...")
try:
# 转换帧为 RGB 格式(PaddleHub 模型需要 RGB 格式)
frame_rgb = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
# 使用 PaddleHub 模型进行分类
result = module.classification(images=[frame_rgb])
results.append(result)
except Exception as e:
print(f"处理第 {frame_count} 帧时出错: {e}")
frame_count += 1
cap.release()
print("视频处理完成!")
return results
def recognize_text(image_path):
"""使用 PP-OCRv5 模型识别图像中的文字并检测图像"""
ocr = PaddleOCR(
text_detection_model_name="PP-OCRv5_mobile_det",
text_recognition_model_name="PP-OCRv5_mobile_rec",
use_doc_orientation_classify=False,
use_doc_unwarping=False,
use_textline_orientation=False,
)
result = ocr.predict(image_path)
print("result:", result) # 打印结果以调试
return result[0]['rec_texts']
def transcribe_audio(filepath):
"""使用 Vosk 模型将音频转换为文本"""
model_path = "models/vosk-model-small-cn-0.22"
if not os.path.exists(model_path):
raise FileNotFoundError("Vosk 模型未找到,请检查路径!")
model = Model(model_path)
wf = wave.open(filepath, "rb")
rec = KaldiRecognizer(model, wf.getframerate())
result_text = ""
while True:
data = wf.readframes(4000)
if len(data) == 0:
break
if rec.AcceptWaveform(data):
result = rec.Result()
result_text += json.loads(result).get("text", "")
wf.close()
return result_text
# MQTT配置
MQTT_BROKER = "localhost" # 或EMQX服务器地址
MQTT_PORT = 1883
# 存储最新温度和风扇状态
mytemp = None
myfan = "off"
def init_device(device_name, device_type):
"""初始化设备到数据库"""
with app.app_context():
device = Device.query.filter_by(name=device_name).first()
if not device:
device = Device(name=device_name, type=device_type, status="offline")
db.session.add(device)
db.session.commit()
def save_sensor_data(device_name, value):
"""保存传感器数据到 SensorData 表"""
with app.app_context():
device = Device.query.filter_by(name=device_name).first()
if device:
sensor_data = SensorData(device_id=device.id, value=value)
db.session.add(sensor_data)
db.session.commit()
def save_command(device_name, command):
"""保存控制指令到 Command 表"""
with app.app_context():
device = Device.query.filter_by(name=device_name).first()
if device:
cmd = Command(device_id=device.id, command=command)
db.session.add(cmd)
db.session.commit()
def on_connect(client, userdata, flags, rc):
print(f"MQTT连接结果: {rc}")
client.subscribe("topic/temp") # 订阅温度主题
def on_message(client, userdata, msg):
global mytemp, myfan
try:
if msg.topic == "topic/temp":
encrypted_payload = msg.payload.decode()
decrypted_payload = decrypt_data(encrypted_payload, SECRET_KEY)
payload = json.loads(decrypted_payload)
mytemp = payload["temp"]
print(f"解密温度数据: {mytemp}°C")
# 保存传感器数据到数据库
save_sensor_data("temp", mytemp)
# 根据温度控制风扇
if mytemp >= 30:
control_fan("on")
myfan = "on"
else:
control_fan("off")
myfan = "off"
# 实时推送温度数据到前端
socketio.emit('sensor_data', {'temp': mytemp, 'fan': myfan})
except Exception as e:
print(f"解密失败或处理异常: {e}")
def control_fan(command):
"""发送加密控制指令给风扇并保存到数据库"""
payload = json.dumps({"fan": command})
encrypted_payload = encrypt_data(payload.encode(), SECRET_KEY)
mqtt_client.publish("topic/fan", encrypted_payload)
print(f"发送加密控制指令: {encrypted_payload}")
# 保存控制指令到数据库
save_command("fan", command)
def run_mqtt_client():
global mqtt_client
mqtt_client = mqtt.Client()
mqtt_client.on_connect = on_connect
mqtt_client.on_message = on_message
mqtt_client.username_pw_set("admin", "admin") # 账号密码验证
mqtt_client.connect(MQTT_BROKER, MQTT_PORT, 60)
mqtt_client.loop_forever()
@app.route('/')
@login_required # 保护 chart.html
def index():
return render_template('chart.html') # 渲染前端页面
if __name__ == "__main__":
# 初始化设备
with app.app_context():
init_device("temp", "sensor")
init_device("fan", "actuator")
# 启动MQTT客户端线程
mqtt_thread = Thread(target=run_mqtt_client)
mqtt_thread.daemon = True
mqtt_thread.start()
# 启动Flask-SocketIO应用,启用TLS
socketio.run(app, host="0.0.0.0", port=9000, debug=False, ssl_context=("ca/server.crt", "ca/server.key"))怎么改使用一个页面上传按钮,可同时上传图像、音频、视频,并将识别结果显示在该页面。
最新发布