230 lines
10 KiB
Python
230 lines
10 KiB
Python
from langchain_core.tools import tool
|
||
from miio import DeviceFactory
|
||
from miio.miot_device import MiotDevice
|
||
import json
|
||
from pydantic import BaseModel, Field
|
||
import time
|
||
import logging
|
||
import threading
|
||
|
||
# 配置日志
|
||
logger = logging.getLogger(__name__)
|
||
|
||
# 设备配置
|
||
LAMP_IP = "192.168.110.122"
|
||
LAMP_TOKEN = "4a90f98aaa1273ca34685d66d6e13958"
|
||
LAMP_MODEL = "yeelink.light.bslamp2"
|
||
|
||
try:
|
||
device = DeviceFactory.create(LAMP_IP, LAMP_TOKEN)
|
||
logger.info(f"使用 DeviceFactory 创建设备成功: {LAMP_MODEL}")
|
||
except Exception as e:
|
||
logger.warning(f"DeviceFactory 创建失败,使用 MiotDevice: {e}")
|
||
|
||
# 添加线程锁,确保同一时间只有一个操作
|
||
device_lock = threading.Lock()
|
||
|
||
|
||
@tool("get_lamp_status", description="获取床头灯当前状态,包括电源、亮度、色温、颜色等信息")
|
||
def get_lamp_status():
|
||
"""获取床头灯设备状态并以 JSON 格式返回"""
|
||
try:
|
||
with device_lock: # 使用锁确保串行执行
|
||
# 使用 status() 方法获取 DeviceStatus 对象
|
||
device_status = device.status()
|
||
|
||
# 构建状态字典
|
||
status = {
|
||
"power": device_status.power if hasattr(device_status, 'power') else None,
|
||
"is_on": device_status.is_on if hasattr(device_status, 'is_on') else None,
|
||
"brightness": device_status.brightness if hasattr(device_status, 'brightness') else None,
|
||
"color_temp": device_status.color_temp if hasattr(device_status, 'color_temp') else None,
|
||
"color_mode": device_status.color_mode if hasattr(device_status, 'color_mode') else None,
|
||
"rgb": device_status.rgb if hasattr(device_status, 'rgb') else None,
|
||
"online": True,
|
||
"model": LAMP_MODEL
|
||
}
|
||
|
||
return json.dumps(status, indent=2, ensure_ascii=False)
|
||
except Exception as e:
|
||
logger.error(f"获取床头灯状态失败: {e}")
|
||
error_status = {
|
||
"error": f"获取设备状态失败: {str(e)}",
|
||
"message": "请检查:\n1. 设备是否已开启并连接到网络\n2. 设备IP地址是否配置正确(当前配置:{ip})\n3. 设备Token是否正确".format(ip=LAMP_IP),
|
||
"online": False,
|
||
"model": LAMP_MODEL
|
||
}
|
||
return json.dumps(error_status, indent=2, ensure_ascii=False)
|
||
|
||
|
||
class PowerArgs(BaseModel):
|
||
power: bool = Field(..., description="床头灯电源状态,true 开启,false 关闭")
|
||
|
||
|
||
@tool("set_lamp_power", args_schema=PowerArgs, description="开启或关闭床头灯。power=true 开启,power=false 关闭")
|
||
def set_lamp_power(power: bool):
|
||
"""开启或关闭床头灯"""
|
||
try:
|
||
with device_lock: # 使用锁确保串行执行
|
||
if power:
|
||
result = device.on()
|
||
else:
|
||
result = device.off()
|
||
action = "开启" if power else "关闭"
|
||
logger.info(f"床头灯已{action}")
|
||
return json.dumps({
|
||
"message": f"床头灯已{action}",
|
||
"power": power,
|
||
"result": str(result)
|
||
}, indent=2, ensure_ascii=False)
|
||
except Exception as e:
|
||
logger.error(f"设置床头灯电源失败: {e}")
|
||
return json.dumps({
|
||
"error": f"设置电源状态失败: {str(e)}",
|
||
"message": "请检查:\n1. 设备是否已开启并连接到网络\n2. 设备IP地址是否配置正确(当前配置:{ip})\n3. 设备Token是否正确".format(ip=LAMP_IP),
|
||
"online": False,
|
||
"model": LAMP_MODEL
|
||
}, indent=2, ensure_ascii=False)
|
||
|
||
|
||
class BrightnessArgs(BaseModel):
|
||
brightness: int = Field(..., ge=1, le=100, description="亮度值,范围 1-100")
|
||
|
||
|
||
@tool("set_lamp_brightness", args_schema=BrightnessArgs, description="设置床头灯亮度(1-100)")
|
||
def set_lamp_brightness(brightness: int):
|
||
"""设置床头灯亮度"""
|
||
try:
|
||
with device_lock:
|
||
# 参考 Iot.py - 设置亮度 (siid=2, piid=2)
|
||
result = device.send("set_bright", [brightness])
|
||
logger.info(f"亮度已设置为{brightness}%")
|
||
return json.dumps({
|
||
"message": f"亮度已设置为{brightness}%",
|
||
"brightness": brightness,
|
||
"result": str(result)
|
||
}, indent=2, ensure_ascii=False)
|
||
except Exception as e:
|
||
logger.error(f"设置床头灯亮度失败: {e}")
|
||
return json.dumps({
|
||
"error": f"设置亮度失败: {str(e)}",
|
||
"message": "请检查:\n1. 设备是否已开启并连接到网络\n2. 设备IP地址是否配置正确(当前配置:{ip})\n3. 设备Token是否正确".format(ip=LAMP_IP),
|
||
"online": False,
|
||
"model": LAMP_MODEL
|
||
}, indent=2, ensure_ascii=False)
|
||
|
||
|
||
class ColorTempArgs(BaseModel):
|
||
color_temp: int = Field(..., ge=1700, le=6500, description="色温值,范围 1700-6500K")
|
||
|
||
|
||
@tool("set_lamp_color_temp", args_schema=ColorTempArgs, description="设置床头灯色温(1700-6500K,暖光到冷光)")
|
||
def set_lamp_color_temp(color_temp: int):
|
||
"""设置床头灯色温"""
|
||
try:
|
||
with device_lock:
|
||
result = device.send("set_ct_abx", [color_temp, "smooth", 500])
|
||
temp_desc = "暖光" if color_temp < 3000 else "中性光" if color_temp < 5000 else "冷光"
|
||
logger.info(f"色温已设置为{color_temp}K ({temp_desc})")
|
||
return json.dumps({
|
||
"message": f"色温已设置为{color_temp}K ({temp_desc})",
|
||
"color_temp": color_temp,
|
||
"description": temp_desc,
|
||
"result": str(result)
|
||
}, indent=2, ensure_ascii=False)
|
||
except Exception as e:
|
||
logger.error(f"设置床头灯色温失败: {e}")
|
||
return json.dumps({
|
||
"error": f"设置色温失败: {str(e)}",
|
||
"message": "请检查:\n1. 设备是否已开启并连接到网络\n2. 设备IP地址是否配置正确(当前配置:{ip})\n3. 设备Token是否正确".format(ip=LAMP_IP),
|
||
"online": False,
|
||
"model": LAMP_MODEL
|
||
}, indent=2, ensure_ascii=False)
|
||
|
||
|
||
class ColorArgs(BaseModel):
|
||
red: int = Field(..., ge=0, le=255, description="红色值,范围 0-255")
|
||
green: int = Field(..., ge=0, le=255, description="绿色值,范围 0-255")
|
||
blue: int = Field(..., ge=0, le=255, description="蓝色值,范围 0-255")
|
||
|
||
|
||
@tool("set_lamp_color", args_schema=ColorArgs, description="设置床头灯RGB颜色(红、绿、蓝各0-255)")
|
||
def set_lamp_color(red: int, green: int, blue: int):
|
||
"""设置床头灯RGB颜色"""
|
||
try:
|
||
with device_lock:
|
||
color_value = (red << 16) | (green << 8) | blue
|
||
result = device.send("set_rgb", [color_value])
|
||
logger.info(f"颜色已设置为 RGB({red}, {green}, {blue})")
|
||
return json.dumps({
|
||
"message": f"颜色已设置为 RGB({red}, {green}, {blue})",
|
||
"red": red,
|
||
"green": green,
|
||
"blue": blue,
|
||
"color_hex": f"#{red:02x}{green:02x}{blue:02x}",
|
||
"result": str(result)
|
||
}, indent=2, ensure_ascii=False)
|
||
except Exception as e:
|
||
logger.error(f"设置床头灯颜色失败: {e}")
|
||
return json.dumps({
|
||
"error": f"设置颜色失败: {str(e)}",
|
||
"message": "请检查:\n1. 设备是否已开启并连接到网络\n2. 设备IP地址是否配置正确(当前配置:{ip})\n3. 设备Token是否正确".format(ip=LAMP_IP),
|
||
"online": False,
|
||
"model": LAMP_MODEL
|
||
}, indent=2, ensure_ascii=False)
|
||
|
||
|
||
class SceneArgs(BaseModel):
|
||
scene: str = Field(..., description="场景名称: 'reading' (阅读), 'sleep' (睡眠), 'romantic' (浪漫), 'night' (夜灯)")
|
||
|
||
|
||
@tool("set_lamp_scene", args_schema=SceneArgs, description="设置床头灯预设场景(阅读/睡眠/浪漫/夜灯)")
|
||
def set_lamp_scene(scene: str):
|
||
"""设置床头灯预设场景"""
|
||
# 定义预设场景
|
||
scenes = {
|
||
"reading": {"brightness": 100, "color_temp": 4000, "desc": "阅读模式:100%亮度,4000K中性光"},
|
||
"sleep": {"brightness": 10, "color_temp": 2000, "desc": "睡眠模式:10%亮度,2000K暖光"},
|
||
"romantic": {"brightness": 30, "color": (255, 100, 100), "desc": "浪漫模式:30%亮度,粉红色"},
|
||
"night": {"brightness": 5, "color_temp": 1700, "desc": "夜灯模式:5%亮度,1700K极暖光"}
|
||
}
|
||
|
||
if scene not in scenes:
|
||
return json.dumps({
|
||
"error": f"未知场景: {scene}",
|
||
"available_scenes": list(scenes.keys())
|
||
}, indent=2, ensure_ascii=False)
|
||
|
||
try:
|
||
with device_lock:
|
||
scene_config = scenes[scene]
|
||
|
||
# 设置亮度
|
||
device.set_property_by(2, 2, scene_config["brightness"])
|
||
time.sleep(0.3) # 给设备一点响应时间
|
||
|
||
# 设置色温或颜色
|
||
if "color_temp" in scene_config:
|
||
device.set_property_by(2, 3, scene_config["color_temp"])
|
||
elif "color" in scene_config:
|
||
r, g, b = scene_config["color"]
|
||
color_value = (r << 16) | (g << 8) | b
|
||
device.set_property_by(2, 5, color_value)
|
||
|
||
logger.info(f"场景已设置为: {scene}")
|
||
return json.dumps({
|
||
"message": f"场景已设置为: {scene}",
|
||
"scene": scene,
|
||
"description": scene_config["desc"],
|
||
"config": scene_config
|
||
}, indent=2, ensure_ascii=False)
|
||
except Exception as e:
|
||
logger.error(f"设置床头灯场景失败: {e}")
|
||
return json.dumps({
|
||
"error": f"设置场景失败: {str(e)}",
|
||
"message": "请检查:\n1. 设备是否已开启并连接到网络\n2. 设备IP地址是否配置正确(当前配置:{ip})\n3. 设备Token是否正确".format(ip=LAMP_IP),
|
||
"online": False,
|
||
"model": LAMP_MODEL
|
||
}, indent=2, ensure_ascii=False)
|
||
|