From dfef6c3d281e0e9b80e82f1c23fdebe209fac8b0 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Tue, 6 Sep 2022 18:12:32 -0500 Subject: [PATCH] Small tweaks to improve performance of bluetooth matching (#77934) * Small tweaks to improve performance of bluetooth matching * Small tweaks to improve performance of bluetooth matching * cleanup --- homeassistant/components/bluetooth/match.py | 68 +++++++++++---------- 1 file changed, 36 insertions(+), 32 deletions(-) diff --git a/homeassistant/components/bluetooth/match.py b/homeassistant/components/bluetooth/match.py index 813acfc8cda..dd1c9c1fa3c 100644 --- a/homeassistant/components/bluetooth/match.py +++ b/homeassistant/components/bluetooth/match.py @@ -180,12 +180,20 @@ class BluetoothMatcherIndexBase(Generic[_T]): We put them in the bucket that they are most likely to match. """ + # Local name is the cheapest to match since its just a dict lookup if LOCAL_NAME in matcher: self.local_name.setdefault( _local_name_to_index_key(matcher[LOCAL_NAME]), [] ).append(matcher) return + # Manufacturer data is 2nd cheapest since its all ints + if MANUFACTURER_ID in matcher: + self.manufacturer_id.setdefault(matcher[MANUFACTURER_ID], []).append( + matcher + ) + return + if SERVICE_UUID in matcher: self.service_uuid.setdefault(matcher[SERVICE_UUID], []).append(matcher) return @@ -196,12 +204,6 @@ class BluetoothMatcherIndexBase(Generic[_T]): ) return - if MANUFACTURER_ID in matcher: - self.manufacturer_id.setdefault(matcher[MANUFACTURER_ID], []).append( - matcher - ) - return - def remove(self, matcher: _T) -> None: """Remove a matcher from the index. @@ -214,6 +216,10 @@ class BluetoothMatcherIndexBase(Generic[_T]): ) return + if MANUFACTURER_ID in matcher: + self.manufacturer_id[matcher[MANUFACTURER_ID]].remove(matcher) + return + if SERVICE_UUID in matcher: self.service_uuid[matcher[SERVICE_UUID]].remove(matcher) return @@ -222,10 +228,6 @@ class BluetoothMatcherIndexBase(Generic[_T]): self.service_data_uuid[matcher[SERVICE_DATA_UUID]].remove(matcher) return - if MANUFACTURER_ID in matcher: - self.manufacturer_id[matcher[MANUFACTURER_ID]].remove(matcher) - return - def build(self) -> None: """Rebuild the index sets.""" self.service_uuid_set = set(self.service_uuid) @@ -235,33 +237,36 @@ class BluetoothMatcherIndexBase(Generic[_T]): def match(self, service_info: BluetoothServiceInfoBleak) -> list[_T]: """Check for a match.""" matches = [] - if len(service_info.name) >= LOCAL_NAME_MIN_MATCH_LENGTH: + if service_info.name and len(service_info.name) >= LOCAL_NAME_MIN_MATCH_LENGTH: for matcher in self.local_name.get( service_info.name[:LOCAL_NAME_MIN_MATCH_LENGTH], [] ): if ble_device_matches(matcher, service_info): matches.append(matcher) - for service_data_uuid in self.service_data_uuid_set.intersection( - service_info.service_data - ): - for matcher in self.service_data_uuid[service_data_uuid]: - if ble_device_matches(matcher, service_info): - matches.append(matcher) + if self.service_data_uuid_set and service_info.service_data: + for service_data_uuid in self.service_data_uuid_set.intersection( + service_info.service_data + ): + for matcher in self.service_data_uuid[service_data_uuid]: + if ble_device_matches(matcher, service_info): + matches.append(matcher) - for manufacturer_id in self.manufacturer_id_set.intersection( - service_info.manufacturer_data - ): - for matcher in self.manufacturer_id[manufacturer_id]: - if ble_device_matches(matcher, service_info): - matches.append(matcher) + if self.manufacturer_id_set and service_info.manufacturer_data: + for manufacturer_id in self.manufacturer_id_set.intersection( + service_info.manufacturer_data + ): + for matcher in self.manufacturer_id[manufacturer_id]: + if ble_device_matches(matcher, service_info): + matches.append(matcher) - for service_uuid in self.service_uuid_set.intersection( - service_info.service_uuids - ): - for matcher in self.service_uuid[service_uuid]: - if ble_device_matches(matcher, service_info): - matches.append(matcher) + if self.service_uuid_set and service_info.service_uuids: + for service_uuid in self.service_uuid_set.intersection( + service_info.service_uuids + ): + for matcher in self.service_uuid[service_uuid]: + if ble_device_matches(matcher, service_info): + matches.append(matcher) return matches @@ -347,8 +352,6 @@ def ble_device_matches( service_info: BluetoothServiceInfoBleak, ) -> bool: """Check if a ble device and advertisement_data matches the matcher.""" - device = service_info.device - # Don't check address here since all callers already # check the address and we don't want to double check # since it would result in an unreachable reject case. @@ -379,7 +382,8 @@ def ble_device_matches( return False if (local_name := matcher.get(LOCAL_NAME)) and ( - (device_name := advertisement_data.local_name or device.name) is None + (device_name := advertisement_data.local_name or service_info.device.name) + is None or not _memorized_fnmatch( device_name, local_name,