allow to use MQTT for discovery of IPs if mDNS is no option (#3887)

Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com>
This commit is contained in:
Markus
2023-05-17 06:29:56 +02:00
committed by GitHub
parent 0de47e2a4e
commit c5a45645a6
9 changed files with 353 additions and 21 deletions
+86 -3
View File
@@ -1,4 +1,5 @@
import base64
import binascii
import codecs
import collections
import functools
@@ -76,6 +77,10 @@ class DashboardSettings:
def status_use_ping(self):
return get_bool_env("ESPHOME_DASHBOARD_USE_PING")
@property
def status_use_mqtt(self):
return get_bool_env("ESPHOME_DASHBOARD_USE_MQTT")
@property
def using_ha_addon_auth(self):
if not self.on_ha_addon:
@@ -583,6 +588,12 @@ class DashboardEntry:
return None
return self.storage.address
@property
def no_mdns(self):
if self.storage is None:
return None
return self.storage.no_mdns
@property
def web_port(self):
if self.storage is None:
@@ -775,9 +786,12 @@ class MDNSStatusThread(threading.Thread):
stat.start()
while not STOP_EVENT.is_set():
entries = _list_dashboard_entries()
stat.request_query(
{entry.filename: f"{entry.name}.local." for entry in entries}
)
hosts = {}
for entry in entries:
if entry.no_mdns is not True:
hosts[entry.filename] = f"{entry.name}.local."
stat.request_query(hosts)
IMPORT_RESULT = imports.import_state
PING_REQUEST.wait()
@@ -801,6 +815,9 @@ class PingStatusThread(threading.Thread):
entries = _list_dashboard_entries()
queue = collections.deque()
for entry in entries:
if entry.no_mdns is True:
continue
if entry.address is None:
PING_RESULT[entry.filename] = None
continue
@@ -832,10 +849,67 @@ class PingStatusThread(threading.Thread):
PING_REQUEST.clear()
class MqttStatusThread(threading.Thread):
def run(self):
from esphome import mqtt
entries = _list_dashboard_entries()
config = mqtt.config_from_env()
topic = "esphome/discover/#"
def on_message(client, userdata, msg):
nonlocal entries
payload = msg.payload.decode(errors="backslashreplace")
if len(payload) > 0:
data = json.loads(payload)
if "name" not in data:
return
for entry in entries:
if entry.name == data["name"]:
PING_RESULT[entry.filename] = True
return
def on_connect(client, userdata, flags, return_code):
client.publish("esphome/discover", None, retain=False)
mqttid = str(binascii.hexlify(os.urandom(6)).decode())
client = mqtt.prepare(
config,
[topic],
on_message,
on_connect,
None,
None,
f"esphome-dashboard-{mqttid}",
)
client.loop_start()
while not STOP_EVENT.wait(2):
# update entries
entries = _list_dashboard_entries()
# will be set to true on on_message
for entry in entries:
if entry.no_mdns:
PING_RESULT[entry.filename] = False
client.publish("esphome/discover", None, retain=False)
MQTT_PING_REQUEST.wait()
MQTT_PING_REQUEST.clear()
client.disconnect()
client.loop_stop()
class PingRequestHandler(BaseHandler):
@authenticated
def get(self):
PING_REQUEST.set()
if settings.status_use_mqtt:
MQTT_PING_REQUEST.set()
self.set_header("content-type", "application/json")
self.write(json.dumps(PING_RESULT))
@@ -910,6 +984,7 @@ PING_RESULT: dict = {}
IMPORT_RESULT = {}
STOP_EVENT = threading.Event()
PING_REQUEST = threading.Event()
MQTT_PING_REQUEST = threading.Event()
class LoginHandler(BaseHandler):
@@ -1197,6 +1272,11 @@ def start_web_server(args):
else:
status_thread = MDNSStatusThread()
status_thread.start()
if settings.status_use_mqtt:
status_thread_mqtt = MqttStatusThread()
status_thread_mqtt.start()
try:
tornado.ioloop.IOLoop.current().start()
except KeyboardInterrupt:
@@ -1204,5 +1284,8 @@ def start_web_server(args):
STOP_EVENT.set()
PING_REQUEST.set()
status_thread.join()
if settings.status_use_mqtt:
status_thread_mqtt.join()
MQTT_PING_REQUEST.set()
if args.socket is not None:
os.remove(args.socket)