hass_set_state.py 6.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179
  1. from plugins_func.register import register_function, ToolType, ActionResponse, Action
  2. from plugins_func.functions.hass_init import initialize_hass_handler
  3. from config.logger import setup_logging
  4. import asyncio
  5. import requests
  6. from typing import TYPE_CHECKING
  7. if TYPE_CHECKING:
  8. from core.connection import ConnectionHandler
  9. TAG = __name__
  10. logger = setup_logging()
  11. hass_set_state_function_desc = {
  12. "type": "function",
  13. "function": {
  14. "name": "hass_set_state",
  15. "description": "设置homeassistant里设备的状态,包括开、关,调整灯光亮度、颜色、色温,调整播放器的音量,设备的暂停、继续、静音操作",
  16. "parameters": {
  17. "type": "object",
  18. "properties": {
  19. "state": {
  20. "type": "object",
  21. "properties": {
  22. "type": {
  23. "type": "string",
  24. "description": "需要操作的动作,打开设备:turn_on,关闭设备:turn_off,增加亮度:brightness_up,降低亮度:brightness_down,设置亮度:brightness_value,增加音量:volume_up,降低音量:volume_down,设置音量:volume_set,设置色温:set_kelvin,设置颜色:set_color,设备暂停:pause,设备继续:continue,静音/取消静音:volume_mute",
  25. },
  26. "input": {
  27. "type": "integer",
  28. "description": "只有在设置音量,设置亮度时候才需要,有效值为1-100,对应音量和亮度的1%-100%",
  29. },
  30. "is_muted": {
  31. "type": "string",
  32. "description": "只有在设置静音操作时才需要,设置静音的时候该值为true,取消静音时该值为false",
  33. },
  34. "rgb_color": {
  35. "type": "array",
  36. "items": {"type": "integer"},
  37. "description": "只有在设置颜色时需要,这里填目标颜色的rgb值",
  38. },
  39. },
  40. "required": ["type"],
  41. },
  42. "entity_id": {
  43. "type": "string",
  44. "description": "需要操作的设备id,homeassistant里的entity_id",
  45. },
  46. },
  47. "required": ["state", "entity_id"],
  48. },
  49. },
  50. }
  51. @register_function("hass_set_state", hass_set_state_function_desc, ToolType.SYSTEM_CTL)
  52. def hass_set_state(conn: "ConnectionHandler", entity_id="", state=None):
  53. if state is None:
  54. state = {}
  55. try:
  56. ha_response = handle_hass_set_state(conn, entity_id, state)
  57. return ActionResponse(Action.REQLLM, ha_response, None)
  58. except asyncio.TimeoutError:
  59. logger.bind(tag=TAG).error("设置Home Assistant状态超时")
  60. return ActionResponse(Action.ERROR, "请求超时", None)
  61. except Exception as e:
  62. error_msg = f"执行Home Assistant操作失败"
  63. logger.bind(tag=TAG).error(error_msg)
  64. return ActionResponse(Action.ERROR, error_msg, None)
  65. def handle_hass_set_state(conn: "ConnectionHandler", entity_id, state):
  66. ha_config = initialize_hass_handler(conn)
  67. api_key = ha_config.get("api_key")
  68. base_url = ha_config.get("base_url")
  69. """
  70. state = { "type":"brightness_up","input":"80","is_muted":"true"}
  71. """
  72. domains = entity_id.split(".")
  73. if len(domains) > 1:
  74. domain = domains[0]
  75. else:
  76. return "执行失败,错误的设备id"
  77. action = ""
  78. arg = ""
  79. value = ""
  80. if state["type"] == "turn_on":
  81. description = "设备已打开"
  82. if domain == "cover":
  83. action = "open_cover"
  84. elif domain == "vacuum":
  85. action = "start"
  86. else:
  87. action = "turn_on"
  88. elif state["type"] == "turn_off":
  89. description = "设备已关闭"
  90. if domain == "cover":
  91. action = "close_cover"
  92. elif domain == "vacuum":
  93. action = "stop"
  94. else:
  95. action = "turn_off"
  96. elif state["type"] == "brightness_up":
  97. description = "灯光已调亮"
  98. action = "turn_on"
  99. arg = "brightness_step_pct"
  100. value = 10
  101. elif state["type"] == "brightness_down":
  102. description = "灯光已调暗"
  103. action = "turn_on"
  104. arg = "brightness_step_pct"
  105. value = -10
  106. elif state["type"] == "brightness_value":
  107. description = f"亮度已调整到{state['input']}"
  108. action = "turn_on"
  109. arg = "brightness_pct"
  110. value = state["input"]
  111. elif state["type"] == "set_color":
  112. description = f"颜色已调整到{state['rgb_color']}"
  113. action = "turn_on"
  114. arg = "rgb_color"
  115. value = state["rgb_color"]
  116. elif state["type"] == "set_kelvin":
  117. description = f"色温已调整到{state['input']}K"
  118. action = "turn_on"
  119. arg = "kelvin"
  120. value = state["input"]
  121. elif state["type"] == "volume_up":
  122. description = "音量已调大"
  123. action = state["type"]
  124. elif state["type"] == "volume_down":
  125. description = "音量已调小"
  126. action = state["type"]
  127. elif state["type"] == "volume_set":
  128. description = f"音量已调整到{state['input']}"
  129. action = state["type"]
  130. arg = "volume_level"
  131. value = state["input"]
  132. if state["input"] >= 1:
  133. value = state["input"] / 100
  134. elif state["type"] == "volume_mute":
  135. description = f"设备已静音"
  136. action = state["type"]
  137. arg = "is_volume_muted"
  138. value = state["is_muted"]
  139. elif state["type"] == "pause":
  140. description = f"设备已暂停"
  141. action = state["type"]
  142. if domain == "media_player":
  143. action = "media_pause"
  144. if domain == "cover":
  145. action = "stop_cover"
  146. if domain == "vacuum":
  147. action = "pause"
  148. elif state["type"] == "continue":
  149. description = f"设备已继续"
  150. if domain == "media_player":
  151. action = "media_play"
  152. if domain == "vacuum":
  153. action = "start"
  154. else:
  155. return f"{domain} {state['type']}功能尚未支持"
  156. if arg == "":
  157. data = {
  158. "entity_id": entity_id,
  159. }
  160. else:
  161. data = {"entity_id": entity_id, arg: value}
  162. url = f"{base_url}/api/services/{domain}/{action}"
  163. headers = {"Authorization": f"Bearer {api_key}", "Content-Type": "application/json"}
  164. response = requests.post(url, headers=headers, json=data, timeout=5) # 设置5秒超时
  165. logger.bind(tag=TAG).info(
  166. f"设置状态:{description},url:{url},return_code:{response.status_code}"
  167. )
  168. if response.status_code == 200:
  169. return description
  170. else:
  171. return f"设置失败,错误码: {response.status_code}"