2929 "0000fd3d-0000-1000-8000-00805f9b34fb" ,
3030 "00000d00-0000-1000-8000-00805f9b34fb" ,
3131)
32+ MFR_DATA_ORDER = (2409 , 89 )
3233
3334
3435class SwitchbotSupportedType (TypedDict ):
@@ -44,11 +45,15 @@ class SwitchbotSupportedType(TypedDict):
4445 "modelName" : SwitchbotModel .CONTACT_SENSOR ,
4546 "modelFriendlyName" : "Contact Sensor" ,
4647 "func" : process_wocontact ,
48+ "manufacturer_id" : 2409 ,
49+ "manufacturer_data_length" : 13 ,
4750 },
4851 "H" : {
4952 "modelName" : SwitchbotModel .BOT ,
5053 "modelFriendlyName" : "Bot" ,
5154 "func" : process_wohand ,
55+ "service_uuids" : {"cba20d00-224d-11e6-9fb8-0002a5d5c51b" },
56+ "manufacturer_id" : 89 ,
5257 },
5358 "s" : {
5459 "modelName" : SwitchbotModel .MOTION_SENSOR ,
@@ -64,6 +69,12 @@ class SwitchbotSupportedType(TypedDict):
6469 "modelName" : SwitchbotModel .CURTAIN ,
6570 "modelFriendlyName" : "Curtain" ,
6671 "func" : process_wocurtain ,
72+ "service_uuids" : {
73+ "00001800-0000-1000-8000-00805f9b34fb" ,
74+ "00001801-0000-1000-8000-00805f9b34fb" ,
75+ "cba20d00-224d-11e6-9fb8-0002a5d5c51b" ,
76+ },
77+ "manufacturer_id" : 89 ,
6778 },
6879 "T" : {
6980 "modelName" : SwitchbotModel .METER ,
@@ -107,50 +118,98 @@ class SwitchbotSupportedType(TypedDict):
107118 },
108119}
109120
121+ _SWITCHBOT_MODEL_TO_CHAR = {
122+ model_data ["modelName" ]: model_chr
123+ for model_chr , model_data in SUPPORTED_TYPES .items ()
124+ }
125+
126+ MODELS_BY_MANUFACTURER_DATA : dict [int , list [tuple [str , SwitchbotSupportedType ]]] = {
127+ mfr_id : [] for mfr_id in MFR_DATA_ORDER
128+ }
129+ for model_chr , model in SUPPORTED_TYPES .items ():
130+ if "manufacturer_id" in model :
131+ mfr_id = model ["manufacturer_id" ]
132+ MODELS_BY_MANUFACTURER_DATA [mfr_id ].append ((model_chr , model ))
133+
110134
111135def parse_advertisement_data (
112- device : BLEDevice , advertisement_data : AdvertisementData
136+ device : BLEDevice ,
137+ advertisement_data : AdvertisementData ,
138+ model : SwitchbotModel | None = None ,
113139) -> SwitchBotAdvertisement | None :
114140 """Parse advertisement data."""
115- _mgr_datas = list (advertisement_data .manufacturer_data .values ())
116141 service_data = advertisement_data .service_data
117142
118- if not service_data :
119- return None
120-
121143 _service_data = None
122144 for uuid in SERVICE_DATA_ORDER :
123145 if uuid in service_data :
124146 _service_data = service_data [uuid ]
125147 break
126148
127- if not _service_data :
128- return None
149+ _mfr_data = None
150+ _mfr_id = None
151+ for mfr_id in MFR_DATA_ORDER :
152+ if mfr_id in advertisement_data .manufacturer_data :
153+ _mfr_id = mfr_id
154+ _mfr_data = advertisement_data .manufacturer_data [mfr_id ]
155+ break
129156
130- _mfr_data = _mgr_datas [0 ] if _mgr_datas else None
157+ if _mfr_data is None and _service_data is None :
158+ return None
131159
132160 try :
133- data = _parse_data (_service_data , _mfr_data )
161+ data = _parse_data (
162+ "" .join (sorted (advertisement_data .service_uuids )),
163+ _service_data ,
164+ _mfr_data ,
165+ _mfr_id ,
166+ model ,
167+ )
134168 except Exception as err : # pylint: disable=broad-except
135169 _LOGGER .exception (
136170 "Failed to parse advertisement data: %s: %s" , advertisement_data , err
137171 )
138172 return None
139173
174+ if not data :
175+ return None
176+
140177 return SwitchBotAdvertisement (device .address , data , device , advertisement_data .rssi )
141178
142179
143180@lru_cache (maxsize = 128 )
144181def _parse_data (
145- _service_data : bytes , _mfr_data : bytes | None
182+ _service_uuids_str : str ,
183+ _service_data : bytes | None ,
184+ _mfr_data : bytes | None ,
185+ _mfr_id : int | None = None ,
186+ _switchbot_model : SwitchbotModel | None = None ,
146187) -> SwitchBotAdvertisement | None :
147188 """Parse advertisement data."""
148- _model = chr (_service_data [0 ] & 0b01111111 )
189+ _model = chr (_service_data [0 ] & 0b01111111 ) if _service_data else None
190+
191+ if not _model and _mfr_id and _mfr_id in MODELS_BY_MANUFACTURER_DATA :
192+ _service_uuids = set (_service_uuids_str .split ("," ))
193+ for model_chr , model_data in MODELS_BY_MANUFACTURER_DATA [_mfr_id ]:
194+ if model_data .get ("manufacturer_data_length" ) == len (_mfr_data ):
195+ _model = model_chr
196+ break
197+ if model_data .get ("service_uuids" , set ()).intersection (_service_uuids ):
198+ _model = model_chr
199+ break
200+
201+ if not _model and _switchbot_model and _switchbot_model in _SWITCHBOT_MODEL_TO_CHAR :
202+ _model = _SWITCHBOT_MODEL_TO_CHAR [_switchbot_model ]
203+
204+ if not _model :
205+ return None
206+
207+ _isEncrypted = bool (_service_data [0 ] & 0b10000000 ) if _service_data else False
149208 data = {
150209 "rawAdvData" : _service_data ,
151210 "data" : {},
152211 "model" : _model ,
153- "isEncrypted" : bool ( _service_data [ 0 ] & 0b10000000 ) ,
212+ "isEncrypted" : _isEncrypted ,
154213 }
155214
156215 type_data = SUPPORTED_TYPES .get (_model )
0 commit comments