|
5 | 5 | import re
|
6 | 6 | from collections.abc import Callable
|
7 | 7 | from datetime import datetime, timedelta
|
| 8 | +from pathlib import Path |
8 | 9 | from typing import TYPE_CHECKING, cast
|
9 | 10 |
|
10 | 11 | import voluptuous as vol
|
| 12 | +import yaml |
11 | 13 | from homeassistant.components import bluetooth
|
12 | 14 | from homeassistant.components.bluetooth import (
|
13 | 15 | MONOTONIC_TIME,
|
@@ -136,6 +138,16 @@ def __init__(
|
136 | 138 | self.stamp_last_update: float = 0 # Last time we ran an update, from MONOTONIC_TIME()
|
137 | 139 | self.stamp_last_prune: float = 0 # When we last pruned device list
|
138 | 140 |
|
| 141 | + self.member_uuids = {} |
| 142 | + def load_manufacturer_ids(): |
| 143 | + """Import yaml file containing manufacturer name mappings.""" |
| 144 | + file_path = Path(__file__).parent / "manufacturer_identification" / "member_uuids.yaml" |
| 145 | + |
| 146 | + with file_path.open("r") as f: |
| 147 | + member_uuids_yaml = yaml.safe_load(f)["uuids"] |
| 148 | + self.member_uuids = {hex(member["uuid"])[2:]: member["name"] for member in member_uuids_yaml} |
| 149 | + |
| 150 | + hass.async_add_executor_job(load_manufacturer_ids) |
139 | 151 | super().__init__(
|
140 | 152 | hass,
|
141 | 153 | _LOGGER,
|
@@ -581,11 +593,22 @@ async def _async_update_data(self):
|
581 | 593 | if device.local_name is None and service_info.advertisement.local_name:
|
582 | 594 | device.local_name = clean_charbuf(service_info.advertisement.local_name)
|
583 | 595 | device.manufacturer = device.manufacturer or service_info.manufacturer
|
| 596 | + if device.manufacturer is None: |
| 597 | + if ( |
| 598 | + service_info.service_uuids |
| 599 | + and (member_uuid := service_info.service_uuids[0][4:8]) in self.member_uuids |
| 600 | + ): |
| 601 | + # https://bitbucket.org/bluetooth-SIG/public/src/main/assigned_numbers/uuids/member_uuids.yaml |
| 602 | + device.manufacturer = self.member_uuids[member_uuid] |
584 | 603 | device.connectable = service_info.connectable
|
585 | 604 |
|
586 | 605 | # Try to make a nice name for prefname.
|
587 | 606 | if device.prefname is None or device.prefname.startswith(DOMAIN + "_"):
|
588 |
| - device.prefname = device.name or device.local_name or DOMAIN + "_" + slugify(device.address) |
| 607 | + if device.manufacturer: |
| 608 | + default_prefix = f"{slugify(device.manufacturer)}" |
| 609 | + else: |
| 610 | + default_prefix = DOMAIN |
| 611 | + device.prefname = device.name or device.local_name or f"{default_prefix}_{slugify(device.address)}" |
589 | 612 |
|
590 | 613 | # Work through the scanner entries...
|
591 | 614 | matched_scanners = bluetooth.async_scanner_devices_by_address(self.hass, service_info.address, False)
|
|
0 commit comments