Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add bluetooth tracking #891

Open
wants to merge 9 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 31 additions & 1 deletion custom_components/google_home/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,10 @@

from .api import GlocaltokensApiClient
from .const import (
BT_COORDINATOR,
BT_UPDATE_INTERVAL,
CONF_ANDROID_ID,
CONF_BT_UPDATE_INTERVAL,
CONF_MASTER_TOKEN,
CONF_UPDATE_INTERVAL,
DATA_CLIENT,
Expand All @@ -44,6 +47,9 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
android_id: str = entry.data.get(CONF_ANDROID_ID)
master_token: str = entry.data.get(CONF_MASTER_TOKEN)
update_interval: int = entry.options.get(CONF_UPDATE_INTERVAL, UPDATE_INTERVAL)
bt_update_interval: int = entry.options.get(
CONF_BT_UPDATE_INTERVAL, BT_UPDATE_INTERVAL
)

_LOGGER.debug(
"Coordinator update interval is: %s", timedelta(seconds=update_interval)
Expand All @@ -60,6 +66,7 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
master_token=master_token,
android_id=android_id,
zeroconf_instance=zeroconf_instance,
bt_update_interval=bt_update_interval,
)

coordinator = DataUpdateCoordinator(
Expand All @@ -69,20 +76,35 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
update_method=glocaltokens_client.update_google_devices_information,
update_interval=timedelta(seconds=update_interval),
)
bt_coordinator = DataUpdateCoordinator(
hass,
_LOGGER,
name=f"{DOMAIN}_bt",
update_method=glocaltokens_client.update_google_devices_bt_information,
update_interval=timedelta(seconds=bt_update_interval),
)

await coordinator.async_config_entry_first_refresh()
await bt_coordinator.async_config_entry_first_refresh()

hass.data[DOMAIN][entry.entry_id] = {
DATA_CLIENT: glocaltokens_client,
DATA_COORDINATOR: coordinator,
BT_COORDINATOR: bt_coordinator,
}

await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS)
entry.async_on_unload(entry.add_update_listener(update_listener))

entry.add_update_listener(async_update_entry)
return True


async def update_listener(hass: HomeAssistant, entry: ConfigEntry) -> None:
"""Handle options update."""
# Reload entry to update data
await hass.config_entries.async_reload(entry.entry_id)


async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
"""Handle removal of an entry."""
_LOGGER.debug("Unloading entry...")
Expand All @@ -96,10 +118,18 @@ async def async_update_entry(hass: HomeAssistant, entry: ConfigEntry) -> None:
"""Update config entry."""
_LOGGER.debug("Updating entry...")
update_interval: int = entry.options.get(CONF_UPDATE_INTERVAL, UPDATE_INTERVAL)
bt_update_interval: int = entry.options.get(
CONF_BT_UPDATE_INTERVAL, BT_UPDATE_INTERVAL
)
coordinator: DataUpdateCoordinator[list[GoogleHomeDevice]] = hass.data[DOMAIN][
entry.entry_id
][DATA_COORDINATOR]
coordinator.update_interval = timedelta(seconds=update_interval)
_LOGGER.debug(
"Coordinator update interval is: %s", timedelta(seconds=update_interval)
)
bt_coordinator: DataUpdateCoordinator[list[GoogleHomeDevice]] = hass.data[DOMAIN][
entry.entry_id
][BT_COORDINATOR]
bt_coordinator.update_interval = timedelta(seconds=bt_update_interval)
_LOGGER.debug("Coordinator update interval is: %s", timedelta(seconds=10))
88 changes: 87 additions & 1 deletion custom_components/google_home/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@
API_ENDPOINT_ALARM_DELETE,
API_ENDPOINT_ALARM_VOLUME,
API_ENDPOINT_ALARMS,
API_ENDPOINT_BLUETOOTH_RESULTS,
API_ENDPOINT_BLUETOOTH_SCAN,
API_ENDPOINT_DO_NOT_DISTURB,
API_ENDPOINT_REBOOT,
HEADER_CAST_LOCAL_AUTH,
Expand All @@ -33,7 +35,7 @@
)
from .exceptions import InvalidMasterToken
from .models import GoogleHomeDevice
from .types import AlarmJsonDict, JsonDict, TimerJsonDict
from .types import AlarmJsonDict, BTJsonDict, JsonDict, TimerJsonDict

_LOGGER: logging.Logger = logging.getLogger(__package__)

Expand All @@ -50,6 +52,7 @@ def __init__(
master_token: str | None = None,
android_id: str | None = None,
zeroconf_instance: Zeroconf | None = None,
bt_update_interval: int = 90,
):
"""Sample API Client."""
self.hass = hass
Expand All @@ -67,6 +70,7 @@ def __init__(
)
self.google_devices: list[GoogleHomeDevice] = []
self.zeroconf_instance = zeroconf_instance
self._bt_update_interval = bt_update_interval

async def async_get_master_token(self) -> str:
"""Get master API token"""
Expand Down Expand Up @@ -160,6 +164,35 @@ async def update_google_devices_information(self) -> list[GoogleHomeDevice]:
)
return coordinator_data

async def update_google_devices_bt_information(self) -> list[GoogleHomeDevice]:
"""Retrieves devices from glocaltokens and
fetches alarm/timer data from each of the device
"""

devices = await self.get_google_devices()

# Gives the user a warning if the device is offline
for device in devices:
if not device.ip_address and device.available:
device.available = False
_LOGGER.debug(
(
"Failed to fetch timers/alarms information "
"from device %s. We could not determine its IP address, "
"the device is either offline or is not compatible "
"Google Home device. Will try again later."
),
device.name,
)

coordinator_data = [
await self.update_bluetooth_list(device)
for device in devices
if device.ip_address and device.auth_token
]

return coordinator_data

async def collect_data_from_endpoints(
self, device: GoogleHomeDevice
) -> GoogleHomeDevice:
Expand Down Expand Up @@ -246,6 +279,59 @@ async def delete_alarm_or_timer(
response,
)

async def request_bluetooth_scan(self, device: GoogleHomeDevice) -> None:
"""Rescans for visible bluetooth devices."""

# Clear results should be set to False to prevent the data from disappearing
# if it was not seen in the most recent scan.
data = {
"enable": True,
"clear_results": False,
"timeout": self._bt_update_interval,
}

_LOGGER.debug(
"Trying to scan for Bluetooth device list on Google Home device %s",
device.name,
)

response = await self.request(
method="POST",
endpoint=API_ENDPOINT_BLUETOOTH_SCAN,
device=device,
data=data,
)

if response is not None:
_LOGGER.info(
"Successfully asked %s For Bluetooth device list scan.",
device.name,
)

async def update_bluetooth_list(self, device: GoogleHomeDevice) -> GoogleHomeDevice:
"""Fetches bluettooth items list from google device"""

_LOGGER.info("Reading bluetooth info for %s", device.name)

response = await self.request(
method="GET",
endpoint=API_ENDPOINT_BLUETOOTH_RESULTS,
device=device,
polling=True,
)

if response is not None:
device.set_bt(cast(list[BTJsonDict], response))

_LOGGER.debug(
"Successfully retrieved some data from %s. Response: %s",
device.name,
response,
)
await self.request_bluetooth_scan(device)

return device

async def reboot_google_device(self, device: GoogleHomeDevice) -> None:
"""Reboots a Google Home device if it supports this."""

Expand Down
Loading
Loading