77import re
88import hashlib
99import base64
10+ import paho .mqtt .client as mqtt
1011import homeassistant .helpers .config_validation as cv
1112from homeassistant .const import CONF_NAME , CONF_USERNAME , CONF_PASSWORD , CONF_CLIENT_ID
1213from homeassistant .helpers .selector import SelectSelector , SelectSelectorConfig , SelectSelectorMode
6061 "cmobd.com" : "中移行车卫士(*密码填写token)" ,
6162 "hellobike.com" : "哈啰智能芯(*密码填写token)" ,
6263 "auto.amap.com" : "高德车机版(*密码填写 Key||sessionid||paramdata)" ,
63- "macless_haystack" : "macless_haystack(*用户名填写 服务器Url,密码填写Key Json)"
64+ "macless_haystack" : "macless_haystack(*用户名填写 服务器Url,密码填写Key Json)" ,
65+ "gps_mqtt" : "gps_mqtt(*用户名填写 mqtt服务器 server||user||password,密码填写mqtt主题)"
6466}
6567
6668API_HOST_TUQIANG123 = "https://www.tuqiang123.com" # https://www.tuqiangol.com 或者 https://www.tuqiang123.com
@@ -286,6 +288,60 @@ def basic_auth(self, username, password):
286288 userpass = f"{ username } :{ password } "
287289 encoded_credentials = base64 .b64encode (userpass .encode ('utf-8' )).decode ('utf-8' )
288290 return "Basic " + encoded_credentials
291+
292+ def test_mqtt_connection (self , server , port , username , password ):
293+ """测试 MQTT 服务器连接"""
294+ result = {"success" : False , "error" : "" }
295+ connected = False
296+
297+ def on_connect (client , userdata , flags , rc ):
298+ nonlocal connected
299+ if rc == 0 :
300+ connected = True
301+ else :
302+ result ["error" ] = f"Connection failed with code { rc } "
303+
304+ client = mqtt .Client ()
305+ client .on_connect = on_connect
306+
307+ if username and password :
308+ client .username_pw_set (username , password )
309+
310+ try :
311+ client .connect (server , port , 5 ) # 5秒连接超时
312+ client .loop_start () # 启动网络循环(非阻塞)
313+
314+ # 等待连接结果,最多5秒
315+ for _ in range (10 ):
316+ if connected :
317+ result ["success" ] = True
318+ break
319+ time .sleep (0.5 )
320+ else :
321+ if not result ["error" ]:
322+ result ["error" ] = "Connection timed out"
323+ except Exception as e :
324+ result ["error" ] = str (e )
325+ finally :
326+ try :
327+ client .loop_stop ()
328+ client .disconnect ()
329+ except :
330+ pass
331+
332+ return result
333+
334+ def parse_mqtt_server (self , server_str ):
335+ """解析 MQTT 服务器地址,支持 host:port 格式"""
336+ if ":" in server_str :
337+ parts = server_str .split (":" )
338+ host = parts [0 ]
339+ try :
340+ port = int (parts [1 ])
341+ except ValueError :
342+ port = 1883
343+ return host , port
344+ return server_str , 1883
289345
290346 async def async_step_user (self , user_input = {}):
291347 self ._errors = {}
@@ -574,13 +630,13 @@ async def async_step_user(self, user_input={}):
574630 return await self ._show_config_form (user_input )
575631
576632 try :
577- json_data = json .loads (password )
633+ json_data = json .loads (password )
578634 except :
579- _LOGGER .error ("填写的json数据解析错误 " )
580- self ._errors ["base" ] = "填写的json数据解析错误 。"
635+ _LOGGER .error ("密码中填写的json数据解析错误 " )
636+ self ._errors ["base" ] = "密码中填写的json数据解析错误 。"
581637 return await self ._show_config_form (user_input )
582638 if not isinstance (json_data , list ):
583- self ._errors ["base" ] = "填写的json数据格式不正确 。"
639+ self ._errors ["base" ] = "密码中填写的json数据格式不正确 。"
584640 return await self ._show_config_form (user_input )
585641
586642 if isinstance (json_data , list ):
@@ -600,9 +656,64 @@ async def async_step_user(self, user_input={}):
600656 return self .async_create_entry (
601657 title = user_input [CONF_NAME ], data = config_data
602658 )
603- else :
604- self ._errors ["base" ] = "communication"
659+ elif webhost == "gps_mqtt" :
660+ # 确保用户名格式正确
661+ if "||" not in username :
662+ _LOGGER .error ("mqtt服务器格式不正确,请按 server||username||password 格式" )
663+ self ._errors ["base" ] = "mqtt服务器格式不正确,请按 server||username||password 格式"
664+ return await self ._show_config_form (user_input )
665+
666+ parts = username .split ("||" )
667+ if len (parts ) < 3 :
668+ _LOGGER .error ("mqtt服务器格式不正确,请按 server||username||password 格式" )
669+ self ._errors ["base" ] = "mqtt服务器格式不正确,请按 server||username||password 格式"
605670 return await self ._show_config_form (user_input )
671+
672+ server_str = parts [0 ]
673+ mqtt_username = parts [1 ]
674+ mqtt_password = parts [2 ]
675+
676+ # 解析服务器地址和端口
677+ server , port = await self .hass .async_add_executor_job (
678+ self .parse_mqtt_server , server_str
679+ )
680+
681+ # 测试 MQTT 连接
682+ connection_test = await self .hass .async_add_executor_job (
683+ self .test_mqtt_connection ,
684+ server ,
685+ port ,
686+ mqtt_username ,
687+ mqtt_password
688+ )
689+
690+ if not connection_test ["success" ]:
691+ _LOGGER .error ("MQTT 连接失败: %s" , connection_test ["error" ])
692+ self ._errors ["base" ] = f"MQTT 连接失败: { connection_test ['error' ]} "
693+ return await self ._show_config_form (user_input )
694+
695+ # 验证主题格式
696+ topic = user_input [CONF_PASSWORD ]
697+ if not topic or not topic .strip ():
698+ self ._errors ["base" ] = "主题不能为空"
699+ return await self ._show_config_form (user_input )
700+
701+ devices .append (topic )
702+
703+ await self .async_set_unique_id (f"cloudpgs-{ server } -{ user_input [CONF_PASSWORD ]} " .replace ("." ,"_" ).replace ("/" ,"_" ))
704+ self ._abort_if_unique_id_configured ()
705+
706+ config_data [CONF_USERNAME ] = username
707+ config_data [CONF_PASSWORD ] = password
708+ config_data [CONF_DEVICES ] = devices
709+ config_data [CONF_WEB_HOST ] = webhost
710+
711+ _LOGGER .debug (devices )
712+
713+ return self .async_create_entry (
714+ title = user_input [CONF_NAME ], data = config_data
715+ )
716+
606717 else :
607718 self ._errors ["base" ] = "未选择有效平台"
608719
@@ -755,6 +866,25 @@ async def async_step_user(self, user_input=None):
755866
756867 SWITCHSLIST = []
757868 BUTTONSLIST = []
869+ elif self .config_entry .data .get (CONF_WEB_HOST ) == "gps_mqtt" :
870+ SENSORSLIST = [
871+ {"value" : KEY_PARKING_TIME , "label" : "parkingtime" },
872+ {"value" : KEY_LASTSTOPTIME , "label" : "laststoptime" },
873+ {"value" : KEY_ADDRESS , "label" : "address" },
874+ {"value" : KEY_SPEED , "label" : "speed" },
875+ {"value" : KEY_TOTALKM , "label" : "totalkm" },
876+ {"value" : KEY_STATUS , "label" : "status" },
877+ {"value" : KEY_ACC , "label" : "acc" },
878+ {"value" : KEY_BATTERY , "label" : "powbattery" }
879+ ]
880+
881+ SWITCHSLIST = [
882+ {"value" : "open_lock" , "label" : "open_lock" },
883+ ]
884+ BUTTONSLIST = [
885+ {"value" : "nowtrack" , "label" : "nowtrack" },
886+ {"value" : "reboot" , "label" : "reboot" }
887+ ]
758888 else :
759889 SENSORSLIST = [
760890 {"value" : KEY_PARKING_TIME , "label" : "parkingtime" },
0 commit comments