Home Assistant custom component for control Xiaomi Multimode Gateway (aka Gateway 3), Xiaomi Multimode Gateway 2, Aqara Hub E1 on default firmwares over LAN
# .../core/gate/miot.py
class MIoTGateway(XGateway):
def miot_on_mqtt_publish(self, msg: MQTTMessage):
if msg.topic in ("miio/report", "central/report"):
...
elif msg.topic == "miio/command_ack":
# check if it is response from `get_properties` command
...
self.miot_process_properties(result, from_cache=True) # <-- 4. Got response
def miot_process_properties(self, params: list, from_cache: bool):
...
for did, params in devices.items():
device = self.devices[did]
device.on_report(params, self, ts) # <-- 5. There is a `dispatch` here, will call all listeners, include `set_result`
if self.stats_domain and device.type in (BLE, MESH):
device.dispatch({device.type: ts}) # <-- 6. Second `dispatch`, `set_result` but the Future is already done.
...
async def miot_send(self, device: XDevice, payload: dict):
...
# check if we can send command via any second gateway
gw2 = next((gw for gw in device.gateways if gw != self and gw.available), None)
if gw2:
await self.mqtt_publish_multiple(device, payload, gw2) # <-- 1. Send a `get_properties` command
else:
await self.mqtt.publish("miio/command", payload)
async def mqtt_publish_multiple(
self, device: XDevice, payload: dict, gw2, delay: float = 1.0
):
fut = asyncio.get_event_loop().create_future()
device.add_listener(fut.set_result) # <-- 2. Add the listener
await self.mqtt.publish("miio/command", payload)
try:
async with asyncio.timeout(delay):
await fut # <-- 3. Awaiting
except TimeoutError:
await gw2.mqtt.publish("miio/command", payload)
finally:
device.remove_listener(fut.set_result) # <-- 7. Maybe too late
Since this Future is only used to detect timeout, it should be safe to add a done check to avoid multiple calls.
Or if you have a better fix, feel free to close this.
Fix #1325
Error logs:
Reproduction process:
Since this Future is only used to detect timeout, it should be safe to add a done check to avoid multiple calls. Or if you have a better fix, feel free to close this.