Skip to content

Commit ba502d8

Browse files
authored
增加mqtt获取数据方式
1 parent 26baa3b commit ba502d8

9 files changed

Lines changed: 806 additions & 21 deletions

File tree

custom_components/cloud_gps/__init__.py

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
Description :
66
Date : 2023-11-16
77
LastEditors : dscao
8-
LastEditTime : 2025-6-11
8+
LastEditTime : 2025-6-15
99
'''
1010
"""
1111
Component to integrate with Cloud_GPS.
@@ -79,7 +79,7 @@
7979
)
8080

8181
TYPE_GEOFENCE = "Geofence"
82-
__version__ = '2025.6.11'
82+
__version__ = '2025.6.22'
8383

8484
_LOGGER = logging.getLogger(__name__)
8585

@@ -102,6 +102,7 @@
102102
"hellobike.com": "hellobike_data_fetcher",
103103
"auto.amap.com": "autoamap_data_fetcher",
104104
"macless_haystack": "macless_haystack_data_fetcher",
105+
"gps_mqtt": "gps_mqtt_data_fetcher",
105106
}
106107

107108

@@ -118,7 +119,7 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
118119
webhost = entry.data[CONF_WEB_HOST]
119120
gps_conver = entry.options.get(CONF_GPS_CONVER, ["wgs84"])
120121
device_imei = entry.options.get(CONF_DEVICE_IMEI, [])
121-
update_interval_seconds = entry.options.get(CONF_UPDATE_INTERVAL, 60)
122+
update_interval_seconds = entry.options.get(CONF_UPDATE_INTERVAL, 300 if webhost == "macless_haystack" else 60 )
122123
attr_show = entry.options.get(CONF_ATTR_SHOW, True)
123124
address_distance = entry.options.get(CONF_UPDATE_ADDRESSDISTANCE, 50)
124125
addressapi = entry.options.get(CONF_ADDRESSAPI, "none")
@@ -156,7 +157,7 @@ async def check_data_and_create_entities(_now):
156157
cancel_timer = async_track_time_interval(
157158
hass,
158159
check_data_and_create_entities,
159-
datetime.timedelta(seconds=30),
160+
datetime.timedelta(seconds=60),
160161
)
161162

162163
# 将取消函数注册到卸载事件中,以确保在卸载集成时停止定时器
@@ -248,12 +249,14 @@ def __init__(self, hass, data_fetcher_class, username, password, webhost, gps_co
248249

249250
self._entity_created = False
250251
self._retry_count = 0
252+
253+
self.timeout_second = 120 if webhost == "macless_haystack" or webhost == "gps_mqtt" else 30
251254

252255

253256
async def _async_update_data(self):
254257
"""Update data via library."""
255258
try:
256-
async with timeout(20):
259+
async with timeout(self.timeout_second):
257260
data = await self._fetcher.get_data()
258261
_LOGGER.debug("%s update_data: %s", self.device_imei, data)
259262
_LOGGER.debug("%s gps_conver: %s", self.device_imei, self._gps_conver)
@@ -314,7 +317,7 @@ async def ensure_entities_created(self):
314317

315318
async def _get_address_frome_api(self, imei, addressapi, api_key, private_key):
316319
try:
317-
async with timeout(5):
320+
async with timeout(10):
318321
if addressapi == "baidu" and api_key:
319322
_LOGGER.debug("baidu:"+api_key)
320323
addressdata = await self._hass.async_add_executor_job(self.get_baidu_geocoding, self._coords[imei][1], self._coords[imei][0], api_key, private_key)

custom_components/cloud_gps/button.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,12 @@
4343
"name": "nowtrack",
4444
"icon": "mdi:map-marker-check",
4545
"device_class": "restart",
46+
},
47+
"reboot": {
48+
"label": "reboot",
49+
"name": "reboot",
50+
"icon": "mdi:restart",
51+
"device_class": "restart",
4652
}
4753
}
4854

@@ -94,6 +100,8 @@ def __init__(self, hass, webhost, username, password, imei, description, coordin
94100
from .tuqiang123_data_fetcher import DataButton
95101
elif webhost == "hellobike.com":
96102
from .hellobike_data_fetcher import DataButton
103+
elif webhost == "gps_mqtt":
104+
from .gps_mqtt_data_fetcher import DataButton
97105
else:
98106
_LOGGER.error("配置的实体平台不支持,请不要启用此按钮实体!")
99107
return
@@ -149,6 +157,10 @@ async def async_press(self) -> None:
149157
self._state = await self._button._action("rent.order.bell")
150158
elif self._webhost == "tuqiang123.com" and self._description['label']=="nowtrack":
151159
self._state = await self._button._action("立即定位")
160+
elif self._webhost == "gps_mqtt" and self._description['label']=="nowtrack":
161+
self._state = await self._button._action({"cmd":"dw"})
162+
elif self._webhost == "gps_mqtt" and self._description['label']=="reboot":
163+
self._state = await self._button._action({"cmd":"reboot"})
152164

153165

154166
async def async_added_to_hass(self):

custom_components/cloud_gps/config_flow.py

Lines changed: 137 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
import re
88
import hashlib
99
import base64
10+
import paho.mqtt.client as mqtt
1011
import homeassistant.helpers.config_validation as cv
1112
from homeassistant.const import CONF_NAME, CONF_USERNAME, CONF_PASSWORD, CONF_CLIENT_ID
1213
from homeassistant.helpers.selector import SelectSelector, SelectSelectorConfig, SelectSelectorMode
@@ -60,7 +61,8 @@
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

6668
API_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

Comments
 (0)