ha_devices_module.py 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279
  1. import yaml
  2. import requests
  3. import os
  4. import json
  5. from utils.logger_config import logger
  6. class HADeviceController:
  7. def __init__(self, config_path='ha_config_prod.yaml'):
  8. self.config_path = config_path
  9. self.config = self._load_config()
  10. self.ha_config = self.config.get('home_assistant', {})
  11. self.base_url = self.ha_config.get('url', '').rstrip('/')
  12. self.token = self.ha_config.get('token', '')
  13. self.headers = {
  14. "Authorization": f"Bearer {self.token}",
  15. "Content-Type": "application/json",
  16. }
  17. self.devices = self.config.get('ha_devices', {})
  18. def _load_config(self):
  19. if not os.path.exists(self.config_path):
  20. logger.error(f"配置文件不存在: {self.config_path}")
  21. return {}
  22. try:
  23. with open(self.config_path, 'r', encoding='utf-8') as f:
  24. return yaml.safe_load(f) or {}
  25. except Exception as e:
  26. logger.error(f"加载配置文件失败: {e}")
  27. return {}
  28. def _get_device_state(self, entity_id):
  29. """获取设备当前状态"""
  30. if not self.base_url or not self.token:
  31. return None
  32. url = f"{self.base_url}/api/states/{entity_id}"
  33. try:
  34. logger.info(f"正在检查设备状态: {entity_id}")
  35. response = requests.get(url, headers=self.headers, timeout=5)
  36. if response.status_code == 200:
  37. data = response.json()
  38. state = data.get('state')
  39. logger.info(f"设备 {entity_id} 当前状态: {state}")
  40. return state
  41. else:
  42. logger.error(f"获取设备状态失败: {entity_id}, code={response.status_code}")
  43. return None
  44. except Exception as e:
  45. logger.error(f"获取设备状态异常: {entity_id}, {e}")
  46. return None
  47. def _call_ha_service(self, entity_id, service="turn_on", domain="switch"):
  48. if not self.base_url or not self.token:
  49. logger.error("Home Assistant 配置缺失 (url or token)")
  50. return False
  51. if not entity_id:
  52. logger.warning("未配置 entity_id")
  53. return False
  54. url = f"{self.base_url}/api/services/{domain}/{service}"
  55. payload = {"entity_id": entity_id}
  56. logger.info(f"准备调用HA接口: URL={url}, Entity={entity_id}, Service={domain}.{service}")
  57. try:
  58. response = requests.post(url, headers=self.headers, json=payload, timeout=5)
  59. if response.status_code in [200, 201]:
  60. logger.info(f"HA调用成功: {entity_id} -> {domain}.{service}, 响应: {response.text}")
  61. return True
  62. else:
  63. logger.error(f"HA调用失败: {response.status_code} - {response.text}")
  64. return False
  65. except Exception as e:
  66. logger.error(f"HA请求异常: {e}")
  67. return False
  68. def control_device(self, device_key, action="turn_on"):
  69. """
  70. 通用设备控制方法
  71. :param device_key: 配置文件 ha_devices 中的 key (如 'entrance_light_1')
  72. :param action: 动作 'turn_on' 或 'turn_off'
  73. """
  74. device_info = self.devices.get(device_key)
  75. if not device_info:
  76. logger.error(f"未找到设备配置: {device_key}")
  77. return False
  78. entity_id = device_info.get('entity_id')
  79. if not entity_id:
  80. logger.error(f"设备 {device_key} 未配置 entity_id")
  81. return False
  82. # 检查设备当前状态
  83. current_state = self._get_device_state(entity_id)
  84. if current_state:
  85. if action == "turn_on" and current_state == "on":
  86. logger.info(f"设备 {device_key} ({entity_id}) 已经是开启状态,跳过开启操作")
  87. return True
  88. if action == "turn_off" and current_state == "off":
  89. logger.info(f"设备 {device_key} ({entity_id}) 已经是关闭状态,跳过关闭操作")
  90. return True
  91. # 自动判断 domain
  92. domain = entity_id.split('.')[0]
  93. return self._call_ha_service(entity_id, service=action, domain=domain)
  94. # 具体设备控制方法
  95. def turn_on_entrance_lights(self):
  96. """打开一楼大门玄关顶灯 (全部)"""
  97. r1 = self.control_device('entrance_light_1', 'turn_on')
  98. r2 = self.control_device('entrance_light_2', 'turn_on')
  99. return r1 and r2
  100. def turn_off_entrance_lights(self):
  101. """关闭一楼大门玄关顶灯 (全部)"""
  102. r1 = self.control_device('entrance_light_1', 'turn_off')
  103. r2 = self.control_device('entrance_light_2', 'turn_off')
  104. return r1 and r2
  105. def turn_on_exhibition_spotlight(self):
  106. """打开一楼大门玄关射灯"""
  107. return self.control_device('exhibition_spotlight', 'turn_on')
  108. def turn_off_exhibition_spotlight(self):
  109. """关闭一楼大门玄关射灯"""
  110. return self.control_device('exhibition_spotlight', 'turn_off')
  111. def turn_on_exhibition_ceiling_lights(self):
  112. """打开一楼展厅顶灯 (全部)"""
  113. r1 = self.control_device('exhibition_ceiling_light_1', 'turn_on')
  114. r2 = self.control_device('exhibition_ceiling_light_2', 'turn_on')
  115. r3 = self.control_device('exhibition_ceiling_light_3', 'turn_on')
  116. return r1 and r2 and r3
  117. def turn_off_exhibition_ceiling_lights(self):
  118. """关闭一楼展厅顶灯 (全部)"""
  119. r1 = self.control_device('exhibition_ceiling_light_1', 'turn_off')
  120. r2 = self.control_device('exhibition_ceiling_light_2', 'turn_off')
  121. r3 = self.control_device('exhibition_ceiling_light_3', 'turn_off')
  122. return r1 and r2 and r3
  123. def set_exhibition_ceiling_light_level(self, level):
  124. """
  125. 设置一楼展厅顶灯等级 (0-3)
  126. 0: 全关
  127. 1: 开启灯1 (关2,3)
  128. 2: 开启灯1,2 (关3)
  129. 3: 全开 (1,2,3)
  130. """
  131. logger.info(f"设置展厅顶灯等级: {level}")
  132. # 确保 level 在 0-3 之间
  133. try:
  134. level = int(level)
  135. if level < 0: level = 0
  136. if level > 3: level = 3
  137. except (ValueError, TypeError):
  138. logger.error(f"无效的灯光等级: {level}")
  139. return False
  140. r1, r2, r3 = True, True, True
  141. if level == 0:
  142. # 0: 全关
  143. r1 = self.control_device('exhibition_ceiling_light_1', 'turn_off')
  144. r2 = self.control_device('exhibition_ceiling_light_2', 'turn_off')
  145. r3 = self.control_device('exhibition_ceiling_light_3', 'turn_off')
  146. elif level == 1:
  147. # 1: 开启灯1 (关2,3)
  148. r1 = self.control_device('exhibition_ceiling_light_1', 'turn_on')
  149. r2 = self.control_device('exhibition_ceiling_light_2', 'turn_off')
  150. r3 = self.control_device('exhibition_ceiling_light_3', 'turn_off')
  151. elif level == 2:
  152. # 2: 开启灯1,2 (关3)
  153. r1 = self.control_device('exhibition_ceiling_light_1', 'turn_on')
  154. r2 = self.control_device('exhibition_ceiling_light_2', 'turn_on')
  155. r3 = self.control_device('exhibition_ceiling_light_3', 'turn_off')
  156. elif level == 3:
  157. # 3: 全开
  158. r1 = self.control_device('exhibition_ceiling_light_1', 'turn_on')
  159. r2 = self.control_device('exhibition_ceiling_light_2', 'turn_on')
  160. r3 = self.control_device('exhibition_ceiling_light_3', 'turn_on')
  161. return r1 and r2 and r3
  162. def turn_on_exhibition_desktop_switch(self):
  163. """打开展厅桌面的灯座总开关"""
  164. return self.control_device('exhibition_desktop_switch', 'turn_on')
  165. def turn_off_exhibition_desktop_switch(self):
  166. """关闭展厅桌面的灯座总开关"""
  167. return self.control_device('exhibition_desktop_switch', 'turn_off')
  168. def turn_on_exhibition_3d_fan(self):
  169. """打开展厅桌面3D风扇投影"""
  170. return self.control_device('exhibition_3d_fan', 'turn_on')
  171. def turn_off_exhibition_3d_fan(self):
  172. """关闭展厅桌面3D风扇投影"""
  173. return self.control_device('exhibition_3d_fan', 'turn_off')
  174. def turn_on_exhibition_stand_light_strip(self):
  175. """打开展台桌子灯带"""
  176. return self.control_device('exhibition_stand_light_strip', 'turn_on')
  177. def turn_off_exhibition_stand_light_strip(self):
  178. """关闭展台桌子灯带"""
  179. return self.control_device('exhibition_stand_light_strip', 'turn_off')
  180. def turn_on_all(self):
  181. """打开所有配置的HA设备"""
  182. results = []
  183. for key in self.devices:
  184. results.append(self.control_device(key, 'turn_on'))
  185. return all(results) if results else False
  186. def turn_off_all(self):
  187. """关闭所有配置的HA设备"""
  188. results = []
  189. for key in self.devices:
  190. results.append(self.control_device(key, 'turn_off'))
  191. return all(results) if results else False
  192. # 单例实例
  193. ha_device_controller = HADeviceController()
  194. # 模块级便捷函数
  195. def control_device(device_key, action="turn_on"):
  196. return ha_device_controller.control_device(device_key, action)
  197. def turn_on_entrance_lights():
  198. return ha_device_controller.turn_on_entrance_lights()
  199. def turn_off_entrance_lights():
  200. return ha_device_controller.turn_off_entrance_lights()
  201. def turn_on_exhibition_spotlight():
  202. return ha_device_controller.turn_on_exhibition_spotlight()
  203. def turn_off_exhibition_spotlight():
  204. return ha_device_controller.turn_off_exhibition_spotlight()
  205. def turn_on_exhibition_ceiling_lights():
  206. return ha_device_controller.turn_on_exhibition_ceiling_lights()
  207. def turn_off_exhibition_ceiling_lights():
  208. return ha_device_controller.turn_off_exhibition_ceiling_lights()
  209. def set_exhibition_ceiling_light_level(level):
  210. return ha_device_controller.set_exhibition_ceiling_light_level(level)
  211. def turn_on_exhibition_desktop_switch():
  212. return ha_device_controller.turn_on_exhibition_desktop_switch()
  213. def turn_off_exhibition_desktop_switch():
  214. return ha_device_controller.turn_off_exhibition_desktop_switch()
  215. def turn_on_exhibition_3d_fan():
  216. return ha_device_controller.turn_on_exhibition_3d_fan()
  217. def turn_off_exhibition_3d_fan():
  218. return ha_device_controller.turn_off_exhibition_3d_fan()
  219. def turn_on_exhibition_stand_light_strip():
  220. return ha_device_controller.turn_on_exhibition_stand_light_strip()
  221. def turn_off_exhibition_stand_light_strip():
  222. return ha_device_controller.turn_off_exhibition_stand_light_strip()
  223. def turn_on_all():
  224. return ha_device_controller.turn_on_all()
  225. def turn_off_all():
  226. return ha_device_controller.turn_off_all()