2525from datetime import time
2626from typing import Any
2727
28- from roborock .exceptions import RoborockException
29- from roborock .protocols .a01_protocol import decode_rpc_response
30-
3128from roborock .data import DyadProductInfo , DyadSndState , HomeDataProduct , RoborockCategory
3229from roborock .data .dyad .dyad_code_mappings import (
3330 DyadBrushSpeed ,
4441 ZeoDetergentType ,
4542 ZeoDryingMode ,
4643 ZeoError ,
44+ ZeoFeatureBits ,
4745 ZeoMode ,
4846 ZeoProgram ,
4947 ZeoRinse ,
5654from roborock .devices .traits import Trait
5755from roborock .devices .traits .common import TraitUpdateListener
5856from roborock .devices .transport .mqtt_channel import MqttChannel
57+ from roborock .exceptions import RoborockException
58+ from roborock .protocols .a01_protocol import decode_rpc_response
5959from roborock .roborock_message import (
6060 RoborockDyadDataProtocol ,
6161 RoborockMessage ,
@@ -179,10 +179,16 @@ def __init__(self, channel: MqttChannel) -> None:
179179 self ._channel = channel
180180 self ._dps_cache : dict [int , Any ] = {}
181181 self ._dps_unsub : Callable [[], None ] | None = None
182+ self ._feature_bits : int = 0
182183
183184 async def start (self ) -> None :
184- """Subscribe to MQTT push (called once after device connects)."""
185+ """Subscribe to MQTT push and discover device features.
186+
187+ Subscribes to the DPS MQTT topic, then queries FEATURE_BITS
188+ (DP 237) to wake the device and cache supported capabilities.
189+ """
185190 await self ._ensure_subscribed ()
191+ await self ._discover_features ()
186192
187193 def close (self ) -> None :
188194 """Unsubscribe from MQTT push and release resources."""
@@ -196,6 +202,24 @@ async def _ensure_subscribed(self) -> None:
196202 return
197203 self ._dps_unsub = await self ._channel .subscribe (self ._on_dps_message )
198204
205+ async def _discover_features (self ) -> None :
206+ """Query FEATURE_BITS to wake the device and cache capabilities.
207+
208+ Sending an RPC query after subscribing triggers the device to
209+ start pushing its full state — equivalent to how V1's
210+ ``discover_features()`` uses ``device_features.refresh()`` to
211+ initiate the push cycle.
212+ """
213+ try :
214+ result = await self .query_values ([RoborockZeoProtocol .FEATURE_BITS ])
215+ self ._feature_bits = result .get (RoborockZeoProtocol .FEATURE_BITS , 0 )
216+ except Exception :
217+ self ._feature_bits = 0
218+
219+ def supports (self , feature : ZeoFeatureBits ) -> bool :
220+ """Check whether the device supports a given feature bit."""
221+ return bool (self ._feature_bits & (1 << feature .value ))
222+
199223 def _on_dps_message (self , message : RoborockMessage ) -> None :
200224 """Handle unsolicited MQTT push (protocol 102 — RPC_RESPONSE).
201225
0 commit comments